filterable-by 0.5.0 → 0.5.3

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: 1369751aa86d400aa27ab43ad4cdbbcc92ff06a2ca19162582f8c95914882814
4
- data.tar.gz: ca8d93901d61b20377196045430f22e63bf622e05abdab58ec4f85a578526458
3
+ metadata.gz: 7df6ffda90f61fe76374ea6f338c2857a262f7a824620719fb36df05ba22fa0e
4
+ data.tar.gz: 31cec22f5a8c62759ca8b5155ea9060ff84a85f5dc702baf9237bc3aa3219baa
5
5
  SHA512:
6
- metadata.gz: 5e487f8f1e5337faaaf0a25c009ded133b78f21a89c07b49b4d81ead360b6cbfb1a21acb4787ff96b2e5d35931ed126f42c1d1acdfa6b23b8d8ad190da260a41
7
- data.tar.gz: e68b17a74b428364b6bb46bf92da38f18b0fda824eef32823af567b336f172ef011c2dd15a904e41accad29ea0e0c103f078e8949a30dedd08c7a6b96407152b
6
+ metadata.gz: c3e36d0fb257cb048470825bc2669b050759fec897588a0bdad51e2f5b9a7f4702d68abf32fad5412f718ef233d9a6ca5a5c4a8c64c2a6768a1140a09cb57a88
7
+ data.tar.gz: 6a488f6969ab64b3dad1091966a5da7aa072da42b973f714fdb85934a4fbfe555a9543d2d25bfad2a035b5d542033fb1411dbdb324faa921a70e41520653e3c3
@@ -0,0 +1,21 @@
1
+ name: Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ ruby:
11
+ runs-on: ubuntu-latest
12
+ strategy:
13
+ matrix:
14
+ ruby-version: ["2.7", "3.0", "3.1"]
15
+ steps:
16
+ - uses: actions/checkout@v2
17
+ - uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: ${{ matrix.ruby-version }}
20
+ bundler-cache: true
21
+ - run: bundle exec rake
data/.rubocop.yml CHANGED
@@ -1,6 +1,11 @@
1
- inherit_from:
2
- - https://gitlab.com/bsm/misc/raw/master/rubocop/default.yml
1
+ inherit_gem:
2
+ rubocop-bsm:
3
+ - default.yml
4
+ inherit_mode:
5
+ merge:
6
+ - Exclude
7
+
3
8
  AllCops:
4
- TargetRubyVersion: "2.5"
9
+ TargetRubyVersion: "2.7"
5
10
  Naming/FileName:
6
11
  Exclude: [lib/filterable-by.rb]
data/Gemfile.lock CHANGED
@@ -1,63 +1,77 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- filterable-by (0.5.0)
4
+ filterable-by (0.5.3)
5
5
  activerecord
6
6
  activesupport
7
7
 
8
8
  GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
- activemodel (6.0.0)
12
- activesupport (= 6.0.0)
13
- activerecord (6.0.0)
14
- activemodel (= 6.0.0)
15
- activesupport (= 6.0.0)
16
- activesupport (6.0.0)
11
+ activemodel (7.0.2.2)
12
+ activesupport (= 7.0.2.2)
13
+ activerecord (7.0.2.2)
14
+ activemodel (= 7.0.2.2)
15
+ activesupport (= 7.0.2.2)
16
+ activesupport (7.0.2.2)
17
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
18
- i18n (>= 0.7, < 2)
19
- minitest (~> 5.1)
20
- tzinfo (~> 1.1)
21
- zeitwerk (~> 2.1, >= 2.1.8)
22
- ast (2.4.0)
23
- concurrent-ruby (1.1.5)
24
- diff-lcs (1.3)
25
- i18n (1.6.0)
18
+ i18n (>= 1.6, < 2)
19
+ minitest (>= 5.1)
20
+ tzinfo (~> 2.0)
21
+ ast (2.4.2)
22
+ concurrent-ruby (1.1.9)
23
+ diff-lcs (1.5.0)
24
+ i18n (1.10.0)
26
25
  concurrent-ruby (~> 1.0)
