effective_datatables 2.12.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +632 -512
- data/app/assets/javascripts/dataTables/buttons/buttons.html5.js +176 -177
- data/app/assets/javascripts/dataTables/buttons/buttons.print.js +2 -0
- data/app/assets/javascripts/dataTables/buttons/dataTables.buttons.js +14 -14
- data/app/assets/javascripts/dataTables/dataTables.bootstrap.js +1 -1
- data/app/assets/javascripts/dataTables/jquery.dataTables.js +246 -217
- data/app/assets/javascripts/effective_datatables.js +2 -3
- data/app/assets/javascripts/effective_datatables/events.js.coffee +7 -0
- data/app/assets/javascripts/effective_datatables/filters.js.coffee +6 -0
- data/app/assets/javascripts/effective_datatables/initialize.js.coffee +42 -39
- data/app/assets/javascripts/effective_datatables/reset.js.coffee +7 -0
- data/app/assets/javascripts/vendor/jquery.delayedChange.js +1 -1
- data/app/assets/stylesheets/dataTables/dataTables.bootstrap.css +0 -1
- data/app/assets/stylesheets/effective_datatables.scss +1 -2
- data/app/assets/stylesheets/effective_datatables/{_scopes.scss → _filters.scss} +1 -1
- data/app/assets/stylesheets/effective_datatables/_overrides.scss +1 -1
- data/app/controllers/effective/datatables_controller.rb +2 -4
- data/app/helpers/effective_datatables_helper.rb +56 -91
- data/app/helpers/effective_datatables_private_helper.rb +55 -64
- data/app/models/effective/datatable.rb +103 -177
- data/app/models/effective/datatable_column.rb +28 -0
- data/app/models/effective/datatable_column_tool.rb +110 -0
- data/app/models/effective/datatable_dsl_tool.rb +28 -0
- data/app/models/effective/datatable_value_tool.rb +142 -0
- data/app/models/effective/effective_datatable/attributes.rb +25 -0
- data/app/models/effective/effective_datatable/collection.rb +38 -0
- data/app/models/effective/effective_datatable/compute.rb +154 -0
- data/app/models/effective/effective_datatable/cookie.rb +29 -0
- data/app/models/effective/effective_datatable/dsl.rb +14 -8
- data/app/models/effective/effective_datatable/dsl/bulk_actions.rb +5 -6
- data/app/models/effective/effective_datatable/dsl/charts.rb +7 -9
- data/app/models/effective/effective_datatable/dsl/datatable.rb +107 -57
- data/app/models/effective/effective_datatable/dsl/filters.rb +50 -0
- data/app/models/effective/effective_datatable/format.rb +157 -0
- data/app/models/effective/effective_datatable/hooks.rb +0 -18
- data/app/models/effective/effective_datatable/params.rb +34 -0
- data/app/models/effective/effective_datatable/resource.rb +108 -0
- data/app/models/effective/effective_datatable/state.rb +178 -0
- data/app/views/effective/datatables/_actions_column.html.haml +9 -42
- data/app/views/effective/datatables/_bulk_actions_column.html.haml +1 -1
- data/app/views/effective/datatables/_bulk_actions_dropdown.html.haml +2 -3
- data/app/views/effective/datatables/_chart.html.haml +1 -1
- data/app/views/effective/datatables/_datatable.html.haml +7 -25
- data/app/views/effective/datatables/_filters.html.haml +21 -0
- data/app/views/effective/datatables/_reset.html.haml +2 -0
- data/app/views/effective/datatables/_resource_column.html.haml +8 -0
- data/app/views/effective/datatables/index.html.haml +0 -1
- data/config/effective_datatables.rb +9 -32
- data/lib/effective_datatables.rb +2 -6
- data/lib/effective_datatables/engine.rb +1 -1
- data/lib/effective_datatables/version.rb +1 -1
- data/lib/generators/effective_datatables/install_generator.rb +2 -2
- metadata +39 -19
- data/app/assets/javascripts/dataTables/colreorder/dataTables.colReorder.js +0 -27
- data/app/assets/javascripts/dataTables/jszip/jszip.js +0 -9155
- data/app/assets/javascripts/effective_datatables/scopes.js.coffee +0 -9
- data/app/models/effective/active_record_datatable_tool.rb +0 -242
- data/app/models/effective/array_datatable_tool.rb +0 -97
- data/app/models/effective/effective_datatable/ajax.rb +0 -101
- data/app/models/effective/effective_datatable/charts.rb +0 -20
- data/app/models/effective/effective_datatable/dsl/scopes.rb +0 -23
- data/app/models/effective/effective_datatable/helpers.rb +0 -24
- data/app/models/effective/effective_datatable/options.rb +0 -309
- data/app/models/effective/effective_datatable/rendering.rb +0 -365
- data/app/views/effective/datatables/_scopes.html.haml +0 -21
@@ -0,0 +1,28 @@
|
|
1
|
+
module Effective
|
2
|
+
class DatatableColumn
|
3
|
+
attr_accessor :attributes
|
4
|
+
|
5
|
+
delegate :[], :[]=, to: :attributes
|
6
|
+
|
7
|
+
def initialize(attributes)
|
8
|
+
@attributes = attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
self[:name]
|
13
|
+
end
|
14
|
+
|
15
|
+
def format(&block)
|
16
|
+
@attributes[:format] = block; self
|
17
|
+
end
|
18
|
+
|
19
|
+
def search(&block)
|
20
|
+
@attributes[:search_method] = block; self
|
21
|
+
end
|
22
|
+
|
23
|
+
def sort(&block)
|
24
|
+
@attributes[:sort_method] = block; self
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Effective
|
2
|
+
class DatatableColumnTool
|
3
|
+
attr_reader :datatable
|
4
|
+
attr_reader :columns
|
5
|
+
|
6
|
+
def initialize(datatable)
|
7
|
+
@datatable = datatable
|
8
|
+
|
9
|
+
if datatable.active_record_collection?
|
10
|
+
@columns = datatable.columns.select { |_, col| col[:sql_column].present? }
|
11
|
+
else
|
12
|
+
@columns = {}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def scoped
|
17
|
+
@scoped ||= datatable._scopes[datatable.scope]
|
18
|
+
end
|
19
|
+
|
20
|
+
def searched
|
21
|
+
@searched ||= datatable.search.select { |name, _| columns.key?(name) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def ordered
|
25
|
+
@ordered_column ||= columns[datatable.order_name]
|
26
|
+
end
|
27
|
+
|
28
|
+
def order(collection)
|
29
|
+
return collection unless ordered.present?
|
30
|
+
|
31
|
+
collection = if ordered[:sort_method]
|
32
|
+
datatable.dsl_tool.instance_exec(collection, datatable.order_direction, ordered, ordered[:sql_column], &ordered[:sort_method])
|
33
|
+
else
|
34
|
+
order_column(collection, datatable.order_direction, ordered, ordered[:sql_column])
|
35
|
+
end
|
36
|
+
|
37
|
+
raise 'sort method must return an ActiveRecord::Relation object' unless collection.kind_of?(ActiveRecord::Relation)
|
38
|
+
|
39
|
+
collection
|
40
|
+
end
|
41
|
+
|
42
|
+
def order_column(collection, direction, column, sql_column)
|
43
|
+
Rails.logger.info "COLUMN TOOL: order_column #{column.to_s} #{direction} #{sql_column}"
|
44
|
+
|
45
|
+
if column[:sql_as_column]
|
46
|
+
collection.order("#{sql_column} #{datatable.resource.sql_direction(direction)}")
|
47
|
+
else
|
48
|
+
Effective::Resource.new(collection)
|
49
|
+
.order(column[:name], direction, as: column[:as], sort: column[:sort], sql_column: column[:sql_column])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def scope(collection)
|
54
|
+
return collection unless scoped.present?
|
55
|
+
|
56
|
+
collection.send(scoped[:name], *scoped[:args])
|
57
|
+
end
|
58
|
+
|
59
|
+
def search(collection)
|
60
|
+
searched.each do |name, value|
|
61
|
+
column = columns[name]
|
62
|
+
|
63
|
+
collection = if column[:search_method]
|
64
|
+
datatable.dsl_tool.instance_exec(collection, value, column, column[:sql_column], &column[:search_method])
|
65
|
+
else
|
66
|
+
search_column(collection, value, column, column[:sql_column])
|
67
|
+
end
|
68
|
+
|
69
|
+
raise 'search method must return an ActiveRecord::Relation object' unless collection.kind_of?(ActiveRecord::Relation)
|
70
|
+
end
|
71
|
+
|
72
|
+
collection
|
73
|
+
end
|
74
|
+
|
75
|
+
def search_column(collection, value, column, sql_column)
|
76
|
+
Rails.logger.info "COLUMN TOOL: search_column #{column.to_s} #{value} #{sql_column}"
|
77
|
+
|
78
|
+
Effective::Resource.new(collection)
|
79
|
+
.search(column[:name], value, as: column[:as], fuzzy: column[:search][:fuzzy], sql_column: sql_column)
|
80
|
+
end
|
81
|
+
|
82
|
+
def paginate(collection)
|
83
|
+
collection.page(datatable.page).per(datatable.per_page)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Not every ActiveRecord query will work when calling the simple .count
|
87
|
+
# Custom selects:
|
88
|
+
# User.select(:email, :first_name).count will throw an error
|
89
|
+
# Grouped Queries:
|
90
|
+
# User.all.group(:email).count will return a Hash
|
91
|
+
def size(collection)
|
92
|
+
count = (collection.size rescue nil)
|
93
|
+
|
94
|
+
case count
|
95
|
+
when Integer
|
96
|
+
count
|
97
|
+
when Hash
|
98
|
+
count.size # This represents the number of displayed datatable rows, not the sum all groups (which might be more)
|
99
|
+
else
|
100
|
+
if collection.klass.connection.respond_to?(:unprepared_statement)
|
101
|
+
collection_sql = collection.klass.connection.unprepared_statement { collection.to_sql }
|
102
|
+
(collection.klass.connection.exec_query("SELECT COUNT(*) FROM (#{collection_sql}) AS datatables_total_count").rows[0][0] rescue 1)
|
103
|
+
else
|
104
|
+
(collection.klass.connection.exec_query("SELECT COUNT(*) FROM (#{collection.to_sql}) AS datatables_total_count").rows[0][0] rescue 1)
|
105
|
+
end.to_i
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Effective
|
2
|
+
|
3
|
+
class DatatableDslTool
|
4
|
+
attr_reader :datatable
|
5
|
+
attr_reader :view
|
6
|
+
|
7
|
+
include Effective::EffectiveDatatable::Dsl::BulkActions
|
8
|
+
include Effective::EffectiveDatatable::Dsl::Charts
|
9
|
+
include Effective::EffectiveDatatable::Dsl::Datatable
|
10
|
+
include Effective::EffectiveDatatable::Dsl::Filters
|
11
|
+
|
12
|
+
def initialize(datatable)
|
13
|
+
@datatable = datatable
|
14
|
+
@view = datatable.view
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(method, *args)
|
18
|
+
if datatable.respond_to?(method)
|
19
|
+
datatable.send(method, *args)
|
20
|
+
elsif view.respond_to?(method)
|
21
|
+
view.send(method, *args)
|
22
|
+
else
|
23
|
+
super
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,142 @@
|
|
1
|
+
module Effective
|
2
|
+
# The collection is an Array of Arrays
|
3
|
+
class DatatableValueTool
|
4
|
+
attr_reader :datatable
|
5
|
+
attr_reader :columns
|
6
|
+
|
7
|
+
def initialize(datatable)
|
8
|
+
@datatable = datatable
|
9
|
+
|
10
|
+
if datatable.array_collection?
|
11
|
+
@columns = datatable.columns
|
12
|
+
else
|
13
|
+
@columns = datatable.columns.select { |_, col| col[:sql_column].blank? }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def searched
|
18
|
+
@searched ||= datatable.search.select { |name, _| columns.key?(name) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def ordered
|
22
|
+
@ordered ||= columns[datatable.order_name]
|
23
|
+
end
|
24
|
+
|
25
|
+
def order(collection)
|
26
|
+
return collection unless ordered.present?
|
27
|
+
|
28
|
+
collection = if ordered[:sort_method]
|
29
|
+
datatable.dsl_tool.instance_exec(collection, datatable.order_direction, ordered, ordered[:index], &ordered[:sort_method])
|
30
|
+
else
|
31
|
+
order_column(collection, datatable.order_direction, ordered, ordered[:index])
|
32
|
+
end
|
33
|
+
|
34
|
+
raise 'sort method must return an Array' unless collection.kind_of?(Array)
|
35
|
+
|
36
|
+
collection
|
37
|
+
end
|
38
|
+
|
39
|
+
def order_column(collection, direction, column, index)
|
40
|
+
Rails.logger.info "VALUE TOOL: order_column :#{column.to_s} :#{direction} #{index}"
|
41
|
+
|
42
|
+
if direction == :asc
|
43
|
+
collection.sort! do |x, y|
|
44
|
+
x[index] <=> y[index] || x[index].to_s <=> y[index].to_s || 0
|
45
|
+
end
|
46
|
+
else
|
47
|
+
collection.sort! do |x, y|
|
48
|
+
y[index] <=> x[index] || y[index].to_s <=> x[index].to_s || 0
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
collection
|
53
|
+
end
|
54
|
+
|
55
|
+
def search(collection)
|
56
|
+
searched.each do |name, value|
|
57
|
+
column = columns[name]
|
58
|
+
|
59
|
+
collection = if column[:search_method]
|
60
|
+
datatable.dsl_tool.instance_exec(collection, value, column, column[:index], &column[:search_method])
|
61
|
+
else
|
62
|
+
search_column(collection, value, column, column[:index])
|
63
|
+
end
|
64
|
+
|
65
|
+
raise 'search method must return an Array object' unless collection.kind_of?(Array)
|
66
|
+
end
|
67
|
+
|
68
|
+
collection
|
69
|
+
end
|
70
|
+
|
71
|
+
def search_column(collection, value, column, index)
|
72
|
+
Rails.logger.info "VALUE TOOL: search_column #{column.to_s} #{value} #{index}"
|
73
|
+
|
74
|
+
fuzzy = column[:search][:fuzzy]
|
75
|
+
term = Effective::Attribute.new(column[:as]).parse(value, name: column[:name])
|
76
|
+
term_downcased = term.downcase if fuzzy && term.kind_of?(String)
|
77
|
+
|
78
|
+
if term == 'nil'
|
79
|
+
return (collection.select! { |row| row[index].nil? } || collection)
|
80
|
+
end
|
81
|
+
|
82
|
+
# See effective_resources gem search() method # relation.rb
|
83
|
+
collection.select! do |row|
|
84
|
+
case column[:as]
|
85
|
+
when :duration
|
86
|
+
if fuzzy && (term % 60 == 0) && value.to_s.include?('m') == false
|
87
|
+
if term < 0
|
88
|
+
row[index] <= term && row[index] > (term - 60)
|
89
|
+
else
|
90
|
+
row[index] >= term && row[index] < (term + 60)
|
91
|
+
end
|
92
|
+
else
|
93
|
+
row[index] == term
|
94
|
+
end
|
95
|
+
when :decimal, :currency
|
96
|
+
if fuzzy && (term.round(0) == term) && value.to_s.include?('.') == false
|
97
|
+
if term < 0
|
98
|
+
row[index] <= term && row[index] > (term - 1.0)
|
99
|
+
else
|
100
|
+
row[index] >= term && row[index] < (term + 1.0)
|
101
|
+
end
|
102
|
+
else
|
103
|
+
row[index] == term
|
104
|
+
end
|
105
|
+
when :resource
|
106
|
+
Array(row[index]).any? do |resource|
|
107
|
+
if term.kind_of?(Integer) && resource.respond_to?(:id)
|
108
|
+
resource.id == term || resource.to_param == term
|
109
|
+
elsif term.kind_of?(Array) && resource.respond_to?(:id)
|
110
|
+
term.any? { |term| resource.id == term || resource.to_param == term || resource.to_param == value }
|
111
|
+
else
|
112
|
+
fuzzy ? resource.to_s.downcase == term_downcased : resource.to_s == term
|
113
|
+
end
|
114
|
+
end
|
115
|
+
when :string, :text, :email
|
116
|
+
if fuzzy
|
117
|
+
row[index].to_s.downcase.include?(term_downcased)
|
118
|
+
else
|
119
|
+
row[index] == term
|
120
|
+
end
|
121
|
+
else
|
122
|
+
row[index] == term
|
123
|
+
end
|
124
|
+
end || collection
|
125
|
+
end
|
126
|
+
|
127
|
+
def paginate(collection)
|
128
|
+
Kaminari.paginate_array(collection).page(datatable.page).per(datatable.per_page)
|
129
|
+
end
|
130
|
+
|
131
|
+
def size(collection)
|
132
|
+
collection.size
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# [
|
139
|
+
# [1, 'title 1'],
|
140
|
+
# [2, 'title 2'],
|
141
|
+
# [3, 'title 3']
|
142
|
+
# ]
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Effective
|
2
|
+
module EffectiveDatatable
|
3
|
+
module Attributes
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
def initial_attributes(args)
|
8
|
+
raise "#{self.class.name}.new() expected Hash like arguments" unless args.kind_of?(Hash)
|
9
|
+
args
|
10
|
+
end
|
11
|
+
|
12
|
+
def load_attributes!
|
13
|
+
if datatables_ajax_request?
|
14
|
+
raise 'Expected attributes cookie to be present' unless cookie
|
15
|
+
@attributes = cookie[:attributes]
|
16
|
+
end
|
17
|
+
|
18
|
+
unless datatables_ajax_request?
|
19
|
+
@attributes[:controller_namespace] ||= view.controller_path.split('/')[0...-1].join('/').presence
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Effective
|
2
|
+
module EffectiveDatatable
|
3
|
+
module Collection
|
4
|
+
|
5
|
+
# Used for authorization. We authorize with authorized?(:index, collection_class)
|
6
|
+
def collection_class
|
7
|
+
@collection_class # Will be either User/Post/etc or Array
|
8
|
+
end
|
9
|
+
|
10
|
+
def active_record_collection?
|
11
|
+
@active_record_collection == true
|
12
|
+
end
|
13
|
+
|
14
|
+
def array_collection?
|
15
|
+
@array_collection == true
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def load_collection!
|
21
|
+
raise 'No collection defined. Please add a collection with collection do ... end' if collection.nil?
|
22
|
+
|
23
|
+
@collection_class = (collection.respond_to?(:klass) ? collection.klass : self.class)
|
24
|
+
@active_record_collection = (collection.ancestors.include?(ActiveRecord::Base) rescue false)
|
25
|
+
@array_collection = (collection.kind_of?(Array) && (collection.length == 0 || collection.first.kind_of?(Array)))
|
26
|
+
|
27
|
+
unless active_record_collection? || array_collection?
|
28
|
+
raise "Unsupported collection type. Expecting an ActiveRecord class, ActiveRecord relation, or an Array of Arrays [[1, 'foo'], [2, 'bar']]"
|
29
|
+
end
|
30
|
+
|
31
|
+
_scopes.each do |scope, _|
|
32
|
+
raise "invalid scope: :#{scope}. The collection must respond to :#{scope}" unless collection.respond_to?(scope)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
module Effective
|
2
|
+
module EffectiveDatatable
|
3
|
+
module Compute
|
4
|
+
BLANK = ''.freeze
|
5
|
+
|
6
|
+
private
|
7
|
+
|
8
|
+
# So the idea here is that we want to do as much as possible on the database in ActiveRecord
|
9
|
+
# And then run any array_columns through in post-processed results
|
10
|
+
def compute
|
11
|
+
col = collection
|
12
|
+
|
13
|
+
# Assign total records
|
14
|
+
@total_records = (active_record_collection? ? column_tool.size(col) : value_tool.size(col))
|
15
|
+
|
16
|
+
# Apply scope
|
17
|
+
col = column_tool.scope(col)
|
18
|
+
|
19
|
+
# Apply column searching
|
20
|
+
col = column_tool.search(col)
|
21
|
+
@display_records = column_tool.size(col) unless value_tool.searched.present?
|
22
|
+
|
23
|
+
# Apply column ordering
|
24
|
+
col = column_tool.order(col)
|
25
|
+
|
26
|
+
# Arrayize if we have value tool work to do
|
27
|
+
col = arrayize(col) if value_tool.searched.present? || value_tool.ordered.present?
|
28
|
+
|
29
|
+
# Apply value searching
|
30
|
+
col = value_tool.search(col)
|
31
|
+
@display_records = value_tool.size(col) if value_tool.searched.present?
|
32
|
+
|
33
|
+
# Apply value ordering
|
34
|
+
col = value_tool.order(col)
|
35
|
+
|
36
|
+
# Apply pagination
|
37
|
+
col = col.kind_of?(Array) ? value_tool.paginate(col) : column_tool.paginate(col)
|
38
|
+
|
39
|
+
# Arrayize the searched, ordered, paginated results
|
40
|
+
col = arrayize(col) unless col.kind_of?(Array)
|
41
|
+
|
42
|
+
# Assign display records
|
43
|
+
@display_records ||= @total_records
|
44
|
+
|
45
|
+
# Compute aggregate data
|
46
|
+
@aggregates_data = aggregate(col) if _aggregates.present?
|
47
|
+
|
48
|
+
# Charts too
|
49
|
+
@charts_data = chart(col) if _charts.present?
|
50
|
+
|
51
|
+
# Format all results
|
52
|
+
format(col)
|
53
|
+
|
54
|
+
# Finalize hook
|
55
|
+
finalize(col)
|
56
|
+
end
|
57
|
+
|
58
|
+
def arrayize(collection)
|
59
|
+
collection.map do |obj|
|
60
|
+
columns.map do |name, opts|
|
61
|
+
if state[:visible][name] == false && (name != order_name) # Sort by invisible array column
|
62
|
+
BLANK
|
63
|
+
elsif opts[:partial] || (opts[:format] && !opts[:compute])
|
64
|
+
active_record_collection? ? obj : obj[opts[:index]]
|
65
|
+
elsif opts[:compute]
|
66
|
+
dsl_tool.instance_exec(obj, (active_record_collection? ? collection : obj[opts[:index]]), &opts[:compute])
|
67
|
+
elsif opts[:as] == :effective_obfuscation
|
68
|
+
obj.to_param
|
69
|
+
elsif array_collection?
|
70
|
+
obj[opts[:index]]
|
71
|
+
elsif opts[:sql_as_column]
|
72
|
+
obj[name] || obj.send(name)
|
73
|
+
else
|
74
|
+
obj.send(name)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def aggregate(collection)
|
81
|
+
cols = collection.transpose
|
82
|
+
|
83
|
+
_aggregates.map do |_, aggregate|
|
84
|
+
columns.map do |name, opts|
|
85
|
+
next if state[:visible][name] == false && datatables_ajax_request?
|
86
|
+
|
87
|
+
values = cols[opts[:index]] || []
|
88
|
+
|
89
|
+
if state[:visible][name] == false
|
90
|
+
BLANK
|
91
|
+
elsif aggregate[:compute]
|
92
|
+
dsl_tool.instance_exec(values, columns[name], &aggregate[:compute])
|
93
|
+
else
|
94
|
+
format_column(aggregate_column(values, opts, aggregate), opts)
|
95
|
+
end
|
96
|
+
end.compact
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def aggregate_column(values, column, aggregate)
|
101
|
+
labeled = false
|
102
|
+
length = values.length
|
103
|
+
values = values.reject { |value| value.nil? }
|
104
|
+
|
105
|
+
if [:bulk_actions, :actions].include?(column[:as]) || length == 0
|
106
|
+
return BLANK
|
107
|
+
end
|
108
|
+
|
109
|
+
case aggregate[:name]
|
110
|
+
when :total
|
111
|
+
if values.all? { |value| value.kind_of?(Numeric) }
|
112
|
+
values.sum
|
113
|
+
elsif values.all? { |value| value == true || value == false }
|
114
|
+
"#{values.count { |val| val == true }} / #{values.count { |val| val == false}}"
|
115
|
+
elsif !labeled
|
116
|
+
labeled = aggregate[:label]
|
117
|
+
elsif values.any? { |value| value.kind_of?(String) == false }
|
118
|
+
"#{values.flatten.count} total"
|
119
|
+
end
|
120
|
+
when :average
|
121
|
+
if values.all? { |value| value.kind_of?(Numeric) }
|
122
|
+
values.sum / [length, 1].max
|
123
|
+
elsif values.all? { |value| value == true || value == false }
|
124
|
+
values.count { |val| val == true } >= (length / 2) ? true : false
|
125
|
+
elsif !labeled
|
126
|
+
labeled = aggregate[:label]
|
127
|
+
elsif values.any? { |value| value.kind_of?(String) == false }
|
128
|
+
'-'
|
129
|
+
end
|
130
|
+
else
|
131
|
+
raise 'not implemented'
|
132
|
+
end || BLANK
|
133
|
+
end
|
134
|
+
|
135
|
+
def chart(collection)
|
136
|
+
_charts.inject({}) do |retval, (name, chart)|
|
137
|
+
retval[name] = {
|
138
|
+
as: chart[:as],
|
139
|
+
data: dsl_tool.instance_exec(collection, &chart[:compute]),
|
140
|
+
name: chart[:name],
|
141
|
+
options: chart[:options]
|
142
|
+
}
|
143
|
+
|
144
|
+
unless retval[name][:data].kind_of?(Array) && retval[name][:data].first.kind_of?(Array)
|
145
|
+
raise "invalid chart :#{name}. The block must return an Array of Arrays"
|
146
|
+
end
|
147
|
+
|
148
|
+
retval
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|