require "datagrid/utils"
module Datagrid
class OrderUnsupported < StandardError
end
module Columns
require "datagrid/columns/column"
def self.included(base)
base.extend ClassMethods
base.class_eval do
include Datagrid::Core
datagrid_attribute :order
datagrid_attribute :descending do |value|
Datagrid::Utils.booleanize(value)
end
end
base.send :include, InstanceMethods
end # self.included
module ClassMethods
def columns
@columns ||= []
end
# Defines column that will be used to display data.
#
# Example:
#
# class UserGrid
# include Datagrid
#
# scope do
# User.order("users.created_at desc").includes(:group)
# end
#
# column(:name)
# column(:group, :order => "groups.name")
# self.group.name
# end
# column(:active, :header => "Activated") do |user|
# !user.disabled
# end
#
# end
#
# Each column will be used to generate data.
# In order to create grid that display all users:
#
# grid = UserGrid.new
# grid.rows
# grid.header # => ["Group", "Name", "Disabled"]
# grid.rows # => [
# # ["Steve", "Spammers", true],
# # [ "John", "Spoilers", true],
# # ["Berry", "Good people", false]
# # ]
# grid.data # => Header & Rows
#
# = Column value
#
# Column value can be defined by passing a block to #column method.
# If no block given column it is generated automatically by sending column name method to model.
# The block could have zero arguments(instance_eval) or one argument that is model object.
#
# = Column options
#
# The following options can be passed to column definition:
#
# * :order - an order SQL that should be used to sort by this column.
# Default: report column name if there is database column with this name.
# * :order_desc - descending order expression from this column. Default: "#{order} desc".
#
# TODO: frontend options description
#
# = Columns order
#
# Each column supports :order option that is used to specify SQL to sort data by the given column.
# In order to specify order the following attributes are used:
#
# * :order - column name to sort with as Symbol. Default: nil.
# * :descending - if true descending suffix is added to specified order. Default: false.
#
# Example:
#
# grid = UserGrid.new(:order => :group, :descending => true)
# grid.assets # => Return assets ordered by :group column descending
#
def column(name, options = {}, &block)
check_scope_defined!("Scope should be defined before columns")
block ||= lambda do |model|
model.send(name)
end
self.columns << Datagrid::Columns::Column.new(self, name, options, &block)
end
end # ClassMethods
module InstanceMethods
def header
self.class.columns.map(&:header)
end
def row_for(asset)
self.class.columns.map do |column|
column.value(asset)
end
end
def hash_for(asset)
result = {}
self.class.columns.each do |column|
result[column.name] = column.value(asset)
end
result
end
def rows
self.assets.map do |asset|
self.row_for(asset)
end
end
def data
self.rows.unshift(self.header)
end
def data_hash
self.assets.map do |asset|
hash_for(asset)
end
end
def assets
result = super
if self.order
column = column_by_name(self.order)
raise Datagrid::OrderUnsupported, "Can not sort #{self.inspect} by #{name.inspect}" unless column
result = result.order(self.descending ? column.desc_order : column.order)
end
result
end
def to_csv(options = {})
require "fastercsv"
FasterCSV.generate(
{:headers => self.header, :write_headers => true}.merge(options)
) do |csv|
self.rows.each do |row|
csv << row
end
end
end
def columns
self.class.columns
end
def column_by_name(name)
self.columns.find do |col|
col.name.to_sym == name.to_sym
end
end
end # InstanceMethods
end
end