anony 1.0.2 → 1.2.0

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: 88bf95173034cbd95ed89c352c9bfc3bc3308c4f7f372d8bf3942c887fba8cb2
4
- data.tar.gz: 775f8d5e284ce65376ad8871228ec0263eb123c8b5f7b5179776099196ddbb22
3
+ metadata.gz: ebdd2184f519951dc6fef205b2e55c0b1eaadc50849f91e17087521e6ec3d887
4
+ data.tar.gz: 8be91d24763bf780ba745cd0c4a4d9f9283637b79a2a3b32c4e0476366a675ce
5
5
  SHA512:
6
- metadata.gz: 41fcac8cededd6f62e625dba57392e051f8240abeafe180c20b880b892f0ae22fc83c3e99cb802d5594c6589055072d2480416405a152e3cbb1e33ede2c82679
7
- data.tar.gz: fa6c5c1516044ebf8d23fed1beb249ed32499dad731388a28f290646abc5d2b716f6ea8d9ee99f542373b205710e1dbbf74f76a52a84fc4853519d61622d4804
6
+ metadata.gz: 57a6da624bb0c53df166d32b530cbae6fb17966091a72105424513044e6fcabdf67179a56d494e7d242b836559edd5a0384f13a20544e43f564e27fa9a437fd8
7
+ data.tar.gz: 51533727cb125403cc1ee87fb95ea23b8fcaff4cf73a1f5fa67a071805e299ff6924f9093e0458037805fd8fc219221091ac7657b4b85e81dd7d95ba079a9d72
@@ -0,0 +1,7 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ open-pull-requests-limit: 10
@@ -0,0 +1,37 @@
1
+ name: tests
2
+
3
+ on:
4
+ push:
5
+
6
+ jobs:
7
+ rubocop:
8
+ runs-on: ubuntu-latest
9
+ steps:
10
+ - uses: actions/checkout@v3
11
+ - name: Set up Ruby
12
+ uses: ruby/setup-ruby@v1
13
+ with:
14
+ bundler-cache: true
15
+ - name: Run rubocop
16
+ run: bundle exec rubocop --parallel --extra-details --display-style-guide
17
+
18
+ tests:
19
+ strategy:
20
+ matrix:
21
+ ruby-version: [2.6, 2.7, 3.0, 3.1]
22
+ rails-version: [6.0, 6.1, 7.0]
23
+ exclude:
24
+ - ruby-version: 2.6
25
+ rails-version: 7.0
26
+ runs-on: ubuntu-latest
27
+ env:
28
+ RAILS_VERSION: ${{ matrix.rails-version }}
29
+ steps:
30
+ - uses: actions/checkout@v3
31
+ - name: Set up Ruby
32
+ uses: ruby/setup-ruby@v1
33
+ with:
34
+ bundler-cache: true
35
+ ruby-version: "${{ matrix.ruby-version }}"
36
+ - name: Run tests
37
+ run: bundle exec rspec --format RSpec::Github::Formatter
data/.rubocop.yml CHANGED
@@ -3,7 +3,17 @@ inherit_gem:
3
3
  gc_ruboconfig: rubocop.yml
4
4
 
5
5
  AllCops:
6
- TargetRubyVersion: 2.4
6
+ TargetRubyVersion: 3.0
7
+ NewCops: enable
7
8
 
8
9
  Layout/LineLength:
9
10
  Max: 100
