activerecord-filter 8.0.0 → 9.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09732db42dbc93213eb9cb72360f1fe556cb8ee1be65ef4ab9a5ea66460e463e'
4
- data.tar.gz: a363687983fb83cdb7db3a65c02e279ec56b57566c2199a844dc6bc973c50382
3
+ metadata.gz: a42ca6ff3bdbd4318c13518684d31f6bdf3b7a98aa02e877e2d8147c83d2efef
4
+ data.tar.gz: 5b595328b4129bced6fa43a87c6f93398d6cb35942bb0aa693874b49a062de91
5
5
  SHA512:
6
- metadata.gz: b98da863c302b2aba54a339f3a78de317a9dbf11235f318f6ffb750bdb80265e5fde571d1a7d7eef72066bb37ad7e9a70bcbf7095b598b9d6b66911e9fd1db03
7
- data.tar.gz: d2969b02282399759622892ad30ef2fc181358bd2c9c2c2051f772c7d08395f951f446588a0cd7a060573580b582970ac614252d9be68a1c937c781cfada928f
6
+ metadata.gz: 3e1500189bf99dae963016fff43cb78cda9aa42fe771907fd7842562d67ea704b4609822e85a3f559f96776c3f084a54e790cfdd4bc1026731da44989b7a5109
7
+ data.tar.gz: 6f9aeae084bf0b7efde2b69dc20a1ac15b8312bbedd800820f5b84fa7e733147357a09fdcf8272e2c1a263077f2fff93a40c5aebd60b9ef3ce2adbd90537385a
data/README.md CHANGED
@@ -1,9 +1,13 @@
1
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 an easy way to accept user input and filter a query by that input.
4
4
 
5
- Installtion
6
- -----------
5
+ ## Requirements
6
+
7
+ - Ruby >= 3.3
8
+ - ActiveRecord >= 8.0
9
+
10
+ ## Installation
7
11
 
8
12
  - Add `gem 'activerecord-filter', require: 'active_record/filter'`
9
13
  - Run `bundle install`
@@ -124,10 +128,39 @@ Property.filter("metadata.key": { eq: 'value' }).to_sql
124
128
  # => "...WHERE "properties"."metadata" #> '{key}' = 'value'..."
125
129
  ```
126
130
 
127
- It can also sort on relations:
131
+ It can also filter across associations. Any association (`belongs_to`,
132
+ `has_many`, `has_one`, `has_and_belongs_to_many`, `has_many :through`) can be
133
+ nested, and the join is added for you:
128
134
 
129
135
  ```ruby
130
136
  Photo.filter(property: {name: 'Empire State'}).to_sql
131
137
  # => "... LEFT OUTER JOIN properties ON properties.id = photos.property_id ...
132
138
  # => "... WHERE properties.name = 'Empire State'"
