require 'stockboy/configuration'
require 'stockboy/exceptions'
require 'stockboy/configurator'
require 'stockboy/template_file'
require 'stockboy/filter_chain'
require 'stockboy/candidate_record'
module Stockboy
class Job
attr_accessor :provider
attr_accessor :reader
attr_reader :attributes
attr_reader :filters
attr_reader :triggers
attr_reader :records
attr_reader :unfiltered_records
attr_reader :all_records
def initialize(params={}, &block)
@provider = params[:provider]
@reader = params[:reader]
@attributes = params[:attributes] || AttributeMap.new
@filters = FilterChain.new params[:filters]
@triggers = Hash.new { |h,k| h[k] = [] }
@triggers.replace params[:triggers] if params[:triggers]
yield self if block_given?
reset
end
def self.define(template_name)
return nil unless template = TemplateFile.read(template_name)
job = Configurator.new(template, TemplateFile.find(template_name)).to_job
yield job if block_given?
job
end
def process
with_query_caching do
load_records
yield @records if block_given?
end
provider.errors.empty?
end
def data(&block)
provider.data(&block)
end
def data?(reduction=:all?)
provider.data?(reduction)
end
def total_records
@all_records.size
end
def record_counts
@records.reduce(Hash.new) { |a, (k,v)| a[k] = v.size; a }
end
def triggers=(new_triggers)
@triggers.replace new_triggers
end
def trigger(key, *args)
return nil unless triggers.key?(key)
triggers[key].each do |c|
c.call(self, *args)
end
end
def method_missing(name, *args)
if triggers.key?(name)
trigger(name, *args)
else
super
end
end
def filters=(new_filters)
@filters.replace new_filters
reset
@filters
end
def attributes=(new_attributes)
@attributes = new_attributes
reset
@attributes
end
def processed?
!!@processed
end
def inspect
prov = "provider=#{Providers.all.key(provider.class) || provider.class}"
read = "reader=#{Readers.all.key(reader.class) || reader.class}"
attr = "attributes=#{attributes.map(&:to)}"
filt = "filters=#{filters.keys}"
cnts = "record_counts=#{record_counts}"
"#<#{self.class}:#{self.object_id} #{prov}, #{read}, #{attr}, #{filt}, #{cnts}>"
end
private
def reset
@records = filters.reset
@all_records = []
@unfiltered_records = []
@processed = false
true
end
def load_records
reset
load_all_records
return unless provider.data?
partition_all_records
@processed = true
end
def load_all_records
each_reader_row do |row|
@all_records << CandidateRecord.new(row, @attributes)
end
end
def partition_all_records
@all_records.each do |record|
record_partition(record) << record
end
end
def each_reader_row
return to_enum(__method__) unless block_given?
with_provider_data do |data|
reader.parse(data).each do |row|
yield row
end
end
end
def with_provider_data
return to_enum(__method__) unless block_given?
yielded = nil
provider.data do |data|
if data
yielded = true
yield data
end
end
return if yielded
yield(provider.data) if provider.data
end
def record_partition(record)
if key = record.partition(filters)
@records[key]
else
@unfiltered_records
end
end
def with_query_caching(&block)
if defined? ActiveRecord
ActiveRecord::Base.cache(&block)
else
yield
end
end
end
end