27
- jaro_winkler (1.5.3)
28
- minitest (5.11.3)
29
- parallel (1.17.0)
30
- parser (2.6.3.0)
31
- ast (~> 2.4.0)
32
- rainbow (3.0.0)
33
- rake (12.3.3)
34
- rspec (3.8.0)
35
- rspec-core (~> 3.8.0)
36
- rspec-expectations (~> 3.8.0)
37
- rspec-mocks (~> 3.8.0)
38
- rspec-core (3.8.2)
39
- rspec-support (~> 3.8.0)
40
- rspec-expectations (3.8.4)
26
+ minitest (5.15.0)
27
+ parallel (1.21.0)
28
+ parser (3.1.1.0)
29
+ ast (~> 2.4.1)
30
+ rainbow (3.1.1)
31
+ rake (13.0.6)
32
+ regexp_parser (2.2.1)
33
+ rexml (3.2.5)
34
+ rspec (3.11.0)
35
+ rspec-core (~> 3.11.0)
36
+ rspec-expectations (~> 3.11.0)
37
+ rspec-mocks (~> 3.11.0)
38
+ rspec-core (3.11.0)
39
+ rspec-support (~> 3.11.0)
40
+ rspec-expectations (3.11.0)
41
41
  diff-lcs (>= 1.2.0, < 2.0)
42
- rspec-support (~> 3.8.0)
43
- rspec-mocks (3.8.1)
42
+ rspec-support (~> 3.11.0)
43
+ rspec-mocks (3.11.0)
44
44
  diff-lcs (>= 1.2.0, < 2.0)
45
- rspec-support (~> 3.8.0)
46
- rspec-support (3.8.2)
47
- rubocop (0.74.0)
48
- jaro_winkler (~> 1.5.1)
45
+ rspec-support (~> 3.11.0)
46
+ rspec-support (3.11.0)
47
+ rubocop (1.25.1)
49
48
  parallel (~> 1.10)
50
- parser (>= 2.6)
49
+ parser (>= 3.1.0.0)
51
50
  rainbow (>= 2.2.2, < 4.0)
51
+ regexp_parser (>= 1.8, < 3.0)
52
+ rexml
53
+ rubocop-ast (>= 1.15.1, < 2.0)
52
54
  ruby-progressbar (~> 1.7)
53
- unicode-display_width (>= 1.4.0, < 1.7)
54
- ruby-progressbar (1.10.1)
55
- sqlite3 (1.4.1)
56
- thread_safe (0.3.6)
57
- tzinfo (1.2.5)
58
- thread_safe (~> 0.1)
59
- unicode-display_width (1.6.0)
60
- zeitwerk (2.1.9)
55
+ unicode-display_width (>= 1.4.0, < 3.0)
56
+ rubocop-ast (1.16.0)
57
+ parser (>= 3.1.1.0)
58
+ rubocop-bsm (0.6.0)
59
+ rubocop (~> 1.0)
60
+ rubocop-performance
61
+ rubocop-rake
62
+ rubocop-rspec
63
+ rubocop-performance (1.13.3)
64
+ rubocop (>= 1.7.0, < 2.0)
65
+ rubocop-ast (>= 0.4.0)
66
+ rubocop-rake (0.6.0)
67
+ rubocop (~> 1.0)
68
+ rubocop-rspec (2.9.0)
69
+ rubocop (~> 1.19)
70
+ ruby-progressbar (1.11.0)
71
+ sqlite3 (1.4.2)
72
+ tzinfo (2.0.4)
73
+ concurrent-ruby (~> 1.0)
74
+ unicode-display_width (2.1.0)
61
75
 
62
76
  PLATFORMS
63
77
  ruby
@@ -67,8 +81,8 @@ DEPENDENCIES
67
81
  filterable-by!
68
82
  rake
69
83
  rspec
70
- rubocop
84
+ rubocop-bsm
71
85
  sqlite3
72
86
 
73
87
  BUNDLED WITH
74
- 2.0.2
88
+ 2.3.6
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Filterable By
2
2
 
