effective_datatables 3.7.10 → 4.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 (69) hide show
  1. checksums.yaml +5 -5
  2. data/MIT-LICENSE +1 -1
  3. data/README.md +32 -32
  4. data/app/assets/images/dataTables/sort-down.svg +1 -0
  5. data/app/assets/images/dataTables/sort-up.svg +1 -0
  6. data/app/assets/images/dataTables/sort.svg +1 -0
  7. data/app/assets/javascripts/dataTables/buttons/{buttons.bootstrap.js → buttons.bootstrap4.js} +7 -15
  8. data/app/assets/javascripts/dataTables/dataTables.bootstrap4.js +184 -0
  9. data/app/assets/javascripts/dataTables/responsive/dataTables.responsive.js +30 -11
  10. data/app/assets/javascripts/dataTables/responsive/{responsive.bootstrap.js → responsive.bootstrap4.js} +6 -6
  11. data/app/assets/javascripts/effective_datatables/bulk_actions.js.coffee +43 -43
  12. data/app/assets/javascripts/effective_datatables/events.js.coffee +7 -4
  13. data/app/assets/javascripts/effective_datatables/filters.js.coffee +0 -1
  14. data/app/assets/javascripts/effective_datatables/initialize.js.coffee +45 -49
  15. data/app/assets/javascripts/effective_datatables/overrides.js +12 -0
  16. data/app/assets/javascripts/effective_datatables/reset.js.coffee +1 -1
  17. data/app/assets/javascripts/effective_datatables.js +4 -4
  18. data/app/assets/stylesheets/dataTables/buttons/{buttons.bootstrap.scss → buttons.bootstrap4.css} +68 -1
  19. data/app/assets/stylesheets/dataTables/{dataTables.bootstrap.scss → dataTables.bootstrap4.css} +44 -29
  20. data/app/assets/stylesheets/dataTables/responsive/{responsive.bootstrap.scss → responsive.bootstrap4.css} +3 -3
  21. data/app/assets/stylesheets/effective_datatables/_overrides.scss +72 -152
  22. data/app/assets/stylesheets/effective_datatables.scss +3 -4
  23. data/app/controllers/effective/datatables_controller.rb +6 -39
  24. data/app/helpers/effective_datatables_helper.rb +55 -50
  25. data/app/helpers/effective_datatables_private_helper.rb +47 -179
  26. data/app/models/effective/datatable.rb +16 -44
  27. data/app/models/effective/datatable_column.rb +0 -1
  28. data/app/models/effective/datatable_column_tool.rb +2 -4
  29. data/app/models/effective/datatable_dsl_tool.rb +3 -11
  30. data/app/models/effective/datatable_value_tool.rb +23 -23
  31. data/app/models/effective/effective_datatable/attributes.rb +13 -5
  32. data/app/models/effective/effective_datatable/collection.rb +3 -18
  33. data/app/models/effective/effective_datatable/compute.rb +6 -17
  34. data/app/models/effective/effective_datatable/cookie.rb +20 -19
  35. data/app/models/effective/effective_datatable/dsl/bulk_actions.rb +25 -14
  36. data/app/models/effective/effective_datatable/dsl/datatable.rb +28 -70
  37. data/app/models/effective/effective_datatable/dsl/filters.rb +5 -5
  38. data/app/models/effective/effective_datatable/dsl.rb +3 -8
  39. data/app/models/effective/effective_datatable/format.rb +50 -95
  40. data/app/models/effective/effective_datatable/params.rb +3 -8
  41. data/app/models/effective/effective_datatable/resource.rb +76 -137
  42. data/app/models/effective/effective_datatable/state.rb +15 -30
  43. data/app/views/effective/datatables/_actions_column.html.haml +8 -1
  44. data/app/views/effective/datatables/_bulk_actions_column.html.haml +1 -1
  45. data/app/views/effective/datatables/_filters.html.haml +11 -12
  46. data/app/views/effective/datatables/_resource_column.html.haml +8 -11
  47. data/config/effective_datatables.rb +14 -12
  48. data/config/routes.rb +0 -1
  49. data/lib/effective_datatables/engine.rb +4 -14
  50. data/lib/effective_datatables/version.rb +1 -1
  51. data/lib/effective_datatables.rb +4 -57
  52. metadata +20 -31
  53. data/app/assets/config/effective_datatables_manifest.js +0 -3
  54. data/app/assets/images/dataTables/sort_asc.png +0 -0
  55. data/app/assets/images/dataTables/sort_both.png +0 -0
  56. data/app/assets/images/dataTables/sort_desc.png +0 -0
  57. data/app/assets/javascripts/dataTables/dataTables.bootstrap.js +0 -182
  58. data/app/assets/javascripts/dataTables/locales/en.lang +0 -33
  59. data/app/assets/javascripts/dataTables/locales/es.lang +0 -36
  60. data/app/assets/javascripts/dataTables/locales/nl.lang +0 -30
  61. data/app/assets/javascripts/effective_datatables/flash.js.coffee +0 -31
  62. data/app/assets/javascripts/effective_datatables/inline_crud.js.coffee +0 -217
  63. data/app/assets/javascripts/effective_datatables/overrides.js.coffee +0 -7
  64. data/app/assets/javascripts/effective_datatables/reorder.js.coffee +0 -43
  65. data/app/assets/stylesheets/effective_datatables/_filters.scss +0 -7
  66. data/app/views/effective/datatables/_reorder_column.html.haml +0 -5
  67. data/config/locales/en.yml +0 -12
  68. data/config/locales/es.yml +0 -12
  69. data/config/locales/nl.yml +0 -12
@@ -1,21 +1,18 @@
1
1
  # These aren't expected to be called by a developer. They are internal methods.
2
- # These aren't expected to be called by a developer. They are internal methods.
3
2
  module EffectiveDatatablesPrivateHelper
4
3
 
5
4
  # https://datatables.net/reference/option/columns
6
5
  def datatable_columns(datatable)
7
- sortable = datatable.sortable?
8
-
9
6
  datatable.columns.map do |name, opts|
10
7
  {
11
- className: opts[:col_class],
12
8
  name: name,
9
+ title: content_tag(:span, opts[:label].presence),
10
+ className: opts[:col_class],
13
11
  responsivePriority: opts[:responsive],
14
12
  search: datatable.state[:search][name],
15
13
  searchHtml: datatable_search_tag(datatable, name, opts),
16
- sortable: (opts[:sort] && sortable),
17
- title: datatable_label_tag(datatable, name, opts),
18
- visible: datatable.state[:visible][name]
14
+ sortable: (opts[:sort] && !datatable.simple?),
15
+ visible: datatable.state[:visible][name],
19
16
  }
20
17
  end.to_json.html_safe
21
18
  end
@@ -26,207 +23,78 @@ module EffectiveDatatablesPrivateHelper
26
23
  end
27
24
  end
28
25
 
29
- def datatable_display_order(datatable)
30
- (datatable.sortable? ? [datatable.order_index, datatable.order_direction] : false).to_json.html_safe
31
- end
32
-
33
26
  def datatable_reset(datatable)
34
- link_to(content_tag(:span, t('effective_datatables.reset')), '#', class: 'btn btn-link btn-sm buttons-reset-search')
35
- end
36
-
37
- def datatable_reorder(datatable)
38
- return unless datatable.reorder? && EffectiveDatatables.authorized?(self, :update, datatable.collection_class)
39
- link_to(content_tag(:span, t('effective_datatables.reorder')), '#', class: 'btn btn-link btn-sm buttons-reorder', disabled: true)
40
- end
41
-
42
- def datatable_new_resource_button(datatable, name, column)
43
- return unless column[:inline] && (column[:actions][:new] != false)
44
-
45
- action = { action: :new, class: ['btn', column[:btn_class].presence].compact.join(' '), 'data-remote': true }
46
-
47
- if column[:actions][:new].kind_of?(Hash) # This might be active_record_array_collection?
48
- action = action.merge(column[:actions][:new])
49
-
50
- effective_resource = (datatable.effective_resource || datatable.fallback_effective_resource)
51
- klass = (column[:actions][:new][:klass] || effective_resource.try(:klass) || datatable.collection_class)
52
- elsif Array(datatable.effective_resource.try(:actions)).include?(:new)
53
- effective_resource = datatable.effective_resource
54
- klass = effective_resource.klass
55
- else
56
- return
57
- end
58
-
59
- # Will only work if permitted
60
- render_resource_actions(klass, actions: { t('effective_datatables.new') => action }, effective_resource: effective_resource)
61
- end
62
-
63
- def datatable_label_tag(datatable, name, opts)
64
- case opts[:as]
65
- when :actions
66
- content_tag(:span, t('effective_datatables.actions'), style: 'display: none;')
67
- when :bulk_actions
68
- content_tag(:span, t('effective_datatables.bulk_actions'), style: 'display: none;')
69
- when :reorder
70
- content_tag(:span, t('effective_datatables.reorder'), style: 'display: none;')
71
- else
72
- content_tag(:span, opts[:label].presence)
73
- end
27
+ link_to(content_tag(:span, 'Reset'), '#', class: 'btn btn-link btn-sm buttons-reset-search')
74
28
  end
