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,9 +0,0 @@
1
- $(document).on 'click', 'a[data-clear-form]', (event) ->
2
- event.preventDefault()
3
- $(event.currentTarget).closest('form').trigger('clear')
4
-
5
- $(document).on 'clear', '.effective-datatable-scopes form', (event) ->
6
- $(this).find('.radio.active').removeClass('active');
7
- $(this).find(':radio').prop('checked', false);
8
- $('form.form-inline input:not([type=submit])').val('');
9
- $(this).submit()
@@ -1,242 +0,0 @@
1
- module Effective
2
- class ActiveRecordDatatableTool
3
- attr_accessor :table_columns
4
-
5
- delegate :page, :per_page, :search_column, :order_column, :collection_class, :quote_sql, :to => :@datatable
6
-
7
- def initialize(datatable, table_columns)
8
- @datatable = datatable
9
- @table_columns = table_columns
10
- end
11
-
12
- def search_terms
13
- @search_terms ||= @datatable.search_terms.select { |name, search_term| table_columns.key?(name) }
14
- end
15
-
16
- def order_by_column
17
- @order_by_column ||= table_columns[@datatable.order_name]
18
- end
19
-
20
- def order(collection)
21
- return collection unless order_by_column.present?
22
-
23
- column_order = order_column(collection, order_by_column, @datatable.order_direction, order_by_column[:column])
24
- raise 'order_column must return an ActiveRecord::Relation object' unless column_order.kind_of?(ActiveRecord::Relation)
25
- column_order
26
- end
27
-
28
- def order_column_with_defaults(collection, table_column, direction, sql_column)
29
- before = ''; after = ''
30
- sql_direction = (direction == :desc ? 'DESC' : 'ASC')
31
-
32
- if postgres?
33
- after = if table_column[:nulls] == :first
34
- ' NULLS FIRST'
35
- elsif table_column[:nulls] == :last
36
- ' NULLS LAST'
37
- else
38
- " NULLS #{direction == :desc ? 'FIRST' : 'LAST' }"
39
- end
40
- elsif mysql?
41
- before = "ISNULL(#{sql_column}), "
42
- end
43
-
44
- if table_column[:type] == :belongs_to_polymorphic
45
- collection.order("#{before}#{sql_column.sub('_id', '_type')} #{sql_direction}, #{sql_column} #{sql_direction}#{after}")
46
- elsif table_column[:sql_as_column] == true
47
- collection.order("#{sql_column} #{sql_direction}")
48
- else
49
- collection.order("#{before}#{sql_column} #{sql_direction}#{after}")
50
- end
51
- end
52
-
53
- def search(collection)
54
- search_terms.each do |name, search_term|
55
- column_search = search_column(collection, table_columns[name], search_term, table_columns[name][:column])
56
- raise 'search_column must return an ActiveRecord::Relation object' unless column_search.kind_of?(ActiveRecord::Relation)
57
- collection = column_search
58
- end
59
- collection
60
- end
61
-
62
- def search_column_with_defaults(collection, table_column, term, sql_column)
63
- sql_op = table_column[:filter][:sql_operation] || :where # only other option is :having
64
-
65
- case table_column[:type]
66
- when :string, :text
67
- if sql_op != :where
68
- collection.public_send(sql_op, "#{sql_column} = :term", term: term)
69
- elsif ['null', 'nil', nil].include?(term)
70
- collection.public_send(sql_op, "#{sql_column} = :term OR #{sql_column} IS NULL", term: '')
71
- elsif table_column[:filter][:fuzzy]
72
- collection.public_send(sql_op, "#{sql_column} #{ilike} :term", term: "%#{term}%")
73
- else
74
- collection.public_send(sql_op, "#{sql_column} = :term", term: term)
75
- end
76
- when :belongs_to_polymorphic
77
- # our key will be something like Post_15, or Event_1
78
- (type, id) = term.split('_')
79
-
80
- if type.present? && id.present?
81
- collection.public_send(sql_op, "#{sql_column} = :id AND #{sql_column.sub('_id', '_type')} = :type", id: id, type: type)
82
- else
83
- collection
84
- end
85
- when :has_many
86
- reflection = collection.klass.reflect_on_association(table_column[:name].to_sym)
87
- raise "unable to find #{collection.klass.name} :has_many :#{table_column[:name]} association" unless reflection
88
-
89
- obj = reflection.build_association({})
90
- klass = obj.class
91
- polymorphic = reflection.options[:as].present?
92
-
93
- inverse = reflection.inverse_of
94
- inverse ||= klass.reflect_on_association(reflection.options[:as]) if polymorphic
95
- inverse ||= klass.reflect_on_association(collection.table_name)
96
- inverse ||= obj.class.reflect_on_association(collection.table_name.singularize)
97
-
98
- raise "unable to find #{klass.name} has_many :#{collection.table_name} or belongs_to :#{collection.table_name.singularize} associations" unless inverse
99
-
100
- ids = if [:select, :grouped_select].include?(table_column[:filter][:as])
101
- # Treat the search term as one or more IDs
102
- inverse_ids = term.split(',').map { |term| (term = term.to_i) == 0 ? nil : term }.compact
103
- return collection unless inverse_ids.present?
104
-
105
- if polymorphic
106
- klass.where(id: inverse_ids).where(reflection.type => collection.klass.name).pluck(reflection.foreign_key)
107
- else
108
- klass.where(id: inverse_ids).joins(inverse.name).pluck(inverse.foreign_key)
109
- end
110
- else
111
- # Treat the search term as a string.
112
- klass_columns = if (sql_column == klass.table_name) # No custom column has been defined
113
- klass.columns.map { |col| col.name if col.text? }.compact # Search all database text? columns
114
- else
115
- [sql_column.gsub("#{klass.table_name}.", '')] # table_column :order_items, column: 'order_items.title'
116
- end
117
-
118
- if polymorphic
119
- klass_columns -= [reflection.type]
120
- end
121
-
122
- conditions = klass_columns.map { |col_name| "#{klass.table_name}.#{col_name} #{ilike} :term" }
123
-
124
- if polymorphic
125
- klass.where(conditions.join(' OR '), term: "%#{term}%", num: term.to_i).where(reflection.type => collection.klass.name).pluck(reflection.foreign_key)
126
- else
127
- klass.where(conditions.join(' OR '), term: "%#{term}%", num: term.to_i).joins(inverse.name).pluck(inverse.foreign_key)
128
- end
129
- end
130
-
131
- collection.public_send(sql_op, id: ids)
132
-
133
- when :has_and_belongs_to_many
134
- reflection = collection.klass.reflect_on_association(table_column[:name].to_sym)
135
- raise "unable to find #{collection.klass.name} :has_and_belongs_to_many :#{table_column[:name]} association" unless reflection
136
-
137
- obj = reflection.build_association({})
138
- klass = obj.class
139
-
140
- inverse = reflection.inverse_of || klass.reflect_on_association(collection.table_name) || obj.class.reflect_on_association(collection.table_name.singularize)
141
- raise "unable to find #{klass.name} has_and_belongs_to_many :#{collection.table_name} or belongs_to :#{collection.table_name.singularize} associations" unless inverse
142
-
143
- ids = if [:select, :grouped_select].include?(table_column[:filter][:as])
144
- # Treat the search term as one or more IDs
145
- inverse_ids = term.split(',').map { |term| (term = term.to_i) == 0 ? nil : term }.compact
146
- return collection unless inverse_ids.present?
147
-
148
- klass.where(id: inverse_ids).flat_map { |klass| (klass.send(inverse.name).pluck(:id) rescue []) }
149
- else
150
- # Treat the search term as a string.
151
-
152
- klass_columns = if (sql_column == klass.table_name) # No custom column has been defined
153
- klass.columns.map { |col| col.name if col.text? }.compact # Search all database text? columns
154
- else
155
- [sql_column.gsub("#{klass.table_name}.", '')] # table_column :order_items, column: 'order_items.title'
156
- end
157
-
158
- conditions = klass_columns.map { |col_name| "#{klass.table_name}.#{col_name} #{ilike} :term" }
159
-
160
- klass.where(conditions.join(' OR '), term: "%#{term}%", num: term.to_i).flat_map { |klass| (klass.send(inverse.name).pluck(:id) rescue []) }
161
- end
162
-
163
- collection.public_send(sql_op, id: ids)
164
- when :obfuscated_id
165
- if (deobfuscated_id = collection.deobfuscate(term)) == term # We weren't able to deobfuscate it, so this is an Invalid ID
166
- collection.public_send(sql_op, "#{sql_column} = :term", term: 0)
167
- else
168
- collection.public_send(sql_op, "#{sql_column} = :term", term: deobfuscated_id)
169
- end
170
- when :effective_address
171
- ids = Effective::Address
172
- .where('addressable_type = ?', collection_class.name)
173
- .where("address1 #{ilike} :term OR address2 #{ilike} :term OR city #{ilike} :term OR postal_code #{ilike} :term OR state_code = :code OR country_code = :code", term: "%#{term}%", code: term)
174
- .pluck(:addressable_id)
175
-
176
- collection.public_send(sql_op, id: ids)
177
- when :effective_roles
178
- collection.with_role(term)
179
- when :datetime, :date
180
- begin
181
- digits = term.scan(/(\d+)/).flatten.map { |digit| digit.to_i }
182
- start_at = Time.zone.local(*digits)
183
-
184
- case digits.length
185
- when 1 # Year
186
- end_at = start_at.end_of_year
187
- when 2 # Year-Month
188
- end_at = start_at.end_of_month
189
- when 3 # Year-Month-Day
190
- end_at = start_at.end_of_day
191
- when 4 # Year-Month-Day Hour
192
- end_at = start_at.end_of_hour
193
- when 5 # Year-Month-Day Hour-Minute
194
- end_at = start_at.end_of_minute
195
- when 6
196
- end_at = start_at + 1.second
197
- else
198
- end_at = start_at
199
- end
200
-
201
- collection.public_send(sql_op, "#{sql_column} >= :start_at AND #{sql_column} <= :end_at", start_at: start_at, end_at: end_at)
202
- rescue => e
203
- collection
204
- end
205
- when :boolean
206
- collection.public_send(sql_op, "#{sql_column} = :term", term: [1, 'true', 'yes'].include?(term.to_s.downcase))
207
- when :integer
208
- collection.public_send(sql_op, "#{sql_column} = :term", term: term.gsub(/\D/, '').to_i)
209
- when :year
210
- collection.public_send(sql_op, "EXTRACT(YEAR FROM #{sql_column}) = :term", term: term.to_i)
211
- when :price
212
- price_in_cents = (term.gsub(/[^0-9|\.]/, '').to_f * 100.0).to_i
213
- collection.public_send(sql_op, "#{sql_column} = :term", term: price_in_cents)
214
- when :currency, :decimal, :number, :percentage
215
- collection.public_send(sql_op, "#{sql_column} = :term", term: term.gsub(/[^0-9|\.]/, '').to_f)
216
- else
217
- collection.public_send(sql_op, "#{sql_column} = :term", term: term)
218
- end
219
- end
220
-
221
- def paginate(collection)
222
- collection.page(page).per(per_page)
223
- end
224
-
225
- protected
226
-
227
- def postgres?
228
- return @postgres unless @postgres.nil?
229
- @postgres ||= (collection_class.connection.kind_of?(ActiveRecord::ConnectionAdapters::PostgreSQLAdapter) rescue false)
230
- end
231
-
232
- def mysql?
233
- return @mysql unless @mysql.nil?
234
- @mysql ||= (collection_class.connection.kind_of?(ActiveRecord::ConnectionAdapters::Mysql2Adapter) rescue false)
235
- end
236
-
237
- def ilike
238
- @ilike ||= (postgres? ? 'ILIKE' : 'LIKE') # Only Postgres supports ILIKE, Mysql and Sqlite3 use LIKE
239
- end
240
-
241
- end
242
- end
@@ -1,97 +0,0 @@
1
- module Effective
2
- # The collection is an Array of Arrays
3
- class ArrayDatatableTool
4
- attr_accessor :table_columns
5
-
6
- delegate :page, :per_page, :search_column, :order_column, :display_table_columns, :convert_to_column_type, :to => :@datatable
7
-
8
- def initialize(datatable, table_columns)
9
- @datatable = datatable
10
- @table_columns = table_columns
11
- end
12
-
13
- def search_terms
14
- @search_terms ||= @datatable.search_terms.select { |name, search_term| table_columns.key?(name) }
15
- end
16
-
17
- def order_by_column
18
- @order_by_column ||= table_columns[@datatable.order_name]
19
- end
20
-
21
- def order(collection)
22
- return collection unless order_by_column.present?
23
-
24
- column_order = order_column(collection, order_by_column, @datatable.order_direction, display_index(order_by_column))
25
- raise 'order_column must return an Array' unless column_order.kind_of?(Array)
26
- column_order
27
- end
28
-
29
- def order_column_with_defaults(collection, table_column, direction, index)
30
- if direction == :asc
31
- collection.sort! do |x, y|
32
- if (x[index] && y[index])
33
- x[index] <=> y[index]
34
- elsif x[index]
35
- -1
36
- elsif y[index]
37
- 1
38
- else
39
- 0
40
- end
41
- end
42
- else
43
- collection.sort! do |x, y|
44
- if (x[index] && y[index])
45
- y[index] <=> x[index]
46
- elsif x[index]
47
- 1
48
- elsif y[index]
49
- -1
50
- else
51
- 0
52
- end
53
- end
54
- end
55
-
56
- collection
57
- end
58
-
59
- def search(collection)
60
- search_terms.each do |name, search_term|
61
- column_search = search_column(collection, table_columns[name], search_term, display_index(table_columns[name]))
62
- raise 'search_column must return an Array object' unless column_search.kind_of?(Array)
63
- collection = column_search
64
- end
65
- collection
66
- end
67
-
68
- def search_column_with_defaults(collection, table_column, search_term, index)
69
- search_term = search_term.downcase if table_column[:filter][:fuzzy]
70
-
71
- collection.select! do |row|
72
- if table_column[:filter][:fuzzy]
73
- row[index].to_s.downcase.include?(search_term)
74
- else
75
- row[index] == search_term
76
- end
77
- end || collection
78
- end
79
-
80
- def paginate(collection)
81
- Kaminari.paginate_array(collection).page(page).per(per_page)
82
- end
83
-
84
- private
85
-
86
- def display_index(column)
87
- display_table_columns.present? ? display_table_columns.keys.index(column[:name]) : column[:index]
88
- end
89
-
90
- end
91
- end
92
-
93
- # [
94
- # [1, 'title 1'],
95
- # [2, 'title 2'],
96
- # [3, 'title 3']
97
- # ]
@@ -1,101 +0,0 @@
1
- # This is extended as class level into Datatable
2
-
3
- module Effective
4
- module EffectiveDatatable
5
- module Ajax
6
-
7
- # This is for the ColReorder plugin
8
- # It sends us a list of columns that are different than our table_columns order
9
- # So this method just returns an array of column names, as per ColReorder
10
- def display_table_columns
11
- return nil if params[:columns].blank?
12
-
13
- @display_table_columns ||= (
14
- {}.tap do |retval|
15
- params[:columns].each do |_, column|
16
- retval[column[:name]] = table_columns[column[:name]] # Same order as ColReordernow
17
- retval[column[:name]][:visible] = (column[:visible] == 'true') # As per ColVis
18
- end
19
- end
20
- )
21
- end
22
-
23
- def order_name
24
- @order_name ||= begin
25
- if params[:order] && params[:columns]
26
- order_by_column_index = (params[:order]['0'][:column] rescue '0')
27
- (params[:columns][order_by_column_index] || {})[:name]
28
- elsif @default_order.present?
29
- @default_order.keys.first
30
- end || table_columns.find { |col, opts| opts[:type] != :bulk_actions_column }.try(:first)
31
- end
32
- end
33
-
34
- def order_index
35
- (table_columns[order_name][:index] || 0) rescue 0
36
- end
37
-
38
- def order_direction
39
- @order_direction ||= if params[:order].present?
40
- params[:order]['0'][:dir] == 'desc' ? :desc : :asc
41
- elsif @default_order.present?
42
- @default_order.values.first.to_s.downcase == 'desc' ? :desc : :asc
43
- else
44
- :asc
45
- end
46
- end
47
-
48
- def display_entries
49
- @display_entries ||= begin
50
- entries = (@default_entries.presence || EffectiveDatatables.default_entries)
51
- entries = -1 if entries.to_s.downcase == 'all'
52
- [5, 10, 25, 50, 100, 250, 1000, -1].include?(entries) ? entries : 25
53
- end
54
- end
55
-
56
- def search_terms
57
- @search_terms ||= HashWithIndifferentAccess.new().tap do |terms|
58
- if params[:columns].present? # This is an AJAX request from the DataTable
59
- (params[:columns] || {}).each do |_, column|
60
- next if table_columns[column[:name]].blank? || (column[:search] || {})[:value].blank?
61
-
62
- terms[column[:name]] = column[:search][:value]
63
- end
64
- else # This is the initial render, and we have to apply default search terms only
65
- table_columns.each do |name, values|
66
- terms[name] = values[:filter][:selected] if values[:filter][:selected].present?
67
- end
68
- end
69
- end
70
- end
71
-
72
- def per_page
73
- return 9999999 if simple?
74
-
75
- length = (params[:length].presence || display_entries).to_i
76
-
77
- if length == -1
78
- 9999999
79
- elsif length > 0
80
- length
81
- else
82
- 25
83
- end
84
- end
85
-
86
- def per_page=(length)
87
- case length
88
- when Integer
89
- params[:length] = length
90
- when :all
91
- params[:length] = -1
92
- end
93
- end
94
-
95
- def page
96
- params[:start].to_i / per_page + 1
97
- end
98
-
99
- end
100
- end
101
- end