effective_datatables 3.7.7 → 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 (68) 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.js +4 -4
  12. data/app/assets/javascripts/effective_datatables/bulk_actions.js.coffee +43 -43
  13. data/app/assets/javascripts/effective_datatables/events.js.coffee +7 -4
  14. data/app/assets/javascripts/effective_datatables/filters.js.coffee +0 -1
  15. data/app/assets/javascripts/effective_datatables/initialize.js.coffee +45 -49
  16. data/app/assets/javascripts/effective_datatables/overrides.js +12 -0
  17. data/app/assets/javascripts/effective_datatables/reset.js.coffee +1 -1
  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.scss +3 -4
  22. data/app/assets/stylesheets/effective_datatables/_overrides.scss +72 -152
  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 +1 -1
  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.rb +3 -8
  36. data/app/models/effective/effective_datatable/dsl/bulk_actions.rb +25 -14
  37. data/app/models/effective/effective_datatable/dsl/datatable.rb +28 -70
  38. data/app/models/effective/effective_datatable/dsl/filters.rb +5 -5
  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.rb +4 -57
  50. data/lib/effective_datatables/engine.rb +1 -1
  51. data/lib/effective_datatables/version.rb +1 -1
  52. metadata +20 -30
  53. data/app/assets/images/dataTables/sort_asc.png +0 -0
  54. data/app/assets/images/dataTables/sort_both.png +0 -0
  55. data/app/assets/images/dataTables/sort_desc.png +0 -0
  56. data/app/assets/javascripts/dataTables/dataTables.bootstrap.js +0 -182
  57. data/app/assets/javascripts/dataTables/locales/en.lang +0 -33
  58. data/app/assets/javascripts/dataTables/locales/es.lang +0 -36
  59. data/app/assets/javascripts/dataTables/locales/nl.lang +0 -30
  60. data/app/assets/javascripts/effective_datatables/flash.js.coffee +0 -31
  61. data/app/assets/javascripts/effective_datatables/inline_crud.js.coffee +0 -217
  62. data/app/assets/javascripts/effective_datatables/overrides.js.coffee +0 -7
  63. data/app/assets/javascripts/effective_datatables/reorder.js.coffee +0 -43
  64. data/app/assets/stylesheets/effective_datatables/_filters.scss +0 -7
  65. data/app/views/effective/datatables/_reorder_column.html.haml +0 -5
  66. data/config/locales/en.yml +0 -12
  67. data/config/locales/es.yml +0 -12
  68. data/config/locales/nl.yml +0 -12
@@ -5,7 +5,7 @@ module Effective
5
5
  def filter(name = nil, value = :_no_value, as: nil, label: nil, parse: nil, required: false, **input_html)
6
6
  return datatable.filter if (name == nil && value == :_no_value) # This lets block methods call 'filter' and get the values
7
7
 
8
- raise 'expected second argument to be a value. the default value for this filter.' if value == :_no_value
8
+ raise 'expected second argument to be a value' if value == :_no_value
9
9
  raise 'parse must be a Proc' if parse.present? && !parse.kind_of?(Proc)
10
10
 
11
11
  # Merge search
@@ -17,19 +17,19 @@ module Effective
17
17
  as ||= (
18
18
  if input_html.key?(:collection)
19
19
  :select
20
- elsif value != nil
20
+ elsif value.present?
21
21
  Effective::Attribute.new(value).type
22
22
  end
23
- ) || :text
23
+ )
24
24
 
25
25
  datatable._filters[name.to_sym] = {
26
26
  value: value,
27
27
  as: as,
28
- label: label,
28
+ label: label || (label == false ? false : name.to_s.titleize),
29
29
  name: name.to_sym,
30
30
  parse: parse,
31
31
  required: required,
32
- }.compact.reverse_merge(input_html)
32
+ }.reverse_merge(input_html)
33
33
  end
34
34
 
35
35
  def scope(name = nil, *args, default: nil, label: nil)
@@ -2,9 +2,6 @@ module Effective
2
2
  module EffectiveDatatable
3
3
  module Format
4
4
  BLANK = ''.freeze