75
29
 
76
30
  def datatable_search_tag(datatable, name, opts)
77
- return datatable_new_resource_button(datatable, name, opts) if name == :_actions
78
-
79
31
  return if opts[:search] == false
80
32
 
81
33
  # Build the search
82
- @_effective_datatables_form_builder || simple_form_for(:datatable_search, url: '#', html: {id: "#{datatable.to_param}-form"}) { |f| @_effective_datatables_form_builder = f }
34
+ @_effective_datatables_form_builder || effective_form_with(scope: :datatable_search, url: '#') { |f| @_effective_datatables_form_builder = f }
83
35
  form = @_effective_datatables_form_builder
84
36
 
85
- include_blank = opts[:search].key?(:include_blank) ? opts[:search][:include_blank] : opts[:label]
86
- pattern = opts[:search][:pattern]
87
- placeholder = opts[:search][:placeholder] || ''
88
- title = opts[:search][:title] || opts[:label]
89
- wrapper_html = { class: 'datatable_search' }
90
-
91
37
  collection = opts[:search].delete(:collection)
92
38
  value = datatable.state[:search][name]
93
39
 
94
- input_html = {
40
+ options = opts[:search].except(:fuzzy).merge!(
95
41
  name: nil,
42
+ feedback: false,
43
+ label: false,
96
44
  value: value,
97
- title: title,
98
- pattern: pattern,
99
- data: {'column-name' => name, 'column-index' => opts[:index]}
100
- }.delete_if { |k, v| v.blank? && k != :name }
45
+ data: { 'column-name': name, 'column-index': opts[:index] }
46
+ )
101
47
 
102
- case opts[:search][:as]
48
+ case options.delete(:as)
103
49
  when :string, :text, :number
104
- form.input name, label: false, required: false, value: value,
105
- as: :string,
106
- placeholder: placeholder,
107
- wrapper_html: wrapper_html,
108
- input_html: input_html
109
- when :effective_obfuscation
110
- input_html[:pattern] ||= '[0-9]{3}-?[0-9]{4}-?[0-9]{3}'
111
- input_html[:title] = 'Expected format: XXX-XXXX-XXX'
112
-
113
- form.input name, label: false, required: false, value: value,
114
- as: :string,
115
- placeholder: placeholder,
116
- wrapper_html: wrapper_html,
117
- input_html: input_html
50
+ form.text_field name, options
118
51
  when :date, :datetime
119
- form.input name, label: false, required: false, value: value,
120
- as: (ActionView::Helpers::FormBuilder.instance_methods.include?(:effective_date_picker) ? :effective_date_picker : :string),
121
- placeholder: placeholder,
122
- wrapper_html: wrapper_html,
123
- input_group: false,
124
- input_html: input_html,
125
- date_linked: false,
126
- input_js: { useStrict: true, keepInvalid: true }
127
- # Keep invalid format like "2015-11" so we can still search by year, month or day
52
+ form.date_field name, options.reverse_merge(
53
+ date_linked: false, prepend: false, input_js: { useStrict: true, keepInvalid: true }
54
+ )
128
55
  when :time
129
- form.input name, label: false, required: false, value: value,
130
- as: (ActionView::Helpers::FormBuilder.instance_methods.include?(:effective_time_picker) ? :effective_time_picker : :string),
131
- placeholder: placeholder,
132
- wrapper_html: wrapper_html,
133
- input_group: false,
134
- input_html: input_html,
135
- date_linked: false,
136
- input_js: { useStrict: false, keepInvalid: true }
56
+ form.time_field name, options.reverse_merge(
57
+ date_linked: false, prepend: false, input_js: { useStrict: false, keepInvalid: true }
58
+ )
137
59
  when :select, :boolean
