filterable-by 0.5.3 → 0.6.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: 7df6ffda90f61fe76374ea6f338c2857a262f7a824620719fb36df05ba22fa0e
4
- data.tar.gz: 31cec22f5a8c62759ca8b5155ea9060ff84a85f5dc702baf9237bc3aa3219baa
3
+ metadata.gz: 5a5cdaf4511167346ca041eea0e8c3e472c0bea213429ffd7907a6f95501b7f8
4
+ data.tar.gz: 6e65a71a9e5aa47f41b37cfc5ac054213166e405fd9b3a81ea1054dca0ffb802
5
5
  SHA512:
6
- metadata.gz: c3e36d0fb257cb048470825bc2669b050759fec897588a0bdad51e2f5b9a7f4702d68abf32fad5412f718ef233d9a6ca5a5c4a8c64c2a6768a1140a09cb57a88
7
- data.tar.gz: 6a488f6969ab64b3dad1091966a5da7aa072da42b973f714fdb85934a4fbfe555a9543d2d25bfad2a035b5d542033fb1411dbdb324faa921a70e41520653e3c3
6
+ metadata.gz: d102876e053de4c60fdc714e0371977f389a0e3066a0538dfcb93b0e1ac8f20582fdb6ef86fe53991729c052ca85ffc22c89dca8bec80016e437d746cbd477ba
7
+ data.tar.gz: c8d8c948a1766a96abb88cab12f51648ebaf21c59e7e53b97cbc54f9778799d58d5baaf6f279d21a808139530076fee4ba47d7da7663e7f49df2a894a58f1d82
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- filterable-by (0.5.3)
4
+ filterable-by (0.6.0)
5
5
  activerecord
6
6
  activesupport
7
7
 
data/README.md CHANGED
@@ -17,15 +17,15 @@ class Comment < ActiveRecord::Base
17
17
  belongs_to :post
18
18
 
19
19
  filterable_by :post_id, :user_id
20
- filterable_by :post_author_id do |scope, value|
21
- scope.joins(:posts).where(:'posts.author_id' => value)
20
+ filterable_by :post_author_id do |value|
21
+ joins(:posts).where(:'posts.author_id' => value)
22
22
  end
23
- filterable_by :only do |scope, value, **opts|
23
+ filterable_by :only do |value, **opts|
24
24
  case value
25
25
  when 'mine'
26
- scope.where(user_id: opts[:user_id]) if opts[:user_id]
26
+ where(user_id: opts[:user_id]) if opts[:user_id]
27
27
  else
28
- scope
28
+ all
29
29
  end
30
30
  end
31
31
  end
@@ -39,6 +39,12 @@ Simple use cases:
39
39
  Comment.filter_by({ 'post_id' => '1' })
40
40
  # => WHERE post_id = 1
41
41
 
42
+ Comment.filter_by({ 'post_id' => ['1', '2'] })
43
+ # => WHERE post_id IN (1, 2)
44
+
45
+ Comment.filter_by({ 'post_id_not' => '3' })
46
+ # => WHERE post_id != 3
47
+
42
48
  Comment.filter_by({ 'user_id' => '2', 'ignored' => '3' })
43
49
  # => WHERE user_id = 2
44
50
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'filterable-by'
3
- s.version = '0.5.3'
3
+ s.version = '0.6.0'
4
4
  s.authors = ['Dimitrij Denissenko']
5
5
  s.email = ['dimitrij@blacksquaremedia.com']
6
6
  s.summary = 'Generate white-listed filter scopes from URL parameter values'
data/lib/filterable_by.rb CHANGED
@@ -13,6 +13,28 @@ module ActiveRecord
13
13
  end
14
14
  end
15
15
 
16
+ def self.merge(scope, unscoped, hash, name, **opts, &block)
17
+ key = name
18
+ positive = normalize(hash[key]) if hash.key?(key)
19
+ if positive.present?
20
+ sub = block.arity == 2 ? yield(unscoped, positive, **opts) : yield(positive, **opts)
21
+ return nil unless sub
22
+
23
+ scope = scope.merge(sub)
24
+ end
25
+
26
+ key = "#{name}_not"
27
+ negative = normalize(hash[key]) if hash.key?(key)
28
+ if negative.present?
29
+ sub = block.arity == 2 ? yield(unscoped, negative, **opts) : yield(negative, **opts)
30
+ return nil unless sub
31
+
32
+ scope = scope.merge(sub.invert_where)
33
+ end
34
+
35
+ scope
36
+ end
37
+
16
38
  module ClassMethods
17
39
  def self.extended(base) # :nodoc:
18
40
  base.class_attribute :_filterable_by_config, instance_accessor: false, instance_predicate: false
@@ -26,8 +48,12 @@ module ActiveRecord
26
48
  end
27
49
 
28
50
  def filterable_by(*names, &block)
51
+ if block && block.arity > 1
52
+ ActiveSupport::Deprecation.warn('using scope in filterable_by blocks is deprecated. Please use filterable_by(:x) {|val| where(field: val) } instead.')
53
+ end
54
+
29
55
  names.each do |name|
