lib/pivot_table/grid.rb
module PivotTable
class Grid
attr_accessor :source_data, :row_name, :column_name, :value_name, :field_name
attr_reader :columns, :rows, :data_grid, :configuration
DEFAULT_OPTIONS = {
:sort => true
}
def initialize(opts = {}, &block)
yield(self) if block_given?
@configuration = Configuration.new(DEFAULT_OPTIONS.merge(opts))
end
def build
populate_grid
build_rows
build_columns
self
end
def build_rows
@rows = []
data_grid.each_with_index do |data, index|
rows << Row.new(
header: row_headers[index],
data: data,
value_name: value_name,
orthogonal_headers: column_headers
)
end
end
def build_columns
@columns = []
data_grid.transpose.each_with_index do |data, index|
columns << Column.new(
header: column_headers[index],
data: data,
value_name: value_name,
orthogonal_headers: row_headers
)
end
end
def column_headers
@column_headers ||= headers column_name
end
def row_headers
@row_headers ||= headers row_name
end
def column_totals
columns.map { |c| c.total }
end
def row_totals
rows.map { |r| r.total }
end
def grand_total
column_totals.inject(0) { |t, x| t + x }
end
def prepare_grid
@data_grid = []
row_headers.count.times do
data_grid << column_headers.count.times.inject([]) { |col| col << nil }
end
data_grid
end
def populate_grid
prepare_grid
row_headers.each_with_index do |row, row_index|
data_grid[row_index] = build_data_row(row)
end
data_grid
end
private
def headers(method)
hdrs = source_data.collect { |c| c.send method }.uniq
configuration.sort ? hdrs.sort : hdrs
end
def build_data_row(row)
current_row = []
column_headers.each_with_index do |col, col_index|
current_row[col_index] = derive_row_value(row, col)
end
current_row
end
def find_data_item(row, col)
source_data.find do |item|
item.send(row_name) == row && item.send(column_name) == col
end
end
def derive_row_value(row, col)
data_item = find_data_item(row, col)
if has_field_name?(data_item)
data_item.send(field_name)
else
data_item
end
end
def has_field_name?(data_item)
!!(field_name && data_item.respond_to?(field_name))
end
end
end