138
- form.input name, label: false, required: false, value: value,
139
- as: (ActionView::Helpers::FormBuilder.instance_methods.include?(:effective_select) ? :effective_select : :select),
140
- collection: collection,
141
- selected: opts[:search][:value],
142
- multiple: opts[:search][:multiple],
143
- grouped: opts[:search][:grouped],
144
- polymorphic: opts[:search][:polymorphic],
145
- template: opts[:search][:template],
146
- include_blank: include_blank,
147
- wrapper_html: wrapper_html,
148
- input_html: input_html,
149
- input_js: { placeholder: placeholder }
60
+ options[:input_js] = (options[:input_js] || {}).reverse_merge(placeholder: '')
61
+ form.select name, collection, options
150
62
  when :bulk_actions
151
- input_html[:data]['role'] = 'bulk-actions'
152
-
153
- form.input name, label: false, required: false, value: nil,
154
- as: :boolean,
155
- input_html: input_html
156
- end
157
- end
158
-
159
- def render_datatable_filters(datatable)
160
- raise 'expected datatable to be present' unless datatable
161
-
162
- datatable.view ||= self
163
- return unless datatable._scopes.present? || datatable._filters.present?
164
-
165
- if datatable._filters_form_required?
166
- render partial: 'effective/datatables/filters', locals: { datatable: datatable }
167
- else
168
- render(partial: 'effective/datatables/filters', locals: { datatable: datatable }).gsub('<form', '<div').gsub('/form>', '/div>').html_safe
63
+ options[:data]['role'] = 'bulk-actions-all'
64
+ form.check_box name, options.merge(custom: false)
169
65
  end
170
-
171
66
  end
172
67
 
173
68
  def datatable_filter_tag(form, datatable, name, opts)
174
- as = opts[:as].to_s.chomp('_field').to_sym
69
+ placeholder = opts.delete(:label)
70
+
71
+ collection = opts.delete(:collection)
175
72
  value = datatable.state[:filter][name]
176
- collection = opts[:collection]
177
- input_html = opts[:input_html] || {}
178
73
 
179
- attributes = {
74
+ options = opts.except(:parse).merge(
75
+ placeholder: placeholder,
76
+ feedback: false,
77
+ label: false,
180
78
  value: value,
181
- selected: value,
182
- as: as,
183
- collection: collection,
184
- label: opts[:label],
185
- required: input_html.delete(:required) || opts[:required],
186
- multiple: input_html.delete(:multiple) || opts[:multiple],
187
- include_blank: input_html.delete(:include_blank) || opts[:include_blank],
188
- group_method: input_html.delete(:group_method),
189
- group_label_method: input_html.delete(:group_label_method),
190
- value_method: input_html.delete(:value_method),
191
- label_method: input_html.delete(:label_method),
192
- input_html: (({name: ''} unless datatable._filters_form_required?) || {}).merge(input_html),
193
- input_js: ({ placeholder: ''} if as == :effective_select),
194
- wrapper_html: {class: 'form-group-sm'}
195
- }.compact
196
-
197
- form.input name, **attributes
198
- end
199
-
200
- def datatable_scope_tag(form, datatable, opts = {})
201
- collection = datatable._scopes.map { |name, opts| [opts[:label], name] }
202
- value = datatable.state[:scope]
79
+ wrapper: { class: 'form-group col-auto'},
80
+ autocomplete: 'off'
81
+ )
203
82
 
204
- form.input :scope, label: false, required: false, checked: value,
205
- as: (defined?(EffectiveFormInputs) ? :effective_radio_buttons : :radio_buttons),
206
- collection: collection,
207
- buttons: true,
208
- wrapper_html: {class: 'btn-group-sm'}
209
- end
210
-
211
- def render_datatable_charts(datatable)
212
- raise 'expected datatable to be present' unless datatable
213
-
214
- datatable.view ||= self
215
- return unless datatable._charts.present?
216
-
217
- datatable._charts.map { |name, _| render_datatable_chart(datatable, name) }.join.html_safe
218
- end
219
-
220
- def render_datatable_chart(datatable, name)
221
- raise 'expected datatable to be present' unless datatable
83
+ options[:name] = '' unless datatable._filters_form_required?
222
84
 
