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