activerecord-filter 6.0.0.7 → 6.1.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f04e35c30cfd4a96db7ba90fa1221a808121bacde16bdb70080e105274ae33d5
4
- data.tar.gz: 5aabf016e7b0cec65b87b09f191fd0da5eb55b0f48cdf94cf4f1727b2c0867af
3
+ metadata.gz: 02d2dcb9fb2f2fce2bcd28836c71fda9835fafcf85917a2dc0ce7366e7ce2446
4
+ data.tar.gz: f48abf819e7b5229f3b16176eb93a04d95190358d68267ddf9228590422e5ecf
5
5
  SHA512:
6
- metadata.gz: af710f6e3adfd0b3dab81c34a9ae1b2578e9d6047f81c0fed362e30dd97a06ee110eb189eb670bd3e0875792196a36f574e35051585aef37ec0eaca4b4849b69
7
- data.tar.gz: 869eab8791079a03a395f45ce510d31f05fb0399914ab6996cf8ef4ff4f3450da6a70941cd6068c24a193d4923ac12a539aea4620f16da292c9ae610b77a4419
6
+ metadata.gz: d9ee89bb0e6352d38c0fc2455074c904ceb2ab665495b7f7fdf3c30434ac1576fd778b417cecd2ecf1028437e170927b512ec470ffedabe513672aaf1c273e1e
7
+ data.tar.gz: 2d8014c494e72c3f77f9b7c4c6021713b90de72218c6929ffc28b848ab5491f75ce32091acbe55f5e136e87b2dbbf32266de6b14fb3547d6a98273217bd0fbf8
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # ActiveRecord::filter
1
+ # ActiveRecord::Filter
2
2
 
3
- `ActiveRecord::filter` provides and easy way to accept user input and filter a query by the input.
3
+ `ActiveRecord::Filter` provides and easy way to accept user input and filter a query by the input.
4
4
 
5
5
  Installtion
6
6
  -----------
@@ -73,6 +73,9 @@ String columns:
73
73
  Property.filter(name: {like: 'nam%'}).to_sql
74
74
  # => "... WHERE properties.name LIKE 'nam%' ..."
75
75
 
76
+ Property.filter(name: {ilike: 'nam%'}).to_sql
77
+ # => "... WHERE properties.name ILIKE 'nam%' ..."
78
+
76
79
  Property.filter(name: {ts_match: 'name'}).to_sql
77
80
  # => "... WHERE to_tsvector("properties"."name") @@ to_tsquery('name') ..."
78
81
  ```
@@ -125,6 +128,6 @@ It can also sort on relations:
125
128
 
126
129
  ```ruby
127
130
  Photo.filter(property: {name: 'Empire State'}).to_sql
128
- # => "... INNER JOIN properties ON properties.id = photos.property_id ...
131
+ # => "... LEFT OUTER JOIN properties ON properties.id = photos.property_id ...
129
132
  # => "... WHERE properties.name = 'Empire State'"
130
- ```
133
+ ```
@@ -4,6 +4,20 @@ 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
@@ -60,16 +74,21 @@ module ActiveRecord
60
74
  elsif reflection = klass._reflections[key.to_s]
61
75
  if value.is_a?(Hash)
62
76
  relations << if reflection.polymorphic?
63
- join_klass = value[:as].safe_constantize
64
-
65
- right_table = join_klass.arel_table.alias("#{join_klass.table_name}_as_#{reflection.name}")
77
+ value = value.dup
78
+ join_klass = value.delete(:as).safe_constantize
79
+ right_table = join_klass.arel_table
66
80
  left_table = reflection.active_record.arel_table
67
81
 
68
82
  on = right_table[join_klass.primary_key].
69
83
  eq(left_table[reflection.foreign_key]).
70
84
  and(left_table[reflection.foreign_type].eq(join_klass.name))
71
85
 
72
- left_table.join(right_table, Arel::Nodes::OuterJoin).on(on).join_sources
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
+ ]
73
92
  else
74
93
  {
75
94
  key => build_filter_joins(reflection.klass, value, [], custom)
@@ -84,7 +103,7 @@ module ActiveRecord
84
103
  elsif value != true && value != false && value != 'true' && value != 'false' && !value.nil?
85
104
  relations << key
86
105
  end
87
- elsif !klass.columns_hash.has_key?(key.to_s) && key.to_s.ends_with?('_ids') && reflection = klass._reflections[key.to_s.gsub(/_ids$/, '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')]
88
107
  relations << reflection.name
89
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 }
90
109
  reflection = klass._reflections[klass._reflections[reflection.name.to_s].send(:delegate_reflection).options[:through].to_s]
