activerecord-filter 6.0.0.1 → 6.0.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: '09bb100ef828af898f984d813b053e52af5c0264e1f5991eb62062d641dce71d'
4
- data.tar.gz: 97fbdfccc6e9dff58be2d37cb4a17cc292c458c4d60610d5d5e31330d4c516d7
3
+ metadata.gz: 3f64c71b3f52eecc00c0e0ba35bc824466a756ec0781ee87fae30b2cde539a34
4
+ data.tar.gz: c4efd0dd23d922090881e9f07ce2e091551795b6e21975fa76a92d07b5101799
5
5
  SHA512:
6
- metadata.gz: 6d13276e0475e34a6232804118693ffa8e103f5bc34fa781a5a31444da88f968e17c8a81dc37753539681968605c3cbd79f16ef88ded994cb8a7db577c0f2f4a
7
- data.tar.gz: 772aff1eba3e010a77783ba6e3a40942b20bd4f5a7d4a7c218e31780ac211e9ec6a7fe4b9810b802277f801c9b00cd8434873096e5b62573a6f8f8d4b6789a86
6
+ metadata.gz: 432f672949da926119e4d06ffbcc105daaf06dddae7d2f08349b1c6b950f8002b3fb839caf191b41da2afaa0139028e135136640c7a9fbb2b0aa8224fe55dff2
7
+ data.tar.gz: 42503928c6ff4ef0cd83e56b6c7258b480d87236da66db61fa79603da351788a7b201eb0d63f2a14bebb35b9377ee841adc55e513130e2885f61f1fd27a15782
data/README.md CHANGED
@@ -56,8 +56,24 @@ Property.filter(:tags => {overlaps: ['Skyscraper', 'Brick']}).to_sql
56
56
 
57
57
  Property.filter(:tags => {contains: ['Skyscraper', 'Brick']}).to_sql
58
58
  # => "...WHERE accounts.tags @> '{"Skyscraper", "Brick"}')..."
59
+ ```
60
+
61
+ And JSON columns:
62
+
63
+ ```ruby
64
+ Property.filter(metadata: { eq: { key: 'value' } }).to_sql
65
+ # => "...WHERE "properties"."metadata" = '{\"key\":\"value\"}'..."
59
66
 
67
+ Property.filter(metadata: { contains: { key: 'value' } }).to_sql
68
+ # => "...WHERE "properties"."metadata" @> '{\"key\":\"value\"}'..."
69
+
70
+ Property.filter(metadata: { has_key: 'key' }).to_sql
71
+ # => "...WHERE "properties"."metadata" ? 'key'..."
72
+
73
+ Property.filter("metadata.key": { eq: 'value' }).to_sql
74
+ # => "...WHERE "properties"."metadata" #> '{key}' = 'value'..."
60
75
  ```
76
+
61
77
  It can also sort on relations:
62
78
 
63
79
  ```ruby
@@ -77,13 +77,13 @@ module ActiveRecord
77
77
  relations
78
78
  end
79
79
 
80
- def build_from_filter_hash(attributes, join_dependency)
80
+ def build_from_filter_hash(attributes)
81
81
  if attributes.is_a?(Array)
82
- node = build_from_filter_hash(attributes.shift, join_dependency)
82
+ node = build_from_filter_hash(attributes.shift)
83
83
 
84
84
  n = attributes.shift(2)
85
85
  while !n.empty?
86
- n[1] = build_from_filter_hash(n[1], join_dependency)
86
+ n[1] = build_from_filter_hash(n[1])
87
87
  if n[0] == 'AND'
88
88
  if node.is_a?(Arel::Nodes::And)
89
89
  node.children.push(n[1])
@@ -100,26 +100,26 @@ module ActiveRecord
100
100
 
101
101
  node
102
102
  elsif attributes.is_a?(Hash)
103
- expand_from_filter_hash(attributes, join_dependency)
103
+ expand_from_filter_hash(attributes)
104
104
  else
105
- expand_from_filter_hash({id: attributes}, join_dependency)
105
+ expand_from_filter_hash({id: attributes})
106
106
  end
107
107
  end
108
108
 
109
- def expand_from_filter_hash(attributes, join_dependency)
109
+ def expand_from_filter_hash(attributes)
110
110
  klass = table.send(:klass)
