attr_redactor 0.1.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/.gitignore +6 -0
- data/.travis.yml +17 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +3 -0
- data/MIT-LICENSE +20 -0
- data/README.md +259 -0
- data/Rakefile +22 -0
- data/attr_redactor.gemspec +52 -0
- data/lib/attr_redactor/adapters/active_record.rb +86 -0
- data/lib/attr_redactor/adapters/data_mapper.rb +21 -0
- data/lib/attr_redactor/version.rb +17 -0
- data/lib/attr_redactor.rb +318 -0
- data/test/active_record_test.rb +187 -0
- data/test/attr_redactor_test.rb +278 -0
- data/test/run.sh +12 -0
- data/test/test_helper.rb +36 -0
- metadata +208 -0
@@ -0,0 +1,278 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require_relative 'test_helper'
|
3
|
+
class User
|
4
|
+
extend AttrRedactor
|
5
|
+
|
6
|
+
def self.redaction_hash
|
7
|
+
{
|
8
|
+
:ssn => :remove,
|
9
|
+
:email => :digest,
|
10
|
+
:medical_notes => :encrypt
|
11
|
+
}
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.encryption_key
|
15
|
+
'really, really secure, no one will guess it'
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.digest_salt
|
19
|
+
'digest salt'
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_redactor :data, redact: redaction_hash,
|
23
|
+
encryption_key: encryption_key,
|
24
|
+
digest_salt: digest_salt
|
25
|
+
attr_redactor :attr1, :attr2, redact: redaction_hash,
|
26
|
+
encryption_key: encryption_key,
|
27
|
+
digest_salt: digest_salt
|
28
|
+
attr_redactor :extra, redact: redaction_hash,
|
29
|
+
encryption_key: encryption_key,
|
30
|
+
digest_salt: digest_salt,
|
31
|
+
prefix: 'totally_',
|
32
|
+
suffix: '_secret'
|
33
|
+
attr_redactor :secret, redact: redaction_hash,
|
34
|
+
encryption_key: encryption_key,
|
35
|
+
digest_salt: digest_salt,
|
36
|
+
attribute: 'renamed_redacted_attribute'
|
37
|
+
attr_accessor :name
|
38
|
+
end
|
39
|
+
|
40
|
+
class AlternativeClass
|
41
|
+
extend AttrRedactor
|
42
|
+
|
43
|
+
def self.redaction_hash
|
44
|
+
{
|
45
|
+
:name => :remove,
|
46
|
+
:uid => :digest,
|
47
|
+
:address => :encrypt
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.encryption_key
|
52
|
+
'a completely different key, equally unguessable'
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.digest_salt
|
56
|
+
'saltier'
|
57
|
+
end
|
58
|
+
|
59
|
+
attr_redactor_options.merge! redact: redaction_hash,
|
60
|
+
encryption_key: encryption_key,
|
61
|
+
digest_salt: digest_salt
|
62
|
+
|
63
|
+
attr_redactor :secret
|
64
|
+
end
|
65
|
+
|
66
|
+
class SubClass < AlternativeClass
|
67
|
+
attr_redactor :testing
|
68
|
+
end
|
69
|
+
|
70
|
+
class SomeOtherClass
|
71
|
+
extend AttrRedactor
|
72
|
+
|
73
|
+
def self.call(object)
|
74
|
+
object.class
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class AttrRedactorTest < Minitest::Test
|
79
|
+
def setup
|
80
|
+
@iv = SecureRandom.random_bytes(12)
|
81
|
+
end
|
82
|
+
|
83
|
+
i_suck_and_my_tests_are_order_dependent!
|
84
|
+
|
85
|
+
def data_to_redact
|
86
|
+
{
|
87
|
+
:ssn => 'my secret ssn',
|
88
|
+
:email => 'personal@email.com',
|
89
|
+
:medical_notes => 'This is very personal and private'
|
90
|
+
}
|
91
|
+
end
|
92
|
+
|
93
|
+
def assert_redacted_hashes_equiv(hash1, hash2, redact)
|
94
|
+
# Since encryption uses a new iv each time, they cannot be compared
|
95
|
+
# only tested for presence
|
96
|
+
keys = redact.select{ |key,method| method == :encrypt }.keys
|
97
|
+
uncomparable_keys = keys.map{ |key| [ :"encrypted_#{key}", :"encrypted_#{key}_iv" ] }.flatten
|
98
|
+
|
99
|
+
h1 = hash1.select{ |key, value| !uncomparable_keys.include?(key) }
|
100
|
+
h2 = hash2.select{ |key, value| !uncomparable_keys.include?(key) }
|
101
|
+
|
102
|
+
assert_equal h1, h2
|
103
|
+
uncomparable_keys.each do |key|
|
104
|
+
assert hash1.has_key? key
|
105
|
+
assert hash2.has_key? key
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Unredacting data will always look slightly different since removed
|
110
|
+
# keys will remain removed, and digested will remain digested
|
111
|
+
# This hash contains what a structure should look like after redaction
|
112
|
+
def unredacted_data
|
113
|
+
r = HashRedactor::HashRedactor.new(redact: User.redaction_hash,
|
114
|
+
encryption_key: User.encryption_key,
|
115
|
+
digest_salt: User.digest_salt)
|
116
|
+
|
117
|
+
r.decrypt(r.redact(data_to_redact))
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_should_store_datal_in_redacted_attributes
|
121
|
+
assert User.redacted_attributes.include?(:data)
|
122
|
+
end
|
123
|
+
|
124
|
+
def test_attr_redacted_should_return_true_for_data
|
125
|
+
assert User.attr_redacted?('data')
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_attr_redacted_has_attr_redact_hash_attribute
|
129
|
+
expected_hash = {
|
130
|
+
:ssn => :remove,
|
131
|
+
:email => :digest,
|
132
|
+
:medical_notes => :encrypt
|
133
|
+
}
|
134
|
+
|
135
|
+
assert expected_hash, User.new.data_redact_hash
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_attr_redacted_should_not_use_the_same_attribute_name_for_two_attributes_in_the_same_line
|
139
|
+
refute_equal User.redacted_attributes[:attr1][:attribute], User.redacted_attributes[:attr2][:attribute]
|
140
|
+
end
|
141
|
+
|
142
|
+
def test_attr_redacted_should_return_false_for_name
|
143
|
+
assert !User.attr_redacted?('name')
|
144
|
+
end
|
145
|
+
|
146
|
+
def test_should_generate_redacted_attribute
|
147
|
+
assert User.new.respond_to?(:redacted_data)
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_should_generate_redacted_attribute_with_a_prefix_and_suffix
|
151
|
+
assert User.new.respond_to?(:totally_extra_secret)
|
152
|
+
end
|
153
|
+
|
154
|
+
def test_should_generate_redacted_attribute_with_the_attribute_option
|
155
|
+
assert User.new.respond_to?(:renamed_redacted_attribute)
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_should_not_change_nil_value
|
159
|
+
assert_nil User.redact_data(nil, iv: @iv)
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_should_redact_data
|
163
|
+
refute_nil User.redact_data(data_to_redact)
|
164
|
+
refute_equal data_to_redact, User.redact_data(data_to_redact)
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_should_use_hash_redactor
|
168
|
+
r = HashRedactor::HashRedactor.new(redact: User.redaction_hash,
|
169
|
+
encryption_key: User.encryption_key,
|
170
|
+
digest_salt: User.digest_salt)
|
171
|
+
|
172
|
+
assert_redacted_hashes_equiv r.redact(data_to_redact),
|
173
|
+
User.redact_data(data_to_redact),
|
174
|
+
User.redaction_hash
|
175
|
+
end
|
176
|
+
|
177
|
+
def test_should_redact_when_modifying_the_attr_writer
|
178
|
+
@user = User.new
|
179
|
+
assert_nil @user.redacted_data
|
180
|
+
@user.data = data_to_redact
|
181
|
+
refute_nil @user.redacted_data
|
182
|
+
assert_redacted_hashes_equiv User.redact_data(data_to_redact),
|
183
|
+
@user.redacted_data,
|
184
|
+
User.redaction_hash
|
185
|
+
end
|
186
|
+
|
187
|
+
def test_should_not_unredact_nil_value
|
188
|
+
assert_nil User.unredact_data(nil)
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_should_unredact
|
192
|
+
r = HashRedactor::HashRedactor.new(redact: User.redaction_hash,
|
193
|
+
encryption_key: User.encryption_key,
|
194
|
+
digest_salt: User.digest_salt)
|
195
|
+
|
196
|
+
redacted_data = User.redact_data(data_to_redact)
|
197
|
+
refute_equal data_to_redact, redacted_data
|
198
|
+
assert_equal r.decrypt(redacted_data), User.unredact_data(redacted_data)
|
199
|
+
end
|
200
|
+
|
201
|
+
def test_should_unredact_when_reading
|
202
|
+
@user = User.new
|
203
|
+
assert_nil @user.data
|
204
|
+
@user.redacted_data = User.redact_data(data_to_redact)
|
205
|
+
assert_equal unredacted_data, @user.data
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_should_use_options_found_in_the_attr_redactor_options_attribute
|
209
|
+
@user = AlternativeClass.new
|
210
|
+
|
211
|
+
alternative_redact_data = {
|
212
|
+
:name => 'Mr Murray',
|
213
|
+
:uid => 124356677,
|
214
|
+
:address => '1 Over the Rainbow, Somewhere'
|
215
|
+
}
|
216
|
+
|
217
|
+
assert_nil @user.secret
|
218
|
+
@user.secret = alternative_redact_data
|
219
|
+
refute_nil @user.secret
|
220
|
+
|
221
|
+
r = HashRedactor::HashRedactor.new(redact: AlternativeClass.redaction_hash,
|
222
|
+
encryption_key: AlternativeClass.encryption_key,
|
223
|
+
digest_salt: AlternativeClass.digest_salt)
|
224
|
+
|
225
|
+
redacted = r.redact(alternative_redact_data)
|
226
|
+
|
227
|
+
assert_redacted_hashes_equiv(redacted, @user.redacted_secret,
|
228
|
+
AlternativeClass.redaction_hash)
|
229
|
+
end
|
230
|
+
|
231
|
+
def test_should_inherit_redacted_attributes
|
232
|
+
assert_equal [AlternativeClass.redacted_attributes.keys, :testing].flatten.collect { |key| key.to_s }.sort, SubClass.redacted_attributes.keys.collect { |key| key.to_s }.sort
|
233
|
+
end
|
234
|
+
|
235
|
+
def test_should_inherit_attr_redactor_options
|
236
|
+
assert !SubClass.attr_redactor_options.empty?
|
237
|
+
assert_equal AlternativeClass.attr_redactor_options, SubClass.attr_redactor_options
|
238
|
+
end
|
239
|
+
|
240
|
+
def test_should_not_inherit_unrelated_attributes
|
241
|
+
assert SomeOtherClass.attr_redactor_options.empty?
|
242
|
+
assert SomeOtherClass.redacted_attributes.empty?
|
243
|
+
end
|
244
|
+
|
245
|
+
def test_should_evaluate_a_symbol_option
|
246
|
+
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_redactor_option, :class)
|
247
|
+
end
|
248
|
+
|
249
|
+
def test_should_evaluate_a_proc_option
|
250
|
+
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_redactor_option, proc { |object| object.class })
|
251
|
+
end
|
252
|
+
|
253
|
+
def test_should_evaluate_a_lambda_option
|
254
|
+
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_redactor_option, lambda { |object| object.class })
|
255
|
+
end
|
256
|
+
|
257
|
+
def test_should_evaluate_a_method_option
|
258
|
+
assert_equal SomeOtherClass, SomeOtherClass.new.send(:evaluate_attr_redactor_option, SomeOtherClass.method(:call))
|
259
|
+
end
|
260
|
+
|
261
|
+
def test_should_return_a_string_option
|
262
|
+
class_string = 'SomeOtherClass'
|
263
|
+
assert_equal class_string, SomeOtherClass.new.send(:evaluate_attr_redactor_option, class_string)
|
264
|
+
end
|
265
|
+
|
266
|
+
def test_should_create_query_accessor
|
267
|
+
@user = User.new
|
268
|
+
assert !@user.data?
|
269
|
+
@user.data = data_to_redact
|
270
|
+
assert @user.data?
|
271
|
+
end
|
272
|
+
|
273
|
+
def test_should_redact_immediately
|
274
|
+
@user = User.new
|
275
|
+
@user.data = data_to_redact
|
276
|
+
assert_equal unredacted_data, @user.data
|
277
|
+
end
|
278
|
+
end
|
data/test/run.sh
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env sh -e
|
2
|
+
|
3
|
+
for RUBY in 1.9.3 2.0.0 2.1 2.2
|
4
|
+
do
|
5
|
+
for RAILS in 2.3.8 3.0.0 3.1.0 3.2.0 4.0.0 4.1.0 4.2.0
|
6
|
+
do
|
7
|
+
if [[ $RUBY -gt 1.9.3 && $RAILS -lt 4.0.0 ]]; then
|
8
|
+
continue
|
9
|
+
fi
|
10
|
+
RBENV_VERSION=$RUBY ACTIVERECORD=$RAILS bundle && bundle exec rake
|
11
|
+
done
|
12
|
+
done
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "codeclimate-test-reporter"
|
2
|
+
CodeClimate::TestReporter.start
|
3
|
+
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
# Rails 4.0.x pins to an old minitest
|
7
|
+
unless defined?(MiniTest::Test)
|
8
|
+
MiniTest::Test = MiniTest::Unit::TestCase
|
9
|
+
end
|
10
|
+
|
11
|
+
require 'active_record'
|
12
|
+
require 'data_mapper'
|
13
|
+
require 'digest/sha2'
|
14
|
+
require 'sequel'
|
15
|
+
|
16
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
17
|
+
$:.unshift(File.dirname(__FILE__))
|
18
|
+
require 'attr_redactor'
|
19
|
+
|
20
|
+
DB = if defined?(RUBY_ENGINE) && RUBY_ENGINE.to_sym == :jruby
|
21
|
+
Sequel.jdbc('jdbc:sqlite::memory:')
|
22
|
+
else
|
23
|
+
Sequel.sqlite
|
24
|
+
end
|
25
|
+
|
26
|
+
# The :after_initialize hook was removed in Sequel 4.0
|
27
|
+
# and had been deprecated for a while before that:
|
28
|
+
# http://sequel.rubyforge.org/rdoc-plugins/classes/Sequel/Plugins/AfterInitialize.html
|
29
|
+
# This plugin re-enables it.
|
30
|
+
Sequel::Model.plugin :after_initialize
|
31
|
+
|
32
|
+
SECRET_KEY = SecureRandom.random_bytes(32)
|
33
|
+
|
34
|
+
def base64_encoding_regex
|
35
|
+
/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{4})$/
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,208 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: attr_redactor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Jensen
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: hash_redactor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.2.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.2.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activerecord
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: actionpack
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: datamapper
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: minitest
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sequel
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: sqlite3
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: dm-sqlite-adapter
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: codeclimate-test-reporter
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
description: Generates attr_accessors that redact certain values in the JSON structure
|
154
|
+
before saving.
|
155
|
+
email:
|
156
|
+
- chris@broadthought.co
|
157
|
+
executables: []
|
158
|
+
extensions: []
|
159
|
+
extra_rdoc_files: []
|
160
|
+
files:
|
161
|
+
- .gitignore
|
162
|
+
- .travis.yml
|
163
|
+
- CHANGELOG.md
|
164
|
+
- Gemfile
|
165
|
+
- MIT-LICENSE
|
166
|
+
- README.md
|
167
|
+
- Rakefile
|
168
|
+
- attr_redactor.gemspec
|
169
|
+
- lib/attr_redactor.rb
|
170
|
+
- lib/attr_redactor/adapters/active_record.rb
|
171
|
+
- lib/attr_redactor/adapters/data_mapper.rb
|
172
|
+
- lib/attr_redactor/version.rb
|
173
|
+
- test/active_record_test.rb
|
174
|
+
- test/attr_redactor_test.rb
|
175
|
+
- test/run.sh
|
176
|
+
- test/test_helper.rb
|
177
|
+
homepage: http://github.com/chrisjensen/attr_redactor
|
178
|
+
licenses: []
|
179
|
+
metadata: {}
|
180
|
+
post_install_message:
|
181
|
+
rdoc_options:
|
182
|
+
- --line-numbers
|
183
|
+
- --inline-source
|
184
|
+
- --main
|
185
|
+
- README.rdoc
|
186
|
+
require_paths:
|
187
|
+
- lib
|
188
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
189
|
+
requirements:
|
190
|
+
- - '>='
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
version: 2.0.0
|
193
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
194
|
+
requirements:
|
195
|
+
- - '>='
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
requirements: []
|
199
|
+
rubyforge_project:
|
200
|
+
rubygems_version: 2.4.6
|
201
|
+
signing_key:
|
202
|
+
specification_version: 4
|
203
|
+
summary: Redact JSON attributes before saving
|
204
|
+
test_files:
|
205
|
+
- test/active_record_test.rb
|
206
|
+
- test/attr_redactor_test.rb
|
207
|
+
- test/run.sh
|
208
|
+
- test/test_helper.rb
|