@@ -142,7 +161,7 @@ module ActiveRecord
142
161
  expand_filter_for_column(key, column, value, relation_trail)
143
162
  elsif relation = klass.reflect_on_association(key)
144
163
  expand_filter_for_relationship(relation, value, relation_trail, alias_tracker)
145
- elsif key.to_s.ends_with?('_ids') && relation = klass.reflect_on_association(key.to_s.gsub(/_ids$/, 's'))
164
+ elsif key.to_s.end_with?('_ids') && relation = klass.reflect_on_association(key.to_s.gsub(/_ids$/, 's'))
146
165
  expand_filter_for_relationship(relation, {id: value}, relation_trail, alias_tracker)
147
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 }
148
167
  expand_filter_for_join_table(relation, value, relation_trail, alias_tracker)
@@ -160,7 +179,7 @@ module ActiveRecord
160
179
  end
161
180
 
162
181
  def expand_filter_for_column(key, column, value, relation_trail)
163
- attribute = table.arel_attribute(column.name)
182
+ attribute = table.arel_table[column.name]
164
183
  relation_trail.each do |rt|
165
184
  attribute = Arel::Attributes::Relation.new(attribute, rt)
166
185
  end
@@ -196,13 +215,13 @@ module ActiveRecord
196
215
  def expand_filter_for_arel_attribute(column, attribute, key, value)
197
216
  case key.to_sym
198
217
  when :contains
199
- attribute.contains(column.array ? Array(value) : value)
218
+ attribute.contains(Arel::Nodes::Casted.new(column.array ? Array(value) : value, attribute))
200
219
  when :contained_by
201
- attribute.contained_by(column.array ? Array(value) : value)
220
+ attribute.contained_by(Arel::Nodes::Casted.new(column.array ? Array(value) : value, attribute))
202
221
  when :equal_to, :eq
203
222
  attribute.eq(value)
204
223
  when :excludes
205
- attribute.excludes(Array(value))
224
+ attribute.excludes(Arel::Nodes::Casted.new(column.array ? Array(value) : value, attribute))
206
225
  when :greater_than, :gt
207
226
  attribute.gt(value)
208
227
  when :greater_than_or_equal_to, :gteq, :gte
@@ -238,14 +257,16 @@ module ActiveRecord
238
257
  attribute.lt(value)
239
258
  when :less_than_or_equal_to, :lteq, :lte
240
259
  attribute.lteq(value)
241
- when :like, :ilike
242
- attribute.matches(value)
260
+ when :like
261
+ attribute.matches(value, nil, true)
262
+ when :ilike
263
+ attribute.matches(value, nil, false)
243
264
  when :not, :not_equal, :neq
244
265
  attribute.not_eq(value)
245
266
  when :not_in
246
267
  attribute.not_in(value)
247
268
  when :overlaps
248
- attribute.overlaps(value)
269
+ attribute.overlaps(Arel::Nodes::Casted.new(column.array ? Array(value) : value, attribute))
249
270
  when :not_overlaps
250
271
  attribute.not_overlaps(value)
251
272
  when :ts_match
@@ -277,14 +298,14 @@ module ActiveRecord
277
298
  if value == true || value == 'true'
278
299
  counter_cache_column_name = relation.counter_cache_column || "#{relation.plural_name}_count"
279
300
  if relation.active_record.column_names.include?(counter_cache_column_name.to_s)
280
- return table.arel_attribute(counter_cache_column_name.to_sym).gt(0)
301
+ return table.arel_table[counter_cache_column_name.to_sym].gt(0)
281
302
  else
282
303
  raise "Not Supported: #{relation.name}"
283
304
  end
284
305
  elsif value == false || value == 'false'
285
306
  counter_cache_column_name = relation.counter_cache_column || "#{relation.plural_name}_count"
286
307
  if relation.active_record.column_names.include?(counter_cache_column_name.to_s)
287
- return table.arel_attribute(counter_cache_column_name.to_sym).eq(0)
308
+ return table.arel_table[counter_cache_column_name.to_sym].eq(0)
288
309
  else
289
310
  raise "Not Supported: #{relation.name}"