111
111
 
112
112
  children = attributes.flat_map do |key, value|
113
113
  if custom_filter = klass.filters[key]
114
- self.instance_exec(klass, table, key, value, join_dependency, &custom_filter[:block])
114
+ self.instance_exec(klass, table, key, value, &custom_filter[:block])
115
115
  elsif column = klass.columns_hash[key.to_s] || klass.columns_hash[key.to_s.split('.').first]
116
116
  expand_filter_for_column(key, column, value)
117
117
  elsif relation = klass.reflect_on_association(key)
118
- expand_filter_for_relationship(relation, value, join_dependency)
118
+ expand_filter_for_relationship(relation, value)
119
119
  elsif key.to_s.ends_with?('_ids') && relation = klass.reflect_on_association(key.to_s.gsub(/_ids$/, 's'))
120
- expand_filter_for_relationship(relation, {id: value}, join_dependency)
120
+ expand_filter_for_relationship(relation, {id: value})
121
121
  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
- expand_filter_for_join_table(relation, value, join_dependency)
122
+ expand_filter_for_join_table(relation, value)
123
123
  else
124
124
  raise ActiveRecord::UnkownFilterError.new("Unkown filter \"#{key}\" for #{klass}.")
125
125
  end
@@ -133,17 +133,21 @@ module ActiveRecord
133
133
  end
134
134
  end
135
135
 
136
- def convert_filter_value(column, value)
137
- caster = table.send(:klass).attribute_types[column.name]
138
- if value.is_a?(Array) && !column.array
139
- value.map {|v| caster.cast(v) }
136
+ def expand_filter_for_column(key, column, value)
137
+ # Not sure why
138
+ # activerecord/lib/active_record/table_metadata.rb#arel_attribute
139
+ # doesn't work here, something's not working with a
140
+ # Arel::Nodes::TableAlias, would like to go back to it one day
141
+ attribute = if klass = table.send(:klass)
142
+ if Arel::Nodes::TableAlias === table.send(:arel_table)
143
+ klass.arel_attribute(column.name, table.send(:arel_table).left)
144
+ else
145
+ klass.arel_attribute(column.name, table.send(:arel_table))
146
+ end
140
147
  else
141
- caster.cast(value)
148
+ table.send(:arel_table)[column.name]
142
149
  end
143
- end
144
-
145
- def expand_filter_for_column(key, column, value)
146
- attribute = table.arel_attribute(column.name)
150
+
147
151
  if column.type == :json || column.type == :jsonb
148
152
  names = key.to_s.split('.')
149
153
  names.shift
@@ -162,9 +166,9 @@ module ActiveRecord
162
166
  elsif value == false || value == 'false'
163
167
  column.type == :boolean ? attribute.eq(false) : attribute.eq(nil)
164
168
  elsif value.is_a?(Array) && !column.array
165
- attribute.in(convert_filter_value(column, value))
169
+ attribute.in(value)
166
170
  elsif column.type != :json && column.type != :jsonb
167
- converted_value = convert_filter_value(column, column.array ? Array(value) : value)
171
+ converted_value = column.array ? Array(value) : value
168
172
  attribute.eq(converted_value)
169
173
  else
170
174
  raise ActiveRecord::UnkownFilterError.new("Unkown type for #{column}. (type #{value.class})")
@@ -175,17 +179,17 @@ module ActiveRecord
175
179
  def expand_filter_for_arel_attribute(column, attribute, key, value)
176
180
  case key.to_sym
177
181
  when :contains
178
- attribute.contains(column.array ? convert_filter_value(column, Array(value)) : convert_filter_value(column, value))
182
+ attribute.contains(column.array ? Array(value) : value)
179
183
  when :contained_by
180
- attribute.contained_by(column.array ? convert_filter_value(column, Array(value)) : convert_filter_value(column, value))
184
+ attribute.contained_by(column.array ? Array(value) : value)
181
185
  when :equal_to, :eq
182
- attribute.eq(convert_filter_value(column, value))
186
+ attribute.eq(value)
183
187
  when :excludes
184
- attribute.excludes(convert_filter_value(column, Array(value)))
188
+ attribute.excludes(Array(value))
185
189
  when :greater_than, :gt
