filterable-by 0.5.1 → 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: ae571004dd8c2aebddad1f69b8a698b7f52f0fa5bb2d3338a74de5f6a9e18e1a
4
- data.tar.gz: f1744d312f8b99eab4ada84590b65361e1cb183ef3a41ce1969569ca723880e5
3
+ metadata.gz: 5a5cdaf4511167346ca041eea0e8c3e472c0bea213429ffd7907a6f95501b7f8
4
+ data.tar.gz: 6e65a71a9e5aa47f41b37cfc5ac054213166e405fd9b3a81ea1054dca0ffb802
5
5
  SHA512:
6
- metadata.gz: ce8c6eabe14c36dd917c8f30f29926a6ae88baa494067683912f63dd3af6445ca93b74426e1d24c3f8992e2d740d340a4bbb8b494c033872fb1a0ed51924e085
7
- data.tar.gz: eed08234ac28e40f99fc786225bc2e65ead127808e93714d8747a30fcb0c4a01fe27b450f4dfa9de713d08d05cd1c65c2e6c4412402a0567b48d5f05a1b1011b
6
+ metadata.gz: d102876e053de4c60fdc714e0371977f389a0e3066a0538dfcb93b0e1ac8f20582fdb6ef86fe53991729c052ca85ffc22c89dca8bec80016e437d746cbd477ba
7
+ data.tar.gz: c8d8c948a1766a96abb88cab12f51648ebaf21c59e7e53b97cbc54f9778799d58d5baaf6f279d21a808139530076fee4ba47d7da7663e7f49df2a894a58f1d82
@@ -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,68 +1,77 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- filterable-by (0.5.1)
4
+ filterable-by (0.6.0)
5
5
  activerecord
6
6
  activesupport
7
7
 
8
8
  GEM
9
9
  remote: http://rubygems.org/
10
10
  specs:
11
- activemodel (6.0.3.2)
12
- activesupport (= 6.0.3.2)
13
- activerecord (6.0.3.2)
14
- activemodel (= 6.0.3.2)
15
- activesupport (= 6.0.3.2)
16
- activesupport (6.0.3.2)
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.2, >= 2.2.2)
22
- ast (2.4.1)
23
- concurrent-ruby (1.1.6)
24
- diff-lcs (1.4.4)
25
- i18n (1.8.3)
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
- minitest (5.14.1)
28
- parallel (1.19.2)
29
- parser (2.7.1.4)
26
+ minitest (5.15.0)
27
+ parallel (1.21.0)
28
+ parser (3.1.1.0)
30
29
  ast (~> 2.4.1)
31
- rainbow (3.0.0)
32
- rake (13.0.1)
33
- regexp_parser (1.7.1)
34
- rexml (3.2.4)
35
- rspec (3.9.0)
36
- rspec-core (~> 3.9.0)
37
- rspec-expectations (~> 3.9.0)
38
- rspec-mocks (~> 3.9.0)
39
- rspec-core (3.9.2)
40
- rspec-support (~> 3.9.3)
41
- rspec-expectations (3.9.2)
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)
42
41
  diff-lcs (>= 1.2.0, < 2.0)
43
- rspec-support (~> 3.9.0)
44
- rspec-mocks (3.9.1)
42
+ rspec-support (~> 3.11.0)
43
+ rspec-mocks (3.11.0)
45
44
  diff-lcs (>= 1.2.0, < 2.0)
46
- rspec-support (~> 3.9.0)
47
- rspec-support (3.9.3)
48
- rubocop (0.87.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.7.1.1)
49
+ parser (>= 3.1.0.0)
51
50
  rainbow (>= 2.2.2, < 4.0)
52
- regexp_parser (>= 1.7)
51
+ regexp_parser (>= 1.8, < 3.0)
53
52
  rexml
54
- rubocop-ast (>= 0.1.0, < 1.0)
53
+ rubocop-ast (>= 1.15.1, < 2.0)
55
54
  ruby-progressbar (~> 1.7)
56
- unicode-display_width (>= 1.4.0, < 2.0)
57
- rubocop-ast (0.1.0)
58
- parser (>= 2.7.0.1)
59
- ruby-progressbar (1.10.1)
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)
60
71
  sqlite3 (1.4.2)
61
- thread_safe (0.3.6)
62
- tzinfo (1.2.7)
63
- thread_safe (~> 0.1)
64
- unicode-display_width (1.7.0)
65
- zeitwerk (2.3.1)
72
+ tzinfo (2.0.4)
73
+ concurrent-ruby (~> 1.0)
74
+ unicode-display_width (2.1.0)
66
75
 
67
76
  PLATFORMS
68
77
  ruby
@@ -72,8 +81,8 @@ DEPENDENCIES
72
81
  filterable-by!
73
82
  rake
74
83
  rspec
75
- rubocop
84
+ rubocop-bsm
76
85
  sqlite3
77
86
 
78
87
  BUNDLED WITH
79
- 2.1.4
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
@@ -15,15 +17,15 @@ class Comment < ActiveRecord::Base
15
17
  belongs_to :post
16
18
 
17
19
  filterable_by :post_id, :user_id
18
- filterable_by :post_author_id do |scope, value|
19
- scope.joins(:posts).where(:'posts.author_id' => value)
20
+ filterable_by :post_author_id do |value|
21
+ joins(:posts).where(:'posts.author_id' => value)
20
22
  end
21
- filterable_by :only do |scope, value, **opts|
23
+ filterable_by :only do |value, **opts|
22
24
  case value
23
25
  when 'mine'
