effective_datatables 3.7.7 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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