class Repor::Report
Attributes
params[R]
Public Class Methods
aggregator(name, aggregator_class, opts = {})
click to toggle source
# File lib/repor/report.rb, line 110 def aggregator(name, aggregator_class, opts = {}) aggregators[name.to_sym] = { axis_class: aggregator_class, opts: opts } end
aggregators()
click to toggle source
# File lib/repor/report.rb, line 106 def aggregators @aggregators ||= {} end
autoreport_association_name_columns(reflection)
click to toggle source
override this to change which columns of the association are used to auto-label it
# File lib/repor/report.rb, line 182 def autoreport_association_name_columns(reflection) %w(name email title) end
autoreport_column(column)
click to toggle source
can override this method to skip or change certain column declarations
# File lib/repor/report.rb, line 159 def autoreport_column(column) return if column.name == 'id' belongs_to_ref = klass.reflections.find { |_, a| a.foreign_key == column.name } if belongs_to_ref name, ref = belongs_to_ref name_col = (ref.klass.column_names & autoreport_association_name_columns(ref)).first if name_col name_expr = "#{ref.klass.table_name}.#{name_col}" category_dimension name, expression: name_expr, relation: ->(r) { r.joins(name) } else category_dimension column.name end elsif column.cast_type.type == :datetime time_dimension column.name elsif column.cast_type.number? number_dimension column.name else category_dimension column.name end end
autoreport_on(class_or_name)
click to toggle source
autoreporting will automatically define dimensions based on columns
# File lib/repor/report.rb, line 152 def autoreport_on(class_or_name) report_on class_or_name klass.columns.each(&method(:autoreport_column)) count_aggregator :count if aggregators.blank? end
default_class()
click to toggle source
# File lib/repor/report.rb, line 130 def default_class self.name.demodulize.sub(/Report$/, '').constantize end
dimension(name, dimension_class, opts = {})
click to toggle source
# File lib/repor/report.rb, line 102 def dimension(name, dimension_class, opts = {}) dimensions[name.to_sym] = { axis_class: dimension_class, opts: opts } end
dimensions()
click to toggle source
# File lib/repor/report.rb, line 98 def dimensions @dimensions ||= {} end
inherited(subclass)
click to toggle source
ensure subclasses gain any aggregators or dimensions defined on their parents
# File lib/repor/report.rb, line 145 def inherited(subclass) instance_values.each do |ivar, ival| subclass.instance_variable_set(:"@#{ivar}", ival.dup) end end
klass()
click to toggle source
# File lib/repor/report.rb, line 134 def klass @klass ||= default_class rescue NameError raise NameError, "must specify a class to report on, e.g. `report_on Post`" end
new(params = {})
click to toggle source
# File lib/repor/report.rb, line 7 def initialize(params = {}) @params = params.deep_symbolize_keys.deep_dup deep_strip_blanks(@params) unless @params[:strip_blanks] == false validate_params! end
report_on(class_or_name)
click to toggle source
# File lib/repor/report.rb, line 140 def report_on(class_or_name) @klass = class_or_name.to_s.constantize end
Public Instance Methods
aggregator()
click to toggle source
# File lib/repor/report.rb, line 25 def aggregator @aggregator ||= aggregators[aggregator_name] end
aggregator_name()
click to toggle source
# File lib/repor/report.rb, line 21 def aggregator_name params.fetch(:aggregator, default_aggregator_name).to_sym end
aggregators()
click to toggle source
# File lib/repor/report.rb, line 17 def aggregators @aggregators ||= build_axes(self.class.aggregators) end
base_relation()
click to toggle source
# File lib/repor/report.rb, line 47 def base_relation params.fetch(:relation, klass.all) end
data()
click to toggle source
# File lib/repor/report.rb, line 93 def data nested_data end
dimensions()
click to toggle source
# File lib/repor/report.rb, line 13 def dimensions @dimensions ||= build_axes(self.class.dimensions) end
filters()
click to toggle source
# File lib/repor/report.rb, line 39 def filters @filters ||= dimensions.values.select(&:filtering?) end
flat_data()
click to toggle source
flat hash of { [x1, x2, x3] => y }
# File lib/repor/report.rb, line 83 def flat_data @flat_data ||= Hash[group_values.map { |x| [x, raw_data[x]] }] end
group_values()
click to toggle source
# File lib/repor/report.rb, line 77 def group_values @group_values ||= all_combinations_of(groupers.map(&:group_values)) end
grouper_names()
click to toggle source
# File lib/repor/report.rb, line 29 def grouper_names names = params.fetch(:groupers, default_grouper_names) names = names.is_a?(Hash) ? names.values : Array.wrap(names) names.map(&:to_sym) end
groupers()
click to toggle source
# File lib/repor/report.rb, line 35 def groupers @groupers ||= dimensions.values_at(*grouper_names) end
groups()
click to toggle source
# File lib/repor/report.rb, line 67 def groups @groups ||= groupers.reduce(records) do |relation, dimension| dimension.group(relation) end end
nested_data()
click to toggle source
nested array of
- { key: x3, values: [{ key: x2, values: [{ key: x1, value: y }
-
}] }]
# File lib/repor/report.rb, line 89 def nested_data @nested_data ||= nest_data end
raw_data()
click to toggle source
# File lib/repor/report.rb, line 73 def raw_data @raw_data ||= aggregator.aggregate(groups) end
records()
click to toggle source
# File lib/repor/report.rb, line 61 def records @records ||= filters.reduce(relation) do |relation, dimension| dimension.filter(relation) end end
relation()
click to toggle source
# File lib/repor/report.rb, line 55 def relation @relation ||= relators.reduce(base_relation) do |relation, dimension| dimension.relate(relation) end end
relators()
click to toggle source
# File lib/repor/report.rb, line 43 def relators filters | groupers end
table_name()
click to toggle source
# File lib/repor/report.rb, line 51 def table_name klass.table_name end
Private Instance Methods
all_combinations_of(values)
click to toggle source
# File lib/repor/report.rb, line 191 def all_combinations_of(values) values[0].product(*values[1..-1]) end
build_axes(axes)
click to toggle source
# File lib/repor/report.rb, line 187 def build_axes(axes) axes.map { |name, h| [name, h[:axis_class].new(name, self, h[:opts])] }.to_h end
deep_strip_blanks(hash, depth = 0)
click to toggle source
# File lib/repor/report.rb, line 250 def deep_strip_blanks(hash, depth = 0) raise "very deep hash or, more likely, internal error" if depth > 100 hash.delete_if do |key, value| strippable_blank?( case value when Hash then deep_strip_blanks(value, depth + 1) when Array then value.reject!(&method(:strippable_blank?)) else value end ) end end
default_aggregator_name()
click to toggle source
# File lib/repor/report.rb, line 235 def default_aggregator_name aggregators.keys.first end
default_grouper_names()
click to toggle source
# File lib/repor/report.rb, line 239 def default_grouper_names [dimensions.keys.first] end
invalid_param!(param_key, message)
click to toggle source
# File lib/repor/report.rb, line 231 def invalid_param!(param_key, message) raise InvalidParamsError, "Invalid value for params[:#{param_key}]: #{message}" end
nest_data(groupers=self.groupers, prefix=[])
click to toggle source
# File lib/repor/report.rb, line 195 def nest_data(groupers=self.groupers, prefix=[]) head, rest = groupers.last, groupers[0..-2] head.group_values.map do |x| if rest.any? { key: x, values: nest_data(rest, [x]+prefix) } else { key: x, value: raw_data[([x]+prefix)] } end end end
strippable_blank?(value)
click to toggle source
# File lib/repor/report.rb, line 243 def strippable_blank?(value) case value when String, Array, Hash then value.blank? else false end end
validate_params!()
click to toggle source
# File lib/repor/report.rb, line 206 def validate_params! incomplete_msg = "You must declare at least one aggregator and one " \ "dimension to initialize a report. See the README for more details." if aggregators.blank? raise Repor::InvalidParamsError, "#{self.class} doesn't have any " \ "aggregators declared! #{incomplete_msg}" end if dimensions.blank? raise Repor::InvalidParamsError, "#{self.class} doesn't have any " \ "dimensions declared! #{incomplete_msg}" end unless aggregator.present? invalid_param!(:aggregator, "#{aggregator_name} is not a valid aggregator (should be in #{aggregators.keys})") end unless groupers.all?(&:present?) invalid_param!(:groupers, "one of #{grouper_names} is not a valid dimension (should all be in #{dimensions.keys})") end end