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,112 +1,103 @@
1
- # These aren't expected to be called by a developer.
2
- # They are internal datatables methods, but you could still call them on the view.
1
+ # These aren't expected to be called by a developer. They are internal methods.
3
2
  module EffectiveDatatablesPrivateHelper
4
3
 
5
- def datatable_default_order(datatable)
6
- [datatable.order_index, datatable.order_direction.downcase].to_json()
7
- end
8
-
9
4
  # https://datatables.net/reference/option/columns
10
5
  def datatable_columns(datatable)
11
6
  form = nil
12
- simple_form_for(:datatable_filter, url: '#', html: {id: "#{datatable.to_param}-form"}) { |f| form = f }
7
+ simple_form_for(:datatable_search, url: '#', html: {id: "#{datatable.to_param}-form"}) { |f| form = f }
13
8
 
14
- datatable.table_columns.map do |name, options|
9
+ datatable.columns.map do |name, opts|
15
10
  {
16
- name: options[:name],
17
- title: content_tag(:span, options[:label], class: 'filter-label'),
18
- className: options[:class],
19
- width: options[:width],
20
- responsivePriority: (options[:responsivePriority] || 10000), # 10,000 is datatables default
21
- sortable: (options[:sortable] && !datatable.simple?),
22
- visible: (options[:visible].respond_to?(:call) ? datatable.instance_exec(&options[:visible]) : options[:visible]),
23
- filterHtml: (datatable_header_filter(form, name, datatable.search_terms[name], options) unless datatable.simple?),
24
- filterSelectedValue: options[:filter][:selected]
11
+ name: name,
12
+ title: content_tag(:span, opts[:label], class: 'search-label'),
13
+ className: opts[:col_class],
14
+ searchHtml: (datatable_search_html(form, name, datatable.state[:search][name], opts) unless datatable.simple?),
15
+ responsivePriority: opts[:responsive],
16
+ search: datatable.state[:search][name],
17
+ sortable: (opts[:sort] && !datatable.simple?),
18
+ visible: datatable.state[:visible][name],
25
19
  }
26
- end.to_json()
20
+ end.to_json
27
21
  end
28
22
 
29
23
  def datatable_bulk_actions(datatable)
30
- bulk_actions_column = datatable.table_columns.find { |_, options| options[:bulk_actions_column] }.try(:second)
31
- return false unless bulk_actions_column
24
+ render(partial: '/effective/datatables/bulk_actions_dropdown', locals: { datatable: datatable }) if datatable._bulk_actions.present?
25
+ end
32
26
 
33
- {
34
- dropdownHtml: render(
35
- partial: bulk_actions_column[:dropdown_partial],
36
- locals: { datatable: datatable, dropdown_block: bulk_actions_column[:dropdown_block] }.merge(bulk_actions_column[:partial_locals])
37
- )
38
- }.to_json()
27
+ def datatable_reset(datatable)
28
+ render(partial: '/effective/datatables/reset', locals: { datatable: datatable })
39
29
  end
40
30
 
41
- def datatable_header_filter(form, name, value, opts)
42
- return render(partial: opts[:header_partial], locals: {form: form, name: (opts[:label] || name), column: opts}) if opts[:header_partial].present?
31
+ def datatable_search_html(form, name, value, opts)
32
+ include_blank = opts[:search].key?(:include_blank) ? opts[:search][:include_blank] : opts[:label]
33
+ pattern = opts[:search][:pattern]
34
+ placeholder = opts[:search][:placeholder] || ''
35
+ title = opts[:search][:title] || opts[:label]
36
+ wrapper_html = { class: 'datatable_search' }
43
37
 
44
- include_blank = opts[:filter].key?(:include_blank) ? opts[:filter][:include_blank] : (opts[:label] || name.titleize)
45
- pattern = opts[:filter].key?(:pattern) ? opts[:filter][:pattern] : nil
46
- placeholder = (opts[:filter][:placeholder] || '')
47
- title = opts[:filter].key?(:title) ? opts[:filter][:title] : (opts[:label] || name.titleize)
48
- wrapper_html = { class: 'datatable_filter' }
38
+ input_html = {
39
+ name: nil,
40
+ value: value,
41
+ title: title,
42
+ pattern: pattern,
43
+ autocomplete: 'off',
44
+ data: {'column-name' => name, 'column-index' => opts[:index]}
45
+ }.delete_if { |k, v| v.blank? && k != :name }
49
46
 
50
- case opts[:filter][:as]
47
+ case opts[:search][:as]
51
48
  when :string, :text, :number
52
49
  form.input name, label: false, required: false, value: value,