133
139
  ```
140
+
141
+ Pass `true`/`false` against an association to filter on its presence:
142
+
143
+ ```ruby
144
+ Account.filter(photos: true).to_sql # accounts that have photos
145
+ Account.filter(photos: false).to_sql # accounts with no photos
146
+ ```
147
+
148
+ For a polymorphic association, scope it to a type with `as:`:
149
+
150
+ ```ruby
151
+ View.filter(subject: {as: 'Property', name: 'Name'}).to_sql
152
+ ```
153
+
154
+ Combining conditions with AND / OR
155
+ ----------------------------------
156
+
157
+ Pass an array to group conditions. Use the strings `'AND'` / `'OR'` as
158
+ separators, and nest arrays for precedence:
159
+
160
+ ```ruby
161
+ Property.filter([{id: 10}, 'OR', {name: 'name'}]).to_sql
162
+ # => "... WHERE ((properties.id = 10) OR (properties.name = 'name'))"
163
+
164
+ Property.filter([{id: 10}, 'AND', [{id: 10}, 'OR', {name: 'name'}]]).to_sql
165
+ # => "... WHERE properties.id = 10 AND ((properties.id = 10) OR (properties.name = 'name'))"
166
+ ```
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Filter::AliasTrackerExtension
2
4
 
3
5
  def initialize(*, **)
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ActiveRecord::Filter::FilterClauseFactory
2
4
 
3
5
  def initialize(model, predicate_builder)
4
- @model = model
6
+ @klass = model
5
7
  @predicate_builder = predicate_builder
6
8
  end
7
9
 
@@ -17,6 +19,6 @@ class ActiveRecord::Filter::FilterClauseFactory
17
19
 
18
20
  protected
19
21
 
20
- attr_reader :model, :predicate_builder
22
+ attr_reader :klass, :predicate_builder
21
23
 
22
24
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/concern"
2
4
 
3
5
  module ActiveRecord::Filter::PredicateBuilderExtension
@@ -26,6 +28,15 @@ module ActiveRecord::Filter::PredicateBuilderExtension
26
28
  relations << j
27
29
  end
28
30
  end
31
+ elsif js.is_a?(Proc)
32
+ resolved = js.call(value)
33
+ Array(resolved).compact.each do |j|
34
+ if j.is_a?(String)
35
+ custom << j
36
+ else
37
+ relations << j
38
+ end
39
+ end
29
40
  elsif js
30
41
  if js.is_a?(String)
31
42
  custom << js
@@ -53,17 +64,17 @@ module ActiveRecord::Filter::PredicateBuilderExtension
53
64
  ]
54
65
  else
55
66
  {
56
- key => build_filter_joins(reflection.klass, value, [], custom)
67
+ reflection.name => build_filter_joins(reflection.klass, value, [], custom)
57
68
  }
58
69
  end
59
70
  elsif value.is_a?(Array)
60
71
  value.each do |v|
61
72
  relations << {
62
- key => build_filter_joins(reflection.klass, v, [], custom)
73
+ reflection.name => build_filter_joins(reflection.klass, v, [], custom)
63
74
  }
64
75
  end
65
76
  elsif value != true && value != false && value != 'true' && value != 'false' && !value.nil?
66
- relations << key
77
+ relations << reflection.name
67
78
  end
68
79
  elsif !klass.columns_hash.has_key?(key.to_s) && key.to_s.end_with?('_ids') && reflection = klass._reflections[key.to_s.gsub(/_ids$/, 's').to_sym]
69
80
  relations << reflection.name
@@ -297,18 +308,18 @@ module ActiveRecord::Filter::PredicateBuilderExtension
297
308
  value = value.dup
298
309
  klass = value.delete(:as).safe_constantize
299
310
 
300
- builder = self.class.new(ActiveRecord::TableMetadata.new(
311
+ builder = new_predicate_builder_extension(
301
312
  klass,
302
313
  alias_tracker.aliased_table_for_relation(relation_trail + ["#{klass.table_name}_as_#{relation.name}"], klass.arel_table) { klass.arel_table.name },
303
314
  relation
304
- ))
315
+ )
305
316
  builder.build_from_filter_hash(value, relation_trail + ["#{klass.table_name}_as_#{relation.name}"], alias_tracker)
306
317
  else
307
- builder = self.class.new(ActiveRecord::TableMetadata.new(
318
+ builder = new_predicate_builder_extension(
308
319
  relation.klass,
309
320
  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) },
310
321
  relation
311
- ))
322
+ )
312
323
  builder.build_from_filter_hash(value, relation_trail + [relation.name], alias_tracker)
313
324
  end
314
325
 
@@ -316,12 +327,21 @@ module ActiveRecord::Filter::PredicateBuilderExtension
316
327
 
317
328
  def expand_filter_for_join_table(relation, value, relation_trail, alias_tracker)
318
329
  relation = relation.active_record._reflections[relation.active_record._reflections[relation.name].send(:delegate_reflection).options[:through]]
319
- builder = self.class.new(ActiveRecord::TableMetadata.new(
330
+ builder = new_predicate_builder_extension(
320
331
  relation.klass,
321
332
  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) },
322
333
  relation
323
- ))
334
+ )
324
335
  builder.build_from_filter_hash(value, relation_trail + [relation.name], alias_tracker)
325
336
  end
326
337
 
338
+ if ActiveRecord.version >= "8.1"
339
+ def new_predicate_builder_extension(a,b,c)
340
+ self.class.new(ActiveRecord::TableMetadata.new(a, b))
341
+ end
342
+ else
343
+ def new_predicate_builder_extension(a,b,c)
344
+ self.class.new(ActiveRecord::TableMetadata.new(a, b, c))
345
+ end
346
+ end
327
347
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Filter::QueryMethodsExtension
2
4
  private
3
5
  def build_join_buckets
@@ -8,11 +10,7 @@ private
8
10
  left_joins = select_named_joins(left_outer_joins_values, stashed_left_joins) do |left_join|
9
11
  if left_join.is_a?(ActiveRecord::QueryMethods::CTEJoin)
10
12
  buckets[:join_node] << build_with_join_node(left_join.name, Arel::Nodes::OuterJoin)
11
- # Add this elsif becasuse PR https://github.com/rails/rails/pull/46843
12
- # Changed a line https://github.com/rails/rails/blob/ae2983a75ca658d84afa414dea8eaf1cca87aa23/activerecord/lib/active_record/relation/query_methods.rb#L1769
13
- # that was probably a bug beforehand but allowed nodes to be joined
14
- # which I think was and still is supported?
15
- elsif left_join.is_a?(Arel::Nodes::OuterJoin)
13
+ elsif left_join.is_a?(Arel::Nodes::Join)
16
14
  buckets[:join_node] << left_join
17
15
  else
18
16
  raise ArgumentError, "only Hash, Symbol and Array are allowed"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Filter::RelationExtension
2
4
 
3
5
  def initialize(*, **)
@@ -49,7 +51,14 @@ module ActiveRecord::Filter::RelationExtension
49
51
  @filter_clause_factory ||= ActiveRecord::Filter::FilterClauseFactory.new(klass, predicate_builder)
50
52
  end
51
53
 
52
- if ActiveRecord.version >= "7.2"
54
+ if ActiveRecord.version >= "8.1"
55
+ def build_arel(aliases = nil)
56
+ arel = super
57
+ my_alias_tracker = ActiveRecord::Associations::AliasTracker.create(model.connection_pool, table.name, [])
58
+ build_filters(arel, my_alias_tracker)
59
+ arel
60
+ end
61
+ elsif ActiveRecord.version >= "7.2"
53
62
  def build_arel(connection, aliases = nil)
54
63
  arel = super
55
64
  my_alias_tracker = ActiveRecord::Associations::AliasTracker.create(model.connection_pool, table.name, [])
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord::Filter::SpawnMethodsExtension
2
4
 
3
5
  def except(*skips)
@@ -1,2 +1,4 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class ActiveRecord::UnkownFilterError < NoMethodError
2
4
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module Filter
3
- VERSION = '8.0.0'
5
+ VERSION = '9.0.0'
4
6
  end
5
7
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_record'
2
4
  require 'arel/extensions'
3
5
  require 'active_record/filter/unkown_filter_error'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-filter
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.0
4
+ version: 9.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Bracy
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-01-28 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activerecord
@@ -16,28 +15,28 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: 7.1.0
18
+ version: 8.0.0
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: 7.1.0
25
+ version: 8.0.0
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: arel-extensions
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: 7.1.0
32
+ version: 8.0.0
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
- version: 7.1.0
39
+ version: 8.0.0
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: pg
43
42
  requirement: !ruby/object:Gem::Requirement
@@ -58,14 +57,14 @@ dependencies:
58
57
  requirements:
59
58
  - - ">="
60
59
  - !ruby/object:Gem::Version
61
- version: 6.1.0
60
+ version: 8.0.0
62
61
  type: :development
63
62
  prerelease: false
64
63
  version_requirements: !ruby/object:Gem::Requirement
65
64
  requirements:
66
65
  - - ">="
67
66
  - !ruby/object:Gem::Version
68
- version: 6.1.0
67
+ version: 8.0.0
69
68
  - !ruby/object:Gem::Dependency
70
69
  name: bundler
71
70
  requirement: !ruby/object:Gem::Requirement
@@ -122,6 +121,20 @@ dependencies:
122
121
  - - ">="
123
122
  - !ruby/object:Gem::Version
124
123
  version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: benchmark
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :development
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
125
138
  - !ruby/object:Gem::Dependency
126
139
  name: simplecov
127
140
  requirement: !ruby/object:Gem::Requirement
@@ -142,14 +155,14 @@ dependencies:
142
155
  requirements:
143
156
  - - ">="
144
157
  - !ruby/object:Gem::Version
145
- version: 6.1.0
158
+ version: 8.0.0
146
159
  type: :development
147
160
  prerelease: false
148
161
  version_requirements: !ruby/object:Gem::Requirement
149
162
  requirements:
150
163
  - - ">="
151
164
  - !ruby/object:Gem::Version
152
- version: 6.1.0
165
+ version: 8.0.0
153
166
  - !ruby/object:Gem::Dependency
154
167
  name: faker
155
168
  requirement: !ruby/object:Gem::Requirement
@@ -214,8 +227,11 @@ files:
214
227
  homepage: https://github.com/malomalo/activerecord-filter
215
228
  licenses:
216
229
  - MIT
217
- metadata: {}
218
- post_install_message:
230
+ metadata:
231
+ homepage_uri: https://github.com/malomalo/activerecord-filter
232
+ source_code_uri: https://github.com/malomalo/activerecord-filter
233
+ changelog_uri: https://github.com/malomalo/activerecord-filter/blob/master/CHANGELOG.md
234
+ rubygems_mfa_required: 'true'
219
235
  rdoc_options:
220
236
  - "--main"
221
237
  - README.md
@@ -225,15 +241,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
225
241
  requirements:
226
242
  - - ">="
227
243
  - !ruby/object:Gem::Version
228
- version: '0'
244
+ version: '3.3'
229
245
  required_rubygems_version: !ruby/object:Gem::Requirement
230
246
  requirements:
231
247
  - - ">="
232
248
  - !ruby/object:Gem::Version
233
249
  version: '0'
234
250
  requirements: []
235
- rubygems_version: 3.5.21
236
- signing_key:
251
+ rubygems_version: 4.0.11
237
252
  specification_version: 4
238
253
  summary: A safe way to accept user parameters and query against your ActiveRecord
239
254
  Models