223
- datatable.view ||= self
224
- return unless datatable._charts[name].present?
225
-
226
- chart = datatable._charts[name]
227
- chart_data = datatable.to_json[:charts][name][:data]
228
-
229
- render partial: chart[:partial], locals: { datatable: datatable, chart: chart, chart_data: chart_data }
85
+ case options.delete(:as)
86
+ when :date
87
+ form.date_field name, options
88
+ when :datetime
89
+ form.datetime_field name, options
90
+ when :time
91
+ form.time_field name, options
92
+ when :select, :boolean
93
+ options[:input_js] = (options[:input_js] || {}).reverse_merge(placeholder: placeholder)
94
+ form.select name, collection, options
95
+ else
96
+ form.text_field name, options
97
+ end
230
98
  end
231
99
 
232
100
  end
@@ -1,7 +1,7 @@
1
1
  module Effective
2
2
  class Datatable
3
3
  attr_reader :attributes # Anything that we initialize our table with. That's it. Can't be changed by state.
4
- attr_reader :effective_resource
4
+ attr_reader :resource
5
5
  attr_reader :state
6
6
 
7
7
  # Hashes of DSL options
@@ -15,8 +15,6 @@ module Effective
15
15
 
16
16
  # The collection itself. Only evaluated once.
17
17
  attr_accessor :_collection
18
- attr_accessor :_collection_apply_belongs_to
19
- attr_accessor :_collection_apply_scope
20
18
 
21
19
  # The view
22
20
  attr_reader :view
@@ -33,10 +31,10 @@ module Effective
33
31
  include Effective::EffectiveDatatable::Resource
34
32
  include Effective::EffectiveDatatable::State
35
33
 
36
- def initialize(view = nil, attributes = nil)
34
+ def initialize(view = nil, attributes = {})
37
35
  (attributes = view; view = nil) if view.kind_of?(Hash)
38
36
 
39
- @attributes = (attributes || {})
37
+ @attributes = initial_attributes(attributes)
40
38
  @state = initial_state
41
39
 
42
40
  @_aggregates = {}
@@ -47,7 +45,6 @@ module Effective
47
45
  @_form = {}
48
46
  @_scopes = {}
49
47
 
50
- raise 'expected a hash of arguments' unless @attributes.kind_of?(Hash)
51
48
  raise 'collection is defined as a method. Please use the collection do ... end syntax.' unless collection.nil?
52
49
  self.view = view if view
53
50
  end
@@ -57,23 +54,21 @@ module Effective
57
54
  @view = (view.respond_to?(:view_context) ? view.view_context : view)
58
55
  raise 'expected view to respond to params' unless @view.respond_to?(:params)
59
56
 
60
- assert_attributes!
57
+ load_cookie!
61
58
  load_attributes!
62
59
 
63
60
  # We need early access to filter and scope, to define defaults from the model first
64
- # This means filters do know about attributes but not about columns.
61
+ # This means filters do knows about attributes but not about columns.
65
62
  initialize_filters if respond_to?(:initialize_filters)
66
63
  load_filters!
67
64
  load_state!
68
65
 
69
- # Bulk actions called first so it can add the bulk_actions_col first
70
- initialize_bulk_actions if respond_to?(:initialize_bulk_actions)
71
-
72
66
  # Now we initialize all the columns. columns knows about attributes and filters and scope
73
67
  initialize_datatable if respond_to?(:initialize_datatable)
74
68
  load_columns!
75
69
 
76
70
  # Execute any additional DSL methods
71
+ initialize_bulk_actions if respond_to?(:initialize_bulk_actions)
77
72
  initialize_charts if respond_to?(:initialize_charts)
78
73
 
79
74
  # Load the collection. This is the first time def collection is called on the Datatable itself
@@ -83,10 +78,9 @@ module Effective
83
78
  # Figure out the class, and if it's activerecord, do all the resource discovery on it
84
79
  load_resource!
85
80
 
86
- # Check everything is okay
87
- validate_datatable!
81
+ # If attributes match a belongs_to column, scope the collection and remove the column
82
+ apply_belongs_to_attributes!
88
83
 
89
- # Save for next time
90
84
  save_cookie!
