effective_datatables 2.3.8 → 2.4.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.
- checksums.yaml +4 -4
- data/README.md +76 -24
- data/app/helpers/effective_datatables_helper.rb +3 -3
- data/app/models/effective/active_record_datatable_tool.rb +47 -42
- data/app/models/effective/array_datatable_tool.rb +48 -32
- data/app/models/effective/datatable.rb +2 -1
- data/app/models/effective/effective_datatable/ajax.rb +5 -14
- data/app/models/effective/effective_datatable/hooks.rb +30 -0
- data/app/models/effective/effective_datatable/options.rb +29 -24
- data/app/models/effective/effective_datatable/rendering.rb +1 -5
- data/lib/effective_datatables/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5356cff7e6d831538a222820ad35adde6b9c28a8
|
4
|
+
data.tar.gz: 273bea0ced295a05ef825e2cfa009670c1a0f682
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8c0af0da24feda9e04408d4150fd874818d7bf08ecb29f445797e4a06362cb4d9ec454f65163d77a551bfc5af2efb96e23ed0eebb99125a5224fc6883aaafd5d
|
7
|
+
data.tar.gz: 3f6e5cd755c9d9c773ed9c32e9096fc2c5b496a7dfc5ff0c8aec14519e5b2719db57f09c332617566bc2b2f3f8a8968f6854326e073d75d5635a9e8fbea8e36a
|
data/README.md
CHANGED
@@ -136,7 +136,7 @@ module Effective
|
|
136
136
|
|
137
137
|
table_column :user
|
138
138
|
|
139
|
-
table_column :post_category_id, :filter => {:
|
139
|
+
table_column :post_category_id, :filter => {:as => :select, :collection => Proc.new { PostCategory.all } } do |post|
|
140
140
|
post.post_category.name.titleize
|
141
141
|
end
|
142
142
|
|
@@ -267,7 +267,7 @@ table_column :user
|
|
267
267
|
# Will have the same behaviour as declaring
|
268
268
|
datatable do
|
269
269
|
if attributes[:user_id].blank?
|
270
|
-
table_column :user_id, :filter => {:
|
270
|
+
table_column :user_id, :filter => {:as => :select, :collection => Proc.new { User.all.map { |user| [user.id, user.to_s] }.sort { |x, y| x[1] <=> y[1] } } } do |post|
|
271
271
|
post.user.to_s
|
272
272
|
end
|
273
273
|
end
|
@@ -284,7 +284,7 @@ The difference occurs with sorting and filtering:
|
|
284
284
|
|
285
285
|
array_columns perform searching and sorting on the computed results after all columns have been rendered.
|
286
286
|
|
287
|
-
With a `table_column`, the frontend sends some search terms to the server, the raw database table is searched & sorted using standard ActiveRecord .where(), the appropriate rows returned, and then each row is rendered as per the rendering options.
|
287
|
+
With a `table_column`, the frontend sends some search terms to the server, the raw database table is searched & sorted using standard ActiveRecord .where() and .order(), the appropriate rows returned, and then each row is rendered as per the rendering options.
|
288
288
|
|
289
289
|
With an `array_column`, the front end sends some search terms to the server, all rows are returned and rendered, and then the rendered output is searched & sorted.
|
290
290
|
|
@@ -292,6 +292,18 @@ This allows the output of an `array_column` to be anything complex that cannot b
|
|
292
292
|
|
293
293
|
When searching & sorting with a mix of table_columns and array_columns, all the table_columns are processed first so the most work is put on the database, the least on rails.
|
294
294
|
|
295
|
+
If you're overriding the `search_column` or `order_column` behaviour of an `array_column`, keep in mind that all values will be strings.
|
296
|
+
|
297
|
+
This has the side effect of ordering an `array_column` of numbers, as if they were strings. To keep them ordered as numbers, call:
|
298
|
+
|
299
|
+
```ruby
|
300
|
+
array_column :price, type: :number do |product|
|
301
|
+
number_to_currency(product.price)
|
302
|
+
end
|
303
|
+
```
|
304
|
+
|
305
|
+
The above code will output the price as a currency, but still sort the values as numbers rather than as strings.
|
306
|
+
|
295
307
|
|
296
308
|
### General Options
|
297
309
|
|
@@ -299,7 +311,7 @@ The following options control the general behaviour of the column:
|
|
299
311
|
|
300
312
|
```ruby
|
301
313
|
:column => 'users.id' # Set this if you're doing something tricky with the database. Used internally for .order() and .where() clauses
|
302
|
-
:type => :string # Derived from the ActiveRecord attribute default datatype. Controls searching behaviour. Valid options include :string, :text, :datetime, :integer, :boolean, :year
|
314
|
+
:type => :string # Derived from the ActiveRecord attribute default datatype. Controls searching behaviour. Valid options include :string, :text, :datetime, :date, :integer, :boolean, :year
|
303
315
|
```
|
304
316
|
|
305
317
|
### Display Options
|
@@ -325,17 +337,16 @@ The following options control the filtering behaviour of the column:
|
|
325
337
|
table_column :created_at, :filter => false # Disable filtering on this column entirely
|
326
338
|
table_column :created_at, :filter => {...} # Enable filtering with these options
|
327
339
|
|
328
|
-
:filter => {:
|
329
|
-
:filter => {:
|
330
|
-
|
331
|
-
:filter => {:type => :select, :values => ['One', 'Two'], :selected => 'Two'}
|
332
|
-
:filter => {:type => :select, :values => [*2010..(Time.zone.now.year+6)]}
|
333
|
-
:filter => {:type => :select, :values => Proc.new { PostCategory.all } }
|
334
|
-
:filter => {:type => :select, :values => Proc.new { User.all.order(:email).map { |obj| [obj.id, obj.email] } } }
|
340
|
+
:filter => {:as => :number}
|
341
|
+
:filter => {:as => :text}
|
335
342
|
|
336
|
-
:filter => {:
|
337
|
-
:filter => {:
|
343
|
+
:filter => {:as => :select, :collection => ['One', 'Two'], :selected => 'Two'}
|
344
|
+
:filter => {:as => :select, :collection => [*2010..(Time.zone.now.year+6)]}
|
345
|
+
:filter => {:as => :select, :collection => Proc.new { PostCategory.all } }
|
346
|
+
:filter => {:as => :select, :collection => Proc.new { User.all.order(:email).map { |obj| [obj.id, obj.email] } } }
|
338
347
|
|
348
|
+
:filter => {:as => :grouped_select, :collection => {'Active' => Events.active, 'Past' => Events.past }}
|
349
|
+
:filter => {:as => :grouped_select, :collection => {'Active' => [['Event A', 1], ['Event B', 2]], 'Past' => [['Event C', 3], ['Event D', 4]]} }
|
339
350
|
```
|
340
351
|
|
341
352
|
Some additional, lesser used options include:
|
@@ -537,22 +548,63 @@ This gem does its best to provide "just works" filtering of both raw SQL (table_
|
|
537
548
|
|
538
549
|
It's also very easy to override the filter behaviour on a per-column basis.
|
539
550
|
|
540
|
-
Keep in mind,
|
551
|
+
Keep in mind, that filter terms applied to hidden columns will still be considered in filter results.
|
541
552
|
|
542
|
-
|
553
|
+
To customize filter behaviour, specify a `def search_column` method in the datatables model file.
|
554
|
+
|
555
|
+
If the table column being customized is a table_column:
|
543
556
|
|
544
557
|
```ruby
|
545
|
-
def collection
|
546
|
-
|
547
|
-
.
|
548
|
-
|
549
|
-
|
550
|
-
|
558
|
+
def search_column(collection, table_column, search_term, sql_column)
|
559
|
+
if table_column[:name] == 'subscription_types'
|
560
|
+
collection.where('subscriptions.stripe_plan_id ILIKE ?', "%#{search_term}%")
|
561
|
+
else
|
562
|
+
super
|
563
|
+
end
|
551
564
|
end
|
565
|
+
```
|
566
|
+
|
567
|
+
And if the table column being customized is an array_column:
|
552
568
|
|
553
|
-
|
569
|
+
```ruby
|
570
|
+
def search_column(collection, table_column, search_term, index)
|
571
|
+
if table_column[:name] == 'price'
|
572
|
+
collection.select! { |row| row[index].include?(search_term) }
|
573
|
+
else
|
574
|
+
super
|
575
|
+
end
|
576
|
+
end
|
577
|
+
```
|
578
|
+
|
579
|
+
### Customize Order Behaviour
|
580
|
+
|
581
|
+
The order behaviour can be overridden on a per-column basis.
|
582
|
+
|
583
|
+
To custom order behaviour, specify a `def order_column` method in the datatables model file.
|
584
|
+
|
585
|
+
If the table column being customized is a table_column:
|
586
|
+
|
587
|
+
```ruby
|
588
|
+
def order_column(collection, table_column, direction, sql_column)
|
554
589
|
if table_column[:name] == 'subscription_types'
|
555
|
-
|
590
|
+
sql_direction = (direction == :desc ? 'DESC' : 'ASC')
|
591
|
+
collection.joins(:subscriptions).order("subscriptions.stripe_plan_id #{sql_direction}")
|
592
|
+
else
|
593
|
+
super
|
594
|
+
end
|
595
|
+
end
|
596
|
+
```
|
597
|
+
|
598
|
+
And if the table column being customized is an array_column:
|
599
|
+
|
600
|
+
```ruby
|
601
|
+
def order_column(collection, table_column, direction, index)
|
602
|
+
if table_column[:name] == 'price'
|
603
|
+
if direction == :asc
|
604
|
+
collection.sort! { |a, b| a[index].gsub(/\D/, '').to_i <=> b[index].gsub(/\D/, '').to_i }
|
605
|
+
else
|
606
|
+
collection.sort! { |a, b| b[index].gsub(/\D/, '').to_i <=> a[index].gsub(/\D/, '').to_i }
|
607
|
+
end
|
556
608
|
else
|
557
609
|
super
|
558
610
|
end
|
@@ -679,7 +731,7 @@ the filters and sorting will be automatically configured.
|
|
679
731
|
|
680
732
|
Just define `table_column :roles`
|
681
733
|
|
682
|
-
The `EffectiveRoles.roles` collection will be used for the filter
|
734
|
+
The `EffectiveRoles.roles` collection will be used for the filter collection, and sorting will be done by roles_mask.
|
683
735
|
|
684
736
|
|
685
737
|
## Get access to the raw results
|
@@ -54,7 +54,7 @@ module EffectiveDatatablesHelper
|
|
54
54
|
def datatable_header_filter(form, name, value, opts)
|
55
55
|
return render(partial: opts[:header_partial], locals: {form: form, name: (opts[:label] || name), column: opts}) if opts[:header_partial].present?
|
56
56
|
|
57
|
-
case opts[:filter][:
|
57
|
+
case opts[:filter][:as]
|
58
58
|
when :string, :text, :number
|
59
59
|
form.input name, label: false, required: false, value: value,
|
60
60
|
as: :string,
|
@@ -77,7 +77,7 @@ module EffectiveDatatablesHelper
|
|
77
77
|
when :select, :boolean
|
78
78
|
form.input name, label: false, required: false, value: value,
|
79
79
|
as: (ActionView::Helpers::FormBuilder.instance_methods.include?(:effective_select) ? :effective_select : :select),
|
80
|
-
collection: opts[:filter][:
|
80
|
+
collection: opts[:filter][:collection],
|
81
81
|
selected: opts[:filter][:selected],
|
82
82
|
multiple: opts[:filter][:multiple] == true,
|
83
83
|
include_blank: (opts[:label] || name.titleize),
|
@@ -86,7 +86,7 @@ module EffectiveDatatablesHelper
|
|
86
86
|
when :grouped_select
|
87
87
|
form.input name, label: false, required: false, value: value,
|
88
88
|
as: (ActionView::Helpers::FormBuilder.instance_methods.include?(:effective_select) ? :effective_select : :grouped_select),
|
89
|
-
collection: opts[:filter][:
|
89
|
+
collection: opts[:filter][:collection],
|
90
90
|
selected: opts[:filter][:selected],
|
91
91
|
multiple: opts[:filter][:multiple] == true,
|
92
92
|
include_blank: (opts[:label] || name.titleize),
|
@@ -2,7 +2,7 @@ module Effective
|
|
2
2
|
class ActiveRecordDatatableTool
|
3
3
|
attr_accessor :table_columns
|
4
4
|
|
5
|
-
delegate :
|
5
|
+
delegate :page, :per_page, :search_column, :order_column, :collection_class, :quote_sql, :to => :@datatable
|
6
6
|
|
7
7
|
def initialize(datatable, table_columns)
|
8
8
|
@datatable = datatable
|
@@ -13,67 +13,72 @@ module Effective
|
|
13
13
|
@search_terms ||= @datatable.search_terms.select { |name, search_term| table_columns.key?(name) }
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
@
|
16
|
+
def order_by_column
|
17
|
+
@order_by_column ||= table_columns[@datatable.order_name]
|
18
18
|
end
|
19
19
|
|
20
20
|
def order(collection)
|
21
|
-
return collection
|
21
|
+
return collection unless order_by_column.present?
|
22
22
|
|
23
|
-
|
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)
|
24
29
|
before = ''; after = ''
|
30
|
+
sql_direction = (direction == :desc ? 'DESC' : 'ASC')
|
25
31
|
|
26
32
|
if postgres?
|
27
|
-
after = if
|
33
|
+
after = if table_column[:nulls] == :first
|
28
34
|
' NULLS FIRST'
|
29
|
-
elsif
|
35
|
+
elsif table_column[:nulls] == :last
|
30
36
|
' NULLS LAST'
|
31
37
|
else
|
32
|
-
" NULLS #{
|
38
|
+
" NULLS #{direction == :desc ? 'FIRST' : 'LAST' }"
|
33
39
|
end
|
34
40
|
elsif mysql?
|
35
|
-
before = "ISNULL(#{
|
41
|
+
before = "ISNULL(#{sql_column}), "
|
36
42
|
end
|
37
43
|
|
38
|
-
if
|
39
|
-
collection.order("#{before}#{
|
40
|
-
elsif
|
41
|
-
collection.order("#{
|
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}")
|
42
48
|
else
|
43
|
-
collection.order("#{before}#{
|
49
|
+
collection.order("#{before}#{sql_column} #{sql_direction}#{after}")
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
47
53
|
def search(collection)
|
48
54
|
search_terms.each do |name, search_term|
|
49
|
-
column_search = search_column(collection, table_columns[name], search_term)
|
55
|
+
column_search = search_column(collection, table_columns[name], search_term, table_columns[name][:column])
|
50
56
|
raise 'search_column must return an ActiveRecord::Relation object' unless column_search.kind_of?(ActiveRecord::Relation)
|
51
57
|
collection = column_search
|
52
58
|
end
|
53
59
|
collection
|
54
60
|
end
|
55
61
|
|
56
|
-
def search_column_with_defaults(collection, table_column, term)
|
57
|
-
column = table_column[:column]
|
62
|
+
def search_column_with_defaults(collection, table_column, term, sql_column)
|
58
63
|
sql_op = table_column[:filter][:sql_operation] || :where # only other option is :having
|
59
64
|
|
60
65
|
case table_column[:type]
|
61
66
|
when :string, :text
|
62
|
-
if
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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] != true
|
72
|
+
collection.public_send(sql_op, "#{sql_column} = :term", term: term)
|
68
73
|
else
|
69
|
-
collection.public_send(sql_op, "#{
|
74
|
+
collection.public_send(sql_op, "#{sql_column} #{ilike} :term", term: "%#{term}%")
|
70
75
|
end
|
71
76
|
when :belongs_to_polymorphic
|
72
77
|
# our key will be something like Post_15, or Event_1
|
73
78
|
(type, id) = term.split('_')
|
74
79
|
|
75
80
|
if type.present? && id.present?
|
76
|
-
collection.public_send(sql_op, "#{
|
81
|
+
collection.public_send(sql_op, "#{sql_column} = :id AND #{sql_column.sub('_id', '_type')} = :type", id: id, type: type)
|
77
82
|
else
|
78
83
|
collection
|
79
84
|
end
|
@@ -87,7 +92,7 @@ module Effective
|
|
87
92
|
inverse = reflection.inverse_of || klass.reflect_on_association(collection.table_name) || obj.class.reflect_on_association(collection.table_name.singularize)
|
88
93
|
raise "unable to find #{klass.name} has_many :#{collection.table_name} or belongs_to :#{collection.table_name.singularize} associations" unless inverse
|
89
94
|
|
90
|
-
ids = if [:select, :grouped_select].include?(table_column[:filter][:
|
95
|
+
ids = if [:select, :grouped_select].include?(table_column[:filter][:as])
|
91
96
|
# Treat the search term as one or more IDs
|
92
97
|
inverse_ids = term.split(',').map { |term| (term = term.to_i) == 0 ? nil : term }.compact
|
93
98
|
return collection unless inverse_ids.present?
|
@@ -95,10 +100,10 @@ module Effective
|
|
95
100
|
klass.where(id: inverse_ids).joins(inverse.name).pluck(inverse.foreign_key)
|
96
101
|
else
|
97
102
|
# Treat the search term as a string.
|
98
|
-
klass_columns = if (
|
103
|
+
klass_columns = if (sql_column == klass.table_name) # No custom column has been defined
|
99
104
|
klass.columns.map { |col| col.name if col.text? }.compact # Search all database text? columns
|
100
105
|
else
|
101
|
-
[
|
106
|
+
[sql_column.gsub("#{klass.table_name}.", '')] # table_column :order_items, column: 'order_items.title'
|
102
107
|
end
|
103
108
|
|
104
109
|
conditions = klass_columns.map { |col_name| "#{klass.table_name}.#{col_name} #{ilike} :term" }
|
@@ -118,7 +123,7 @@ module Effective
|
|
118
123
|
inverse = reflection.inverse_of || klass.reflect_on_association(collection.table_name) || obj.class.reflect_on_association(collection.table_name.singularize)
|
119
124
|
raise "unable to find #{klass.name} has_and_belongs_to_many :#{collection.table_name} or belongs_to :#{collection.table_name.singularize} associations" unless inverse
|
120
125
|
|
121
|
-
ids = if [:select, :grouped_select].include?(table_column[:filter][:
|
126
|
+
ids = if [:select, :grouped_select].include?(table_column[:filter][:as])
|
122
127
|
# Treat the search term as one or more IDs
|
123
128
|
inverse_ids = term.split(',').map { |term| (term = term.to_i) == 0 ? nil : term }.compact
|
124
129
|
return collection unless inverse_ids.present?
|
@@ -127,10 +132,10 @@ module Effective
|
|
127
132
|
else
|
128
133
|
# Treat the search term as a string.
|
129
134
|
|
130
|
-
klass_columns = if (
|
135
|
+
klass_columns = if (sql_column == klass.table_name) # No custom column has been defined
|
131
136
|
klass.columns.map { |col| col.name if col.text? }.compact # Search all database text? columns
|
132
137
|
else
|
133
|
-
[
|
138
|
+
[sql_column.gsub("#{klass.table_name}.", '')] # table_column :order_items, column: 'order_items.title'
|
134
139
|
end
|
135
140
|
|
136
141
|
conditions = klass_columns.map { |col_name| "#{klass.table_name}.#{col_name} #{ilike} :term" }
|
@@ -141,9 +146,9 @@ module Effective
|
|
141
146
|
collection.public_send(sql_op, id: ids)
|
142
147
|
when :obfuscated_id
|
143
148
|
if (deobfuscated_id = collection.deobfuscate(term)) == term # We weren't able to deobfuscate it, so this is an Invalid ID
|
144
|
-
collection.public_send(sql_op, "#{
|
149
|
+
collection.public_send(sql_op, "#{sql_column} = :term", term: 0)
|
145
150
|
else
|
146
|
-
collection.public_send(sql_op, "#{
|
151
|
+
collection.public_send(sql_op, "#{sql_column} = :term", term: deobfuscated_id)
|
147
152
|
end
|
148
153
|
when :effective_address
|
149
154
|
ids = Effective::Address
|
@@ -156,7 +161,7 @@ module Effective
|
|
156
161
|
collection.with_role(term)
|
157
162
|
when :datetime, :date
|
158
163
|
begin
|
159
|
-
digits = term.scan(/(\d+)/).flatten.map
|
164
|
+
digits = term.scan(/(\d+)/).flatten.map { |digit| digit.to_i }
|
160
165
|
start_at = Time.zone.local(*digits)
|
161
166
|
|
162
167
|
case digits.length
|
@@ -176,23 +181,23 @@ module Effective
|
|
176
181
|
end_at = start_at
|
177
182
|
end
|
178
183
|
|
179
|
-
collection.public_send(sql_op, "#{
|
184
|
+
collection.public_send(sql_op, "#{sql_column} >= :start_at AND #{sql_column} <= :end_at", start_at: start_at, end_at: end_at)
|
180
185
|
rescue => e
|
181
186
|
collection
|
182
187
|
end
|
183
188
|
when :boolean
|
184
|
-
collection.public_send(sql_op, "#{
|
189
|
+
collection.public_send(sql_op, "#{sql_column} = :term", term: [1, 'true', 'yes'].include?(term.to_s.downcase))
|
185
190
|
when :integer
|
186
|
-
collection.public_send(sql_op, "#{
|
191
|
+
collection.public_send(sql_op, "#{sql_column} = :term", term: term.gsub(/\D/, '').to_i)
|
187
192
|
when :year
|
188
|
-
collection.public_send(sql_op, "EXTRACT(YEAR FROM #{
|
193
|
+
collection.public_send(sql_op, "EXTRACT(YEAR FROM #{sql_column}) = :term", term: term.to_i)
|
189
194
|
when :price
|
190
195
|
price_in_cents = (term.gsub(/[^0-9|\.]/, '').to_f * 100.0).to_i
|
191
|
-
collection.public_send(sql_op, "#{
|
192
|
-
when :currency, :decimal
|
193
|
-
collection.public_send(sql_op, "#{
|
196
|
+
collection.public_send(sql_op, "#{sql_column} = :term", term: price_in_cents)
|
197
|
+
when :currency, :decimal, :number
|
198
|
+
collection.public_send(sql_op, "#{sql_column} = :term", term: term.gsub(/[^0-9|\.]/, '').to_f)
|
194
199
|
else
|
195
|
-
collection.public_send(sql_op, "#{
|
200
|
+
collection.public_send(sql_op, "#{sql_column} = :term", term: term)
|
196
201
|
end
|
197
202
|
end
|
198
203
|
|
@@ -3,7 +3,7 @@ module Effective
|
|
3
3
|
class ArrayDatatableTool
|
4
4
|
attr_accessor :table_columns
|
5
5
|
|
6
|
-
delegate :
|
6
|
+
delegate :page, :per_page, :search_column, :order_column, :display_table_columns, :to => :@datatable
|
7
7
|
|
8
8
|
def initialize(datatable, table_columns)
|
9
9
|
@datatable = datatable
|
@@ -14,37 +14,41 @@ module Effective
|
|
14
14
|
@search_terms ||= @datatable.search_terms.select { |name, search_term| table_columns.key?(name) }
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
@
|
17
|
+
def order_by_column
|
18
|
+
@order_by_column ||= table_columns[@datatable.order_name]
|
19
19
|
end
|
20
20
|
|
21
21
|
def order(collection)
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
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
|
+
cast_array_column_value(table_column, x[index]) <=> cast_array_column_value(table_column, y[index])
|
34
|
+
elsif x[index]
|
35
|
+
-1
|
36
|
+
elsif y[index]
|
37
|
+
1
|
38
|
+
else
|
39
|
+
0
|
36
40
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
41
|
+
end
|
42
|
+
else
|
43
|
+
collection.sort! do |x, y|
|
44
|
+
if (x[index] && y[index])
|
45
|
+
cast_array_column_value(table_column, y[index]) <=> cast_array_column_value(table_column, x[index])
|
46
|
+
elsif x[index]
|
47
|
+
1
|
48
|
+
elsif y[index]
|
49
|
+
-1
|
50
|
+
else
|
51
|
+
0
|
48
52
|
end
|
49
53
|
end
|
50
54
|
end
|
@@ -54,21 +58,20 @@ module Effective
|
|
54
58
|
|
55
59
|
def search(collection)
|
56
60
|
search_terms.each do |name, search_term|
|
57
|
-
column_search = search_column(collection, table_columns[name], search_term)
|
61
|
+
column_search = search_column(collection, table_columns[name], search_term, display_index(table_columns[name]))
|
58
62
|
raise 'search_column must return an Array object' unless column_search.kind_of?(Array)
|
59
63
|
collection = column_search
|
60
64
|
end
|
61
65
|
collection
|
62
66
|
end
|
63
67
|
|
64
|
-
def search_column_with_defaults(collection, table_column, search_term)
|
68
|
+
def search_column_with_defaults(collection, table_column, search_term, index)
|
65
69
|
search_term = search_term.downcase
|
66
|
-
index = display_index(table_column)
|
67
70
|
|
68
71
|
collection.select! do |row|
|
69
72
|
value = row[index].to_s.downcase
|
70
73
|
|
71
|
-
if table_column[:filter][:
|
74
|
+
if table_column[:filter][:fuzzy] != true
|
72
75
|
value == search_term
|
73
76
|
else
|
74
77
|
value.include?(search_term)
|
@@ -86,6 +89,19 @@ module Effective
|
|
86
89
|
display_table_columns.present? ? display_table_columns.keys.index(column[:name]) : column[:array_index]
|
87
90
|
end
|
88
91
|
|
92
|
+
# When we order by Array, it's already a string.
|
93
|
+
# This gives us a mechanism to sort numbers as numbers
|
94
|
+
def cast_array_column_value(table_column, value)
|
95
|
+
case table_column[:type]
|
96
|
+
when :number, :price, :decimal, :float
|
97
|
+
(value.to_s.gsub(/[^0-9|\.]/, '').to_f rescue 0.00)
|
98
|
+
when :integer
|
99
|
+
(value.to_s.gsub(/\D/, '').to_i rescue 0)
|
100
|
+
else
|
101
|
+
value
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
89
105
|
end
|
90
106
|
end
|
91
107
|
|
@@ -11,6 +11,7 @@ module Effective
|
|
11
11
|
extend Effective::EffectiveDatatable::Dsl::ClassMethods
|
12
12
|
|
13
13
|
include Effective::EffectiveDatatable::Ajax
|
14
|
+
include Effective::EffectiveDatatable::Hooks
|
14
15
|
include Effective::EffectiveDatatable::Options
|
15
16
|
include Effective::EffectiveDatatable::Rendering
|
16
17
|
|
@@ -111,7 +112,7 @@ module Effective
|
|
111
112
|
@view.class.send(:attr_accessor, :effective_datatable)
|
112
113
|
@view.effective_datatable = self
|
113
114
|
|
114
|
-
(self.class.instance_methods(false) - [:collection, :search_column]).each do |view_method|
|
115
|
+
(self.class.instance_methods(false) - [:collection, :search_column, :order_column]).each do |view_method|
|
115
116
|
@view.class_eval { delegate view_method, :to => :@effective_datatable }
|
116
117
|
end
|
117
118
|
|
@@ -19,8 +19,8 @@ module Effective
|
|
19
19
|
def order_name
|
20
20
|
@order_name ||= begin
|
21
21
|
if params[:order] && params[:columns]
|
22
|
-
|
23
|
-
(params[:columns][
|
22
|
+
order_by_column_index = (params[:order].first[1][:column] rescue '0')
|
23
|
+
(params[:columns][order_by_column_index] || {})[:name]
|
24
24
|
elsif @default_order.present?
|
25
25
|
@default_order.keys.first
|
26
26
|
end || table_columns.find { |col, opts| opts[:type] != :bulk_actions_column }.first
|
@@ -33,11 +33,11 @@ module Effective
|
|
33
33
|
|
34
34
|
def order_direction
|
35
35
|
@order_direction ||= if params[:order].present?
|
36
|
-
params[:order].first[1][:dir] == 'desc' ?
|
36
|
+
params[:order].first[1][:dir] == 'desc' ? :desc : :asc
|
37
37
|
elsif @default_order.present?
|
38
|
-
@default_order.values.first.to_s.downcase == 'desc' ?
|
38
|
+
@default_order.values.first.to_s.downcase == 'desc' ? :desc : :asc
|
39
39
|
else
|
40
|
-
|
40
|
+
:asc
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -65,15 +65,6 @@ module Effective
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
# This is here so classes that inherit from Datatables can can override the specific where clauses on a search column
|
69
|
-
def search_column(collection, table_column, search_term)
|
70
|
-
if table_column[:array_column]
|
71
|
-
array_tool.search_column_with_defaults(collection, table_column, search_term)
|
72
|
-
else
|
73
|
-
table_tool.search_column_with_defaults(collection, table_column, search_term)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
68
|
def per_page
|
78
69
|
return 9999999 if simple?
|
79
70
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Effective
|
2
|
+
module EffectiveDatatable
|
3
|
+
module Hooks
|
4
|
+
|
5
|
+
# Called on the final collection after searching, ordering, arrayizing and formatting have been completed
|
6
|
+
def finalize(collection) # Override me if you like
|
7
|
+
collection
|
8
|
+
end
|
9
|
+
|
10
|
+
# Override this function to perform custom searching on a column
|
11
|
+
def search_column(collection, table_column, search_term, sql_column_or_array_index)
|
12
|
+
if table_column[:array_column]
|
13
|
+
array_tool.search_column_with_defaults(collection, table_column, search_term, sql_column_or_array_index)
|
14
|
+
else
|
15
|
+
table_tool.search_column_with_defaults(collection, table_column, search_term, sql_column_or_array_index)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Override this function to perform custom ordering on a column
|
20
|
+
# direction will be :asc or :desc
|
21
|
+
def order_column(collection, table_column, direction, sql_column_or_array_index)
|
22
|
+
if table_column[:array_column]
|
23
|
+
array_tool.order_column_with_defaults(collection, table_column, direction, sql_column_or_array_index)
|
24
|
+
else
|
25
|
+
table_tool.order_column_with_defaults(collection, table_column, direction, sql_column_or_array_index)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -141,22 +141,27 @@ module Effective
|
|
141
141
|
col_type = column[:type]
|
142
142
|
sql_column = column[:column].to_s.upcase
|
143
143
|
|
144
|
-
return {
|
144
|
+
return {as: :null} if filter == false
|
145
145
|
|
146
|
-
filter = {
|
146
|
+
filter = {as: filter.to_sym} if filter.kind_of?(String)
|
147
147
|
filter = {} unless filter.kind_of?(Hash)
|
148
148
|
|
149
149
|
# This is a fix for passing filter[:selected] == false, it needs to be 'false'
|
150
150
|
filter[:selected] = filter[:selected].to_s unless filter[:selected].nil?
|
151
151
|
|
152
|
-
# Allow values or collection to be used interchangeably
|
153
|
-
if filter.key?(:
|
154
|
-
filter[:
|
152
|
+
# Allow :values or :collection to be used interchangeably
|
153
|
+
if filter.key?(:values)
|
154
|
+
filter[:collection] ||= filter[:values]
|
155
155
|
end
|
156
156
|
|
157
|
-
#
|
158
|
-
if filter.key?(:
|
159
|
-
filter[:
|
157
|
+
# Allow :as or :type to be used interchangeably
|
158
|
+
if filter.key?(:type)
|
159
|
+
filter[:as] ||= filter[:type]
|
160
|
+
end
|
161
|
+
|
162
|
+
# If you pass a collection, just assume it's a select
|
163
|
+
if filter.key?(:collection) && col_type != :belongs_to_polymorphic
|
164
|
+
filter[:as] ||= :select
|
160
165
|
end
|
161
166
|
|
162
167
|
# Check if this is an aggregate column
|
@@ -167,8 +172,8 @@ module Effective
|
|
167
172
|
case col_type
|
168
173
|
when :belongs_to
|
169
174
|
{
|
170
|
-
|
171
|
-
|
175
|
+
as: :select,
|
176
|
+
collection: (
|
172
177
|
if belongs_to[:klass].respond_to?(:datatables_filter)
|
173
178
|
Proc.new { belongs_to[:klass].datatables_filter }
|
174
179
|
else
|
@@ -177,12 +182,12 @@ module Effective
|
|
177
182
|
)
|
178
183
|
}
|
179
184
|
when :belongs_to_polymorphic
|
180
|
-
{
|
185
|
+
{as: :grouped_select, polymorphic: true, collection: {}}
|
181
186
|
when :has_many
|
182
187
|
{
|
183
|
-
|
188
|
+
as: :select,
|
184
189
|
multiple: true,
|
185
|
-
|
190
|
+
collection: (
|
186
191
|
if has_many[:klass].respond_to?(:datatables_filter)
|
187
192
|
Proc.new { has_many[:klass].datatables_filter }
|
188
193
|
else
|
@@ -192,9 +197,9 @@ module Effective
|
|
192
197
|
}
|
193
198
|
when :has_and_belongs_to_many
|
194
199
|
{
|
195
|
-
|
200
|
+
as: :select,
|
196
201
|
multiple: true,
|
197
|
-
|
202
|
+
collection: (
|
198
203
|
if has_and_belongs_to_manys[:klass].respond_to?(:datatables_filter)
|
199
204
|
Proc.new { has_and_belongs_to_manys[:klass].datatables_filter }
|
200
205
|
else
|
@@ -203,25 +208,25 @@ module Effective
|
|
203
208
|
)
|
204
209
|
}
|
205
210
|
when :effective_address
|
206
|
-
{
|
211
|
+
{as: :string}
|
207
212
|
when :effective_roles
|
208
|
-
{
|
213
|
+
{as: :select, collection: EffectiveRoles.roles}
|
209
214
|
when :integer
|
210
|
-
{
|
215
|
+
{as: :number}
|
211
216
|
when :boolean
|
212
217
|
if EffectiveDatatables.boolean_format == :yes_no
|
213
|
-
{
|
218
|
+
{as: :boolean, collection: [['Yes', true], ['No', false]] }
|
214
219
|
else
|
215
|
-
{
|
220
|
+
{as: :boolean, collection: [['true', true], ['false', false]] }
|
216
221
|
end
|
217
222
|
when :datetime
|
218
|
-
{
|
223
|
+
{as: :datetime}
|
219
224
|
when :date
|
220
|
-
{
|
225
|
+
{as: :date}
|
221
226
|
when :bulk_actions_column
|
222
|
-
{
|
227
|
+
{as: :bulk_actions_column}
|
223
228
|
else
|
224
|
-
{
|
229
|
+
{as: :string}
|
225
230
|
end.merge(filter.symbolize_keys)
|
226
231
|
end
|
227
232
|
|
@@ -5,10 +5,6 @@ module Effective
|
|
5
5
|
module Rendering
|
6
6
|
BLANK = ''.freeze
|
7
7
|
|
8
|
-
def finalize(collection) # Override me if you like
|
9
|
-
collection
|
10
|
-
end
|
11
|
-
|
12
8
|
protected
|
13
9
|
|
14
10
|
# So the idea here is that we want to do as much as possible on the database in ActiveRecord
|
@@ -37,7 +33,7 @@ module Effective
|
|
37
33
|
self.display_records = col.size
|
38
34
|
end
|
39
35
|
|
40
|
-
if array_tool.
|
36
|
+
if array_tool.order_by_column.present?
|
41
37
|
col = self.arrayize(col)
|
42
38
|
col = array_tool.order(col)
|
43
39
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: effective_datatables
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Code and Effect
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -142,6 +142,7 @@ files:
|
|
142
142
|
- app/models/effective/datatable.rb
|
143
143
|
- app/models/effective/effective_datatable/ajax.rb
|
144
144
|
- app/models/effective/effective_datatable/dsl.rb
|
145
|
+
- app/models/effective/effective_datatable/hooks.rb
|
145
146
|
- app/models/effective/effective_datatable/options.rb
|
146
147
|
- app/models/effective/effective_datatable/rendering.rb
|
147
148
|
- app/views/effective/datatables/_actions_column.html.haml
|