class Resque::Reports::BaseReport

Class describes base report class for inheritance. BaseReport successor must implement “write(io, force)” method and may specify file extension with “extension” method call example:

class CustomTypeReport < Resque::Reports::BaseReport
  extension :type # specify that report file must ends
                  # with '.type', e.g. 'abc.type'

  # Method specifies how to output report data
  def write(io, force)
    io << 'Hello World!'
  end
end

BaseReport provides following DSL, example:

class CustomReport < CustomTypeReport
  # include Resque::Reports::Common::BatchedReport
  #   overrides data retrieving to achieve batching
  #   if included 'source :select_data' becomes needless

  queue :custom_reports # Resque queue name
  source :select_data # method called to retrieve report data
  encoding UTF8 # file encoding
  expire_in 86_400 # cache time of the file, default: 86_400

  # Specify in which directory to keep this type files
  directory File.join(Dir.tmpdir, 'resque-reports')

  # Describe table using 'column' method
  table do |element|
    column 'Column 1 Header', :decorate_one
    column 'Column 2 Header', decorate_two(element[1])
    column 'Column 3 Header', 'Column 3 Cell'
    column 'Column 4 Header', :formatted_four, formatter: :just_cute
  end

  # Class initialize if needed
  # NOTE: must be used instead of define 'initialize' method
  # Default behaviour is to receive in *args Hash with report attributes
  # like: CustomReport.new(main_param: 'value') => calls send(:main_param=, 'value')
  create do |param|
    @main_param = param
  end

  def self.just_cute_formatter(column_value)
    "I'm so cute #{column_value}"
  end

  # decorate method, called by symbol-name
  def decorate_one(element)
    "decorate_one: #{element[0]}"
  end

  # decorate method, called directly when filling cell
  def decorate_two(text)
    "decorate_two: #{text}"
  end

  # method returns report data Enumerable
  def select_data
    [[0, 'text0'], [1, 'text1']]
  end
end

Constants

DEFAULT_QUEUE

Attributes

_directory[RW]
_encoding[RW]
_expire_in[RW]
_extension[RW]
_output_filename[RW]
_queue[RW]
create_block[R]
directory[RW]
encoding[RW]
expire_in[RW]
extension[RW]
output_filename[RW]
queue[RW]
super_extension[RW]
job_id[R]

Public Class Methods

build(options = {}) click to toggle source
# File lib/resque/reports/base_report.rb, line 152
def self.build(options = {})
  in_background = options.delete(:background)
  force = options.delete(:force)
  report = new(options)

  in_background ? report.bg_build(force) : report.build(force)

  report
end
new(*args) click to toggle source
# File lib/resque/reports/base_report.rb, line 166
def initialize(*args)
  # TODO: Check consistance, fail if user initialized wrong object
  set_instance(self)

  if create_block
    define_singleton_method(:create_dispatch, create_block)
    create_dispatch(*args)
  else
    if args && (attrs_hash = args.first) && attrs_hash.is_a?(Hash)
      attrs_hash.each { |name, value| send("#{name}=", value) }
    end
  end

  @args = args

  init_cache_file
  init_table
end

Protected Class Methods

create(&block) click to toggle source
# File lib/resque/reports/base_report.rb, line 99
def create(&block)
  @create_block = block
end
encoded_string(obj) click to toggle source

override for Extenstions::TableBuilding, to use custom encoding

# File lib/resque/reports/base_report.rb, line 104
def encoded_string(obj)
  obj.to_s.encode(_encoding,
                  invalid: :replace,
                  undef: :replace)
end
method_missing(method_name, *args, &block) click to toggle source
Calls superclass method
# File lib/resque/reports/base_report.rb, line 114
def method_missing(method_name, *args, &block)
  if @instance.respond_to?(method_name)
    @instance.send(method_name, *args, &block)
  else
    super
  end
end
respond_to?(method, include_private = false) click to toggle source
Calls superclass method
# File lib/resque/reports/base_report.rb, line 122
def respond_to?(method, include_private = false)
  super || @instance.respond_to?(method, include_private)
end
set_instance(obj) click to toggle source
# File lib/resque/reports/base_report.rb, line 95
def set_instance(obj)
  @instance = obj
end

Public Instance Methods

bg_build(force = false) click to toggle source

Builds report in background, returns job_id, to watch progress

# File lib/resque/reports/base_report.rb, line 193
def bg_build(force = false)
  report_class = self.class.to_s

  args_json = [*@args, force].to_json

  # Check report if it already in progress and tring return its job_id...
  @job_id = ReportJob.enqueued?(report_class, args_json).try(:meta_id)

  # ...and start new job otherwise
  @job_id ||= ReportJob.enqueue_to(_queue || DEFAULT_QUEUE, report_class, args_json).try(:meta_id)
end
build(force = false) click to toggle source

Builds report synchronously

# File lib/resque/reports/base_report.rb, line 186
def build(force = false)
  init_table if force

  @cache_file.open(force) { |file| write(file, force) }
end

Protected Instance Methods

init_cache_file() click to toggle source
# File lib/resque/reports/base_report.rb, line 207
def init_cache_file
  self._extension = super_extension || DEFAULT_EXTENSION
  options = {coding: _encoding, expire_in: _expire_in}

  @cache_file = CacheFile.new(_directory,
                              generate_filename(@args, _extension),
                              options)
end
write(io, force) click to toggle source

Method specifies how to output report data @param [IO] io stream for output @param [true, false] force write to output or skip due its existance

# File lib/resque/reports/base_report.rb, line 219
def write(io, force)
  # You must use ancestor methods to work with report data:
  # 1) data_size => returns source data size (calls #count on data
  #                 retrieved from 'source')
  # 2) data_each => yields given block for each source data element
  # 3) build_table_header => returns Array of report column names
  # 4) build_table_row(object) => returns Array of report cell
  #                               values (same order as header)
  # 5) progress_message(progress,
  #                    total) => call to iterate job progress
  # 6) error_message(error) => call to handle error in job
  #
  # HINT: You may override data_size and data_each, to retrieve them
  #       effectively
  fail NotImplementedError
end