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
@@ -1,20 +0,0 @@
|
|
1
|
-
module Effective
|
2
|
-
module EffectiveDatatable
|
3
|
-
module Charts
|
4
|
-
|
5
|
-
def charts_data
|
6
|
-
HashWithIndifferentAccess.new().tap do |retval|
|
7
|
-
(charts || {}).each do |name, chart|
|
8
|
-
retval[name] = {
|
9
|
-
name: chart[:name],
|
10
|
-
type: chart[:type],
|
11
|
-
options: chart[:options],
|
12
|
-
data: (instance_exec(&chart[:block]) if chart[:block])
|
13
|
-
}
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
module Effective
|
2
|
-
module EffectiveDatatable
|
3
|
-
module Dsl
|
4
|
-
module Scopes
|
5
|
-
# Instance Methods inside the scopes do .. end block
|
6
|
-
def scope(name, default = :klass_scope, options = {}, &block)
|
7
|
-
if block_given?
|
8
|
-
raise "You cannot use partial: ... with the block syntax" if options[:partial]
|
9
|
-
options[:block] = block
|
10
|
-
end
|
11
|
-
|
12
|
-
if default == :klass_scope || default == { default: true }
|
13
|
-
options[:klass_scope] = true
|
14
|
-
default = (default == :klass_scope ? nil : true)
|
15
|
-
end
|
16
|
-
|
17
|
-
# This needs to be a {} not WithIndifferentAccess or rendering _scopes won't work correctly
|
18
|
-
(@scopes ||= {})[name] = options.merge(default: default)
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module Effective
|
2
|
-
module EffectiveDatatable
|
3
|
-
module Helpers
|
4
|
-
|
5
|
-
# When we order by Array, it's already a string.
|
6
|
-
# This gives us a mechanism to sort numbers as numbers
|
7
|
-
def convert_to_column_type(table_column, value)
|
8
|
-
if value.html_safe? && value.kind_of?(String) && value.start_with?('<')
|
9
|
-
value = ActionView::Base.full_sanitizer.sanitize(value)
|
10
|
-
end
|
11
|
-
|
12
|
-
case table_column[:type]
|
13
|
-
when :number, :price, :decimal, :float, :percentage
|
14
|
-
(value.to_s.gsub(/[^0-9|\.]/, '').to_f rescue 0.00) unless value.kind_of?(Numeric)
|
15
|
-
when :integer
|
16
|
-
(value.to_s.gsub(/\D/, '').to_i rescue 0) unless value.kind_of?(Integer)
|
17
|
-
else
|
18
|
-
; # Do nothing
|
19
|
-
end || value
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,309 +0,0 @@
|
|
1
|
-
# This is extended as class level into Datatable
|
2
|
-
|
3
|
-
module Effective
|
4
|
-
module EffectiveDatatable
|
5
|
-
module Options
|
6
|
-
|
7
|
-
def initialize_datatable_options
|
8
|
-
@table_columns = _initialize_datatable_options(@table_columns, the_collection)
|
9
|
-
end
|
10
|
-
|
11
|
-
def initialize_attributes(args)
|
12
|
-
_initialize_attributes(args)
|
13
|
-
end
|
14
|
-
|
15
|
-
def initialize_scope_options
|
16
|
-
@scopes = _initialize_scope_options(@scopes)
|
17
|
-
_initialize_current_scope_attribute
|
18
|
-
end
|
19
|
-
|
20
|
-
def initialize_chart_options
|
21
|
-
@charts = _initialize_chart_options(@charts)
|
22
|
-
end
|
23
|
-
|
24
|
-
def quote_sql(name)
|
25
|
-
collection_class.connection.quote_column_name(name) rescue name
|
26
|
-
end
|
27
|
-
|
28
|
-
protected
|
29
|
-
|
30
|
-
def _initialize_attributes(args)
|
31
|
-
args.compact.each do |arg|
|
32
|
-
# ActionController::Parameters / Rails 5 hack. TODO.
|
33
|
-
if arg.respond_to?(:permit)
|
34
|
-
arg = (arg.respond_to?(:to_unsafe_h) ? arg.to_unsafe_h : arg.to_h)
|
35
|
-
end
|
36
|
-
|
37
|
-
raise "#{self.class.name}.new() can only be initialized with a Hash like arguments" unless arg.kind_of?(Hash)
|
38
|
-
|
39
|
-
arg.each { |k, v| self.attributes[k] = v }
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# The scope DSL is
|
44
|
-
# scope :start_date, default_value, options: {}
|
45
|
-
#
|
46
|
-
# The value might already be assigned, but if not, we have to assign the default to attributes
|
47
|
-
|
48
|
-
# A scope comes to us like {:start_date => {default: Time.zone.now, filter: {as: :select, collection: ... input_html :}}}
|
49
|
-
# We want to make sure an input_html: { value: default } exists
|
50
|
-
def _initialize_scope_options(scopes)
|
51
|
-
(scopes || []).each do |name, options|
|
52
|
-
value = attributes.key?(name) ? attributes[name].presence : options[:default]
|
53
|
-
|
54
|
-
if attributes.key?(name) == false
|
55
|
-
self.attributes[name] = options[:default]
|
56
|
-
value = options[:default]
|
57
|
-
end
|
58
|
-
|
59
|
-
if (options[:fallback] || options[:presence]) && attributes[name].blank?
|
60
|
-
self.attributes[name] = options[:default]
|
61
|
-
value = options[:default]
|
62
|
-
end
|
63
|
-
|
64
|
-
options[:filter] ||= {}
|
65
|
-
options[:filter][:input_html] ||= {}
|
66
|
-
options[:filter][:input_html][:value] = value
|
67
|
-
options[:filter][:selected] = value
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def _initialize_current_scope_attribute
|
72
|
-
attributes[:current_scope] ||= klass_scopes.find { |name, options| options[:klass_scope] && options[:default] }.try(:first)
|
73
|
-
|
74
|
-
if attributes[:current_scope].present?
|
75
|
-
attributes[:current_scope] = attributes[:current_scope].to_sym
|
76
|
-
attributes[:current_scope] = nil unless klass_scopes.keys.include?(attributes[:current_scope])
|
77
|
-
end
|
78
|
-
|
79
|
-
if attributes[:current_scope].present?
|
80
|
-
klass_scopes.each { |name, _| attributes[name] = (name == attributes[:current_scope]) }
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def _initialize_chart_options(charts)
|
85
|
-
charts
|
86
|
-
end
|
87
|
-
|
88
|
-
def _initialize_datatable_options(cols, collection)
|
89
|
-
# We set some memoized helper values
|
90
|
-
@collection_class = (collection.respond_to?(:klass) ? collection.klass : self.class)
|
91
|
-
@active_record_collection = (collection.ancestors.include?(ActiveRecord::Base) rescue false)
|
92
|
-
@array_collection = (collection.kind_of?(Array) && (collection.length == 0 || collection.first.kind_of?(Array)))
|
93
|
-
|
94
|
-
# And then parse all the colums
|
95
|
-
sql_table = (collection.table rescue nil)
|
96
|
-
|
97
|
-
# Here we identify all belongs_to associations and build up a Hash like:
|
98
|
-
# {user: {foreign_key: 'user_id', klass: User}, order: {foreign_key: 'order_id', klass: Effective::Order}}
|
99
|
-
belong_tos = (collection.klass.reflect_on_all_associations(:belongs_to) rescue []).inject({}) do |retval, bt|
|
100
|
-
if bt.options[:polymorphic]
|
101
|
-
retval[bt.name.to_s] = {foreign_key: bt.foreign_key, klass: bt.name, polymorphic: true}
|
102
|
-
else
|
103
|
-
klass = bt.klass || (bt.foreign_type.sub('_type', '').classify.constantize rescue nil)
|
104
|
-
retval[bt.name.to_s] = {foreign_key: bt.foreign_key, klass: klass} if bt.foreign_key.present? && klass.present?
|
105
|
-
end
|
106
|
-
|
107
|
-
retval
|
108
|
-
end
|
109
|
-
|
110
|
-
# Figure out has_manys and has_many_belongs_to_many's
|
111
|
-
has_manys = {}
|
112
|
-
has_and_belongs_to_manys = {}
|
113
|
-
|
114
|
-
(collection.klass.reflect_on_all_associations() rescue []).each do |reflect|
|
115
|
-
if reflect.macro == :has_many
|
116
|
-
klass = reflect.klass || (reflect.build_association({}).class)
|
117
|
-
has_manys[reflect.name.to_s] = { klass: klass }
|
118
|
-
elsif reflect.macro == :has_and_belongs_to_many
|
119
|
-
klass = reflect.klass || (reflect.build_association({}).class)
|
120
|
-
has_and_belongs_to_manys[reflect.name.to_s] = { klass: klass }
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
table_columns = (cols || {}).each_with_index do |(name, _), index|
|
125
|
-
sql_column = (collection.columns rescue []).find do |column|
|
126
|
-
column.name == name.to_s || (belong_tos.key?(name) && column.name == belong_tos[name][:foreign_key])
|
127
|
-
end
|
128
|
-
|
129
|
-
cols[name][:array_column] ||= false
|
130
|
-
cols[name][:index] = index # The index of this column in the collection, regardless of hidden table_columns
|
131
|
-
cols[name][:name] ||= name
|
132
|
-
cols[name][:label] ||= name.titleize
|
133
|
-
cols[name][:column] ||= (sql_table && sql_column) ? "#{quote_sql(sql_table.name)}.#{quote_sql(sql_column.name)}" : name
|
134
|
-
cols[name][:width] ||= nil
|
135
|
-
cols[name][:sortable] = true if cols[name][:sortable].nil?
|
136
|
-
cols[name][:visible] = true if cols[name][:visible].nil?
|
137
|
-
|
138
|
-
# Type
|
139
|
-
cols[name][:type] ||= cols[name][:as] # Use as: or type: interchangeably
|
140
|
-
|
141
|
-
cols[name][:type] ||= (
|
142
|
-
if belong_tos.key?(name)
|
143
|
-
if belong_tos[name][:polymorphic]
|
144
|
-
:belongs_to_polymorphic
|
145
|
-
else
|
146
|
-
:belongs_to
|
147
|
-
end
|
148
|
-
elsif has_manys.key?(name)
|
149
|
-
:has_many
|
150
|
-
elsif has_and_belongs_to_manys.key?(name)
|
151
|
-
:has_and_belongs_to_many
|
152
|
-
elsif cols[name][:bulk_actions_column]
|
153
|
-
:bulk_actions_column
|
154
|
-
elsif name.include?('_address') && defined?(EffectiveAddresses) && (collection_class.new rescue nil).respond_to?(:effective_addresses)
|
155
|
-
:effective_address
|
156
|
-
elsif name == 'id' && defined?(EffectiveObfuscation) && collection.respond_to?(:deobfuscate)
|
157
|
-
:obfuscated_id
|
158
|
-
elsif name == 'roles' && defined?(EffectiveRoles) && collection.respond_to?(:with_role)
|
159
|
-
:effective_roles
|
160
|
-
elsif sql_column.try(:type).present?
|
161
|
-
sql_column.type
|
162
|
-
elsif name.end_with?('_id')
|
163
|
-
:integer
|
164
|
-
else
|
165
|
-
:string # When in doubt
|
166
|
-
end
|
167
|
-
)
|
168
|
-
|
169
|
-
cols[name][:class] = "col-#{cols[name][:type]} col-#{name.parameterize} #{cols[name][:class]}".strip
|
170
|
-
|
171
|
-
# Formats
|
172
|
-
if name == 'id' || name.include?('year') || name.end_with?('_id')
|
173
|
-
cols[name][:format] = :non_formatted_integer
|
174
|
-
end
|
175
|
-
|
176
|
-
# Sortable - Disable sorting on these types
|
177
|
-
if [:has_many, :effective_address, :obfuscated_id].include?(cols[name][:type])
|
178
|
-
cols[name][:sortable] = false
|
179
|
-
end
|
180
|
-
|
181
|
-
# EffectiveRoles, if you do table_column :roles, everything just works
|
182
|
-
if cols[name][:type] == :effective_roles
|
183
|
-
cols[name][:column] = sql_table.present? ? "#{quote_sql(sql_table.name)}.#{quote_sql('roles_mask')}" : name
|
184
|
-
end
|
185
|
-
|
186
|
-
# This is a SELECT AS column, or a JOIN column or a delegated object
|
187
|
-
if sql_table.present? && sql_column.blank? && !cols[name][:array_column]
|
188
|
-
cols[name][:sql_as_column] = true
|
189
|
-
end
|
190
|
-
|
191
|
-
cols[name][:filter] = initialize_table_column_filter(cols[name], belong_tos[name], has_manys[name], has_and_belongs_to_manys[name])
|
192
|
-
|
193
|
-
if cols[name][:partial]
|
194
|
-
cols[name][:partial_local] ||= (sql_table.try(:name) || cols[name][:partial].split('/').last(2).first.presence || 'obj').singularize.to_sym
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
end
|
199
|
-
|
200
|
-
def initialize_table_column_filter(column, belongs_to, has_many, has_and_belongs_to_manys)
|
201
|
-
filter = column[:filter]
|
202
|
-
col_type = column[:type]
|
203
|
-
sql_column = column[:column].to_s.upcase
|
204
|
-
|
205
|
-
return {as: :null} if filter == false
|
206
|
-
|
207
|
-
filter = {as: filter.to_sym} if filter.kind_of?(String)
|
208
|
-
filter = {} unless filter.kind_of?(Hash)
|
209
|
-
|
210
|
-
# This is a fix for passing filter[:selected] == false, it needs to be 'false'
|
211
|
-
filter[:selected] = filter[:selected].to_s unless filter[:selected].nil?
|
212
|
-
|
213
|
-
# Allow :values or :collection to be used interchangeably
|
214
|
-
if filter.key?(:values)
|
215
|
-
filter[:collection] ||= filter[:values]
|
216
|
-
end
|
217
|
-
|
218
|
-
# Allow :as or :type to be used interchangeably
|
219
|
-
if filter.key?(:type)
|
220
|
-
filter[:as] ||= filter[:type]
|
221
|
-
end
|
222
|
-
|
223
|
-
# If you pass a collection, just assume it's a select
|
224
|
-
if filter.key?(:collection) && col_type != :belongs_to_polymorphic
|
225
|
-
filter[:as] ||= :select
|
226
|
-
end
|
227
|
-
|
228
|
-
# Fuzzy by default throughout
|
229
|
-
unless filter.key?(:fuzzy)
|
230
|
-
filter[:fuzzy] = true
|
231
|
-
end
|
232
|
-
|
233
|
-
# Check if this is an aggregate column
|
234
|
-
if ['SUM(', 'COUNT(', 'MAX(', 'MIN(', 'AVG('].any? { |str| sql_column.include?(str) }
|
235
|
-
filter[:sql_operation] = :having
|
236
|
-
end
|
237
|
-
|
238
|
-
case col_type
|
239
|
-
when :belongs_to
|
240
|
-
{
|
241
|
-
as: :select,
|
242
|
-
collection: (
|
243
|
-
if belongs_to[:klass].respond_to?(:datatables_filter)
|
244
|
-
Proc.new { belongs_to[:klass].datatables_filter }
|
245
|
-
elsif belongs_to[:klass].respond_to?(:sorted)
|
246
|
-
Proc.new { belongs_to[:klass].sorted }
|
247
|
-
else
|
248
|
-
Proc.new { belongs_to[:klass].all.map { |obj| [obj.to_s, obj.id] }.sort { |x, y| x[0] <=> y[0] } }
|
249
|
-
end
|
250
|
-
)
|
251
|
-
}
|
252
|
-
when :belongs_to_polymorphic
|
253
|
-
{as: :grouped_select, polymorphic: true, collection: {}}
|
254
|
-
when :has_many
|
255
|
-
{
|
256
|
-
as: :select,
|
257
|
-
multiple: true,
|
258
|
-
collection: (
|
259
|
-
if has_many[:klass].respond_to?(:datatables_filter)
|
260
|
-
Proc.new { has_many[:klass].datatables_filter }
|
261
|
-
elsif has_many[:klass].respond_to?(:sorted)
|
262
|
-
Proc.new { has_many[:klass].sorted }
|
263
|
-
else
|
264
|
-
Proc.new { has_many[:klass].all.map { |obj| [obj.to_s, obj.id] }.sort { |x, y| x[0] <=> y[0] } }
|
265
|
-
end
|
266
|
-
)
|
267
|
-
}
|
268
|
-
when :has_and_belongs_to_many
|
269
|
-
{
|
270
|
-
as: :select,
|
271
|
-
multiple: true,
|
272
|
-
collection: (
|
273
|
-
if has_and_belongs_to_manys[:klass].respond_to?(:datatables_filter)
|
274
|
-
Proc.new { has_and_belongs_to_manys[:klass].datatables_filter }
|
275
|
-
elsif has_and_belongs_to_manys[:klass].respond_to?(:sorted)
|
276
|
-
Proc.new { has_and_belongs_to_manys[:klass].sorted }
|
277
|
-
else
|
278
|
-
Proc.new { has_and_belongs_to_manys[:klass].all.map { |obj| [obj.to_s, obj.id] }.sort { |x, y| x[0] <=> y[0] } }
|
279
|
-
end
|
280
|
-
)
|
281
|
-
}
|
282
|
-
when :effective_address
|
283
|
-
{as: :string}
|
284
|
-
when :effective_roles
|
285
|
-
{as: :select, collection: EffectiveRoles.roles}
|
286
|
-
when :obfuscated_id
|
287
|
-
{as: :obfuscated_id}
|
288
|
-
when :integer
|
289
|
-
{as: :number}
|
290
|
-
when :boolean
|
291
|
-
if EffectiveDatatables.boolean_format == :yes_no
|
292
|
-
{as: :boolean, collection: [['Yes', true], ['No', false]] }
|
293
|
-
else
|
294
|
-
{as: :boolean, collection: [['true', true], ['false', false]] }
|
295
|
-
end
|
296
|
-
when :datetime
|
297
|
-
{as: :datetime}
|
298
|
-
when :date
|
299
|
-
{as: :date}
|
300
|
-
when :bulk_actions_column
|
301
|
-
{as: :bulk_actions_column}
|
302
|
-
else
|
303
|
-
{as: :string}
|
304
|
-
end.merge(filter.symbolize_keys)
|
305
|
-
end
|
306
|
-
|
307
|
-
end
|
308
|
-
end
|
309
|
-
end
|
@@ -1,365 +0,0 @@
|
|
1
|
-
# This is extended as class level into Datatable
|
2
|
-
|
3
|
-
module Effective
|
4
|
-
module EffectiveDatatable
|
5
|
-
module Rendering
|
6
|
-
BLANK = ''.freeze
|
7
|
-
|
8
|
-
protected
|
9
|
-
|
10
|
-
# So the idea here is that we want to do as much as possible on the database in ActiveRecord
|
11
|
-
# And then run any array_columns through in post-processed results
|
12
|
-
def table_data
|
13
|
-
col = the_collection
|
14
|
-
|
15
|
-
if active_record_collection?
|
16
|
-
col = table_tool.order(col)
|
17
|
-
col = table_tool.search(col)
|
18
|
-
|
19
|
-
if table_tool.search_terms.present? && array_tool.search_terms.blank?
|
20
|
-
self.display_records = active_record_collection_size(col)
|
21
|
-
end
|
22
|
-
|
23
|
-
if array_tool.search_terms.present?
|
24
|
-
col = self.arrayize(col)
|
25
|
-
col = array_tool.search(col)
|
26
|
-
self.display_records = col.size
|
27
|
-
end
|
28
|
-
|
29
|
-
if array_tool.order_by_column.present?
|
30
|
-
col = self.arrayize(col)
|
31
|
-
col = array_tool.order(col)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
if col.kind_of?(Array)
|
36
|
-
col = array_tool.order(col)
|
37
|
-
col = array_tool.search(col)
|
38
|
-
end
|
39
|
-
|
40
|
-
self.display_records ||= total_records
|
41
|
-
|
42
|
-
if col.kind_of?(Array)
|
43
|
-
col = array_tool.paginate(col)
|
44
|
-
else
|
45
|
-
col = table_tool.paginate(col)
|
46
|
-
end
|
47
|
-
|
48
|
-
col = self.arrayize(col)
|
49
|
-
|
50
|
-
self.format(col)
|
51
|
-
col = self.finalize(col)
|
52
|
-
end
|
53
|
-
|
54
|
-
def arrayize(collection)
|
55
|
-
return collection if @arrayized # Prevent the collection from being arrayized more than once
|
56
|
-
@arrayized = true
|
57
|
-
|
58
|
-
# We want to use the render :collection for each column that renders partials
|
59
|
-
rendered = {}
|
60
|
-
(display_table_columns || table_columns).each do |name, opts|
|
61
|
-
if opts[:partial] && opts[:visible]
|
62
|
-
locals = HashWithIndifferentAccess.new(
|
63
|
-
datatable: self,
|
64
|
-
table_column: table_columns[name],
|
65
|
-
controller_namespace: controller_namespace,
|
66
|
-
show_action: (opts[:partial_locals] || {})[:show_action],
|
67
|
-
edit_action: (opts[:partial_locals] || {})[:edit_action],
|
68
|
-
destroy_action: (opts[:partial_locals] || {})[:destroy_action],
|
69
|
-
unarchive_action: (opts[:partial_locals] || {})[:unarchive_action]
|
70
|
-
)
|
71
|
-
|
72
|
-
locals.merge!(opts[:partial_locals]) if opts[:partial_locals]
|
73
|
-
|
74
|
-
if opts[:type] == :actions
|
75
|
-
add_actions_column_locals(locals)
|
76
|
-
locals[:actions_block] = opts[:actions_block]
|
77
|
-
end
|
78
|
-
|
79
|
-
rendered[name] = (render(
|
80
|
-
:partial => opts[:partial],
|
81
|
-
:as => opts[:partial_local],
|
82
|
-
:collection => collection,
|
83
|
-
:formats => :html,
|
84
|
-
:locals => locals,
|
85
|
-
:spacer_template => '/effective/datatables/spacer_template',
|
86
|
-
) || '').split('EFFECTIVEDATATABLESSPACER')
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
collection.each_with_index.map do |obj, index|
|
91
|
-
(display_table_columns || table_columns).map do |name, opts|
|
92
|
-
begin
|
93
|
-
if opts[:visible] == false && (name != order_name.to_s) # Sort by invisible array column
|
94
|
-
BLANK
|
95
|
-
elsif opts[:block]
|
96
|
-
begin
|
97
|
-
if active_record_collection?
|
98
|
-
view.instance_exec(obj, collection, self, &opts[:block])
|
99
|
-
else
|
100
|
-
view.instance_exec(obj, obj[opts[:index]], collection, self, &opts[:block])
|
101
|
-
end
|
102
|
-
rescue NoMethodError => e
|
103
|
-
if opts[:type] == :actions && e.message == 'super called outside of method'
|
104
|
-
rendered[name][index]
|
105
|
-
else
|
106
|
-
raise(e)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
elsif opts[:proc]
|
110
|
-
if active_record_collection?
|
111
|
-
view.instance_exec(obj, collection, self, &opts[:proc])
|
112
|
-
else
|
113
|
-
view.instance_exec(obj, obj[opts[:index]], collection, self, &opts[:proc])
|
114
|
-
end
|
115
|
-
elsif opts[:partial]
|
116
|
-
rendered[name][index]
|
117
|
-
elsif opts[:type] == :belongs_to
|
118
|
-
(obj.send(name) rescue nil).to_s
|
119
|
-
elsif opts[:type] == :belongs_to_polymorphic
|
120
|
-
(obj.send(name) rescue nil).to_s
|
121
|
-
elsif opts[:type] == :has_many
|
122
|
-
(obj.send(name).map { |obj| obj.to_s }.join('<br>') rescue BLANK)
|
123
|
-
elsif opts[:type] == :has_and_belongs_to_many
|
124
|
-
(obj.send(name).map { |obj| obj.to_s }.join('<br>') rescue BLANK)
|
125
|
-
elsif opts[:type] == :bulk_actions_column
|
126
|
-
BLANK
|
127
|
-
elsif opts[:type] == :year
|
128
|
-
obj.send(name).try(:year)
|
129
|
-
elsif opts[:type] == :obfuscated_id
|
130
|
-
(obj.send(:to_param) rescue nil).to_s
|
131
|
-
elsif opts[:type] == :effective_address
|
132
|
-
(Array(obj.send(name)) rescue [])
|
133
|
-
elsif opts[:type] == :effective_roles
|
134
|
-
(obj.send(:roles) rescue [])
|
135
|
-
elsif obj.kind_of?(Array) # Array backed collection
|
136
|
-
obj[opts[:index]]
|
137
|
-
elsif opts[:sql_as_column]
|
138
|
-
obj[name] || obj.send(name)
|
139
|
-
else
|
140
|
-
obj.send(name)
|
141
|
-
end
|
142
|
-
rescue => e
|
143
|
-
Rails.env.production? ? obj.try(:[], name) : raise(e)
|
144
|
-
end
|
145
|
-
end
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def format(collection)
|
150
|
-
collection.each do |row|
|
151
|
-
(display_table_columns || table_columns).each_with_index do |(name, opts), index|
|
152
|
-
value = row[index]
|
153
|
-
next if value == nil || value == BLANK || opts[:visible] == false
|
154
|
-
next if opts[:block] || opts[:partial] || opts[:proc]
|
155
|
-
|
156
|
-
if opts[:sql_as_column]
|
157
|
-
row[index] = value.to_s
|
158
|
-
end
|
159
|
-
|
160
|
-
case (opts[:format] || opts[:type])
|
161
|
-
when :belongs_to, :belongs_to_polymorphic
|
162
|
-
row[index] = value.to_s
|
163
|
-
when :has_many
|
164
|
-
if value.kind_of?(Array)
|
165
|
-
if value.length == 0
|
166
|
-
row[index] = BLANK
|
167
|
-
elsif value.length == 1
|
168
|
-
row[index] = value.first.to_s
|
169
|
-
elsif opts[:sentence]
|
170
|
-
row[index] = value.map { |v| v.to_s }.to_sentence
|
171
|
-
else
|
172
|
-
row[index] = value.map { |v| v.to_s }.join('<br>')
|
173
|
-
end
|
174
|
-
end
|
175
|
-
when :effective_address
|
176
|
-
row[index] = value.map { |addr| addr.to_html }.join('<br>')
|
177
|
-
when :effective_roles
|
178
|
-
row[index] = value.join(', ')
|
179
|
-
when :datetime
|
180
|
-
row[index] = value.strftime(EffectiveDatatables.datetime_format) rescue BLANK
|
181
|
-
when :date
|
182
|
-
row[index] = value.strftime(EffectiveDatatables.date_format) rescue BLANK
|
183
|
-
when :price
|
184
|
-
# This is an integer value, "number of cents"
|
185
|
-
raise 'column type: price expects an Integer representing the number of cents' unless value.kind_of?(Integer)
|
186
|
-
row[index] = number_to_currency(value / 100.0)
|
187
|
-
when :currency
|
188
|
-
row[index] = number_to_currency(value || 0)
|
189
|
-
when :percentage
|
190
|
-
row[index] = number_to_percentage(value || 0)
|
191
|
-
when :integer
|
192
|
-
if EffectiveDatatables.integer_format.kind_of?(Symbol)
|
193
|
-
row[index] = view.instance_exec { public_send(EffectiveDatatables.integer_format, value) }
|
194
|
-
elsif EffectiveDatatables.integer_format.respond_to?(:call)
|
195
|
-
row[index] = view.instance_exec { EffectiveDatatables.integer_format.call(value) }
|
196
|
-
end
|
197
|
-
when :boolean
|
198
|
-
if EffectiveDatatables.boolean_format == :yes_no && value == true
|
199
|
-
row[index] = 'Yes'
|
200
|
-
elsif EffectiveDatatables.boolean_format == :yes_no && value == false
|
201
|
-
row[index] = 'No'
|
202
|
-
end
|
203
|
-
when :string
|
204
|
-
row[index] = mail_to(value) if name == 'email'
|
205
|
-
else
|
206
|
-
; # Nothing
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
collection
|
212
|
-
end
|
213
|
-
|
214
|
-
# This should return an Array of values the same length as table_data
|
215
|
-
def aggregate_data(table_data)
|
216
|
-
return false unless aggregates.present?
|
217
|
-
|
218
|
-
values = table_data.transpose
|
219
|
-
|
220
|
-
aggregates.map do |name, options|
|
221
|
-
(display_table_columns || table_columns).map.with_index do |(name, column), index|
|
222
|
-
|
223
|
-
if column[:visible] != true
|
224
|
-
''
|
225
|
-
elsif (options[:block] || options[:proc]).respond_to?(:call)
|
226
|
-
view.instance_exec(column, (values[index] || []), values, &(options[:block] || options[:proc]))
|
227
|
-
else
|
228
|
-
''
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
end
|
233
|
-
|
234
|
-
protected
|
235
|
-
|
236
|
-
def add_actions_column_locals(locals)
|
237
|
-
return unless active_record_collection?
|
238
|
-
|
239
|
-
if locals[:show_action] == :authorize
|
240
|
-
locals[:show_action] = (EffectiveDatatables.authorized?(controller, :show, collection_class) rescue false)
|
241
|
-
end
|
242
|
-
|
243
|
-
if locals[:edit_action] == :authorize
|
244
|
-
locals[:edit_action] = (EffectiveDatatables.authorized?(controller, :edit, collection_class) rescue false)
|
245
|
-
end
|
246
|
-
|
247
|
-
if locals[:destroy_action] == :authorize
|
248
|
-
locals[:destroy_action] = (EffectiveDatatables.authorized?(controller, :destroy, collection_class) rescue false)
|
249
|
-
end
|
250
|
-
|
251
|
-
if locals[:unarchive_action] == :authorize
|
252
|
-
locals[:unarchive_action] = (EffectiveDatatables.authorized?(controller, :unarchive, collection_class) rescue false)
|
253
|
-
end
|
254
|
-
|
255
|
-
# Then we look at the routes to see if these actions _actually_ exist
|
256
|
-
|
257
|
-
routes = Rails.application.routes
|
258
|
-
|
259
|
-
begin
|
260
|
-
resource = collection_class.new(id: 123)
|
261
|
-
resource.define_singleton_method(:persisted?) { true } # We override persisted? to get the correct action urls
|
262
|
-
raise 'no to param' unless resource.to_param.present?
|
263
|
-
rescue => e
|
264
|
-
return
|
265
|
-
end
|
266
|
-
|
267
|
-
if (locals[:show_action] == true || locals[:show_action] == :authorize_each) && !locals[:show_path]
|
268
|
-
# Try our namespace
|
269
|
-
url = (view.polymorphic_path([*controller_namespace, resource]) rescue false)
|
270
|
-
url = false if url && !(routes.recognize_path(url) rescue false)
|
271
|
-
|
272
|
-
# Try no namespace
|
273
|
-
unless url
|
274
|
-
url = (view.polymorphic_path(resource) rescue false)
|
275
|
-
url = false if url && !(routes.recognize_path(url) rescue false)
|
276
|
-
end
|
277
|
-
|
278
|
-
# So if we have a URL, this is an action we can link to
|
279
|
-
if url
|
280
|
-
locals[:show_path] = url.gsub("/#{resource.to_param}", '/:to_param')
|
281
|
-
else
|
282
|
-
locals[:show_action] = false
|
283
|
-
end
|
284
|
-
end
|
285
|
-
|
286
|
-
if (locals[:edit_action] == true || locals[:edit_action] == :authorize_each) && !locals[:edit_path]
|
287
|
-
# Try our namespace
|
288
|
-
url = (view.edit_polymorphic_path([*controller_namespace, resource]) rescue false)
|
289
|
-
url = false if url && !(routes.recognize_path(url) rescue false)
|
290
|
-
|
291
|
-
# Try no namespace
|
292
|
-
unless url
|
293
|
-
url = (view.edit_polymorphic_path(resource) rescue false)
|
294
|
-
url = false if url && !(routes.recognize_path(url) rescue false)
|
295
|
-
end
|
296
|
-
|
297
|
-
# So if we have a URL, this is an action we can link to
|
298
|
-
if url
|
299
|
-
locals[:edit_path] = url.gsub("/#{resource.to_param}", '/:to_param')
|
300
|
-
else
|
301
|
-
locals[:edit_action] = false
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
if (locals[:destroy_action] == true || locals[:destroy_action] == :authorize_each) && !locals[:destroy_path]
|
306
|
-
# Try our namespace
|
307
|
-
url = (view.polymorphic_path([*controller_namespace, resource]) rescue false)
|
308
|
-
url = false if url && !(routes.recognize_path(url, method: :delete) rescue false)
|
309
|
-
|
310
|
-
# Try no namespace
|
311
|
-
unless url
|
312
|
-
url = (view.polymorphic_path(resource) rescue false)
|
313
|
-
url = false if url && !(routes.recognize_path(url, method: :delete) rescue false)
|
314
|
-
end
|
315
|
-
|
316
|
-
# So if we have a URL, this is an action we can link to
|
317
|
-
if url
|
318
|
-
locals[:destroy_path] = url.gsub("/#{resource.to_param}", '/:to_param')
|
319
|
-
else
|
320
|
-
locals[:destroy_action] = false
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
if resource.respond_to?(:archived?)
|
325
|
-
if (locals[:unarchive_action] == true || locals[:unarchive_action] == :authorize_each) && !locals[:unarchive_path]
|
326
|
-
# Try our namespace
|
327
|
-
url = (view.polymorphic_path([*controller_namespace, resource], action: :unarchive) rescue false)
|
328
|
-
url = false if url && !(routes.recognize_path(url) rescue false)
|
329
|
-
|
330
|
-
# Try no namespace
|
331
|
-
unless url
|
332
|
-
url = (view.polymorphic_path(resource, action: :unarchive) rescue false)
|
333
|
-
url = false if url && !(routes.recognize_path(url) rescue false)
|
334
|
-
end
|
335
|
-
|
336
|
-
# So if we have a URL, this is an action we can link to
|
337
|
-
if url
|
338
|
-
locals[:unarchive_path] = url.gsub("/#{resource.to_param}", '/:to_param')
|
339
|
-
else
|
340
|
-
locals[:unarchive_action] = false
|
341
|
-
end
|
342
|
-
end
|
343
|
-
else
|
344
|
-
locals[:unarchive_action] = false
|
345
|
-
end
|
346
|
-
|
347
|
-
end
|
348
|
-
|
349
|
-
private
|
350
|
-
|
351
|
-
def controller_namespace
|
352
|
-
@controller_namespace ||= (
|
353
|
-
path = if attributes[:referer].present?
|
354
|
-
URI(attributes[:referer]).path
|
355
|
-
else
|
356
|
-
view.controller_path
|
357
|
-
end
|
358
|
-
|
359
|
-
path.split('/')[0...-1].map { |path| path.downcase.to_sym if path.present? }.compact
|
360
|
-
)
|
361
|
-
end
|
362
|
-
|
363
|
-
end # / Rendering
|
364
|
-
end
|
365
|
-
end
|