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.
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