186
- attribute.gt(convert_filter_value(column, value))
190
+ attribute.gt(value)
187
191
  when :greater_than_or_equal_to, :gteq, :gte
188
- attribute.gteq(convert_filter_value(column, value))
192
+ attribute.gteq(value)
189
193
  when :has_key
190
194
  attribute.has_key(value)
191
195
  when :has_keys
@@ -193,7 +197,7 @@ module ActiveRecord
193
197
  when :has_any_key
194
198
  attribute.has_any_key(*Array(value).map { |x| Arel::Nodes.build_quoted(x) })
195
199
  when :in
196
- attribute.in(convert_filter_value(column, value))
200
+ attribute.in(value)
197
201
  when :intersects
198
202
  # geometry_value = if value.is_a?(Hash) # GeoJSON
199
203
  # Arel::Nodes::NamedFunction.new('ST_GeomFromGeoJSON', [JSON.generate(value)])
@@ -214,24 +218,24 @@ module ActiveRecord
214
218
 
215
219
  Arel::Nodes::NamedFunction.new('ST_Intersects', [attribute, geometry_value])
216
220
  when :less_than, :lt
217
- attribute.lt(convert_filter_value(column, value))
221
+ attribute.lt(value)
218
222
  when :less_than_or_equal_to, :lteq, :lte
219
- attribute.lteq(convert_filter_value(column, value))
223
+ attribute.lteq(value)
220
224
  when :like, :ilike
221
- attribute.matches(convert_filter_value(column, value))
225
+ attribute.matches(value)
222
226
  when :not, :not_equal, :neq
223
- attribute.not_eq(convert_filter_value(column, value))
227
+ attribute.not_eq(value)
224
228
  when :not_in
225
- attribute.not_in(convert_filter_value(column, value))
229
+ attribute.not_in(value)
226
230
  when :overlaps
227
- attribute.overlaps(convert_filter_value(column, value))
231
+ attribute.overlaps(value)
228
232
  when :not_overlaps
229
- attribute.not_overlaps(convert_filter_value(column, value))
233
+ attribute.not_overlaps(value)
230
234
  when :ts_match
231
235
  if value.is_a?(Array)
232
- attribute.ts_query(*convert_filter_value(column, value))
236
+ attribute.ts_query(*value)
233
237
  else
234
- attribute.ts_query(convert_filter_value(column, value))
238
+ attribute.ts_query(value)
235
239
  end
236
240
  when :within
237
241
  if value.is_a?(String)
@@ -250,7 +254,7 @@ module ActiveRecord
250
254
  end
251
255
  end
252
256
 
253
- def expand_filter_for_relationship(relation, value, join_dependency)
257
+ def expand_filter_for_relationship(relation, value)
254
258
  case relation.macro
255
259
  when :has_many
256
260
  if value == true || value == 'true'
@@ -276,27 +280,15 @@ module ActiveRecord
276
280
  end
277
281
  end
278
282
 
279
-
280
-
281
283
  builder = associated_predicate_builder(relation.name.to_sym)
282
-
283
- if join_dependency
284
- join_dependency = join_dependency.children.find { |c| c.reflection.name == relation.name }
285
- builder.send(:table).instance_variable_set(:@arel_table, join_dependency.tables.first)
286
- end
287
-
288
- builder.build_from_filter_hash(value, join_dependency)
284
+ builder.build_from_filter_hash(value)
289
285
  end
290
286
 
291
- def expand_filter_for_join_table(relation, value, join_dependency)
287
+ def expand_filter_for_join_table(relation, value)
292
288
  relation = relation.active_record._reflections[relation.active_record._reflections[relation.name.to_s].send(:delegate_reflection).options[:through].to_s]
293
289
 
294
290
  builder = associated_predicate_builder(relation.name.to_sym)
295
- if join_dependency
296
- join_dependency = join_dependency.children.find { |c| c.reflection.name == relation.name }
297
- builder.send(:table).instance_variable_set(:@arel_table, join_dependency.tables.first)
298
- end
299
- builder.build_from_filter_hash(value, join_dependency)
291
+ builder.build_from_filter_hash(value)
300
292
  end
301
293
 
302
294
  end
