effective_datatables 2.12.2 → 3.0.0

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