11
+
12
+ Gemspec/DevelopmentDependencies:
13
+ EnforcedStyle: gemspec
14
+
15
+ Gemspec/RequiredRubyVersion:
16
+ Enabled: false
17
+
18
+ RSpec/MultipleExpectations:
19
+ Enabled: false
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.6.5
1
+ 3.0.2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ # v1.2.0
2
+
3
+ - Add support for configuring multiple superclasses for the `DefineDeletionStrategy` cop [#98](https://github.com/gocardless/anony/pull/98)
4
+ - Introduce helpers (selectors) for anonymising all a subject's records [#97](https://github.com/gocardless/anony/pull/97)
5
+
6
+ # v1.1.0
7
+
8
+ - Drop ruby 2.4 and 2.5 support
9
+ - Unpin ActiveSupport for Rails 7
10
+
1
11
  # v1.0.2
2
12
 
3
13
  - Unpin ActiveSupport for Rails 6
data/README.md CHANGED
@@ -243,6 +243,42 @@ irb(main):003:0> manager
243
243
  => #<Manager first_name="e9ab2800-d4b9-4227-94a7-7f81118d8a8a" last_name="previous-name-of-42">
244
244
  ```
245
245
 
246
+ ### Anonymising many records, or anonymising by subject
247
+
248
+ **Note**: This is an experimental feature and has not been tested widely
249
+ in production environments.
250
+
251
+ You can use selectors to anonymise multiple records. You first define a block for
252
+ a specific subject that returns a list of anonymisable records.
253
+
254
+ ```ruby
255
+ anonymise do
256
+ selectors do
257
+ for_subject(:user_id) { |user_id| find_all_users(user_id) }
258
+ end
259
+ end
260
+ ```
261
+
262
+ You can also use `scopes`, `where`, etc when defining your selectors:
263
+
264
+ ```ruby
265
+ anonymise do
266
+ selectors do
267
+ for_subject(:user_id) { |user_id| where(user_id: user_id) }
268
+ end
269
+ end
270
+ ```
271
+
272
+ This can then be used to anonymise all those subject using this API:
273
+
274
+ ```ruby
275
+ ModelName.anonymise_for!(:user_id, "user_1234")
276
+ ```
277
+
278
+ If you attempt to anonymise records with a selector that has not been defined it
279
+ will throw an error.
280
+
281
+
246
282
  ### Identifying anonymised records
247
283
 
248
284
  If your model has an `anonymised_at` column, Anony will automatically set that value
@@ -281,6 +317,12 @@ class Employees < ApplicationRecord
281
317
  end
282
318
  ```
283
319
 
320
+ There is also a helper defined when `Anony::Anonymisable" is included:
321
+
322
+ ```ruby
323
+ Employees.anonymised?
324
+ ```
325
+
284
326
  ### Preventing anonymisation
285
327
 
286
328
  You might have a need to preserve model data in some (or all) circumstances. Anony exposes
@@ -365,6 +407,7 @@ Anony::Config.ignore_fields(:id, :created_at, :updated_at)
365
407
  By default, `Config.ignore_fields` is an empty array and all fields are considered
366
408
  anonymisable.
367
409
 
410
+
368
411
  ## Testing
369
412
 
370
413
  This library ships with a set of useful RSpec examples for your specs. Just require them
@@ -446,6 +489,15 @@ Lint/DefineDeletionStrategy:
446
489
  ModelSuperclass: Acme::Record
447
490
  ```
448
491
 
492
+ If your models use multiple superclasses, you can specify a list of superclasses in your `.rubocop.yml`. Note that you will have to specify `ApplicationRecord` explicitly in this list should you want to lint all models which inherit from `ApplicationRecord`.
493
+ ```yml
494
+ Lint/DefineDeletionStrategy:
495
+ ModelSuperclass:
496
+ - Acme::Record
497
+ - UmbrellaCorp::Record
498
+
499
+ ```
500
+
449
501
  ## License & Contributing
450
502
 
451
503
  * Anony is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/anony.gemspec CHANGED
@@ -22,17 +22,18 @@ Gem::Specification.new do |spec|
22
22
  end
23
23
  spec.require_paths = ["lib"]
24
24
 
25
- spec.required_ruby_version = ">= 2.4"
25
+ spec.required_ruby_version = ">= 2.6"
26
26
 
27
- spec.add_development_dependency "bundler", "~> 2.2.0"
28
- spec.add_development_dependency "gc_ruboconfig", "~> 2.23.0"
27
+ spec.add_development_dependency "bundler", "~> 2"
28
+ spec.add_development_dependency "gc_ruboconfig", "~> 3.6.0"
29
29
  spec.add_development_dependency "rspec", "~> 3.9"
30
- spec.add_development_dependency "rspec_junit_formatter", "~> 0.4"
30
+ spec.add_development_dependency "rspec-github", "~> 2.4.0"
31
31
  spec.add_development_dependency "yard", "~> 0.9.20"
32
32
 
33
33
  # For integration testing
34
- spec.add_development_dependency "sqlite3", "~> 1.4.1"
34
+ spec.add_development_dependency "sqlite3", "~> 1.6.1"
35
35
 
36
- spec.add_dependency "activerecord", ">= 5.2", "< 7"
37
- spec.add_dependency "activesupport", ">= 5.2", "< 7"
36
+ spec.add_dependency "activerecord", ">= 5.2", "< 8"
37
+ spec.add_dependency "activesupport", ">= 5.2", "< 8"
38
+ spec.metadata["rubygems_mfa_required"] = "true"
38
39
  end
@@ -2,7 +2,8 @@
2
2
 
3
3
  require "active_support/core_ext/module/delegation"
4
4
 
5
- require_relative "./strategies/overwrite"
5
+ require_relative "not_anonymisable_exception"
6
+ require_relative "strategies/overwrite"
6
7
  require_relative "model_config"
7
8
 
8
9
  module Anony
@@ -54,6 +55,30 @@ module Anony
54
55
  @anonymise_config.valid?
55
56
  end
56
57
 
58
+ # Finds the records that relate to a particular subject and runs anonymise on
59
+ # each of them. If a selector is not defined it will raise an exception.
60
+ def anonymise_for!(subject, subject_id)
61
+ records = anonymise_config.
62
+ select(subject, subject_id)
63
+ records.map do |record|
64
+ if !record.respond_to?(:anonymise!)
65
+ raise NotAnonymisableException, record
66
+ end
67
+
68
+ record.anonymise!
69
+ end
70
+ end
71
+
72
+ # Checks if a selector has been defined for a given subject.
73
+ # This is useful for when writing tests to check all models have a valid selector
74
+ # for a given subject.
75
+ # @return [Boolean]
76
+ # @example
77
+ # Manager.selector_for?(:user_id)
78
+ def selector_for?(subject)
79
+ anonymise_config.selector_for?(subject)
80
+ end
81
+
57
82
  attr_reader :anonymise_config
58
83
  end
59
84
 
@@ -74,6 +99,10 @@ module Anony
74
99
  Result.failed(e)
75
100
  end
76
101
 
102
+ def anonymised?
103
+ anonymised_at.present?
104
+ end
105
+
77
106
  # @!visibility private
78
107
  def self.included(base)
79
108
  base.extend(ClassMethods)
@@ -24,7 +24,7 @@ module RuboCop
24
24
  # class MyNewThing < ApplicationRecord; end
25
25
  class DefineDeletionStrategy < Cop
26
26
  MSG = "Define .anonymise for %<model>s, see https://github.com/gocardless/" \
27
- "anony/blob/#{Anony::VERSION}/README.md for details"
27
+ "anony/blob/#{Anony::VERSION}/README.md for details".freeze
28
28
 
29
29
  def_node_search :uses_anonymise?, "(send nil? :anonymise)"
30
30
 
@@ -36,9 +36,8 @@ module RuboCop
36
36
  end
37
37
 
38
38
  def model?(node)
39
- return unless (superclass = node.children[1])
40
-
41
- superclass.const_name == model_superclass_name
39
+ superclass = node.children[1]
40
+ model_superclass_name.include? superclass&.const_name
42
41
  end
43
42
 
44
43
  def class_name(node)
@@ -46,7 +45,15 @@ module RuboCop
46
45
  end
47
46
 
48
47
  def model_superclass_name
49
- cop_config["ModelSuperclass"] || "ApplicationRecord"
48
+ unless cop_config["ModelSuperclass"]
49
+ return ["ApplicationRecord"]
50
+ end
51
+
52
+ if cop_config["ModelSuperclass"].is_a?(Array)
53
+ return cop_config["ModelSuperclass"]
54
+ end
55
+
56
+ [cop_config["ModelSuperclass"]]
50
57
  end
51
58
  end
52
59
  end
@@ -147,7 +147,7 @@ module Anony
147
147
  # end
148
148
  OverwriteHex = Struct.new(:max_length) do
149
149
  def call(_existing_value)
150
- hex_length = max_length / 2 + 1
150
+ hex_length = (max_length / 2) + 1
151
151
  SecureRandom.hex(hex_length)[0, max_length]
152
152
  end
153
153
  end
@@ -2,8 +2,9 @@
2
2
 
3
3
  require "active_support/core_ext/module/delegation"
4
4
 
5
- require_relative "./strategies/destroy"
6
- require_relative "./strategies/overwrite"
5
+ require_relative "strategies/destroy"
6
+ require_relative "strategies/overwrite"
7
+ require_relative "selectors"
7
8
 
8
9
  module Anony
9
10
  class ModelConfig
@@ -29,6 +30,7 @@ module Anony
29
30
  def initialize(model_class, &block)
30
31
  @model_class = model_class
31
32
  @strategy = UndefinedStrategy.new
33
+ @selectors_config = nil
32
34
  @skip_filter = nil
33
35
  instance_exec(&block) if block
34
36
  end
@@ -45,6 +47,7 @@ module Anony
45
47
  end
46
48
 
47
49
  delegate :valid?, :validate!, to: :@strategy
50
+ delegate :select, to: :@selectors_config
48
51
 
49
52
  # Use the deletion strategy instead of anonymising individual fields. This method is
50
53
  # incompatible with the fields strategy.
@@ -86,6 +89,30 @@ module Anony
86
89
  @strategy = Strategies::Overwrite.new(@model_class, &block)
87
90
  end
88
91
 
92
+ # Define selectors to select records that apply to a particular subject.
93
+ # This method taks a configuration block that then builds Selectors
94
+ #
95
+ # @see Anony::Selectors
96
+ #
97
+ # @example
98
+ #
99
+ # anonymise do
100
+ # selectors do
101
+ # for_subject(:user_id) { |user_id| self.select_for_user(user_id) }
102
+ # end
103
+ # end
104
+ #
105
+ # ModelName.anonymise_for!(:user_id, "user_1234")
106
+ def selectors(&block)
107
+ @selectors_config = Selectors.new(@model_class, &block)
108
+ end
109
+
110
+ def selector_for?(subject)
111
+ return false if @selectors_config.nil?
112
+
113
+ @selectors_config.selectors[subject].present?
114
+ end
115
+
89
116
  # Prevent any anonymisation strategy being applied when the provided block evaluates
90
117
  # to true. The block is executed in the model context.
91
118
  #
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anony
4
+ class NotAnonymisableException < StandardError
5
+ def initialize(record)
6
+ @record = record
7
+ super("Record does not implement anonymise!.
8
+ Have you included Anony::Anonymisable and a config?")
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Anony
4
+ class SelectorNotFoundException < StandardError
5
+ def initialize(selector, model_name)
6
+ super("Selector for #{selector} not found. Make sure you have one defined in #{model_name}")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "selector_not_found_exception"
4
+
5
+ module Anony
6
+ class Selectors
7
+ def initialize(model_class, &block)
8
+ @model_class = model_class
9
+ @selectors = {}
10
+ instance_exec(&block) if block
11
+ end
12
+
13
+ attr_reader :selectors
14
+
15
+ def for_subject(subject, &block)
16
+ selectors[subject] = block
17
+ end
18
+
19
+ def select(subject, subject_id)
20
+ selector = selectors[subject]
21
+ raise SelectorNotFoundException.new(subject.to_s, @model_class.name) if selector.nil?
22
+
23
+ @model_class.instance_exec(subject_id, &selector)
24
+ end
25
+ end
26
+ end
@@ -126,7 +126,7 @@ module Anony
126
126
 
127
127
  if already_ignored.any?
128
128
  raise ArgumentError, "Cannot ignore #{already_ignored.inspect} " \
129
- "(fields already ignored in Anony::Config)"
129
+ "(fields already ignored in Anony::Config)"
130
130
  end
131
131
 
132
132
  no_op(*fields)
data/lib/anony/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Anony
4
- VERSION = "1.0.2"
4
+ VERSION = "1.2.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anony
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GoCardless Engineering
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-11 00:00:00.000000000 Z
11
+ date: 2024-02-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.2.0
19
+ version: '2'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.2.0
26
+ version: '2'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: gc_ruboconfig
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 2.23.0
33
+ version: 3.6.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 2.23.0
40
+ version: 3.6.0
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -53,19 +53,19 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.9'
55
55
  - !ruby/object:Gem::Dependency
56
- name: rspec_junit_formatter
56
+ name: rspec-github
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '0.4'
61
+ version: 2.4.0
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '0.4'
68
+ version: 2.4.0
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: yard
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 1.4.1
89
+ version: 1.6.1
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 1.4.1
96
+ version: 1.6.1
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: activerecord
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -103,7 +103,7 @@ dependencies:
103
103
  version: '5.2'
104
104
  - - "<"
105
105
  - !ruby/object:Gem::Version
106
- version: '7'
106
+ version: '8'
107
107
  type: :runtime
108
108
  prerelease: false
109
109
  version_requirements: !ruby/object:Gem::Requirement
@@ -113,7 +113,7 @@ dependencies:
113
113
  version: '5.2'
114
114
  - - "<"
115
115
  - !ruby/object:Gem::Version
116
- version: '7'
116
+ version: '8'
117
117
  - !ruby/object:Gem::Dependency
118
118
  name: activesupport
119
119
  requirement: !ruby/object:Gem::Requirement
@@ -123,7 +123,7 @@ dependencies:
123
123
  version: '5.2'
124
124
  - - "<"
125
125
  - !ruby/object:Gem::Version
126
- version: '7'
126
+ version: '8'
127
127
  type: :runtime
128
128
  prerelease: false
129
129
  version_requirements: !ruby/object:Gem::Requirement
@@ -133,7 +133,7 @@ dependencies:
133
133
  version: '5.2'
134
134
  - - "<"
135
135
  - !ruby/object:Gem::Version
136
- version: '7'
136
+ version: '8'
137
137
  description:
138
138
  email:
139
139
  - engineering@gocardless.com
@@ -141,7 +141,8 @@ executables: []
141
141
  extensions: []
142
142
  extra_rdoc_files: []
143
143
  files:
144
- - ".circleci/config.yml"
144
+ - ".github/dependabot.yml"
145
+ - ".github/workflows/test.yml"
145
146
  - ".gitignore"
146
147
  - ".rubocop.yml"
147
148
  - ".rubocop_todo.yml"
@@ -161,8 +162,11 @@ files:
161
162
  - lib/anony/field_exception.rb
162
163
  - lib/anony/field_level_strategies.rb
163
164
  - lib/anony/model_config.rb
165
+ - lib/anony/not_anonymisable_exception.rb
164
166
  - lib/anony/result.rb
165
167
  - lib/anony/rspec_shared_examples.rb
168
+ - lib/anony/selector_not_found_exception.rb
169
+ - lib/anony/selectors.rb
166
170
  - lib/anony/skipped_exception.rb
167
171
  - lib/anony/strategies/destroy.rb
168
172
  - lib/anony/strategies/overwrite.rb
@@ -170,7 +174,8 @@ files:
170
174
  homepage: https://github.com/gocardless/anony
171
175
  licenses:
172
176
  - MIT
173
- metadata: {}
177
+ metadata:
178
+ rubygems_mfa_required: 'true'
174
179
  post_install_message:
175
180
  rdoc_options: []
176
181
  require_paths:
@@ -179,14 +184,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
179
184
  requirements:
180
185
  - - ">="
181
186
  - !ruby/object:Gem::Version
182
- version: '2.4'
187
+ version: '2.6'
183
188
  required_rubygems_version: !ruby/object:Gem::Requirement
184
189
  requirements:
185
190
  - - ">="
186
191
  - !ruby/object:Gem::Version
187
192
  version: '0'
188
193
  requirements: []
189
- rubygems_version: 3.0.3
194
+ rubygems_version: 3.2.22
190
195
  signing_key:
191
196
  specification_version: 4
192
197
  summary: A small library that defines how ActiveRecord models should be anonymised
data/.circleci/config.yml DELETED
@@ -1,56 +0,0 @@
1
- version: 2.1
2
-
3
- jobs:
4
- test:
5
- parameters:
6
- ruby-version:
7
- type: string
8
- rails-version:
9
- type: string
10
-
11
- docker:
12
- - image: ruby:<<parameters.ruby-version>>
13
- environment:
14
- - RAILS_VERSION=<<parameters.rails-version>>
15
- steps:
16
- - checkout
17
-
18
- - restore_cache:
19
- key: anony-bundler-{{ checksum "anony.gemspec" }}
20
-
21
- - run: gem install bundler -v 2.2.0
22
- - run: bundle config set path 'vendor/bundle'
23
- - run: bundle install
24
-
25
- - save_cache:
26
- key: anony-bundler-{{ checksum "anony.gemspec" }}
27
- paths:
28
- - vendor/bundle
29
-
30
- - run:
31
- command: |
32
- bundle exec rspec --profile 10 \
33
- --format RspecJunitFormatter \
34
- --out /tmp/test-results/rspec.xml \
35
- --format progress \
36
- spec
37
-
38
- - store_test_results:
39
- path: /tmp/test-results
40
-
41
- - run: bundle exec rubocop --parallel --extra-details --display-style-guide
42
-
43
- workflows:
44
- version: 2
45
- tests:
46
- jobs:
47
- - test:
48
- matrix:
49
- parameters:
50
- ruby-version: ["2.4", "2.5", "2.6", "2.7"]
51
- rails-version: ["5.2", "6.0", "6.1"]
52
- exclude:
53
- - ruby-version: "2.4"
54
- rails-version: "6.0"
55
- - ruby-version: "2.4"
56
- rails-version: "6.1"