91
85
  end
92
86
 
@@ -123,18 +117,11 @@ module Effective
123
117
  )
124
118
  end
125
119
 
126
- # Inline crud
127
- def inline?
128
- attributes[:inline] == true
129
- end
130
-
131
- # Reordering
132
- def reorder?
133
- columns.key?(:_reorder)
134
- end
135
-
136
- def sortable?
137
- !reorder? && attributes[:sortable] != false
120
+ # When simple only a table will be rendered with
121
+ # no sorting, no filtering, no export buttons, no pagination, no per page, no colReorder
122
+ # default sorting only, default visibility only, all records returned, and responsive enabled
123
+ def simple?
124
+ attributes[:simple] == true
138
125
  end
139
126
 
140
127
  # Whether the filters must be rendered as a <form> or we can keep the normal <div> behaviour
@@ -142,12 +129,12 @@ module Effective
142
129
  _form[:verb].present?
143
130
  end
144
131
 
145
- def html_class
146
- Array(attributes[:class] || EffectiveDatatables.html_class).join(' ').presence
132
+ def table_html_class
133
+ attributes[:class] || EffectiveDatatables.html_class
147
134
  end
148
135
 
149
136
  def to_param
150
- "#{self.class.name.underscore.parameterize}-#{[self.class, attributes].hash.abs.to_s.last(12)}"
137
+ @to_param ||= "#{self.class.name.underscore.parameterize}-#{cookie_param}"
151
138
  end
152
139
 
153
140
  def columns
@@ -162,14 +149,6 @@ module Effective
162
149
  @dsl_tool ||= DatatableDslTool.new(self)
163
150
  end
164
151
 
165
- def resource
166
- raise('depecated. Please use .effective_resource instead')
167
- end
168
-
169
- def fallback_effective_resource
170
- @fallback_effective_resource ||= Effective::Resource.new('', namespace: controller_namespace)
171
- end
172
-
173
152
  private
174
153
 
175
154
  def column_tool
@@ -180,12 +159,5 @@ module Effective
180
159
  @value_tool ||= DatatableValueTool.new(self)
181
160
  end
182
161
 
183
- def validate_datatable!
184
- if reorder?
185
- raise 'cannot use reorder with an Array collection' unless active_record_collection?
186
- raise 'cannot use reorder with a non-Integer column' if effective_resource.sql_type(columns[:_reorder][:reorder]) != :integer
187
- end
188
- end
189
-
190
162
  end
191
163
  end
@@ -1,4 +1,3 @@
1
- # In practice this is just a regular hash with the aggregate, format, search, sort do syntax that saves a block
2
1
  module Effective
3
2
  class DatatableColumn
4
3
  attr_accessor :attributes
@@ -43,7 +43,7 @@ module Effective
43
43
  Rails.logger.info "COLUMN TOOL: order_column #{column.to_s} #{direction} #{sql_column}" if EffectiveDatatables.debug
44
44
 
45
45
  if column[:sql_as_column]
46
- collection.order("#{sql_column} #{datatable.effective_resource.sql_direction(direction)}")
46
+ collection.order("#{sql_column} #{datatable.resource.sql_direction(direction)}")
47
47
  else
48
48
  Effective::Resource.new(collection)
49
49
  .order(column[:name], direction, as: column[:as], sort: column[:sort], sql_column: column[:sql_column], limit: datatable.limit)
@@ -75,10 +75,8 @@ module Effective
75
75
  def search_column(collection, value, column, sql_column)
76
76
  Rails.logger.info "COLUMN TOOL: search_column #{column.to_s} #{value} #{sql_column}" if EffectiveDatatables.debug
77
77
 
78
- operation = (column[:search][:fuzzy] && column[:as] == :string) ? :matches : :eq
79
-
80
78
  Effective::Resource.new(collection)
81
- .search(column[:name], value, as: column[:as], operation: operation, column: sql_column)
79
+ .search(column[:name], value, as: column[:as], fuzzy: column[:search][:fuzzy], sql_column: sql_column)
82
80
  end
83
81
 
84
82
  def paginate(collection)
@@ -16,24 +16,16 @@ module Effective
16
16
  @view = datatable.view
17
17
  end
18
18
 
19
- def method_missing(method, *args, &block)
19
+ def method_missing(method, *args)
20
20
  # Catch a common error