5
- NONVISIBLE = '...'.freeze
6
- SPACER = 'EFFECTIVEDATATABLESSPACER'.freeze
7
- SPACER_TEMPLATE = '/effective/datatables/spacer_template'.freeze
8
5
 
9
6
  private
10
7
 
@@ -13,10 +10,12 @@ module Effective
13
10
  rendered = {}
14
11
 
15
12
  columns.each do |name, opts|
16
- next unless state[:visible][name]
17
-
18
- if opts[:partial]
19
- locals = { datatable: self, column: opts }.merge(resource_col_locals(opts))
13
+ if opts[:partial] && state[:visible][name]
14
+ locals = {
15
+ datatable: self,
16
+ column: columns[name],
17
+ controller_namespace: controller_namespace
18
+ }.merge(actions_col_locals(opts)).merge(resource_col_locals(opts))
20
19
 
21
20
  rendered[name] = (view.render(
22
21
  partial: opts[:partial],
@@ -24,42 +23,20 @@ module Effective
24
23
  collection: collection.map { |row| row[opts[:index]] },
25
24
  formats: :html,
26
25
  locals: locals,
27
- spacer_template: SPACER_TEMPLATE
28
- ) || '').split(SPACER)
29
- elsif opts[:as] == :actions # This is actions_col and actions_col do .. end, but not actions_col partial: 'something'
30
- resources = collection.map { |row| row[opts[:index]] }
31
- locals = { datatable: self, column: opts, spacer_template: SPACER_TEMPLATE }
32
-
33
- atts = {
34
- actions: actions_col_actions(opts),
35
- btn_class: opts[:btn_class],
36
- effective_resource: effective_resource,
37
- locals: locals,
38
- partial: opts[:actions_partial],
39
- }.compact.merge(opts[:actions])
40
-
41
- rendered[name] = if effective_resource.blank?
42
- resources.map do |resource|
43
- polymorphic_resource = Effective::Resource.new(resource, namespace: controller_namespace)
44
- (view.render_resource_actions(resource, atts.merge(effective_resource: polymorphic_resource), &opts[:format]) || '')
45
- end
46
- else
47
- (view.render_resource_actions(resources, atts, &opts[:format]) || '').split(SPACER)
48
- end
26
+ spacer_template: '/effective/datatables/spacer_template',
27
+ ) || '').split('EFFECTIVEDATATABLESSPACER')
49
28
  end
50
29
  end
51
30
 
52
31
  collection.each_with_index do |row, row_index|
53
32
  columns.each do |name, opts|
33
+ next unless state[:visible][name]
34
+
54
35
  index = opts[:index]
55
36
  value = row[index]
56
37
 
