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.
Files changed (66) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +632 -512
  3. data/app/assets/javascripts/dataTables/buttons/buttons.html5.js +176 -177
  4. data/app/assets/javascripts/dataTables/buttons/buttons.print.js +2 -0
  5. data/app/assets/javascripts/dataTables/buttons/dataTables.buttons.js +14 -14
  6. data/app/assets/javascripts/dataTables/dataTables.bootstrap.js +1 -1
  7. data/app/assets/javascripts/dataTables/jquery.dataTables.js +246 -217
  8. data/app/assets/javascripts/effective_datatables.js +2 -3
  9. data/app/assets/javascripts/effective_datatables/events.js.coffee +7 -0
  10. data/app/assets/javascripts/effective_datatables/filters.js.coffee +6 -0
  11. data/app/assets/javascripts/effective_datatables/initialize.js.coffee +42 -39
  12. data/app/assets/javascripts/effective_datatables/reset.js.coffee +7 -0
  13. data/app/assets/javascripts/vendor/jquery.delayedChange.js +1 -1
  14. data/app/assets/stylesheets/dataTables/dataTables.bootstrap.css +0 -1
  15. data/app/assets/stylesheets/effective_datatables.scss +1 -2
  16. data/app/assets/stylesheets/effective_datatables/{_scopes.scss → _filters.scss} +1 -1
  17. data/app/assets/stylesheets/effective_datatables/_overrides.scss +1 -1
  18. data/app/controllers/effective/datatables_controller.rb +2 -4
  19. data/app/helpers/effective_datatables_helper.rb +56 -91
  20. data/app/helpers/effective_datatables_private_helper.rb +55 -64
  21. data/app/models/effective/datatable.rb +103 -177
  22. data/app/models/effective/datatable_column.rb +28 -0
  23. data/app/models/effective/datatable_column_tool.rb +110 -0
  24. data/app/models/effective/datatable_dsl_tool.rb +28 -0
  25. data/app/models/effective/datatable_value_tool.rb +142 -0
  26. data/app/models/effective/effective_datatable/attributes.rb +25 -0
  27. data/app/models/effective/effective_datatable/collection.rb +38 -0
  28. data/app/models/effective/effective_datatable/compute.rb +154 -0
  29. data/app/models/effective/effective_datatable/cookie.rb +29 -0
  30. data/app/models/effective/effective_datatable/dsl.rb +14 -8
  31. data/app/models/effective/effective_datatable/dsl/bulk_actions.rb +5 -6
  32. data/app/models/effective/effective_datatable/dsl/charts.rb +7 -9
  33. data/app/models/effective/effective_datatable/dsl/datatable.rb +107 -57
  34. data/app/models/effective/effective_datatable/dsl/filters.rb +50 -0
  35. data/app/models/effective/effective_datatable/format.rb +157 -0
  36. data/app/models/effective/effective_datatable/hooks.rb +0 -18
  37. data/app/models/effective/effective_datatable/params.rb +34 -0
  38. data/app/models/effective/effective_datatable/resource.rb +108 -0
  39. data/app/models/effective/effective_datatable/state.rb +178 -0
  40. data/app/views/effective/datatables/_actions_column.html.haml +9 -42
  41. data/app/views/effective/datatables/_bulk_actions_column.html.haml +1 -1
  42. data/app/views/effective/datatables/_bulk_actions_dropdown.html.haml +2 -3
  43. data/app/views/effective/datatables/_chart.html.haml +1 -1
  44. data/app/views/effective/datatables/_datatable.html.haml +7 -25
  45. data/app/views/effective/datatables/_filters.html.haml +21 -0
  46. data/app/views/effective/datatables/_reset.html.haml +2 -0
  47. data/app/views/effective/datatables/_resource_column.html.haml +8 -0
  48. data/app/views/effective/datatables/index.html.haml +0 -1
  49. data/config/effective_datatables.rb +9 -32
  50. data/lib/effective_datatables.rb +2 -6
  51. data/lib/effective_datatables/engine.rb +1 -1
  52. data/lib/effective_datatables/version.rb +1 -1
  53. data/lib/generators/effective_datatables/install_generator.rb +2 -2
  54. metadata +39 -19
  55. data/app/assets/javascripts/dataTables/colreorder/dataTables.colReorder.js +0 -27
  56. data/app/assets/javascripts/dataTables/jszip/jszip.js +0 -9155
  57. data/app/assets/javascripts/effective_datatables/scopes.js.coffee +0 -9
  58. data/app/models/effective/active_record_datatable_tool.rb +0 -242
  59. data/app/models/effective/array_datatable_tool.rb +0 -97
  60. data/app/models/effective/effective_datatable/ajax.rb +0 -101
  61. data/app/models/effective/effective_datatable/charts.rb +0 -20
  62. data/app/models/effective/effective_datatable/dsl/scopes.rb +0 -23
  63. data/app/models/effective/effective_datatable/helpers.rb +0 -24
  64. data/app/models/effective/effective_datatable/options.rb +0 -309
  65. data/app/models/effective/effective_datatable/rendering.rb +0 -365
  66. 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