53
50
  as: :string,
54
51
  placeholder: placeholder,
55
52
  wrapper_html: wrapper_html,
56
- input_html: { name: nil, value: value, title: title, pattern: pattern, autocomplete: 'off', data: {'column-name' => opts[:name], 'column-index' => opts[:index]} }
57
- when :obfuscated_id
58
- pattern ||= '[0-9]{3}-?[0-9]{4}-?[0-9]{3}'
59
- title = opts[:filter].key?(:title) ? opts[:filter][:title] : 'Expected format: XXX-XXXX-XXX'
53
+ input_html: input_html
54
+ when :effective_obfuscation
55
+ input_html[:pattern] ||= '[0-9]{3}-?[0-9]{4}-?[0-9]{3}'
56
+ input_html[:title] = 'Expected format: XXX-XXXX-XXX'
60
57
 
61
58
  form.input name, label: false, required: false, value: value,
62
59
  as: :string,
63
- placeholder: placeholder.presence || '###-####-###',
60
+ placeholder: placeholder,
64
61
  wrapper_html: wrapper_html,
65
- input_html: { name: nil, value: value, title: title, pattern: pattern, autocomplete: 'off', data: {'column-name' => opts[:name], 'column-index' => opts[:index]} }
66
- when :date
62
+ input_html: input_html
63
+ when :date, :datetime
67
64
  form.input name, label: false, required: false, value: value,
68
65
  as: (ActionView::Helpers::FormBuilder.instance_methods.include?(:effective_date_picker) ? :effective_date_picker : :string),
69
66
  placeholder: placeholder,
70
67
  wrapper_html: wrapper_html,
71
68
  input_group: false,
72
- input_html: { name: nil, value: value, title: title, autocomplete: 'off', data: {'column-name' => opts[:name], 'column-index' => opts[:index]} },
69
+ input_html: input_html,
73
70
  input_js: { useStrict: true, keepInvalid: true }
74
- when :datetime
75
- form.input name, label: false, required: false, value: value,
76
- as: (ActionView::Helpers::FormBuilder.instance_methods.include?(:effective_date_time_picker) ? :effective_date_time_picker : :string),
77
- placeholder: placeholder,
78
- wrapper_html: wrapper_html,
79
- input_group: false,
80
- input_html: { name: nil, value: value, title: title, autocomplete: 'off', data: {'column-name' => opts[:name], 'column-index' => opts[:index]} },
81
- input_js: { useStrict: true, keepInvalid: true } # Keep invalid format like "2015-11" so we can still filter by year, month or day
71
+ # Keep invalid format like "2015-11" so we can still search by year, month or day
82
72
  when :select, :boolean
83
73
  form.input name, label: false, required: false, value: value,
84
74
  as: (ActionView::Helpers::FormBuilder.instance_methods.include?(:effective_select) ? :effective_select : :select),
85
- collection: opts[:filter][:collection],
86
- selected: opts[:filter][:selected],
87
- multiple: opts[:filter][:multiple] == true,
75
+ collection: opts[:search][:collection],
76
+ selected: opts[:search][:value],
77
+ multiple: opts[:search][:multiple] == true,
88
78
  include_blank: include_blank,
89
79
  wrapper_html: wrapper_html,
90
- input_html: { name: nil, value: value, title: title, autocomplete: 'off', data: {'column-name' => opts[:name], 'column-index' => opts[:index]} },
80
+ input_html: input_html,
91
81
  input_js: { placeholder: placeholder }
92
82
  when :grouped_select
93
83
  form.input name, label: false, required: false, value: value,
94
84
  as: (ActionView::Helpers::FormBuilder.instance_methods.include?(:effective_select) ? :effective_select : :grouped_select),
95
- collection: opts[:filter][:collection],
96
- selected: opts[:filter][:selected],
97
- multiple: opts[:filter][:multiple] == true,
98
- include_blank: include_blank,
85
+ collection: opts[:search][:collection],
86
+ selected: opts[:search][:value],
87
+ multiple: opts[:search][:multiple] == true,
99
88
  grouped: true,
100
- polymorphic: opts[:filter][:polymorphic] == true,
101
- group_label_method: opts[:filter][:group_label_method] || :first,
102
- group_method: opts[:filter][:group_method] || :last,
89
+ polymorphic: opts[:search][:polymorphic] == true,
90
+ group_label_method: opts[:search][:group_label_method] || :first,
91
+ group_method: opts[:search][:group_method] || :last,
103
92
  wrapper_html: wrapper_html,