57
38
  row[index] = (
58
- if state[:visible][name] == false
59
- NONVISIBLE
60
- elsif opts[:as] == :actions
61
- rendered[name][row_index]
62
- elsif opts[:format] && rendered.key?(name)
39
+ if opts[:format] && rendered.key?(name)
63
40
  dsl_tool.instance_exec(value, row, rendered[name][row_index], &opts[:format])
64
41
  elsif opts[:format]
65
42
  dsl_tool.instance_exec(value, row, &opts[:format])
@@ -81,22 +58,23 @@ module Effective
81
58
  end
82
59
 
83
60
  case column[:as]
84
- when :actions
85
- raise("please use actions_col instead of col(#{name}, as: :actions)")
86
61
  when :boolean
87
- view.t("effective_datatables.boolean_#{value}")
62
+ case value
63
+ when true ; 'Yes'
64
+ when false ; 'No'
65
+ end
88
66
  when :currency
89
67
  view.number_to_currency(value)
90
68
  when :date
91
- value.respond_to?(:strftime) ? value.strftime(EffectiveDatatables.format_date) : BLANK
69
+ (value.strftime('%F') rescue BLANK)
92
70
  when :datetime
93
- value.respond_to?(:strftime) ? value.strftime(EffectiveDatatables.format_datetime) : BLANK
71
+ (value.strftime('%F %H:%M') rescue BLANK)
94
72
  when :decimal
95
73
  value
96
74
  when :duration
97
75
  view.number_to_duration(value)
98
76
  when :effective_addresses
99
- (value.respond_to?(:to_html) ? value.to_html : value).to_s
77
+ value.to_html
100
78
  when :effective_obfuscation
101
79
  value
102
80
  when :effective_roles
@@ -105,10 +83,10 @@ module Effective
105
83
  view.mail_to(value)
106
84
  when :integer
107
85
  value
108
- when :percent
86
+ when :percentage
109
87
  case value
110
- when Integer ; view.number_to_percentage(value / 1000.0, precision: 3).gsub('.000%', '%')
111
- when Numeric ; view.number_to_percentage(value, precision: 3).gsub('.000%', '%')
88
+ when Integer ; "#{value}%"
89
+ when Numeric ; view.number_to_percentage(value * 100, precision: 2)
112
90
  end
113
91
  when :price
114
92
  case value
@@ -116,74 +94,51 @@ module Effective
116
94
  when Numeric ; view.number_to_currency(value)
117
95
  end
118
96
  when :time
119
- value.respond_to?(:strftime) ? value.strftime(EffectiveDatatables.format_time) : BLANK
97
+ (value.strftime('%H:%M') rescue BLANK)
120
98
  else
121
99
  value.to_s
122
100
  end
123
101
  end
124
102
 
125
- # Takes all default resource actions
126
- # Applies data-remote to anything that's data-method post or delete
127
- # Merges in any extra attributes when passed as a Hash
128
- def actions_col_actions(column)
129
- resource_actions = (effective_resource.try(:resource_actions) || fallback_effective_resource.fallback_resource_actions)
130
-
131
- actions = if column[:inline]
132
- resource_actions.transform_values { |opts| opts['data-remote'] = true; opts }
133
- else
134
- resource_actions.transform_values { |opts| opts['data-remote'] = true if opts['data-method']; opts }
135
- end
136
-
137
- # Merge local options. Special behaviour for remote: false
138
- if column[:actions].kind_of?(Hash)
139
- column[:actions].each do |action, opts|
140
- next unless opts.kind_of?(Hash)
141
-
142
- existing = actions.find { |_, v| v[:action] == action }.try(:first)
143
- next unless existing.present?
103
+ def actions_col_locals(opts)
104
+ return {} unless opts[:as] == :actions
144
105
 
145
- actions[existing]['data-remote'] = opts[:remote] if opts.key?(:remote)
146
- actions[existing]['data-remote'] = opts['remote'] if opts.key?('remote')
147
-
148
- actions[existing].merge!(opts.except(:remote, 'remote'))
149
- end
150
-
151
- actions = actions.sort do |(_, a), (_, b)|
152
- (column[:actions].keys.index(a[:action]) || 99) <=> (column[:actions].keys.index(b[:action]) || 99)
153
- end.to_h
154
-
155
- end
156
-
157
- actions
106
+ locals = {
107
+ show_action: (
108
+ active_record_collection? && opts[:show] && resource.routes[:show] &&
109
+ EffectiveDatatables.authorized?(view.controller, :show, collection_class)
110
+ ),
111
+ edit_action: (
112
+ active_record_collection? && opts[:edit] && resource.routes[:edit] &&
113
+ EffectiveDatatables.authorized?(view.controller, :edit, collection_class)
114
+ ),
115
+ destroy_action: (
116
+ active_record_collection? && opts[:destroy] && resource.routes[:destroy] &&
117
+ EffectiveDatatables.authorized?(view.controller, :destroy, collection_class)
118
+ ),
119
+ effective_resource: resource
120
+ }
158
121
  end
159
122
 
160
123
  def resource_col_locals(opts)
161
- return {} unless (associated_resource = opts[:resource]).present?
162
-
163
- associated = associated_resource.macros.include?(opts[:as])
164
- polymorphic = (opts[:as] == :belongs_to_polymorphic)
165
-
166
- resource_name = opts[:name] if associated
167
- resource_to_s = opts[:name] unless associated || array_collection?
124
+ return {} unless (resource = opts[:resource]).present?
168
125
 
169
- locals = {
170
- resource_name: resource_name,
171
- resource_to_s: resource_to_s,
172
- effective_resource: associated_resource,
173
- show_action: false,
174
- edit_action: false
175
- }
126
+ locals = { name: opts[:name], effective_resource: resource, show_action: false, edit_action: false }
176
127
 
177
128
  case opts[:action]
178
129
  when :edit
179
- locals[:edit_action] = (polymorphic || associated_resource.routes[:edit].present?)
130
+ locals[:edit_action] = (resource.routes[:edit] && EffectiveDatatables.authorized?(view.controller, :edit, resource.klass))
180
131
  when :show
181
- locals[:show_action] = (polymorphic || associated_resource.routes[:show].present?)
132
+ locals[:show_action] = (resource.routes[:show] && EffectiveDatatables.authorized?(view.controller, :show, resource.klass))
182
133
  when false
183
- # Nothing. Already false.
134
+ # Nothing
184
135
  else
185
- locals[:edit_action] = (polymorphic || associated_resource.routes[:edit].present?)
186
- locals[:show_action] = (polymorphic || associated_resource.routes[:show].present?)
136
+ # Fallback to defaults - check edit then show
137
+ if resource.routes[:edit] && EffectiveDatatables.authorized?(view.controller, :edit, resource.klass)
138
+ locals[:edit_action] = true
139
+ elsif resource.routes[:show] && EffectiveDatatables.authorized?(view.controller, :show, resource.klass)
140
+ locals[:show_action] = true
141
+ end
187
142
  end
188
143
 
189
144
  locals
@@ -7,13 +7,8 @@ module Effective
7
7
  def datatables_ajax_request?
8
8
  return @_datatables_ajax_request unless @_datatables_ajax_request.nil?
9
9
 
10
- @_datatables_ajax_request = (view.present? && view.params.key?(:draw) && view.params.key?(:columns))
11
- end
12
-
13
- def datatables_inline_request?
14
- return @_datatables_inline_request unless @_datatables_inline_request.nil?
15
-
16
- @_datatables_inline_request = (view.present? && view.params[:_datatable_id].to_s.split('-')[0...-1] == to_param.split('-')[0...-1])
10
+ @_datatables_ajax_request =
11
+ (view && view.params[:draw] && view.params[:columns] && cookie_keys.include?(view.params[:cookie])) == true
17
12
  end
18
13
 
19
14
  def params
@@ -34,7 +29,7 @@ module Effective
34
29
 
35
30
  def search_params
36
31
  params.select do |name, value|
37
- columns.key?(name) && ![:id, :action].include?(name) && !value.kind_of?(Hash) && value.class.name != 'ActionController::Parameters'.freeze
32
+ columns.key?(name) && (name != :id) && !value.kind_of?(Hash) && value.class.name != 'ActionController::Parameters'.freeze
38
33
  end
39
34
  end
40
35
  end
@@ -8,148 +8,108 @@ module Effective
8
8
  end
9
9
 
10
10
  def controller_namespace
11
- @attributes[:namespace]
12
- end
13
-
14
- def association_macros
15
- [:belongs_to, :belongs_to_polymorphic, :has_many, :has_and_belongs_to_many, :has_one]
11
+ @attributes[:_n]
16
12
  end
17
13
 
18
14
  private
19
15
 
20
- def load_effective_resource!
21
- @effective_resource = if active_record_collection?
22
- Effective::Resource.new(collection_class, namespace: controller_namespace)
23
- end
24
- end
25
-
26
16
  # This looks at all the columns and figures out the as:
27
17
  def load_resource!
28
- load_effective_resource!
18
+ @resource = Effective::Resource.new(collection_class, namespace: controller_namespace)
29
19
 
30
- load_active_record_collection!
31
- load_active_record_array_collection!
32
- load_array_collection!
20
+ if active_record_collection?
21
+ columns.each do |name, opts|
33
22
 
34
- load_resource_columns!
35
- load_resource_belongs_tos!
36
- load_resource_search!
37
- end
23
+ # col 'comments.title'
24
+ if name.kind_of?(String) && name.include?('.')
25
+ raise "invalid datatables column '#{name}'. the joined syntax only supports one dot." if name.scan(/\./).count > 1
38
26
 
39
- def load_effective_resource!
40
- @effective_resource = if active_record_collection?
41
- Effective::Resource.new(collection_class, namespace: controller_namespace)
42
- end
43
- end
44
-
45
- def load_active_record_collection!
46
- return unless active_record_collection?
27
+ (associated, field) = name.split('.').first(2)
47
28
 
48
- columns.each do |name, opts|
49
- # col 'comments.title'
50
- if name.kind_of?(String) && name.include?('.')
51
- raise "invalid datatables column '#{name}'. the joined syntax only supports one dot." if name.scan(/\./).count > 1
29
+ unless resource.macros.include?(resource.sql_type(associated))
30
+ raise "invalid datatables column '#{name}'. unable to find '#{name.split('.').first}' association on '#{resource}'."
31
+ end
52
32
 
53
- (associated, field) = name.split('.').first(2)
33
+ joins_values = (collection.joins_values + collection.left_outer_joins_values)
54
34
 
55
- unless association_macros.include?(effective_resource.sql_type(associated))
56
- raise "invalid datatables column '#{name}'. unable to find '#{name.split('.').first}' association on '#{effective_resource}'."
57
- end
35
+ unless joins_values.include?(associated.to_sym)
36
+ raise "your datatables collection must .joins(:#{associated}) or .left_outer_joins(:#{associated}) to work with the joined syntax"
37
+ end
58
38
 
59
- joins_values = (collection.joins_values + collection.left_outer_joins_values)
39
+ opts[:resource] = Effective::Resource.new(resource.associated(associated), namespace: controller_namespace)
60
40
 
61
- unless joins_values.include?(associated.to_sym)
62
- raise "your datatables collection must .joins(:#{associated}) or .left_outer_joins(:#{associated}) to work with the joined syntax"
63
- end
41
+ if opts[:resource].column(field)
42
+ opts[:as] ||= opts[:resource].sql_type(field)
43
+ opts[:as] = :integer if opts[:resource].sql_type(field) == :belongs_to && field.end_with?('_id')
44
+ opts[:sql_column] = opts[:resource].sql_column(field) if opts[:sql_column].nil?
64
45
 
65
- opts[:resource] = Effective::Resource.new(effective_resource.associated(associated), namespace: controller_namespace)
46
+ opts[:resource].sort_column = field
47
+ opts[:resource].search_columns = field
48
+ end
66
49
 
67
- if opts[:resource].column(field)
68
- opts[:as] ||= opts[:resource].sql_type(field)
69
- opts[:as] = :integer if opts[:resource].sql_type(field) == :belongs_to && field.end_with?('_id')
70
- opts[:sql_column] = opts[:resource].sql_column(field) if opts[:sql_column].nil?
50
+ opts[:resource_field] = field
71
51
 
72
- opts[:resource].sort_column = field
73
- opts[:resource].search_columns = field
52
+ next
74
53
  end
75
54
 
76
- opts[:resource_field] = field
77
-
78
- next
79
- end
80
-
81
- # Regular fields
82
- opts[:as] ||= effective_resource.sql_type(name)
83
- opts[:sql_column] = effective_resource.sql_column(name) if opts[:sql_column].nil?
55
+ # Regular fields
56
+ opts[:as] ||= resource.sql_type(name)
57
+ opts[:sql_column] = resource.sql_column(name) if opts[:sql_column].nil?
84
58
 
85
- case opts[:as]
86
- when *association_macros
87
- opts[:resource] ||= Effective::Resource.new(effective_resource.associated(name), namespace: controller_namespace)
88
- opts[:sql_column] = name if opts[:sql_column].nil?
89
- when Class
90
- if opts[:as].ancestors.include?(ActiveRecord::Base)
91
- opts[:resource] = Effective::Resource.new(opts[:as], namespace: controller_namespace)
92
- opts[:as] = :resource
59
+ case opts[:as]
60
+ when *resource.macros
61
+ opts[:resource] ||= Effective::Resource.new(resource.associated(name), namespace: controller_namespace)
93
62
  opts[:sql_column] = name if opts[:sql_column].nil?
63
+ when Class
64
+ if opts[:as].ancestors.include?(ActiveRecord::Base)
65
+ opts[:resource] = Effective::Resource.new(opts[:as], namespace: controller_namespace)
66
+ opts[:as] = :resource
67
+ opts[:sql_column] = name if opts[:sql_column].nil?
68
+ end
69
+ when :effective_addresses
70
+ opts[:resource] = Effective::Resource.new(resource.associated(name), namespace: controller_namespace)
71
+ opts[:sql_column] = :effective_addresses
72
+ when :effective_roles
73
+ opts[:sql_column] = :effective_roles
74
+ when :string # This is the fallback
75
+ # Anything that doesn't belong to the model or the sql table, we assume is a SELECT SUM|AVG|RANK() as fancy
76
+ opts[:sql_as_column] = true if (resource.table && resource.column(name).blank?)
94
77
  end
95
- when :effective_addresses
96
- opts[:resource] = Effective::Resource.new(effective_resource.associated(name), namespace: controller_namespace)
97
- opts[:sql_column] = :effective_addresses
98
- when :effective_roles
99
- opts[:sql_column] = :effective_roles
100
- when :string # This is the fallback
101
- # Anything that doesn't belong to the model or the sql table, we assume is a SELECT SUM|AVG|RANK() as fancy
102
- opts[:sql_as_column] = true if (effective_resource.table && effective_resource.column(name).blank?)
103
- end
104
78
 
105
- if opts[:sql_column].present? && AGGREGATE_SQL_FUNCTIONS.any? { |str| opts[:sql_column].to_s.start_with?(str) }
106
- opts[:sql_as_column] = true
79
+ if opts[:sql_column].present? && AGGREGATE_SQL_FUNCTIONS.any? { |str| opts[:sql_column].to_s.start_with?(str) }
80
+ opts[:sql_as_column] = true
81
+ end
107
82
  end
108
83
  end
109
- end
110
-
111
- def load_active_record_array_collection!
112
- return unless active_record_array_collection?
113
- end
114
-
115
- def load_array_collection!
116
- return unless array_collection?
117
84
 
118
- row = collection.first
85
+ if array_collection?
86
+ row = collection.first
119
87
 
120
- columns.each do |name, opts|
121
- if opts[:as].kind_of?(Class) && opts[:as].ancestors.include?(ActiveRecord::Base)
122
- opts[:resource] = Effective::Resource.new(opts[:as], namespace: controller_namespace)
123
- opts[:as] = :resource
124
- elsif opts[:as] == nil && row.present?
125
- if (value = Array(row[opts[:index]]).first).kind_of?(ActiveRecord::Base)
126
- opts[:resource] = Effective::Resource.new(value, namespace: controller_namespace)
88
+ columns.each do |name, opts|
89
+ if opts[:as].kind_of?(Class) && opts[:as].ancestors.include?(ActiveRecord::Base)
90
+ opts[:resource] = Effective::Resource.new(opts[:as], namespace: controller_namespace)
127
91
  opts[:as] = :resource
92
+ elsif opts[:as] == nil
93
+ if (value = Array(row[opts[:index]]).first).kind_of?(ActiveRecord::Base)
94
+ opts[:resource] = Effective::Resource.new(value, namespace: controller_namespace)
95
+ opts[:as] = :resource
96
+ end
128
97
  end
129
98
  end
130
99
  end
131
- end
132
100
 
133
- def load_resource_columns!
134
101
  columns.each do |name, opts|
135
102
  opts[:as] ||= :string
136
103
  opts[:as] = :email if (opts[:as] == :string && name.to_s.end_with?('email'))
137
104
 
138
- if opts[:action]
139
- opts[:resource] ||= effective_resource
140
- end
141
-
142
105
  if opts[:resource] && !opts[:resource_field] && opts[:as] != :effective_addresses
143
106
  opts[:partial] ||= '/effective/datatables/resource_column'
144
107
  end
145
108
 
146
- opts[:col_class] = [
147
- "col-#{opts[:as]}",
148
- "col-#{name.to_s.parameterize}",
149
- ('colvis-default' if opts[:visible]),
150
- opts[:col_class].presence
151
- ].compact.join(' ')
109
+ opts[:col_class] = "col-#{opts[:as]} col-#{name.to_s.parameterize} #{opts[:col_class]}".strip
152
110
  end
111
+
112
+ load_resource_search!
153
113
  end
154
114
 
155
115
  def load_resource_search!
@@ -161,7 +121,7 @@ module Effective
161
121
  when Symbol
162
122
  opts[:search] = { as: opts[:search] }
163
123
  when Array, ActiveRecord::Relation
164
- opts[:search] = { as: :select, collection: opts[:search] }
124
+ opts[:search] = { collection: opts[:search] }
165
125
  when Hash
166
126
  # Nothing
167
127
  else
@@ -170,35 +130,31 @@ module Effective
170
130
 
171
131
  search = opts[:search]
172
132
 
173
- # Parameterize collection
174
133
  if search[:collection].kind_of?(ActiveRecord::Relation)
175
134
  search[:collection] = search[:collection].map { |obj| [obj.to_s, obj.to_param] }
176
135
  elsif search[:collection].kind_of?(Array) && search[:collection].first.kind_of?(ActiveRecord::Base)
177
136
  search[:collection] = search[:collection].map { |obj| [obj.to_s, obj.to_param] }
137
+ elsif search[:collection].kind_of?(Array)
138
+ search[:collection].each { |obj| obj[1] = 'nil' if obj[1] == nil }
139
+ elsif search[:collection].kind_of?(Hash)
140
+ search[:collection].each { |k, v| search[:collection][k] = 'nil' if v == nil }
178
141
  end
179
142
 
180
- search[:as] ||= :select if search.key?(:collection)
181
- search[:fuzzy] ||= true unless search.key?(:fuzzy)
182
143
  search[:value] ||= search.delete(:selected) if search.key?(:selected)
183
144
 
184
- # Merge with defaults
185
- search_resource = [opts[:resource], effective_resource, fallback_effective_resource].compact
186
- search_resource = search_resource.find { |res| res.klass.present? } || search_resource.first
145
+ search[:as] ||= :select if (search.key?(:collection) && opts[:as] != :belongs_to_polymorphic)
187
146
 
188
- if array_collection? && opts[:resource].present?
189
- search.reverse_merge!(search_resource.search_form_field(name, collection.first[opts[:index]]))
190
- elsif search[:as] != :string
191
- search.reverse_merge!(search_resource.search_form_field(name, opts[:as]))
192
- end
147
+ search[:fuzzy] = true unless search.key?(:fuzzy)
193
148
 
194
- # Assign default include_null
195
- if search[:as] == :select && !search.key?(:include_null)
196
- search[:include_null] = true
149
+ if array_collection? && opts[:resource].present?
150
+ search.reverse_merge!(resource.search_form_field(name, collection.first[opts[:index]]))
151
+ else
152
+ search.reverse_merge!(resource.search_form_field(name, opts[:as]))
197
153
  end
198
154
  end
199
155
  end
200
156
 
201
- def load_resource_belongs_tos!
157
+ def apply_belongs_to_attributes!
202
158
  return unless active_record_collection?
203
159
 
204
160
  changed = attributes.select do |attribute, value|
@@ -206,27 +162,10 @@ module Effective
206
162
  next unless attribute.ends_with?('_id')
207
163
 
208
164
  associated = attribute.gsub(/_id\z/, '').to_sym # Replace last _id
165
+ next unless columns[associated] && columns[associated][:as] == :belongs_to
209
166
 
210
- next unless columns[associated]
211
-
212
- if columns[associated][:as] == :belongs_to
213
- if @_collection_apply_belongs_to && !@_collection.where_values_hash.include?(attribute)
214
- @_collection = @_collection.where(attribute => value)
215
- end
216
-
217
- columns.delete(associated)
218
- elsif columns[associated][:as] == :belongs_to_polymorphic
219
- associated_type = attributes["#{associated}_type".to_sym] || raise("Expected #{associated}_type attribute to be present when #{associated}_id is present on a polymorphic belongs to")
220
-
221
- if @_collection_apply_belongs_to
222
- if !@_collection.where_values_hash.include?(attribute) && !@_collection.where_values_hash.include?("#{associated}_type")
223
- @_collection = @_collection.where(attribute => value).where("#{associated}_type" => associated_type)
224
- end
225
- end
226
-
227
- columns.delete(associated)
228
- end
229
-
167
+ @_collection = @_collection.where(attribute => value)
168
+ columns.delete(associated)
230
169
  end.present?
231
170
 
232
171
  load_columns! if changed