@@ -311,14 +303,14 @@ module ActiveRecord
311
303
  @predicate_builder = predicate_builder
312
304
  end
313
305
 
314
- def build(filters, join_dependency)
306
+ def build(filters)
315
307
  if filters.is_a?(Hash) || filters.is_a?(Array)
316
308
  # attributes = predicate_builder.resolve_column_aliases(filters)
317
309
  # attributes = klass.send(:expand_hash_conditions_for_aggregates, attributes)
318
310
  # attributes.stringify_keys!
319
311
  #
320
312
  # attributes, binds = predicate_builder.create_binds(attributes)
321
- parts = [predicate_builder.build_from_filter_hash(filters, join_dependency)]
313
+ parts = [predicate_builder.build_from_filter_hash(filters)]
322
314
  else
323
315
  raise ArgumentError, "Unsupported argument type: #{filters.inspect} (#{filters.class})"
324
316
  end
@@ -338,7 +330,6 @@ class ActiveRecord::Relation
338
330
 
339
331
  def initialize(klass, table: klass.arel_table, predicate_builder: klass.predicate_builder, values: {})
340
332
  @filters = []
341
- @join_dependency = nil
342
333
  super
343
334
  end
344
335
 
@@ -369,9 +360,7 @@ class ActiveRecord::Relation
369
360
 
370
361
  def filter!(filters)
371
362
  js = ActiveRecord::PredicateBuilder.filter_joins(klass, filters)
372
- js.each do |j|
373
- joins!(j) if j.present?
374
- end
363
+ js.each { |j| joins!(j) if j.present? }
375
364
  @filters << filters
376
365
  self
377
366
  end
@@ -385,45 +374,10 @@ class ActiveRecord::Relation
385
374
  build_filters(arel)
386
375
  arel
387
376
  end
388
-
389
- def build_join_query(manager, buckets, join_type, aliases)
390
- buckets.default = []
391
-
392
- association_joins = buckets[:association_join]
393
- stashed_joins = buckets[:stashed_join]
394
- join_nodes = buckets[:join_node].uniq
395
- string_joins = buckets[:string_join].map(&:strip).uniq
396
-
397
- join_list = join_nodes + convert_join_strings_to_ast(string_joins)
398
- alias_tracker = alias_tracker(join_list, aliases)
399
-
400
- join_dependency = ActiveRecord::Associations::JoinDependency.new(
401
- klass, table, association_joins
402
- )
403
-
404
- joins = join_dependency.join_constraints(stashed_joins, join_type, alias_tracker)
405
- joins.each { |join| manager.from(join) }
406
- # join_infos = join_dependency.join_constraints stashed_association_joins, join_type
407
-
408
- # join_infos.each do |info|
409
- # info.joins.each { |join| manager.from(join) }
410
- # manager.bind_values.concat info.binds
411
- # end
412
-
413
- # manager.join_sources.concat(join_list)
414
-
415
- manager.join_sources.concat(join_list)
416
-
417
- if klass.connection.class.name != 'ActiveRecord::ConnectionAdapters::SunstoneAPIAdapter'
418
- @join_dependency = join_dependency
419
- end
420
-
421
- alias_tracker.aliases
422
- end
423
377
 
424
378
  def build_filters(manager)
425
379
  @filters.each do |filters|
426
- manager.where(filter_clause_factory.build(filters, @join_dependency&.send(:join_root)).ast)
380
+ manager.where(filter_clause_factory.build(filters).ast)
427
381
  end
428
382
  end
429
383
 
@@ -1,5 +1,5 @@
1
1
  module ActiveRecord
2
2
  module Filter
3
- VERSION = '6.0.0.1'
3
+ VERSION = '6.0.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.1
4
+ version: 6.0.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: 2019-06-11 00:00:00.000000000 Z
11
+ date: 2019-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 6.0.0.rc1
19
+ version: 6.0.0.rc2
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.rc1
26
+ version: 6.0.0.rc2
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: arel-extensions
29
29
  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.rc1
61
+ version: 6.0.0.rc2
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.rc1
68
+ version: 6.0.0.rc2
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.rc1
145
+ version: 6.0.0.rc2
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.rc1
152
+ version: 6.0.0.rc2
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: faker
155
155
  requirement: !ruby/object:Gem::Requirement