anony 1.1.0 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +7 -0
- data/.github/workflows/test.yml +37 -0
- data/.rubocop.yml +7 -0
- data/CHANGELOG.md +5 -0
- data/README.md +52 -0
- data/anony.gemspec +5 -4
- data/lib/anony/anonymisable.rb +30 -1
- data/lib/anony/cops/define_deletion_strategy.rb +11 -4
- data/lib/anony/field_level_strategies.rb +1 -1
- data/lib/anony/model_config.rb +29 -2
- data/lib/anony/not_anonymisable_exception.rb +11 -0
- data/lib/anony/selector_not_found_exception.rb +9 -0
- data/lib/anony/selectors.rb +26 -0
- data/lib/anony/strategies/overwrite.rb +1 -1
- data/lib/anony/version.rb +1 -1
- metadata +18 -13
- data/.circleci/config.yml +0 -54
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ebdd2184f519951dc6fef205b2e55c0b1eaadc50849f91e17087521e6ec3d887
|
4
|
+
data.tar.gz: 8be91d24763bf780ba745cd0c4a4d9f9283637b79a2a3b32c4e0476366a675ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 57a6da624bb0c53df166d32b530cbae6fb17966091a72105424513044e6fcabdf67179a56d494e7d242b836559edd5a0384f13a20544e43f564e27fa9a437fd8
|
7
|
+
data.tar.gz: 51533727cb125403cc1ee87fb95ea23b8fcaff4cf73a1f5fa67a071805e299ff6924f9093e0458037805fd8fc219221091ac7657b4b85e81dd7d95ba079a9d72
|
@@ -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
@@ -4,9 +4,16 @@ inherit_gem:
|
|
4
4
|
|
5
5
|
AllCops:
|
6
6
|
TargetRubyVersion: 3.0
|
7
|
+
NewCops: enable
|
7
8
|
|
8
9
|
Layout/LineLength:
|
9
10
|
Max: 100
|
10
11
|
|
12
|
+
Gemspec/DevelopmentDependencies:
|
13
|
+
EnforcedStyle: gemspec
|
14
|
+
|
11
15
|
Gemspec/RequiredRubyVersion:
|
12
16
|
Enabled: false
|
17
|
+
|
18
|
+
RSpec/MultipleExpectations:
|
19
|
+
Enabled: false
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
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
|
+
|
1
6
|
# v1.1.0
|
2
7
|
|
3
8
|
- Drop ruby 2.4 and 2.5 support
|
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
@@ -24,15 +24,16 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.required_ruby_version = ">= 2.6"
|
26
26
|
|
27
|
-
spec.add_development_dependency "bundler", "~> 2
|
28
|
-
spec.add_development_dependency "gc_ruboconfig", "~>
|
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 "
|
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.
|
34
|
+
spec.add_development_dependency "sqlite3", "~> 1.6.1"
|
35
35
|
|
36
36
|
spec.add_dependency "activerecord", ">= 5.2", "< 8"
|
37
37
|
spec.add_dependency "activesupport", ">= 5.2", "< 8"
|
38
|
+
spec.metadata["rubygems_mfa_required"] = "true"
|
38
39
|
end
|
data/lib/anony/anonymisable.rb
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
require "active_support/core_ext/module/delegation"
|
4
4
|
|
5
|
-
require_relative "
|
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)
|
@@ -36,9 +36,8 @@ module RuboCop
|
|
36
36
|
end
|
37
37
|
|
38
38
|
def model?(node)
|
39
|
-
|
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"]
|
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
|
data/lib/anony/model_config.rb
CHANGED
@@ -2,8 +2,9 @@
|
|
2
2
|
|
3
3
|
require "active_support/core_ext/module/delegation"
|
4
4
|
|
5
|
-
require_relative "
|
6
|
-
require_relative "
|
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,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
|
data/lib/anony/version.rb
CHANGED
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.
|
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:
|
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
|
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
|
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:
|
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:
|
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:
|
56
|
+
name: rspec-github
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
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:
|
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.
|
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.
|
96
|
+
version: 1.6.1
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
name: activerecord
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -141,7 +141,8 @@ executables: []
|
|
141
141
|
extensions: []
|
142
142
|
extra_rdoc_files: []
|
143
143
|
files:
|
144
|
-
- ".
|
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:
|
data/.circleci/config.yml
DELETED
@@ -1,54 +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: cimg/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.33
|
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.6", "2.7", "3.0"]
|
51
|
-
rails-version: ["6.0", "6.1", "7.0"]
|
52
|
-
exclude:
|
53
|
-
- ruby-version: "2.6"
|
54
|
-
rails-version: "7.0"
|