effective_datatables 2.12.2 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|