104
- input_html: { name: nil, value: value, title: title, autocomplete: 'off', data: {'column-name' => opts[:name], 'column-index' => opts[:index]} },
93
+ input_html: input_html,
105
94
  input_js: { placeholder: placeholder }
106
- when :bulk_actions_column
95
+ when :bulk_actions
96
+ input_html[:data]['role'] = 'bulk-actions-all'
97
+
107
98
  form.input name, label: false, required: false, value: nil,
108
99
  as: :boolean,
109
- input_html: { name: nil, value: nil, autocomplete: 'off', data: {'column-name' => opts[:name], 'column-index' => opts[:index], 'role' => 'bulk-actions-all'} }
100
+ input_html: input_html
110
101
  end
111
102
  end
112
103
 
@@ -1,234 +1,160 @@
1
1
  module Effective
2
2
  class Datatable
3
- attr_accessor :display_records, :view, :attributes
4
-
5
- # These two options control the render behaviour of a datatable
6
- attr_accessor :table_html_class, :simple
7
-
8
- delegate :render, :controller, :link_to, :mail_to, :number_to_currency, :number_to_percentage, :to => :@view
3
+ attr_reader :attributes # Anything that we initialize our table with. That's it. Can't be changed by state.
4
+ attr_reader :resource
5
+ attr_reader :state
6
+
7
+ # Hashes of DSL options
8
+ attr_reader :_aggregates
9
+ attr_reader :_bulk_actions
10
+ attr_reader :_charts
11
+ attr_reader :_columns
12
+ attr_reader :_filters
13
+ attr_reader :_form
14
+ attr_reader :_scopes
15
+
16
+ # The collection itself. Only evaluated once.
17
+ attr_accessor :_collection
18
+
19
+ # The view, and the ajax/cookie/default state
20
+ attr_reader :cookie
21
+ attr_reader :view
9
22
 
10
23
  extend Effective::EffectiveDatatable::Dsl
11
24
 
12
- include Effective::EffectiveDatatable::Dsl::BulkActions
13
- include Effective::EffectiveDatatable::Dsl::Charts
14
- include Effective::EffectiveDatatable::Dsl::Datatable
15
- include Effective::EffectiveDatatable::Dsl::Scopes
16
-
17
- include Effective::EffectiveDatatable::Ajax
18
- include Effective::EffectiveDatatable::Charts
19
- include Effective::EffectiveDatatable::Helpers
25
+ include Effective::EffectiveDatatable::Attributes
26
+ include Effective::EffectiveDatatable::Collection
27
+ include Effective::EffectiveDatatable::Compute
28
+ include Effective::EffectiveDatatable::Cookie
29
+ include Effective::EffectiveDatatable::Format
20
30
  include Effective::EffectiveDatatable::Hooks
21
- include Effective::EffectiveDatatable::Options
22
- include Effective::EffectiveDatatable::Rendering
23
-
24
- def initialize(*args)
25
- initialize_attributes(args)
31
+ include Effective::EffectiveDatatable::Params
32
+ include Effective::EffectiveDatatable::Resource
33
+ include Effective::EffectiveDatatable::State
26
34
 
27
- if respond_to?(:initialize_scopes) # There was at least one scope defined in the scopes do .. end block
28
- initialize_scopes
29
- initialize_scope_options
30
- end
31
-
32
- if respond_to?(:initialize_datatable)
33
- initialize_datatable # This creates @table_columns based on the DSL datatable do .. end block
34
- initialize_datatable_options # This normalizes all the options
35
- end
35
+ def initialize(view = nil, attributes = {})
36
+ (attributes = view; view = nil) if view.kind_of?(Hash)
36
37
 
37
- if respond_to?(:initialize_charts)
38
- initialize_charts
39
- initialize_chart_options
40
- end
38
+ @attributes = initial_attributes(attributes)
39
+ @state = initial_state
41
40
 
42
- unless active_record_collection? || array_collection?
43
- raise "Unsupported collection type. Should be ActiveRecord class, ActiveRecord relation, or an Array of Arrays [[1, 'something'], [2, 'something else']]"
44
- end
41
+ @_aggregates = {}
42
+ @_bulk_actions = []
43
+ @_charts = {}
44
+ @_columns = {}
45
+ @_filters = {}
46
+ @_form = {}
47
+ @_scopes = {}
45
48
 
46
- if @default_order.present? && !table_columns.key?((@default_order.keys.first rescue nil))
47
- raise "default_order :#{(@default_order.keys.first rescue 'nil')} must exist as a table_column or array_column"
48
- end
49
+ raise 'collection is defined as a method. Please use the collection do ... end syntax.' unless collection.nil?
50
+ self.view = view if view
49
51
  end
