anony 1.0.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 +7 -0
- data/.circleci/config.yml +86 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +9 -0
- data/.rubocop_todo.yml +28 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +64 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +22 -0
- data/README.md +451 -0
- data/anony.gemspec +38 -0
- data/docs/COMPATIBILITY.md +17 -0
- data/lib/anony.rb +12 -0
- data/lib/anony/anonymisable.rb +80 -0
- data/lib/anony/config.rb +42 -0
- data/lib/anony/cops.rb +3 -0
- data/lib/anony/cops/define_deletion_strategy.rb +54 -0
- data/lib/anony/duplicate_strategy_exception.rb +19 -0
- data/lib/anony/field_exception.rb +20 -0
- data/lib/anony/field_level_strategies.rb +155 -0
- data/lib/anony/model_config.rb +102 -0
- data/lib/anony/result.rb +39 -0
- data/lib/anony/rspec_shared_examples.rb +45 -0
- data/lib/anony/skipped_exception.rb +9 -0
- data/lib/anony/strategies/destroy.rb +39 -0
- data/lib/anony/strategies/overwrite.rb +173 -0
- data/lib/anony/version.rb +5 -0
- metadata +194 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/module/delegation"
|
4
|
+
|
5
|
+
require_relative "./strategies/destroy"
|
6
|
+
require_relative "./strategies/overwrite"
|
7
|
+
|
8
|
+
module Anony
|
9
|
+
class ModelConfig
|
10
|
+
# @api private
|
11
|
+
class UndefinedStrategy
|
12
|
+
def valid?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def validate!
|
17
|
+
raise ArgumentError, "Must specify either :destroy or :overwrite strategy"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
# Constructs a new instance of ModelConfig.
|
23
|
+
#
|
24
|
+
# @param [ActiveRecord::Base] model_class The model class the config is attached to.
|
25
|
+
# @yield [block] For configuration of the ModelConfig instance.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# Anony::ModelConfig.new(Manager) { destroy }
|
29
|
+
def initialize(model_class, &block)
|
30
|
+
@model_class = model_class
|
31
|
+
@strategy = UndefinedStrategy.new
|
32
|
+
@skip_filter = nil
|
33
|
+
instance_exec(&block) if block_given?
|
34
|
+
end
|
35
|
+
|
36
|
+
# @api private
|
37
|
+
# Applies the given strategy, taking into account any filters or conditions.
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# Anony::ModelConfig.new(Manager).apply(Manager.new)
|
41
|
+
def apply(instance)
|
42
|
+
return Result.skipped if @skip_filter && instance.instance_exec(&@skip_filter)
|
43
|
+
|
44
|
+
@strategy.apply(instance)
|
45
|
+
end
|
46
|
+
|
47
|
+
delegate :valid?, :validate!, to: :@strategy
|
48
|
+
|
49
|
+
# Use the deletion strategy instead of anonymising individual fields. This method is
|
50
|
+
# incompatible with the fields strategy.
|
51
|
+
#
|
52
|
+
# This method takes no arguments or blocks.
|
53
|
+
#
|
54
|
+
# @example
|
55
|
+
# anonymise do
|
56
|
+
# destroy
|
57
|
+
# end
|
58
|
+
def destroy
|
59
|
+
raise ArgumentError, ":destroy takes no block" if block_given?
|
60
|
+
unless @strategy.is_a?(UndefinedStrategy)
|
61
|
+
raise ArgumentError, "Cannot specify :destroy when another strategy already defined"
|
62
|
+
end
|
63
|
+
|
64
|
+
@strategy = Strategies::Destroy.new
|
65
|
+
end
|
66
|
+
|
67
|
+
# Use the overwrite strategy to configure rules for individual fields. This method is
|
68
|
+
# incompatible with the destroy strategy.
|
69
|
+
#
|
70
|
+
# This method takes a configuration block. All configuration is applied to
|
71
|
+
# Anony::Strategies::Overwrite.
|
72
|
+
#
|
73
|
+
# @see Anony::Strategies::Overwrite
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
# anonymise do
|
77
|
+
# overwrite do
|
78
|
+
# hex :first_name
|
79
|
+
# end
|
80
|
+
# end
|
81
|
+
def overwrite(&block)
|
82
|
+
unless @strategy.is_a?(UndefinedStrategy)
|
83
|
+
raise ArgumentError, "Cannot specify :overwrite when another strategy already defined"
|
84
|
+
end
|
85
|
+
|
86
|
+
@strategy = Strategies::Overwrite.new(@model_class, &block)
|
87
|
+
end
|
88
|
+
|
89
|
+
# Prevent any anonymisation strategy being applied when the provided block evaluates
|
90
|
+
# to true. The block is executed in the model context.
|
91
|
+
#
|
92
|
+
# @example
|
93
|
+
# anonymise do
|
94
|
+
# skip_if { !persisted? }
|
95
|
+
# end
|
96
|
+
def skip_if(&if_condition)
|
97
|
+
raise ArgumentError, "Block required for :skip_if" unless block_given?
|
98
|
+
|
99
|
+
@skip_filter = if_condition
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/anony/result.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
|
5
|
+
module Anony
|
6
|
+
class Result
|
7
|
+
FAILED = "failed"
|
8
|
+
DESTROYED = "destroyed"
|
9
|
+
OVERWRITTEN = "overwritten"
|
10
|
+
SKIPPED = "skipped"
|
11
|
+
|
12
|
+
attr_reader :status, :fields, :error
|
13
|
+
delegate :failed?, :overwritten?, :skipped?, :destroyed?, to: :status
|
14
|
+
|
15
|
+
def self.failed(error)
|
16
|
+
new(FAILED, error: error)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.overwritten(fields)
|
20
|
+
new(OVERWRITTEN, fields: fields)
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.skipped
|
24
|
+
new(SKIPPED)
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.destroyed
|
28
|
+
new(DESTROYED)
|
29
|
+
end
|
30
|
+
|
31
|
+
private def initialize(status, fields: [], error: nil)
|
32
|
+
raise ArgumentError, "No error provided" if status == FAILED && error.nil?
|
33
|
+
|
34
|
+
@status = ActiveSupport::StringInquirer.new(status)
|
35
|
+
@fields = fields
|
36
|
+
@error = error
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rspec"
|
4
|
+
|
5
|
+
RSpec.shared_examples "overwritten anonymisable model" do
|
6
|
+
it "has a valid strategy defined" do
|
7
|
+
expect(subject.class).to be_valid_anonymisation
|
8
|
+
end
|
9
|
+
|
10
|
+
it "#anonymise! causes overwrite" do
|
11
|
+
result = subject.anonymise!
|
12
|
+
expect(result).to be_overwritten
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
RSpec.shared_examples "skipped anonymisable model" do
|
17
|
+
it "has a valid strategy defined" do
|
18
|
+
expect(subject.class).to be_valid_anonymisation
|
19
|
+
end
|
20
|
+
|
21
|
+
it "#anonymise! is skipped" do
|
22
|
+
result = subject.anonymise!
|
23
|
+
expect(result).to be_skipped
|
24
|
+
end
|
25
|
+
|
26
|
+
it "does not change any fields" do
|
27
|
+
result = subject.anonymise!
|
28
|
+
expect(result.fields).to be_empty
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
RSpec.shared_examples "destroyed anonymisable model" do
|
33
|
+
it "has a valid strategy defined" do
|
34
|
+
expect(subject.class).to be_valid_anonymisation
|
35
|
+
end
|
36
|
+
|
37
|
+
it "destroys the model" do
|
38
|
+
expect { subject.anonymise! }.to change(described_class, :count).by(-1)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "labels the model as destroyed" do
|
42
|
+
result = subject.anonymise!
|
43
|
+
expect(result).to be_destroyed
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Anony
|
4
|
+
module Strategies
|
5
|
+
# The interface for configuring a destroy strategy. This strategy is not compatible
|
6
|
+
# with Anony::Strategies::Overwrite.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# anonymise do
|
10
|
+
# destroy
|
11
|
+
# end
|
12
|
+
class Destroy
|
13
|
+
# Whether the strategy is valid. This strategy takes no configuration, so #valid?
|
14
|
+
# always returns true
|
15
|
+
#
|
16
|
+
# @return [true]
|
17
|
+
def valid?
|
18
|
+
true
|
19
|
+
end
|
20
|
+
|
21
|
+
# Whether the strategy is valid, raising an exception if not. This strategy takes no
|
22
|
+
# configuration, so #validate! always returns true
|
23
|
+
#
|
24
|
+
# @return [true]
|
25
|
+
def validate!
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
# Apply the Destroy strategy to the model instance. In this case, it calls
|
30
|
+
# `#destroy!`.
|
31
|
+
#
|
32
|
+
# @param [ActiveRecord::Base] instance An instance of the model
|
33
|
+
def apply(instance)
|
34
|
+
instance.destroy!
|
35
|
+
Result.destroyed
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../field_level_strategies"
|
4
|
+
|
5
|
+
module Anony
|
6
|
+
module Strategies
|
7
|
+
# The interface for configuring a field-level strategy. All of the methods here are
|
8
|
+
# made available inside the `overwrite { ... }` block:
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# anonymise do
|
12
|
+
# overwrite do
|
13
|
+
# nilable :first_name
|
14
|
+
# email :email_address
|
15
|
+
# with_strategy(:last_name) { "last-#{id}" }
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
class Overwrite
|
19
|
+
include FieldLevelStrategies
|
20
|
+
|
21
|
+
# @!visibility private
|
22
|
+
def initialize(model_class, &block)
|
23
|
+
@model_class = model_class
|
24
|
+
@anonymisable_fields = {}
|
25
|
+
instance_eval(&block) if block_given?
|
26
|
+
end
|
27
|
+
|
28
|
+
# A hash containing the fields and their anonymisation strategies.
|
29
|
+
attr_reader :anonymisable_fields
|
30
|
+
|
31
|
+
# Check whether the combination of field-level rules is valid
|
32
|
+
def valid?
|
33
|
+
validate!
|
34
|
+
true
|
35
|
+
rescue FieldException
|
36
|
+
false
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate!
|
40
|
+
raise FieldException, unhandled_fields if unhandled_fields.any?
|
41
|
+
end
|
42
|
+
|
43
|
+
# Apply the Overwrite strategy on the model instance, which applies each of the
|
44
|
+
# configured transformations and updates the :anonymised_at field if it exists.
|
45
|
+
#
|
46
|
+
# @param [ActiveRecord::Base] instance An instance of the model
|
47
|
+
def apply(instance)
|
48
|
+
if !@anonymisable_fields.key?(:anonymised_at) &&
|
49
|
+
@model_class.column_names.include?("anonymised_at")
|
50
|
+
current_datetime(:anonymised_at)
|
51
|
+
end
|
52
|
+
|
53
|
+
@anonymisable_fields.each_key do |field|
|
54
|
+
anonymise_field(instance, field)
|
55
|
+
end
|
56
|
+
|
57
|
+
result_fields = instance.changes.keys.map(&:to_sym).reject { |s| s == :anonymised_at }
|
58
|
+
|
59
|
+
instance.save!
|
60
|
+
|
61
|
+
Result.overwritten(result_fields)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Configure a custom strategy for one or more fields. If a block is given that is used
|
65
|
+
# as the strategy, otherwise the first argument is used as the strategy.
|
66
|
+
#
|
67
|
+
# @param [Proc, Object] strategy Any object which responds to
|
68
|
+
# `.call(previous_value)`. Not used if a block is provided.
|
69
|
+
# @param [Array<Symbol>] fields A list of one or more fields to apply this strategy to.
|
70
|
+
# @param [Block] &block A block to use as the strategy.
|
71
|
+
# @yieldparam previous [Object] The previous value of the field
|
72
|
+
# @yieldreturn [Object] The value to set on that field.
|
73
|
+
# @raise [ArgumentError] If the combination of strategy, fields and block is invalid.
|
74
|
+
# @raise [DuplicateStrategyException] If more than one strategy is defined for the same field.
|
75
|
+
#
|
76
|
+
# @example With a named class
|
77
|
+
# class Reverse
|
78
|
+
# def self.call(previous)
|
79
|
+
# previous.reverse
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
#
|
83
|
+
# with_strategy(Reverse, :first_name)
|
84
|
+
#
|
85
|
+
# @example With a constant value
|
86
|
+
# with_strategy({}, :metadata)
|
87
|
+
#
|
88
|
+
# @example With a block
|
89
|
+
# with_strategy(:first_name, :last_name) { |previous| previous.reverse }
|
90
|
+
def with_strategy(strategy, *fields, &block)
|
91
|
+
if block_given?
|
92
|
+
fields.unshift(strategy)
|
93
|
+
strategy = block
|
94
|
+
end
|
95
|
+
|
96
|
+
fields = fields.flatten
|
97
|
+
|
98
|
+
raise ArgumentError, "Block or Strategy object required" unless strategy
|
99
|
+
raise ArgumentError, "One or more fields required" unless fields.any?
|
100
|
+
|
101
|
+
guard_duplicate_strategies!(fields)
|
102
|
+
|
103
|
+
fields.each { |field| @anonymisable_fields[field] = strategy }
|
104
|
+
end
|
105
|
+
|
106
|
+
# Helper method to use the :hex strategy
|
107
|
+
# @param [Array<Symbol>] fields A list of one or more fields to apply this strategy to.
|
108
|
+
# @see Strategies::OverwriteHex
|
109
|
+
#
|
110
|
+
# @example
|
111
|
+
# hex :first_name
|
112
|
+
def hex(*fields, max_length: 36)
|
113
|
+
with_strategy(Strategies::OverwriteHex.new(max_length), *fields)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Configure a list of fields that you don't want to anonymise.
|
117
|
+
#
|
118
|
+
# @param [Array<Symbol>] fields The fields to ignore
|
119
|
+
# @raise [ArgumentError] If trying to ignore a field which is already globally
|
120
|
+
# ignored in Anony::Config.ignores
|
121
|
+
#
|
122
|
+
# @example
|
123
|
+
# ignore :external_system_id, :externalised_at
|
124
|
+
def ignore(*fields)
|
125
|
+
already_ignored = fields.select { |field| Config.ignore?(field) }
|
126
|
+
|
127
|
+
if already_ignored.any?
|
128
|
+
raise ArgumentError, "Cannot ignore #{already_ignored.inspect} " \
|
129
|
+
"(fields already ignored in Anony::Config)"
|
130
|
+
end
|
131
|
+
|
132
|
+
no_op(*fields)
|
133
|
+
end
|
134
|
+
|
135
|
+
private def unhandled_fields
|
136
|
+
anonymisable_columns =
|
137
|
+
@model_class.column_names.map(&:to_sym).
|
138
|
+
reject { |c| Config.ignore?(c) }.
|
139
|
+
reject { |c| c == :anonymised_at }
|
140
|
+
|
141
|
+
handled_fields = @anonymisable_fields.keys
|
142
|
+
|
143
|
+
anonymisable_columns - handled_fields
|
144
|
+
end
|
145
|
+
|
146
|
+
private def anonymise_field(instance, field)
|
147
|
+
return unless @model_class.column_names.include?(field.to_s)
|
148
|
+
|
149
|
+
strategy = @anonymisable_fields.fetch(field)
|
150
|
+
current_value = instance.read_attribute(field)
|
151
|
+
|
152
|
+
instance.write_attribute(field, anonymised_value(instance, strategy, current_value))
|
153
|
+
end
|
154
|
+
|
155
|
+
private def anonymised_value(instance, strategy, current_value)
|
156
|
+
if strategy.is_a?(Proc)
|
157
|
+
instance.instance_exec(current_value, &strategy)
|
158
|
+
elsif strategy.respond_to?(:call)
|
159
|
+
strategy.call(current_value)
|
160
|
+
else
|
161
|
+
strategy
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
private def guard_duplicate_strategies!(fields)
|
166
|
+
defined_fields = @anonymisable_fields.keys
|
167
|
+
duplicate_fields = defined_fields & fields
|
168
|
+
|
169
|
+
raise DuplicateStrategyException, duplicate_fields if duplicate_fields.any?
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
metadata
ADDED
@@ -0,0 +1,194 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: anony
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- GoCardless Engineering
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-02-07 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 2.1.4
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 2.1.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: gc_ruboconfig
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.9.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 2.9.0
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.9'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.9'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec_junit_formatter
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.4'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.9.20
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.9.20
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: sqlite3
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 1.4.1
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 1.4.1
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: activerecord
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '5.2'
|
104
|
+
- - "<"
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '6.1'
|
107
|
+
type: :runtime
|
108
|
+
prerelease: false
|
109
|
+
version_requirements: !ruby/object:Gem::Requirement
|
110
|
+
requirements:
|
111
|
+
- - ">="
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
version: '5.2'
|
114
|
+
- - "<"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '6.1'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: activesupport
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '5.2'
|
124
|
+
- - "<"
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '6.1'
|
127
|
+
type: :runtime
|
128
|
+
prerelease: false
|
129
|
+
version_requirements: !ruby/object:Gem::Requirement
|
130
|
+
requirements:
|
131
|
+
- - ">="
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '5.2'
|
134
|
+
- - "<"
|
135
|
+
- !ruby/object:Gem::Version
|
136
|
+
version: '6.1'
|
137
|
+
description:
|
138
|
+
email:
|
139
|
+
- engineering@gocardless.com
|
140
|
+
executables: []
|
141
|
+
extensions: []
|
142
|
+
extra_rdoc_files: []
|
143
|
+
files:
|
144
|
+
- ".circleci/config.yml"
|
145
|
+
- ".gitignore"
|
146
|
+
- ".rubocop.yml"
|
147
|
+
- ".rubocop_todo.yml"
|
148
|
+
- ".ruby-version"
|
149
|
+
- CHANGELOG.md
|
150
|
+
- Gemfile
|
151
|
+
- LICENSE.txt
|
152
|
+
- README.md
|
153
|
+
- anony.gemspec
|
154
|
+
- docs/COMPATIBILITY.md
|
155
|
+
- lib/anony.rb
|
156
|
+
- lib/anony/anonymisable.rb
|
157
|
+
- lib/anony/config.rb
|
158
|
+
- lib/anony/cops.rb
|
159
|
+
- lib/anony/cops/define_deletion_strategy.rb
|
160
|
+
- lib/anony/duplicate_strategy_exception.rb
|
161
|
+
- lib/anony/field_exception.rb
|
162
|
+
- lib/anony/field_level_strategies.rb
|
163
|
+
- lib/anony/model_config.rb
|
164
|
+
- lib/anony/result.rb
|
165
|
+
- lib/anony/rspec_shared_examples.rb
|
166
|
+
- lib/anony/skipped_exception.rb
|
167
|
+
- lib/anony/strategies/destroy.rb
|
168
|
+
- lib/anony/strategies/overwrite.rb
|
169
|
+
- lib/anony/version.rb
|
170
|
+
homepage: https://github.com/gocardless/anony
|
171
|
+
licenses:
|
172
|
+
- MIT
|
173
|
+
metadata: {}
|
174
|
+
post_install_message:
|
175
|
+
rdoc_options: []
|
176
|
+
require_paths:
|
177
|
+
- lib
|
178
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
179
|
+
requirements:
|
180
|
+
- - ">="
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
version: '2.4'
|
183
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - ">="
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
requirements: []
|
189
|
+
rubygems_version: 3.0.3
|
190
|
+
signing_key:
|
191
|
+
specification_version: 4
|
192
|
+
summary: A small library that defines how ActiveRecord models should be anonymised
|
193
|
+
for deletion purposes.
|
194
|
+
test_files: []
|