21
21
  if [:bulk_actions, :charts, :collection, :filters].include?(method) && in_datatables_do_block
22
22
  raise "#{method} block must be declared outside the datatable do ... end block"
23
23
  end
24
24
 
25
25
  if datatable.respond_to?(method)
26
- if block_given?
27
- datatable.send(method, *args) { yield }
28
- else
29
- datatable.send(method, *args)
30
- end
26
+ datatable.send(method, *args)
31
27
  elsif view.respond_to?(method)
32
- if block_given?
33
- view.send(method, *args) { yield }
34
- else
35
- view.send(method, *args)
36
- end
28
+ view.send(method, *args)
37
29
  else
38
30
  super
39
31
  end
@@ -74,12 +74,13 @@ module Effective
74
74
  collection
75
75
  end
76
76
 
77
- def search_column(collection, original, column, index)
78
- Rails.logger.info "VALUE TOOL: search_column #{column.to_s} #{original} #{index}" if EffectiveDatatables.debug
77
+ def search_column(collection, value, column, index)
78
+ Rails.logger.info "VALUE TOOL: search_column #{column.to_s} #{value} #{index}" if EffectiveDatatables.debug
79
79
 
80
+ macros = Effective::Resource.new('').macros
80
81
  fuzzy = column[:search][:fuzzy]
81
82
 
82
- term = Effective::Attribute.new(column[:as]).parse(original, name: column[:name])
83
+ term = Effective::Attribute.new(column[:as]).parse(value, name: column[:name])
83
84
  term_downcased = term.to_s.downcase
84
85
 
85
86
  # term == 'nil' rescue false is a Rails 4.1 fix, where you can't compare a TimeWithZone to 'nil'
@@ -89,19 +90,18 @@ module Effective
89
90
 
90
91
  # See effective_resources gem search() method # relation.rb
91
92
  collection.select! do |row|
92
- obj = row[index]
93
- value = obj_to_value(row[index], column, row)
93
+ obj = obj_to_value(row[index], column, row)
94
94
 
95
95
  case column[:as]
96
96
  when :boolean
97
- if term
98
- ['Yes', 'yes', true, 'true', '1'].include?(value)
97
+ if fuzzy
98
+ term ? (obj == true) : (obj != true)
99
99
  else
100
- ['No', 'no', false, 'false', '0'].include?(value)
100
+ obj == term
101
101
  end
102
102
  when :datetime, :date
103
103
  end_at = (
104
- case (original.to_s.scan(/(\d+)/).flatten).length
104
+ case (value.to_s.scan(/(\d+)/).flatten).length
105
105
  when 1 ; term.end_of_year # Year
106
106
  when 2 ; term.end_of_month # Year-Month
107
107
  when 3 ; term.end_of_day # Year-Month-Day
@@ -111,35 +111,35 @@ module Effective
111
111
  else term
112
112
  end
113
113
  )
114
- value >= term && value <= end_at
114
+ obj >= term && obj <= end_at
115
115
  when :time
116
- (value.hour == term.hour) && (term.min == 0 ? true : (value.min == term.min))
116
+ (obj.hour == term.hour) && (term.min == 0 ? true : (obj.min == term.min))
117
117
  when :decimal, :currency
118
- if fuzzy && (term.round(0) == term) && original.to_s.include?('.') == false
118
+ if fuzzy && (term.round(0) == term) && value.to_s.include?('.') == false
119
119
  if term < 0
120
- value <= term && value > (term - 1.0)
120
+ obj <= term && obj > (term - 1.0)
121
121
  else
122
- value >= term && value < (term + 1.0)
122
+ obj >= term && obj < (term + 1.0)
123
123
  end
124
124
  else
125
- value == term
125
+ obj == term
126
126
  end
127
127
  when :duration
128
- if fuzzy && (term % 60 == 0) && original.to_s.include?('m') == false
128
+ if fuzzy && (term % 60 == 0) && value.to_s.include?('m') == false
129
129
  if term < 0
130
- value <= term && value > (term - 60)
130
+ obj <= term && obj > (term - 60)
131
131
  else
132
- value >= term && value < (term + 60)
132
+ obj >= term && obj < (term + 60)
133
133
  end
134
134
  else
135
- value == term
135
+ obj == term
136
136
  end