50
52
 
51
- def table_columns
52
- @table_columns
53
- end
53
+ # Once the view is assigned, we initialize everything
54
+ def view=(view)
55
+ @view = (view.respond_to?(:view_context) ? view.view_context : view)
56
+ raise 'expected view to respond to params' unless @view.respond_to?(:params)
54
57
 
55
- def scopes
56
- @scopes
57
- end
58
+ load_cookie!
59
+ load_attributes!
58
60
 
59
- def klass_scopes
60
- scopes.select { |name, options| options[:klass_scope] }
61
- end
61
+ # We need early access to filter and scope, to define defaults from the model first
62
+ # This means filters do knows about attributes but not about columns.
63
+ initialize_filters if respond_to?(:initialize_filters)
64
+ load_filters!
65
+ load_state!
62
66
 
63
- def current_scope # The currently selected (klass) scope
64
- attributes[:current_scope]
65
- end
67
+ # Now we initialize all the columns. columns knows about attributes and filters and scope
68
+ initialize_datatable if respond_to?(:initialize_datatable)
69
+ load_columns!
66
70
 
67
- def permitted_params
68
- scopes.keys + [:current_scope, :referer]
69
- end
71
+ # Execute any additional DSL methods
72
+ initialize_bulk_actions if respond_to?(:initialize_bulk_actions)
73
+ initialize_charts if respond_to?(:initialize_charts)
70
74
 
71
- def charts
72
- @charts
73
- end
75
+ # Load the collection. This is the first time def collection is called on the Datatable itself
76
+ initialize_collection if respond_to?(:initialize_collection)
77
+ load_collection!
74
78
 
75
- def aggregates
76
- @aggregates
77
- end
79
+ # Figure out the class, and if it's activerecord, do all the resource discovery on it
80
+ load_resource!
78
81
 
79
- # Any attributes set on initialize will be echoed back and available to the class
80
- def attributes
81
- @attributes ||= HashWithIndifferentAccess.new
82
+ save_cookie!
82
83
  end
83
84
 
84
- def to_key; []; end # Searching & Filters
85
+ def present?(view = nil)
86
+ unless (@view || view)
87
+ raise 'unable to call present? without an assigned view. In your view, either call render_datatable(@datatable) first, or use @datatable.present?(self)'
88
+ end
85
89
 
86
- # Instance method. In Rails 4.2 this needs to be defined on the instance, before it was on the class
87
- def model_name # Searching & Filters
88
- @model_name ||= ActiveModel::Name.new(self.class)
89
- end
90
+ self.view ||= view
90
91
 
91
- def self.model_name # Searching & Filters
92
- @model_name ||= ActiveModel::Name.new(self)
92
+ to_json[:recordsTotal] > 0
93
93
  end
94
94
 
95
- def to_param
96
- @to_param ||= self.class.name.underscore
97
- end
95
+ def blank?(view = nil)
96
+ unless (@view || view)
97
+ raise 'unable to call blank? without an assigned view. In your view, either call render_datatable(@datatable) first, or use @datatable.blank?(self)'
98
+ end
98
99
 
99
- def collection
100
- raise "You must define a collection. Something like an ActiveRecord User.all or an Array of Arrays [[1, 'something'], [2, 'something else']]"
101
- end
100
+ self.view ||= view
102
101
 
103
- def collection_class # This is set by initialize_datatable_options()
104
- @collection_class # Will be either User/Post/etc or Array
102
+ to_json[:recordsTotal] == 0
105
103
  end
106
104
 
107
105
  def to_json
