activerecord-filter 6.0.0.4 → 6.1.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 +77 -30
- data/lib/active_record/filter.rb +119 -65
- data/lib/active_record/filter/version.rb +1 -1
- metadata +14 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 29fb0cf2e61a150f0ea6aaa4877961ccd58008daeb7af2bb9766f726f9b37e46
|
4
|
+
data.tar.gz: 7492a6050879102efac043637217972388f607af7c62a3b4a176b04d7695ea9b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c2f00f35c2eeff0a93866024fbacaf5a24f14843d82ccf81e5b0eefb36918a3892424c00a3b5f01d8f42f176e63331b3ca90fb54be388900517572be44857d9
|
7
|
+
data.tar.gz: e3bd52216a2cbd41e9769cf7b813a5b69af63f03c45a7616e7cb0eea444711f746fd8a1bcb7b377b5b8c604463c011263f802473db072990d7a1559590c06e0d
|
data/README.md
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
Installtion
|
6
6
|
-----------
|
7
7
|
|
8
|
-
- Add `gem 'activerecord-filter', require: 'active_record/filter'
|
8
|
+
- Add `gem 'activerecord-filter', require: 'active_record/filter'`
|
9
9
|
- Run `bundle install`
|
10
10
|
|
11
11
|
Examples
|
@@ -14,48 +14,89 @@ Examples
|
|
14
14
|
Normal columns:
|
15
15
|
|
16
16
|
```ruby
|
17
|
-
Property.filter(:
|
18
|
-
|
17
|
+
Property.filter(id: 5).to_sql
|
18
|
+
Property.filter(id: {eq: 5}).to_sql
|
19
|
+
Property.filter(id: {equal_to: 5}).to_sql
|
20
|
+
# => "... WHERE properties.id = 5 ..."
|
19
21
|
|
20
|
-
Property.filter(:
|
21
|
-
|
22
|
+
Property.filter(id: {not: 5}).to_sql
|
23
|
+
Property.filter(id: {neq: 5}).to_sql
|
24
|
+
Property.filter(id: {not_equal: 5}).to_sql
|
25
|
+
# => "... WHERE properties.id != 5 ..."
|
22
26
|
|
23
|
-
Property.filter(:
|
24
|
-
# => "...WHERE properties.id
|
27
|
+
Property.filter(id: [5, 10, 15]).to_sql
|
28
|
+
# => "... WHERE properties.id IN (5, 10, 15) ..."
|
25
29
|
|
26
|
-
Property.filter(:
|
27
|
-
# => "...WHERE properties.id
|
30
|
+
Property.filter(id: {in: [5, 10, 15]}).to_sql
|
31
|
+
# => "... WHERE properties.id IN (5, 10, 15) ..."
|
28
32
|
|
29
|
-
Property.filter(:
|
30
|
-
# => "...WHERE properties.id
|
33
|
+
Property.filter(id: {not_in: [5, 10, 15]}).to_sql
|
34
|
+
# => "... WHERE properties.id NOT IN (5, 10, 15) ..."
|
31
35
|
|
32
|
-
Property.filter(:
|
33
|
-
|
36
|
+
Property.filter(id: {gt: 5}).to_sql
|
37
|
+
Property.filter(id: {greater_than: 5}).to_sql
|
38
|
+
# => "... WHERE properties.id > 5 ..."
|
34
39
|
|
35
|
-
Property.filter(:
|
36
|
-
|
40
|
+
Property.filter(id: {gte: 5}).to_sql
|
41
|
+
Property.filter(id: {gteq: 5}).to_sql
|
42
|
+
Property.filter(id: {greater_than_or_equal_to: 5}).to_sql
|
43
|
+
# => "... WHERE properties.id >= 5 ..."
|
37
44
|
|
38
|
-
Property.filter(:
|
39
|
-
|
45
|
+
Property.filter(id: {lt: 5}).to_sql
|
46
|
+
Property.filter(id: {less_than: 5}).to_sql
|
47
|
+
# => "... WHERE properties.id < 5 ..."
|
40
48
|
|
41
|
-
Property.filter(:
|
42
|
-
|
49
|
+
Property.filter(id: {lte: 5}).to_sql
|
50
|
+
Property.filter(id: {lteq: 5}).to_sql
|
51
|
+
Property.filter(id: {less_than_or_equal_to: 5}).to_sql
|
52
|
+
# => "... WHERE properties.id <= 5 ..."
|
53
|
+
|
54
|
+
Property.filter(address_id: nil).to_sql
|
55
|
+
# => "... WHERE properties.address_id IS NULL ..."
|
56
|
+
|
57
|
+
Property.filter(address_id: false).to_sql
|
58
|
+
# => "... WHERE properties.address_id IS NULL ..."
|
59
|
+
|
60
|
+
Property.filter(boolean_column: false).to_sql
|
61
|
+
# => "... WHERE properties.boolean_column = FALSE ..."
|
62
|
+
|
63
|
+
Property.filter(address_id: true).to_sql
|
64
|
+
# => "... WHERE properties.address_id IS NOT NULL ..."
|
65
|
+
|
66
|
+
Property.filter(boolean_column: true).to_sql
|
67
|
+
# => "... WHERE properties.boolean_column = TRUE ..."
|
68
|
+
```
|
69
|
+
|
70
|
+
String columns:
|
71
|
+
|
72
|
+
```ruby
|
73
|
+
Property.filter(name: {like: 'nam%'}).to_sql
|
74
|
+
# => "... WHERE properties.name LIKE 'nam%' ..."
|
75
|
+
|
76
|
+
Property.filter(name: {ts_match: 'name'}).to_sql
|
77
|
+
# => "... WHERE to_tsvector("properties"."name") @@ to_tsquery('name') ..."
|
43
78
|
```
|
44
79
|
|
45
80
|
It can also work with array columns:
|
46
81
|
|
47
82
|
```ruby
|
48
|
-
Property.filter(:
|
83
|
+
Property.filter(tags: 'Skyscraper').to_sql
|
49
84
|
# => "...WHERE properties.tags = '{'Skyscraper'}'..."
|
50
85
|
|
51
|
-
Property.filter(:
|
52
|
-
# => "...WHERE
|
86
|
+
Property.filter(tags: ['Skyscraper', 'Brick']).to_sql
|
87
|
+
# => "...WHERE properties.tags = '{"Skyscraper", "Brick"}'..."
|
88
|
+
|
89
|
+
Property.filter(tags: {overlaps: ['Skyscraper', 'Brick']}).to_sql
|
90
|
+
# => "...WHERE properties.tags && '{"Skyscraper", "Brick"}'..."
|
91
|
+
|
92
|
+
Property.filter(tags: {contains: ['Skyscraper', 'Brick']}).to_sql
|
93
|
+
# => "...WHERE accounts.tags @> '{"Skyscraper", "Brick"}'..."
|
53
94
|
|
54
|
-
Property.filter(:
|
55
|
-
# => "...WHERE
|
95
|
+
Property.filter(tags: {excludes: ['Skyscraper', 'Brick']}).to_sql
|
96
|
+
# => "...WHERE NOT (accounts.tags @> '{"Skyscraper", "Brick"}')..."
|
56
97
|
|
57
|
-
Property.filter(:
|
58
|
-
# => "...WHERE accounts.tags
|
98
|
+
Property.filter(tags: {contained_by: ['Skyscraper', 'Brick']}).to_sql
|
99
|
+
# => "...WHERE accounts.tags <@ '{"Skyscraper", "Brick"}'..."
|
59
100
|
```
|
60
101
|
|
61
102
|
And JSON columns:
|
@@ -70,6 +111,12 @@ Property.filter(metadata: { contains: { key: 'value' } }).to_sql
|
|
70
111
|
Property.filter(metadata: { has_key: 'key' }).to_sql
|
71
112
|
# => "...WHERE "properties"."metadata" ? 'key'..."
|
72
113
|
|
114
|
+
Property.filter(metadata: { has_keys: ['key1', 'key2'] }).to_sql
|
115
|
+
# => "...WHERE "properties"."metadata" ?& array['key1', 'key2']..."
|
116
|
+
|
117
|
+
Property.filter(metadata: { has_any_key: ['key1', 'key2'] }).to_sql
|
118
|
+
# => "...WHERE "properties"."metadata" ?| array['key1', 'key2']..."
|
119
|
+
|
73
120
|
Property.filter("metadata.key": { eq: 'value' }).to_sql
|
74
121
|
# => "...WHERE "properties"."metadata" #> '{key}' = 'value'..."
|
75
122
|
```
|
@@ -77,7 +124,7 @@ Property.filter("metadata.key": { eq: 'value' }).to_sql
|
|
77
124
|
It can also sort on relations:
|
78
125
|
|
79
126
|
```ruby
|
80
|
-
Photo.filter(:
|
81
|
-
# => "...INNER JOIN properties ON properties.id = photos.property_id
|
82
|
-
# => "
|
83
|
-
```
|
127
|
+
Photo.filter(property: {name: 'Empire State'}).to_sql
|
128
|
+
# => "... INNER JOIN properties ON properties.id = photos.property_id ...
|
129
|
+
# => "... WHERE properties.name = 'Empire State'"
|
130
|
+
```
|
data/lib/active_record/filter.rb
CHANGED
@@ -4,26 +4,40 @@ require 'arel/extensions'
|
|
4
4
|
class ActiveRecord::UnkownFilterError < NoMethodError
|
5
5
|
end
|
6
6
|
|
7
|
+
class ActiveRecord::Associations::AliasTracker
|
8
|
+
|
9
|
+
def initialize(connection, aliases)
|
10
|
+
@aliases = aliases
|
11
|
+
@connection = connection
|
12
|
+
@relation_trail = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def aliased_table_for_relation(trail, arel_table, &block)
|
16
|
+
@relation_trail[trail] ||= aliased_table_for(arel_table, &block)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
7
21
|
module ActiveRecord::Filter
|
8
22
|
|
9
23
|
delegate :filter, :filter_for, to: :all
|
10
|
-
|
24
|
+
|
11
25
|
def inherited(subclass)
|
12
26
|
super
|
13
27
|
subclass.instance_variable_set('@filters', HashWithIndifferentAccess.new)
|
14
28
|
end
|
15
|
-
|
29
|
+
|
16
30
|
def filters
|
17
31
|
@filters
|
18
32
|
end
|
19
|
-
|
33
|
+
|
20
34
|
def filter_on(name, dependent_joins=nil, &block)
|
21
35
|
@filters[name.to_s] = {
|
22
36
|
joins: dependent_joins,
|
23
37
|
block: block
|
24
38
|
}
|
25
39
|
end
|
26
|
-
|
40
|
+
|
27
41
|
end
|
28
42
|
|
29
43
|
module ActiveRecord
|
@@ -33,7 +47,7 @@ module ActiveRecord
|
|
33
47
|
custom = []
|
34
48
|
[build_filter_joins(klass, filters, [], custom), custom]
|
35
49
|
end
|
36
|
-
|
50
|
+
|
37
51
|
def self.build_filter_joins(klass, filters, relations=[], custom=[])
|
38
52
|
if filters.is_a?(Array)
|
39
53
|
filters.each { |f| build_filter_joins(klass, f, relations, custom) }.compact
|
@@ -41,7 +55,7 @@ module ActiveRecord
|
|
41
55
|
filters.each do |key, value|
|
42
56
|
if klass.filters.has_key?(key.to_sym)
|
43
57
|
js = klass.filters.dig(key.to_sym, :joins)
|
44
|
-
|
58
|
+
|
45
59
|
if js.is_a?(Array)
|
46
60
|
js.each do |j|
|
47
61
|
if j.is_a?(String)
|
@@ -59,13 +73,37 @@ module ActiveRecord
|
|
59
73
|
end
|
60
74
|
elsif reflection = klass._reflections[key.to_s]
|
61
75
|
if value.is_a?(Hash)
|
62
|
-
relations <<
|
63
|
-
|
64
|
-
|
76
|
+
relations << if reflection.polymorphic?
|
77
|
+
value = value.dup
|
78
|
+
join_klass = value.delete(:as).safe_constantize
|
79
|
+
right_table = join_klass.arel_table
|
80
|
+
left_table = reflection.active_record.arel_table
|
81
|
+
|
82
|
+
on = right_table[join_klass.primary_key].
|
83
|
+
eq(left_table[reflection.foreign_key]).
|
84
|
+
and(left_table[reflection.foreign_type].eq(join_klass.name))
|
85
|
+
|
86
|
+
cross_boundry_joins = join_klass.left_outer_joins(ActiveRecord::PredicateBuilder.filter_joins(join_klass, value).flatten).send(:build_joins, [])
|
87
|
+
|
88
|
+
[
|
89
|
+
left_table.join(right_table, Arel::Nodes::OuterJoin).on(on).join_sources,
|
90
|
+
cross_boundry_joins
|
91
|
+
]
|
92
|
+
else
|
93
|
+
{
|
94
|
+
key => build_filter_joins(reflection.klass, value, [], custom)
|
95
|
+
}
|
96
|
+
end
|
97
|
+
elsif value.is_a?(Array)
|
98
|
+
value.each do |v|
|
99
|
+
relations << {
|
100
|
+
key => build_filter_joins(reflection.klass, v, [], custom)
|
101
|
+
}
|
102
|
+
end
|
65
103
|
elsif value != true && value != false && value != 'true' && value != 'false' && !value.nil?
|
66
104
|
relations << key
|
67
105
|
end
|
68
|
-
elsif !klass.columns_hash.has_key?(key.to_s) && key.to_s.
|
106
|
+
elsif !klass.columns_hash.has_key?(key.to_s) && key.to_s.end_with?('_ids') && reflection = klass._reflections[key.to_s.gsub(/_ids$/, 's')]
|
69
107
|
relations << reflection.name
|
70
108
|
elsif reflection = klass.reflect_on_all_associations(:has_and_belongs_to_many).find {|r| r.join_table == key.to_s && value.keys.first.to_s == r.association_foreign_key.to_s }
|
71
109
|
reflection = klass._reflections[klass._reflections[reflection.name.to_s].send(:delegate_reflection).options[:through].to_s]
|
@@ -73,10 +111,10 @@ module ActiveRecord
|
|
73
111
|
end
|
74
112
|
end
|
75
113
|
end
|
76
|
-
|
114
|
+
|
77
115
|
relations
|
78
116
|
end
|
79
|
-
|
117
|
+
|
80
118
|
def build_from_filter_hash(attributes, relation_trail, alias_tracker)
|
81
119
|
if attributes.is_a?(Array)
|
82
120
|
node = build_from_filter_hash(attributes.shift, relation_trail, alias_tracker)
|
@@ -92,12 +130,19 @@ module ActiveRecord
|
|
92
130
|
end
|
93
131
|
elsif n[0] == 'OR'
|
94
132
|
node = Arel::Nodes::Grouping.new(node).or(Arel::Nodes::Grouping.new(n[1]))
|
133
|
+
elsif !n[0].is_a?(String)
|
134
|
+
n[0] = build_from_filter_hash(n[0], relation_trail, alias_tracker)
|
135
|
+
if node.is_a?(Arel::Nodes::And)
|
136
|
+
node.children.push(n[0])
|
137
|
+
else
|
138
|
+
node = node.and(n[0])
|
139
|
+
end
|
95
140
|
else
|
96
141
|
raise 'lll'
|
97
142
|
end
|
98
143
|
n = attributes.shift(2)
|
99
144
|
end
|
100
|
-
|
145
|
+
|
101
146
|
node
|
102
147
|
elsif attributes.is_a?(Hash)
|
103
148
|
expand_from_filter_hash(attributes, relation_trail, alias_tracker)
|
@@ -105,10 +150,10 @@ module ActiveRecord
|
|
105
150
|
expand_from_filter_hash({id: attributes}, relation_trail, alias_tracker)
|
106
151
|
end
|
107
152
|
end
|
108
|
-
|
153
|
+
|
109
154
|
def expand_from_filter_hash(attributes, relation_trail, alias_tracker)
|
110
155
|
klass = table.send(:klass)
|
111
|
-
|
156
|
+
|
112
157
|
children = attributes.flat_map do |key, value|
|
113
158
|
if custom_filter = klass.filters[key]
|
114
159
|
self.instance_exec(klass, table, key, value, relation_trail, alias_tracker, &custom_filter[:block])
|
@@ -116,7 +161,7 @@ module ActiveRecord
|
|
116
161
|
expand_filter_for_column(key, column, value, relation_trail)
|
117
162
|
elsif relation = klass.reflect_on_association(key)
|
118
163
|
expand_filter_for_relationship(relation, value, relation_trail, alias_tracker)
|
119
|
-
elsif key.to_s.
|
164
|
+
elsif key.to_s.end_with?('_ids') && relation = klass.reflect_on_association(key.to_s.gsub(/_ids$/, 's'))
|
120
165
|
expand_filter_for_relationship(relation, {id: value}, relation_trail, alias_tracker)
|
121
166
|
elsif relation = klass.reflect_on_all_associations(:has_and_belongs_to_many).find {|r| r.join_table == key.to_s && value.keys.first.to_s == r.association_foreign_key.to_s }
|
122
167
|
expand_filter_for_join_table(relation, value, relation_trail, alias_tracker)
|
@@ -124,7 +169,7 @@ module ActiveRecord
|
|
124
169
|
raise ActiveRecord::UnkownFilterError.new("Unkown filter \"#{key}\" for #{klass}.")
|
125
170
|
end
|
126
171
|
end
|
127
|
-
|
172
|
+
|
128
173
|
children.compact!
|
129
174
|
if children.size > 1
|
130
175
|
Arel::Nodes::And.new(children)
|
@@ -132,19 +177,19 @@ module ActiveRecord
|
|
132
177
|
children.first
|
133
178
|
end
|
134
179
|
end
|
135
|
-
|
180
|
+
|
136
181
|
def expand_filter_for_column(key, column, value, relation_trail)
|
137
|
-
attribute = table.
|
182
|
+
attribute = table.arel_table[column.name]
|
138
183
|
relation_trail.each do |rt|
|
139
184
|
attribute = Arel::Attributes::Relation.new(attribute, rt)
|
140
185
|
end
|
141
|
-
|
186
|
+
|
142
187
|
if column.type == :json || column.type == :jsonb
|
143
188
|
names = key.to_s.split('.')
|
144
189
|
names.shift
|
145
190
|
attribute = attribute.dig(names)
|
146
191
|
end
|
147
|
-
|
192
|
+
|
148
193
|
if value.is_a?(Hash)
|
149
194
|
nodes = value.map do |subkey, subvalue|
|
150
195
|
expand_filter_for_arel_attribute(column, attribute, subkey, subvalue)
|
@@ -164,19 +209,19 @@ module ActiveRecord
|
|
164
209
|
else
|
165
210
|
raise ActiveRecord::UnkownFilterError.new("Unkown type for #{column}. (type #{value.class})")
|
166
211
|
end
|
167
|
-
|
212
|
+
|
168
213
|
end
|
169
|
-
|
214
|
+
|
170
215
|
def expand_filter_for_arel_attribute(column, attribute, key, value)
|
171
216
|
case key.to_sym
|
172
217
|
when :contains
|
173
|
-
attribute.contains(column.array ? Array(value) : value)
|
218
|
+
attribute.contains(Arel::Nodes::Casted.new(column.array ? Array(value) : value, attribute))
|
174
219
|
when :contained_by
|
175
|
-
attribute.contained_by(column.array ? Array(value) : value)
|
220
|
+
attribute.contained_by(Arel::Nodes::Casted.new(column.array ? Array(value) : value, attribute))
|
176
221
|
when :equal_to, :eq
|
177
222
|
attribute.eq(value)
|
178
223
|
when :excludes
|
179
|
-
attribute.excludes(Array(value))
|
224
|
+
attribute.excludes(Arel::Nodes::Casted.new(column.array ? Array(value) : value, attribute))
|
180
225
|
when :greater_than, :gt
|
181
226
|
attribute.gt(value)
|
182
227
|
when :greater_than_or_equal_to, :gteq, :gte
|
@@ -197,7 +242,7 @@ module ActiveRecord
|
|
197
242
|
# elsif # EWKT
|
198
243
|
# elsif # WKT
|
199
244
|
# end
|
200
|
-
|
245
|
+
|
201
246
|
# TODO us above if to determin if SRID sent
|
202
247
|
geometry_value = if value.is_a?(Hash)
|
203
248
|
Arel::Nodes::NamedFunction.new('ST_SetSRID', [Arel::Nodes::NamedFunction.new('ST_GeomFromGeoJSON', [Arel::Nodes.build_quoted(JSON.generate(subvalue))]), 4326])
|
@@ -219,7 +264,7 @@ module ActiveRecord
|
|
219
264
|
when :not_in
|
220
265
|
attribute.not_in(value)
|
221
266
|
when :overlaps
|
222
|
-
attribute.overlaps(value)
|
267
|
+
attribute.overlaps(Arel::Nodes::Casted.new(column.array ? Array(value) : value, attribute))
|
223
268
|
when :not_overlaps
|
224
269
|
attribute.not_overlaps(value)
|
225
270
|
when :ts_match
|
@@ -244,60 +289,61 @@ module ActiveRecord
|
|
244
289
|
raise "Not Supported: #{key.to_sym} on column \"#{column.name}\" of type #{column.type}"
|
245
290
|
end
|
246
291
|
end
|
247
|
-
|
292
|
+
|
248
293
|
def expand_filter_for_relationship(relation, value, relation_trail, alias_tracker)
|
249
294
|
case relation.macro
|
250
295
|
when :has_many
|
251
296
|
if value == true || value == 'true'
|
252
297
|
counter_cache_column_name = relation.counter_cache_column || "#{relation.plural_name}_count"
|
253
298
|
if relation.active_record.column_names.include?(counter_cache_column_name.to_s)
|
254
|
-
return table.
|
299
|
+
return table.arel_table[counter_cache_column_name.to_sym].gt(0)
|
255
300
|
else
|
256
301
|
raise "Not Supported: #{relation.name}"
|
257
302
|
end
|
258
303
|
elsif value == false || value == 'false'
|
259
304
|
counter_cache_column_name = relation.counter_cache_column || "#{relation.plural_name}_count"
|
260
305
|
if relation.active_record.column_names.include?(counter_cache_column_name.to_s)
|
261
|
-
return table.
|
306
|
+
return table.arel_table[counter_cache_column_name.to_sym].eq(0)
|
262
307
|
else
|
263
308
|
raise "Not Supported: #{relation.name}"
|
264
309
|
end
|
265
310
|
end
|
311
|
+
|
266
312
|
when :belongs_to
|
267
313
|
if value == true || value == 'true'
|
268
|
-
return table.
|
314
|
+
return table.arel_table[relation.foreign_key].not_eq(nil)
|
269
315
|
elsif value == false || value == 'false' || value.nil?
|
270
|
-
return table.
|
316
|
+
return table.arel_table[relation.foreign_key].eq(nil)
|
271
317
|
end
|
272
318
|
end
|
273
319
|
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
320
|
+
if relation.polymorphic?
|
321
|
+
value = value.dup
|
322
|
+
klass = value.delete(:as).safe_constantize
|
323
|
+
|
324
|
+
builder = self.class.new(TableMetadata.new(
|
325
|
+
klass,
|
326
|
+
alias_tracker.aliased_table_for_relation(relation_trail + ["#{klass.table_name}_as_#{relation.name}"], klass.arel_table) { klass.arel_table.name },
|
327
|
+
relation
|
328
|
+
))
|
329
|
+
builder.build_from_filter_hash(value, relation_trail + ["#{klass.table_name}_as_#{relation.name}"], alias_tracker)
|
330
|
+
else
|
331
|
+
builder = self.class.new(TableMetadata.new(
|
332
|
+
relation.klass,
|
333
|
+
alias_tracker.aliased_table_for_relation(relation_trail + [relation.name], relation.klass.arel_table) { relation.alias_candidate(table.arel_table.name || relation.klass.arel_table) },
|
334
|
+
relation
|
335
|
+
))
|
336
|
+
builder.build_from_filter_hash(value, relation_trail + [relation.name], alias_tracker)
|
337
|
+
end
|
338
|
+
|
284
339
|
end
|
285
|
-
|
286
|
-
|
340
|
+
|
341
|
+
|
287
342
|
def expand_filter_for_join_table(relation, value, relation_trail, alias_tracker)
|
288
343
|
relation = relation.active_record._reflections[relation.active_record._reflections[relation.name.to_s].send(:delegate_reflection).options[:through].to_s]
|
289
|
-
STDOUT.puts [
|
290
|
-
relation.table_name,
|
291
|
-
relation.alias_candidate(table.send(:arel_table).name)
|
292
|
-
|
293
|
-
].inspect
|
294
344
|
builder = self.class.new(TableMetadata.new(
|
295
345
|
relation.klass,
|
296
|
-
alias_tracker.
|
297
|
-
relation.table_name,
|
298
|
-
relation.alias_candidate(table.send(:arel_table).name),
|
299
|
-
relation.klass.type_caster
|
300
|
-
),
|
346
|
+
alias_tracker.aliased_table_for_relation(relation_trail + [relation.name], relation.klass.arel_table) { relation.alias_candidate(table.arel_table.name || relation.klass.arel_table) },
|
301
347
|
relation
|
302
348
|
))
|
303
349
|
builder.build_from_filter_hash(value, relation_trail + [relation.name], alias_tracker)
|
@@ -321,7 +367,7 @@ module ActiveRecord
|
|
321
367
|
else
|
322
368
|
raise ArgumentError, "Unsupported argument type: #{filters.inspect} (#{filters.class})"
|
323
369
|
end
|
324
|
-
|
370
|
+
|
325
371
|
WhereClause.new(parts)
|
326
372
|
end
|
327
373
|
|
@@ -339,12 +385,12 @@ class ActiveRecord::Relation
|
|
339
385
|
@filters = []
|
340
386
|
super
|
341
387
|
end
|
342
|
-
|
388
|
+
|
343
389
|
def initialize_copy(other)
|
344
390
|
@filters = @filters.deep_dup
|
345
391
|
super
|
346
392
|
end
|
347
|
-
|
393
|
+
|
348
394
|
def clean_filters(value)
|
349
395
|
if value.class.name == 'ActionController::Parameters'.freeze
|
350
396
|
value.to_unsafe_h
|
@@ -357,33 +403,41 @@ class ActiveRecord::Relation
|
|
357
403
|
|
358
404
|
def filter(filters)
|
359
405
|
filters = clean_filters(filters)
|
360
|
-
|
406
|
+
|
361
407
|
if filters.nil? || filters.empty?
|
362
408
|
self
|
363
409
|
else
|
364
410
|
spawn.filter!(filters)
|
365
411
|
end
|
366
412
|
end
|
367
|
-
|
413
|
+
|
368
414
|
def filter!(filters)
|
369
415
|
js = ActiveRecord::PredicateBuilder.filter_joins(klass, filters)
|
370
|
-
js.each
|
416
|
+
js.flatten.each do |j|
|
417
|
+
if j.is_a?(String)
|
418
|
+
joins!(j)
|
419
|
+
elsif j.is_a?(Arel::Nodes::Join)
|
420
|
+
joins!(j)
|
421
|
+
elsif j.present?
|
422
|
+
left_outer_joins!(j)
|
423
|
+
end
|
424
|
+
end
|
371
425
|
@filters << filters
|
372
426
|
self
|
373
427
|
end
|
374
|
-
|
428
|
+
|
375
429
|
def filter_clause_factory
|
376
430
|
@filter_clause_factory ||= FilterClauseFactory.new(klass, predicate_builder)
|
377
431
|
end
|
378
|
-
|
432
|
+
|
379
433
|
def build_arel(aliases)
|
380
434
|
arel = super
|
381
435
|
my_alias_tracker = ActiveRecord::Associations::AliasTracker.create(connection, table.name, [])
|
382
436
|
build_filters(arel, my_alias_tracker)
|
383
437
|
arel
|
384
438
|
end
|
385
|
-
|
386
|
-
def build_filters(manager,
|
439
|
+
|
440
|
+
def build_filters(manager, alias_tracker)
|
387
441
|
@filters.each do |filters|
|
388
442
|
manager.where(filter_clause_factory.build(filters, alias_tracker).ast)
|
389
443
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-filter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.
|
4
|
+
version: 6.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jon Bracy
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 6.
|
19
|
+
version: 6.1.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: 6.
|
26
|
+
version: 6.1.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: arel-extensions
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 6.
|
33
|
+
version: 6.1.0.rc2
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 6.
|
40
|
+
version: 6.1.0.rc2
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: pg
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +58,14 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: 6.
|
61
|
+
version: 6.1.0
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 6.
|
68
|
+
version: 6.1.0
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: bundler
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -142,14 +142,14 @@ dependencies:
|
|
142
142
|
requirements:
|
143
143
|
- - ">="
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version: 6.
|
145
|
+
version: 6.1.0
|
146
146
|
type: :development
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
150
|
- - ">="
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: 6.
|
152
|
+
version: 6.1.0
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: faker
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -194,7 +194,7 @@ homepage: https://github.com/malomalo/activerecord-filter
|
|
194
194
|
licenses:
|
195
195
|
- MIT
|
196
196
|
metadata: {}
|
197
|
-
post_install_message:
|
197
|
+
post_install_message:
|
198
198
|
rdoc_options:
|
199
199
|
- "--main"
|
200
200
|
- README.md
|
@@ -211,8 +211,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
211
211
|
- !ruby/object:Gem::Version
|
212
212
|
version: '0'
|
213
213
|
requirements: []
|
214
|
-
rubygems_version: 3.
|
215
|
-
signing_key:
|
214
|
+
rubygems_version: 3.2.3
|
215
|
+
signing_key:
|
216
216
|
specification_version: 4
|
217
217
|
summary: A safe way to accept user parameters and query against your ActiveRecord
|
218
218
|
Models
|