137
- when *datatable.association_macros, :resource
137
+ when *macros, :resource
138
138
  Array(obj).any? do |resource|
139
139
  Array(term).any? do |term|
140
140
  matched = false
141
141
 
142
- if term.kind_of?(Integer) && resource.respond_to?(:id) && resource.respond_to?(:to_param)
142
+ if term.kind_of?(Integer) && resource.respond_to?(:to_param)
143
143
  matched = (resource.id == term || resource.to_param == term)
144
144
  end
145
145
 
@@ -148,9 +148,9 @@ module Effective
148
148
  end
149
149
  else # :string, :text, :email
150
150
  if fuzzy
151
- value.to_s.downcase.include?(term_downcased)
151
+ obj.to_s.downcase.include?(term_downcased)
152
152
  else
153
- value == term || (value.to_s == term.to_s)
153
+ obj == term || (obj.to_s == term.to_s)
154
154
  end
155
155
  end
156
156
  end || collection
@@ -4,14 +4,22 @@ module Effective
4
4
 
5
5
  private
6
6
 
7
- def assert_attributes!
8
- if datatables_ajax_request? || datatables_inline_request?
9
- raise 'expected attributes to be present' unless attributes.present?
10
- end
7
+ def initial_attributes(args)
8
+ raise "#{self.class.name}.new() expected Hash like arguments" unless args.kind_of?(Hash)
9
+ args
11
10
  end
12
11
 
13
12
  def load_attributes!
14
- @attributes[:namespace] ||= view.controller_path.split('/')[0...-1].join('/')
13
+ if datatables_ajax_request?
14
+ raise 'expected cookie to be present' unless cookie
15
+ raise 'expected attributes cookie to be present' unless cookie[:attributes]
16
+
17
+ @attributes = cookie.delete(:attributes)
18
+ end
19
+
20
+ unless datatables_ajax_request?
21
+ @attributes[:_n] ||= view.controller_path.split('/')[0...-1].join('/').presence
22
+ end
15
23
  end
16
24
 
17
25
  end
@@ -7,23 +7,10 @@ module Effective
7
7
  @collection_class # Will be either User/Post/etc or Array
8
8
  end
9
9
 
10
- # User.all
11
10
  def active_record_collection?
12
11
  @active_record_collection == true
13
12
  end
14
13
 
15
- # [User<1>, User<2>, Post<1>, Page<3>]
16
- def active_record_array_collection?
17
- @active_record_array_collection == true
18
- end
19
-
20
- def active_record_polymorphic_array_collection?
21
- return false unless active_record_array_collection?
22
- return @active_record_polymorphic_array_collection unless @active_record_polymorphic_array_collection.nil?
23
- @active_record_polymorphic_array_collection = collection.map { |obj| obj.class }.uniq.length > 1
24
- end
25
-
26
- # [[1, 'foo'], [2, 'bar']]
27
14
  def array_collection?
28
15
  @array_collection == true
29
16
  end
@@ -34,13 +21,11 @@ module Effective
34
21
  raise 'No collection defined. Please add a collection with collection do ... end' if collection.nil?
35
22
 
36
23
  @collection_class = (collection.respond_to?(:klass) ? collection.klass : self.class)
37
-
38
24
  @active_record_collection = (collection.ancestors.include?(ActiveRecord::Base) rescue false)
39
- @active_record_array_collection = collection.kind_of?(Array) && collection.present? && collection.first.kind_of?(ActiveRecord::Base)
40
- @array_collection = collection.kind_of?(Array) && (collection.blank? || collection.first.kind_of?(Array))
25
+ @array_collection = (collection.kind_of?(Array) && (collection.length == 0 || collection.first.kind_of?(Array)))
41
26
 
42
- unless active_record_collection? || active_record_array_collection? || array_collection?
43
- raise "Unsupported collection. Expecting an ActiveRecord relation, an Array of ActiveRecord objects, or an Array of Arrays [[1, 'foo'], [2, 'bar']]"
27
+ unless active_record_collection? || array_collection?
28
+ raise "Unsupported collection type. Expecting an ActiveRecord class, ActiveRecord relation, or an Array of Arrays [[1, 'foo'], [2, 'bar']]"
44
29
  end
45
30
 
46
31
  _scopes.each do |scope, _|