24
- scope.where(user_id: opts[:user_id]) if opts[:user_id]
26
+ where(user_id: opts[:user_id]) if opts[:user_id]
25
27
  else
26
- scope
28
+ all
27
29
  end
28
30
  end
29
31
  end
@@ -37,6 +39,12 @@ Simple use cases:
37
39
  Comment.filter_by({ 'post_id' => '1' })
38
40
  # => WHERE post_id = 1
39
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
+
40
48
  Comment.filter_by({ 'user_id' => '2', 'ignored' => '3' })
41
49
  # => WHERE user_id = 2
42
50
 
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'filterable-by'
3
- s.version = '0.5.1'
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'
@@ -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
@@ -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,28 +48,29 @@ 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
 
34
- # @param [Hash] hash the filter params
60
+ # @param [Hash|ActionController::Parameters] the filter params
35
61
  # @return [ActiveRecord::Relation] the scoped relation
36
62
  def filter_by(hash = nil, **opts)
37
63
  if hash.nil?
38
64
  hash = opts
39
65
  opts = {}
40
66
  end
67
+
41
68
  scope = all
42
- return scope unless hash.is_a?(Hash)
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
@@ -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,13 +7,13 @@ describe ActiveRecord::FilterableBy do
7
7
  let(:apost) { POSTS[:alices] }
8
8
  let(:bpost) { POSTS[:bobs] }
9
9
 
10
- it 'should have config' do
11
- expect(Comment.send(:_filterable_by_config).count).to eq(3)
12
- expect(Rating.send(:_filterable_by_config).count).to eq(2)
10
+ it 'has config' do
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
 
16
- it 'should ignore bad inputs' do
16
+ it 'ignores bad inputs' do
17
17
  expect(Comment.filter_by.count).to eq(4)
18
18
  expect(Comment.filter_by(nil).count).to eq(4)
19
19
  expect(Comment.filter_by(nil, extra: true).count).to eq(4)
@@ -23,7 +23,7 @@ describe ActiveRecord::FilterableBy do
23
23
  expect(Comment.filter_by('author_id' => []).count).to eq(4)
24
24
  end
25
25
 
26
- it 'should generate simple scopes' do
26
+ it 'generates simple scopes' do
27
27
  expect(Comment.filter_by('author_id' => alice.id).pluck(:title)).to match_array(%w[AA AB])
28
28
  expect(Comment.filter_by('author_id' => bob.id).pluck(:title)).to match_array(%w[BA BB])
29
29
  expect(Comment.filter_by('author_id' => [alice.id, '']).pluck(:title)).to match_array(%w[AA AB])
@@ -40,7 +40,7 @@ describe ActiveRecord::FilterableBy do
40
40
  expect(Post.filter_by('author_id' => bob.id).count).to eq(1)
41
41
  end
42
42
 
43
- it 'should generate combined scopes' do
43
+ it 'generates combined scopes' do
44
44
  expect(Comment.filter_by('author_id' => alice.id, 'post_id' => apost.id).pluck(:title)).to match_array(['AA'])
45
45
  expect(Comment.filter_by('author_id' => alice.id, 'post_id' => bpost.id).pluck(:title)).to match_array(['AB'])
46
46
  expect(Comment.filter_by('author_id' => bob.id, 'post_id' => apost.id).pluck(:title)).to match_array(['BA'])
@@ -50,12 +50,20 @@ describe ActiveRecord::FilterableBy do
50
50
  expect(scope.pluck(:title)).to match_array(%w[AB BB])
51
51
  end
52
52
 
53
- it 'should combine with other scopes' do
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
+
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'])
56
64
  end
57
65
 
58
- it 'should allow custom options' do
66
+ it 'allows custom options' do
59
67
  scope = Post.filter_by({ 'only' => 'me' }, user_id: alice.id)
60
68
  expect(scope).to match_array([apost])
61
69
 
@@ -66,15 +74,21 @@ describe ActiveRecord::FilterableBy do
66
74
  expect(scope.count).to eq(0)
67
75
  end
68
76
 
69
- it 'should allow custom options from params' do
77
+ it 'allows custom options from params' do
70
78
  filter = { 'only' => 'me' }
71
79
  expect(Post.filter_by(filter, user_id: alice.id)).to match_array([apost])
72
80
  expect(Post.filter_by(filter).count).to eq(0)
73
81
  end
74
82
 
75
- it 'should ignore invalid scopes' do
83
+ it 'ignores invalid scopes' do
76
84
  expect(Comment.filter_by('invalid' => 1).count).to eq(4)
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
@@ -7,6 +7,7 @@ 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,12 +28,12 @@ class Post < ActiveRecord::Base
27
28
  belongs_to :author
28
29
 
29
30
  filterable_by :author_id
30
- filterable_by :only do |scope, value, **opts|
31
+ filterable_by :only do |value, **opts|
31
32
  case value
32
33
  when 'me'
33
- scope.where(author_id: opts[:user_id]) if opts[:user_id]
34
+ where(author_id: opts[:user_id]) if opts[:user_id]
34
35
  else
35
- scope
36
+ all
36
37
  end
37
38
  end
38
39
  end
@@ -42,11 +43,20 @@ class Feedback < ActiveRecord::Base
42
43
  belongs_to :post
43
44
 
44
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
45
55
  end
46
56
 
47
57
  class Comment < Feedback
48
- filterable_by :post_author_id do |scope, value|
49
- 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))
50
60
  end
51
61
  end
52
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.1
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: 2020-07-13 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
@@ -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.1.4
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,6 +0,0 @@
1
- rvm:
2
- - 2.5
3
- - 2.6
4
- - 2.7
5
- gemfile:
6
- - Gemfile