lib/ceo/iterator.rb
module CEO
# Public: Delegates pagination and filters attributes.
#
# Examples
#
# iterator = CEO::Iterator.new(Apple, current_page: 2, filters: { only: [:id, :name] })
# iterator.total_pages
# # => 7
# iterator.current_page
# # => 2
class Iterator
# Public: Returns the total pages paginated.
attr_reader :total_pages
# model - The model to perform queries on.
# options - A hash of options for the iterator.
# query - An array of nested queries.
# filters - A hash of filters (only/except).
# current_page - The current page to be paginated.
def initialize(model, options = {})
@model = model
@options = options
@queries = @options[:query] || []
@filters = @options[:filters] || {}
end
# Public: Iterates through all parts.
#
# Yields or returns an Enumerator.
def each
if block_yielded?
all.each do |thing|
yield(thing)
end
else
all.to_enum
end
end
# Public: Returns a hash of titleized attributes mapped to their values.
#
# Uses pagination.
#
# options -
# current_page - currently paginated page
# per_page - # of things to list per page
#
# Returns a paginated hash of data.
def all
attribute_maps = [] # [{...}, {...}]
@pages = CEO::Paginator.new(
@model,
current_page: current_page,
per_page: @options.fetch(:per_page, 20)
)
self.total_pages = @pages.total_pages
@pages.each do |thing|
attr_object = {}
# TODO: Make all of this into a method.
# map first-level values to a hash
keys.each do |key|
attr_object[self.class.acronymize(key)] = thing[key.to_s]
end
# map nested values to a hash
@queries.each do |query|
attr_object.merge! query_eval(thing, query)
end
attribute_maps << attr_object
end
attribute_maps
end
# { 'Country Name' => 'South Korea' }
def query_eval(scope, query)
query_parts = query.split('.')
if query_parts.length > 2
title = self.class.acronymize(query_parts[-2..-1].join(' '))
resp = 'None' if scope.instance_eval(query_parts[0]).nil? || scope.instance_eval(query_parts[0..1].join('.')).nil?
elsif query_parts[-1] == 'name'
title = self.class.acronymize(query_parts.join(' '))
resp = 'None' if scope.instance_eval(query_parts[0]).nil?
else
title = self.class.acronymize query_parts[-1]
resp = 'None' if scope.instance_eval(query_parts[0]).nil?
end
resp = scope.instance_eval(query) unless resp == 'None'
{ title => resp }
end
# Public: Filters an enum based on a hash of params.
#
# things - An enum of things to filter.
# filters - A hash of filters (ALLOWED: [:only, :except]).
#
# Since 'only' and 'except' are mutually exclusive, 'only'
# will be prefered over 'except'.
#
# Returns a hash of filtered keys.
def self.filter(things, filters)
return self.only(things, filters[:only]) unless filters[:only].nil? || filters[:only].empty?
return self.except(things, filters[:except]) unless filters[:except].nil? || filters[:except].empty?
return things # if nothing to filter, just return the things
end
# Public: Blacklists keys based on an array.
#
# things - An array of keys to be filtered.
# blacklist - An array of keys that are not allowed.
#
# Examples
#
# blacklist = [:this, :that]
# except([:this, :that, :here, :now], blacklist)
# # => [:here, :now]
#
# Returns a filtered hash of keys.
def self.except(things, blacklist)
blacklist = blacklist.map(&:to_s)
things = things.map(&:to_s)
things.select { |thing| !blacklist.include? thing }
end
# Public: Whitelists keys based on an array.
#
# things - An array of keys to be filtered.
# whitelist - An array of keys that are only allowed.
#
# Examples
#
# whitelist = [:this, :that]
# only([:this, :that, :here, :now], whitelist)
# # => [:this, :that]
#
# Returns a filtered hash of keys.
def self.only(things, whitelist)
whitelist = whitelist.map(&:to_s)
things = things.map(&:to_s)
things.select { |thing| whitelist.include? thing }
end
# Public: Returns the current page.
def current_page
(@options[:current_page] || 1).to_i
end
# Public: Titleizes normal stuff, but upcases acronyms.
def self.acronymize(key)
parsed_key = key.to_s.titleize
parsed_key = parsed_key.gsub 'Id', 'ID'
parsed_key.gsub 'Iata Code', 'IATA'
end
private
attr_writer :total_pages
def keys
singleton_class.send(:filter, @model.new.attributes.keys, @filters)
end
end
end