30
- _filterable_by_config[name.to_s] = block || ->(scope, value, **) { scope.where(name.to_sym => value) }
56
+ _filterable_by_config[name.to_s] = block || ->(value, **) { where(name.to_sym => value) }
31
57
  end
32
58
  end
33
59
 
@@ -38,16 +64,13 @@ module ActiveRecord
38
64
  hash = opts
39
65
  opts = {}
40
66
  end
67
+
41
68
  scope = all
42
69
  return scope unless hash.respond_to?(:key?) && hash.respond_to?(:[])
43
70
 
44
71
  _filterable_by_config.each do |name, block|
45
- next unless hash.key?(name)
46
-
47
- value = FilterableBy.normalize(hash[name])
48
- next if value.blank?
49
-
50
- scope = block.call(scope, value, **opts)
72
+ scope = FilterableBy.merge(scope, unscoped, hash, name, **opts, &block)
73
+ break unless scope
51
74
  end
52
75
 
53
76
  scope || none
@@ -8,8 +8,8 @@ describe ActiveRecord::FilterableBy do
8
8
  let(:bpost) { POSTS[:bobs] }
9
9
 
10
10
  it 'has config' do
11
- expect(Comment.send(:_filterable_by_config).count).to eq(3)
12
- expect(Rating.send(:_filterable_by_config).count).to eq(2)
11
+ expect(Comment.send(:_filterable_by_config).count).to eq(5)
12
+ expect(Rating.send(:_filterable_by_config).count).to eq(4)
13
13
  expect(Post.send(:_filterable_by_config).count).to eq(2)
14
14
  end
15
15
 
@@ -50,6 +50,14 @@ describe ActiveRecord::FilterableBy do
50
50
  expect(scope.pluck(:title)).to match_array(%w[AB BB])
51
51
  end
52
52
 
53
+ it 'generates negated scopes' do
54
+ expect(Comment.filter_by('author_id_not' => alice.id).pluck(:title)).to match_array(%w[BA BB])
55
+ expect(Comment.filter_by('author_id_not' => [alice.id, bob.id]).pluck(:title)).to match_array(%w[])
56
+ expect(Comment.filter_by('post_id_not' => apost.id).pluck(:title)).to match_array(%w[AB BB])
57
+ expect(Comment.filter_by('post_author_id_not' => alice.id).pluck(:title)).to match_array(%w[AB BB])
58
+ expect(Comment.filter_by('author_id' => bob.id, 'post_id_not' => bpost.id).pluck(:title)).to match_array(['BA'])
59
+ end
60
+
53
61
  it 'combines with other scopes' do
54
62
  scope = Comment.where(author_id: alice.id).filter_by('post_id' => apost.id)
55
63
  expect(scope.pluck(:title)).to match_array(['AA'])
@@ -77,4 +85,10 @@ describe ActiveRecord::FilterableBy do
77
85
  expect(Post.filter_by('post_id' => bpost.id).count).to eq(2)
78
86
  expect(Rating.filter_by('post_author_id' => bob.id).count).to eq(1)
79
87
  end
88
+
89
+ it 'supports deprecated scoping' do
90
+ expect(Comment.filter_by('deprecated' => alice.id).pluck(:title)).to match_array(%w[AA AB])
91
+ expect(Comment.filter_by('deprecated_with_opts' => alice.id).pluck(:title)).to match_array(%w[AA AB])
92
+ expect(Comment.filter_by('deprecated_not' => alice.id).pluck(:title)).to match_array(%w[BA BB])
93
+ end
80
94
  end
data/spec/spec_helper.rb CHANGED
@@ -28,12 +28,12 @@ class Post < ActiveRecord::Base
28
28
  belongs_to :author
29
29
 
30
30
  filterable_by :author_id
31
- filterable_by :only do |scope, value, **opts|
31
+ filterable_by :only do |value, **opts|
32
32
  case value
33
33
  when 'me'
34
- scope.where(author_id: opts[:user_id]) if opts[:user_id]
34
+ where(author_id: opts[:user_id]) if opts[:user_id]
35
35
  else
36
- scope
36
+ all
37
37
  end
38
38
  end
39
39
  end
@@ -43,11 +43,20 @@ class Feedback < ActiveRecord::Base
43
43
  belongs_to :post
44
44
 
45
45
  filterable_by :post_id, :author_id
46
+
47
+ ActiveSupport::Deprecation.silence do
48
+ filterable_by :deprecated do |scope, value|
49
+ scope.where(author_id: value)
50
+ end
51
+ filterable_by :deprecated_with_opts do |scope, value, **_opts|
52
+ scope.where(author_id: value)
53
+ end
54
+ end
46
55
  end
47
56
 
48
57
  class Comment < Feedback
49
- filterable_by :post_author_id do |scope, value|
50
- scope.joins(:post).where(Post.arel_table[:author_id].eq(value))
58
+ filterable_by :post_author_id do |value|
59
+ joins(:post).where(Post.arel_table[:author_id].eq(value))
51
60
  end
52
61
  end
53
62
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: filterable-by
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.3
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitrij Denissenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-08 00:00:00.000000000 Z
11
+ date: 2022-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord