filterable-by 0.5.0 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
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