290
311
  end
@@ -292,33 +313,31 @@ module ActiveRecord
292
313
 
293
314
  when :belongs_to
294
315
  if value == true || value == 'true'
295
- return table.arel_attribute(relation.foreign_key).not_eq(nil)
316
+ return table.arel_table[relation.foreign_key].not_eq(nil)
296
317
  elsif value == false || value == 'false' || value.nil?
297
- return table.arel_attribute(relation.foreign_key).eq(nil)
318
+ return table.arel_table[relation.foreign_key].eq(nil)
298
319
  end
299
320
  end
300
321
 
301
- builder = if relation.polymorphic?
322
+ if relation.polymorphic?
302
323
  value = value.dup
303
324
  klass = value.delete(:as).safe_constantize
304
325
 
305
- self.class.new(TableMetadata.new(
306
- klass,
307
- Arel::Table.new("#{klass.table_name}_as_#{relation.name}", type_caster: klass.type_caster),
326
+ builder = self.class.new(TableMetadata.new(
327
+ klass,
328
+ alias_tracker.aliased_table_for_relation(relation_trail + ["#{klass.table_name}_as_#{relation.name}"], klass.arel_table) { klass.arel_table.name },
308
329
  relation
309
330
  ))
331
+ builder.build_from_filter_hash(value, relation_trail + ["#{klass.table_name}_as_#{relation.name}"], alias_tracker)
310
332
  else
311
- self.class.new(TableMetadata.new(
333
+ builder = self.class.new(TableMetadata.new(
312
334
  relation.klass,
313
- alias_tracker.aliased_table_for(
314
- relation.table_name,
315
- relation.alias_candidate(table.send(:arel_table).name),
316
- relation.klass.type_caster
317
- ),
335
+ 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) },
318
336
  relation
319
337
  ))
338
+ builder.build_from_filter_hash(value, relation_trail + [relation.name], alias_tracker)
320
339
  end
321
- builder.build_from_filter_hash(value, relation_trail + [relation.name], alias_tracker)
340
+
322
341
  end
323
342
 
324
343
 
@@ -326,11 +345,7 @@ module ActiveRecord
326
345
  relation = relation.active_record._reflections[relation.active_record._reflections[relation.name.to_s].send(:delegate_reflection).options[:through].to_s]
327
346
  builder = self.class.new(TableMetadata.new(
328
347
  relation.klass,
329
- alias_tracker.aliased_table_for(
330
- relation.table_name,
331
- relation.alias_candidate(table.send(:arel_table).name),
332
- relation.klass.type_caster
333
- ),
348
+ 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
349
  relation
335
350
  ))
336
351
  builder.build_from_filter_hash(value, relation_trail + [relation.name], alias_tracker)
@@ -417,14 +432,14 @@ class ActiveRecord::Relation
417
432
  @filter_clause_factory ||= FilterClauseFactory.new(klass, predicate_builder)
418
433
  end
419
434
 
420
- def build_arel(aliases)
435
+ def build_arel(aliases = nil)
421
436
  arel = super
422
437
  my_alias_tracker = ActiveRecord::Associations::AliasTracker.create(connection, table.name, [])
423
438
  build_filters(arel, my_alias_tracker)
424
439
  arel
425
440
  end
426
441
 
427
- def build_filters(manager, aliases)
442
+ def build_filters(manager, alias_tracker)
428
443
  @filters.each do |filters|
429
444
  manager.where(filter_clause_factory.build(filters, alias_tracker).ast)
430
445
  end
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Filter
3
- VERSION = '6.0.0.7'
3
+ VERSION = '6.1.0.2'
4
4
  end
5
5
  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.0.0.7
4
+ version: 6.1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Bracy
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-06-26 00:00:00.000000000 Z
11
+ date: 2021-07-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.0.0
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.0.0
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.0.0.8
33
+ version: 6.1.0
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.0.0.8
40
+ version: 6.1.0
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.0.0
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.0.0
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.0.0
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.0.0
152
+ version: 6.1.0
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: faker
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -211,7 +211,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
211
211
  - !ruby/object:Gem::Version
212
212
  version: '0'
213
213
  requirements: []
214
- rubygems_version: 3.1.2
214
+ rubygems_version: 3.2.3
215
215
  signing_key:
216
216
  specification_version: 4
217
217
  summary: A safe way to accept user parameters and query against your ActiveRecord