3
+ [![Test](https://github.com/bsm/filterable-by/actions/workflows/test.yml/badge.svg)](https://github.com/bsm/filterable-by/actions/workflows/test.yml)
4
+
3
5
  ActiveRecord plugin to parse e.g. a `filter` query parameter apply scopes. Useful for [JSON-API][jsonapi] compatibility.
4
6
 
5
7
  [jsonapi]: http://jsonapi.org/format/#fetching-filtering
@@ -16,22 +18,33 @@ class Comment < ActiveRecord::Base
16
18
 
17
19
  filterable_by :post_id, :user_id
18
20
  filterable_by :post_author_id do |scope, value|
19
- scope.joins(:posts).where(:"posts.author_id" => value)
21
+ scope.joins(:posts).where(:'posts.author_id' => value)
22
+ end
23
+ filterable_by :only do |scope, value, **opts|
24
+ case value
25
+ when 'mine'
26
+ scope.where(user_id: opts[:user_id]) if opts[:user_id]
27
+ else
28
+ scope
29
+ end
20
30
  end
21
31
  end
22
32
 
23
- Comment.filter_by(params[:filter]) # => ActiveRecord::Relation
33
+ Comment.filter_by(params[:filter], user_id: current_user.id) # => ActiveRecord::Relation
24
34
  ```
25
35
 
26
36
  Simple use cases:
27
37
 
28
38
  ```ruby
29
- Comment.filter_by({ "post_id" => "1" })
39
+ Comment.filter_by({ 'post_id' => '1' })
30
40
  # => WHERE post_id = 1
31
41
 
32
- Comment.filter_by({ "user_id" => "2", "ignored" => "3" })
42
+ Comment.filter_by({ 'user_id' => '2', 'ignored' => '3' })
33
43
  # => WHERE user_id = 2
34
44
 
35
- Comment.filter_by({ "post_author_id" => "5" })
45
+ Comment.filter_by({ 'only' => 'mine' }, user_id: 4)
46
+ # => WHERE user_id = 4
47
+
48
+ Comment.filter_by({ 'post_author_id' => '5' })
36
49
  # => JOINS posts ON posts.id = comments.post_id WHERE posts.author_id = 5
37
50
  ```
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'filterable-by'
3
- s.version = '0.5.0'
3
+ s.version = '0.5.3'
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'
@@ -11,7 +11,7 @@ Gem::Specification.new do |s|
11
11
  s.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^spec/}) }
12
12
  s.test_files = `git ls-files -z -- spec/*`.split("\x0")
13
13
  s.require_paths = ['lib']
14
- s.required_ruby_version = '>= 2.5'
14
+ s.required_ruby_version = '>= 2.7'
15
15
 
16
16
  s.add_dependency 'activerecord'
17
17
  s.add_dependency 'activesupport'
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.add_development_dependency 'bundler'
20
20
  s.add_development_dependency 'rake'
21
21
  s.add_development_dependency 'rspec'
22
- s.add_development_dependency 'rubocop'
22
+ s.add_development_dependency 'rubocop-bsm'
23
23
  s.add_development_dependency 'sqlite3'
24
+ s.metadata['rubygems_mfa_required'] = 'true'
24
25
  end
data/lib/filterable_by.rb CHANGED
@@ -27,15 +27,19 @@ module ActiveRecord
27
27
 
28
28
  def filterable_by(*names, &block)
29
29
  names.each do |name|
30
- _filterable_by_config[name.to_s] = block || ->(scope, v) { scope.where(name.to_sym => v) }
30
+ _filterable_by_config[name.to_s] = block || ->(scope, value, **) { scope.where(name.to_sym => value) }
31
31
  end
32
32
  end
33
33
 
34
- # @param [Hash] hash the filter params
34
+ # @param [Hash|ActionController::Parameters] the filter params
35
35
  # @return [ActiveRecord::Relation] the scoped relation
36
- def filter_by(hash)
36
+ def filter_by(hash = nil, **opts)
37
+ if hash.nil?
38
+ hash = opts
39
+ opts = {}
40
+ end
37
41
  scope = all
38
- return scope unless hash.is_a?(Hash)
42
+ return scope unless hash.respond_to?(:key?) && hash.respond_to?(:[])
39
43
 
40
44
  _filterable_by_config.each do |name, block|
41
45
  next unless hash.key?(name)
@@ -43,9 +47,10 @@ module ActiveRecord
43
47
  value = FilterableBy.normalize(hash[name])
44
48
  next if value.blank?
45
49
 
46
- scope = block.call(scope, value)
50
+ scope = block.call(scope, value, **opts)
47
51
  end
48
- scope
52
+
53
+ scope || none
49
54
  end
50
55
  end
51
56
  end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe ActiveRecord::FilterableBy do
4
4
  let(:alice) { AUTHORS[:alice] }
@@ -7,21 +7,23 @@ describe ActiveRecord::FilterableBy do
7
7
  let(:apost) { POSTS[:alices] }
8
8
  let(:bpost) { POSTS[:bobs] }
9
9
 
10
- it 'should have config' do
10
+ it 'has config' do
11
11
  expect(Comment.send(:_filterable_by_config).count).to eq(3)
12
12
  expect(Rating.send(:_filterable_by_config).count).to eq(2)
13
- expect(Post.send(:_filterable_by_config).count).to eq(1)
13
+ expect(Post.send(:_filterable_by_config).count).to eq(2)
14
14
  end
15
15
 
16
- it 'should ignore bad inputs' do
16
+ it 'ignores bad inputs' do
17
+ expect(Comment.filter_by.count).to eq(4)
17
18
  expect(Comment.filter_by(nil).count).to eq(4)
18
- expect(Comment.filter_by({}).count).to eq(4)
19
+ expect(Comment.filter_by(nil, extra: true).count).to eq(4)
20
+ expect(Comment.filter_by('bad').count).to eq(4)
19
21
 
20
22
  expect(Comment.filter_by('author_id' => '').count).to eq(4)
21
23
  expect(Comment.filter_by('author_id' => []).count).to eq(4)
22
24
  end
23
25
 
24
- it 'should generate simple scopes' do
26
+ it 'generates simple scopes' do
25
27
  expect(Comment.filter_by('author_id' => alice.id).pluck(:title)).to match_array(%w[AA AB])
26
28
  expect(Comment.filter_by('author_id' => bob.id).pluck(:title)).to match_array(%w[BA BB])
27
29
  expect(Comment.filter_by('author_id' => [alice.id, '']).pluck(:title)).to match_array(%w[AA AB])
@@ -38,7 +40,7 @@ describe ActiveRecord::FilterableBy do
38
40
  expect(Post.filter_by('author_id' => bob.id).count).to eq(1)
39
41
  end
40
42
 
41
- it 'should generate combined scopes' do
43
+ it 'generates combined scopes' do
42
44
  expect(Comment.filter_by('author_id' => alice.id, 'post_id' => apost.id).pluck(:title)).to match_array(['AA'])
43
45
  expect(Comment.filter_by('author_id' => alice.id, 'post_id' => bpost.id).pluck(:title)).to match_array(['AB'])
44
46
  expect(Comment.filter_by('author_id' => bob.id, 'post_id' => apost.id).pluck(:title)).to match_array(['BA'])
@@ -48,12 +50,29 @@ describe ActiveRecord::FilterableBy do
48
50
  expect(scope.pluck(:title)).to match_array(%w[AB BB])
49
51
  end
50
52
 
51
- it 'should combine with other scopes' do
53
+ it 'combines with other scopes' do
52
54
  scope = Comment.where(author_id: alice.id).filter_by('post_id' => apost.id)
53
55
  expect(scope.pluck(:title)).to match_array(['AA'])
54
56
  end
55
57
 
56
- it 'should ignore invalid scopes' do
58
+ it 'allows custom options' do
59
+ scope = Post.filter_by({ 'only' => 'me' }, user_id: alice.id)
60
+ expect(scope).to match_array([apost])
61
+
62
+ scope = Post.filter_by({ 'only' => '??' }, user_id: alice.id)
63
+ expect(scope.count).to eq(2)
64
+
65
+ scope = Post.filter_by({ 'only' => 'me' })
66
+ expect(scope.count).to eq(0)
67
+ end
68
+
69
+ it 'allows custom options from params' do
70
+ filter = { 'only' => 'me' }
71
+ expect(Post.filter_by(filter, user_id: alice.id)).to match_array([apost])
72
+ expect(Post.filter_by(filter).count).to eq(0)
73
+ end
74
+
75
+ it 'ignores invalid scopes' do
57
76
  expect(Comment.filter_by('invalid' => 1).count).to eq(4)
58
77
  expect(Post.filter_by('post_id' => bpost.id).count).to eq(2)
59
78
  expect(Rating.filter_by('post_author_id' => bob.id).count).to eq(1)
data/spec/spec_helper.rb CHANGED
@@ -2,11 +2,12 @@ ENV['RACK_ENV'] ||= 'test'
2
2
  require 'filterable-by'
3
3
  require 'rspec'
4
4
 
5
- ActiveRecord::Base.configurations['test'] = { 'adapter' => 'sqlite3', 'database' => ':memory:' }
5
+ ActiveRecord::Base.configurations = { 'test' => { 'adapter' => 'sqlite3', 'database' => ':memory:' } }
6
6
  ActiveRecord::Base.establish_connection :test
7
7
 
8
8
  ActiveRecord::Base.connection.instance_eval do
9
9
  create_table :authors do |_|
10
+ # no columns
10
11
  end
11
12
  create_table :posts do |t|
12
13
  t.integer :author_id, null: false
@@ -27,6 +28,14 @@ class Post < ActiveRecord::Base
27
28
  belongs_to :author
28
29
 
29
30
  filterable_by :author_id
31
+ filterable_by :only do |scope, value, **opts|
32
+ case value
33
+ when 'me'
34
+ scope.where(author_id: opts[:user_id]) if opts[:user_id]
35
+ else
36
+ scope
37
+ end
38
+ end
30
39
  end
31
40
 
32
41
  class Feedback < ActiveRecord::Base
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.0
4
+ version: 0.5.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dimitrij Denissenko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-27 00:00:00.000000000 Z
11
+ date: 2022-03-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -81,7 +81,7 @@ dependencies:
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
- name: rubocop
84
+ name: rubocop-bsm
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - ">="
@@ -115,9 +115,9 @@ executables: []
115
115
  extensions: []
116
116
  extra_rdoc_files: []
117
117
  files:
118
+ - ".github/workflows/test.yml"
118
119
  - ".gitignore"
119
120
  - ".rubocop.yml"
120
- - ".travis.yml"
121
121
  - Gemfile
122
122
  - Gemfile.lock
123
123
  - LICENSE
@@ -126,12 +126,13 @@ files:
126
126
  - filterable-by.gemspec
127
127
  - lib/filterable-by.rb
128
128
  - lib/filterable_by.rb
129
- - spec/filterable_by_spec.rb
129
+ - spec/active_record/filterable_by_spec.rb
130
130
  - spec/spec_helper.rb
131
131
  homepage: https://github.com/bsm/filterable-by
132
132
  licenses:
133
133
  - MIT
134
- metadata: {}
134
+ metadata:
135
+ rubygems_mfa_required: 'true'
135
136
  post_install_message:
136
137
  rdoc_options: []
137
138
  require_paths:
@@ -140,17 +141,17 @@ required_ruby_version: !ruby/object:Gem::Requirement
140
141
  requirements:
141
142
  - - ">="
142
143
  - !ruby/object:Gem::Version
143
- version: '2.5'
144
+ version: '2.7'
144
145
  required_rubygems_version: !ruby/object:Gem::Requirement
145
146
  requirements:
146
147
  - - ">="
147
148
  - !ruby/object:Gem::Version
148
149
  version: '0'
149
150
  requirements: []
150
- rubygems_version: 3.0.3
151
+ rubygems_version: 3.3.3
151
152
  signing_key:
152
153
  specification_version: 4
153
154
  summary: Generate white-listed filter scopes from URL parameter values
154
155
  test_files:
155
- - spec/filterable_by_spec.rb
156
+ - spec/active_record/filterable_by_spec.rb
156
157
  - spec/spec_helper.rb
data/.travis.yml DELETED
@@ -1,5 +0,0 @@
1
- rvm:
2
- - 2.5
3
- - 2.6
4
- gemfile:
5
- - Gemfile