108
- raise 'Effective::Datatable to_json called with a nil view. Please call render_datatable(@datatable) or @datatable.view = view before this method' unless view.present?
109
-
110
- @json ||= begin
111
- data = table_data
112
-
106
+ @json ||= (
113
107
  {
108
+ data: (compute || []),
114
109
  draw: (params[:draw] || 0),
115
- data: (data || []),
116
- recordsTotal: (total_records || 0),
117
- recordsFiltered: (display_records || 0),
118
- aggregates: (aggregate_data(data) || []),
119
- charts: (charts_data || {})
110
+ recordsTotal: (@total_records || 0),
111
+ recordsFiltered: (@display_records || 0),
112
+ aggregates: (@aggregates_data || []),
113
+ charts: (@charts_data || {})
120
114
  }
121
- end
122
- end
123
-
124
- def present?
125
- total_records > 0 || current_scope.present?
126
- end
127
-
128
- def empty?
129
- total_records == 0 && current_scope.blank?
130
- end
131
-
132
- def total_records
133
- @total_records ||= (active_record_collection? ? active_record_collection_size(the_collection) : the_collection.size)
134
- end
135
-
136
- def view=(view_context)
137
- @view = view_context
138
- @view.formats = [:html]
139
-
140
- # 'Just work' with attributes
141
- @view.class.send(:attr_accessor, :attributes)
142
- @view.attributes = self.attributes
143
-
144
- # Delegate any methods defined on the datatable directly to our view
145
- @view.class.send(:attr_accessor, :effective_datatable)
146
- @view.effective_datatable = self
147
-
148
- unless @view.respond_to?(:bulk_action)
149
- @view.class.send(:include, Effective::EffectiveDatatable::Dsl::BulkActions)
150
- end
151
-
152
- Effective::EffectiveDatatable::Helpers.instance_methods(false).each do |helper_method|
153
- @view.class_eval { delegate helper_method, to: :@effective_datatable }
154
- end
155
-
156
- (self.class.instance_methods(false) - [:initialize_datatable, :collection, :search_column, :order_column]).each do |view_method|
157
- @view.class_eval { delegate view_method, to: :@effective_datatable }
158
- end
159
-
160
- # Clear the search_terms memoization
161
- @search_terms = nil
162
- @order_name = nil
163
- @order_direction = nil
164
- end
165
-
166
- def view_context
167
- view
168
- end
169
-
170
- def table_html_class
171
- @table_html_class.presence || 'table table-bordered table-striped'
115
+ )
172
116
  end
173
117
 
174
118
  # When simple only a table will be rendered with
175
119
  # no sorting, no filtering, no export buttons, no pagination, no per page, no colReorder
176
120
  # default sorting only, default visibility only, all records returned, and responsive enabled
177
121
  def simple?
178
- @simple == true
122
+ attributes[:simple] == true
179
123
  end
180
124
 
181
- protected
125
+ # Whether the filters must be rendered as a <form> or we can keep the normal <div> behaviour
126
+ def _filters_form_required?
127
+ _form[:verb].present?
128
+ end
182
129
 
183
- def the_collection
184
- @memoized_collection ||= collection
130
+ def table_html_class
131
+ attributes[:class] || 'table table-bordered table-striped'
185
132
  end
186
133
 
187
- def params
188
- view.try(:params) || HashWithIndifferentAccess.new()
134
+ def to_param
135
+ @to_param ||= self.class.name.underscore.parameterize
189
136
  end
190
137
 
191
- def table_tool
192
- @table_tool ||= ActiveRecordDatatableTool.new(self, table_columns.reject { |_, col| col[:array_column] })
138
+ def columns
139
+ @_columns
193
140
  end
194
141
 
195
- def array_tool
196
- @array_tool ||= ArrayDatatableTool.new(self, table_columns.select { |_, col| col[:array_column] })
142
+ def collection
143
+ @_collection
197
144
  end
198
145
 
199
- # TODO
200
- # Check if collection has an order() clause and warn about it
201
- # Usually that will make the table results look weird.
202
- def active_record_collection?
203
- @active_record_collection == true
146
+ def dsl_tool
147
+ @dsl_tool ||= DatatableDslTool.new(self)
204
148
  end
205
149
 
206
- def array_collection?
207
- @array_collection == true
150
+ private
151
+
152
+ def column_tool
153
+ @column_tool ||= DatatableColumnTool.new(self)
208
154
  end
209
155
 
210
- # Not every ActiveRecord query will work when calling the simple .count
211
- # Custom selects:
212
- # User.select(:email, :first_name).count will throw an error
213
- # .count(:all) and .size seem to work
214
- # Grouped Queries:
215
- # User.all.group(:email).count will return a Hash
216
- def active_record_collection_size(collection)
217
- count = (collection.size rescue nil)
218
-
219
- case count
220
- when Integer
221
- count
222
- when Hash
223
- count.size # This represents the number of displayed datatable rows, not the sum all groups (which might be more)
224
- else
225
- if collection.klass.connection.respond_to?(:unprepared_statement)
226
- collection_sql = collection.klass.connection.unprepared_statement { collection.to_sql }
227
- (collection.klass.connection.exec_query("SELECT COUNT(*) FROM (#{collection_sql}) AS datatables_total_count").rows[0][0] rescue 1)
228
- else
229
- (collection.klass.connection.exec_query("SELECT COUNT(*) FROM (#{collection.to_sql}) AS datatables_total_count").rows[0][0] rescue 1)
230
- end.to_i
231
- end
156
+ def value_tool
157
+ @value_tool ||= DatatableValueTool.new(self)
232
158
  end
233
159
 
234
160
  end