attr_masker 0.1.0 → 0.3.1
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 +5 -5
- data/.github/workflows/tests.yml +91 -0
- data/.gitignore +5 -1
- data/.rubocop.yml +13 -1069
- data/CHANGELOG.adoc +31 -0
- data/Gemfile +5 -0
- data/README.adoc +81 -30
- data/Rakefile +0 -27
- data/attr_masker.gemspec +15 -10
- data/bin/console +14 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/setup +9 -0
- data/gemfiles/Rails-4.2.gemfile +2 -3
- data/gemfiles/Rails-5.0.gemfile +2 -3
- data/gemfiles/Rails-5.1.gemfile +2 -3
- data/gemfiles/Rails-5.2.gemfile +4 -0
- data/gemfiles/Rails-6.0.gemfile +3 -0
- data/gemfiles/Rails-6.1.gemfile +3 -0
- data/gemfiles/Rails-head.gemfile +1 -3
- data/gemfiles/common.gemfile +4 -0
- data/lib/attr_masker.rb +6 -210
- data/lib/attr_masker/attribute.rb +80 -0
- data/lib/attr_masker/error.rb +1 -0
- data/lib/attr_masker/maskers/replacing.rb +20 -3
- data/lib/attr_masker/maskers/simple.rb +20 -5
- data/lib/attr_masker/model.rb +143 -0
- data/lib/attr_masker/performer.rb +56 -17
- data/lib/attr_masker/version.rb +1 -16
- data/lib/tasks/db.rake +13 -4
- data/spec/dummy/app/models/non_persisted_model.rb +2 -0
- data/spec/dummy/config/attr_masker.rb +1 -0
- data/spec/dummy/config/mongoid.yml +33 -0
- data/spec/dummy/config/routes.rb +0 -1
- data/spec/dummy/db/schema.rb +1 -0
- data/spec/features/active_record_spec.rb +97 -0
- data/spec/features/mongoid_spec.rb +36 -0
- data/spec/features/shared_examples.rb +382 -0
- data/spec/spec_helper.rb +26 -3
- data/spec/support/00_control_constants.rb +2 -0
- data/spec/support/10_mongoid_env.rb +9 -0
- data/spec/support/20_combustion.rb +10 -0
- data/spec/support/db_cleaner.rb +13 -2
- data/spec/support/force_config_file_reload.rb +9 -0
- data/spec/support/rake.rb +1 -1
- data/spec/unit/attribute_spec.rb +210 -0
- data/spec/{maskers → unit/maskers}/replacing_spec.rb +0 -0
- data/spec/{maskers → unit/maskers}/simple_spec.rb +2 -2
- data/spec/unit/model_spec.rb +12 -0
- data/spec/unit/rake_task_spec.rb +30 -0
- metadata +139 -32
- data/.travis.yml +0 -32
- data/gemfiles/Rails-4.0.gemfile +0 -5
- data/gemfiles/Rails-4.1.gemfile +0 -5
- data/spec/features_spec.rb +0 -203
- data/spec/support/0_combustion.rb +0 -5
data/lib/attr_masker.rb
CHANGED
@@ -1,227 +1,23 @@
|
|
1
1
|
# (c) 2017 Ribose Inc.
|
2
2
|
#
|
3
3
|
|
4
|
+
require "ruby-progressbar"
|
5
|
+
|
4
6
|
# Adds attr_accessors that mask an object's attributes
|
5
7
|
module AttrMasker
|
6
8
|
autoload :Version, "attr_masker/version"
|
9
|
+
autoload :Attribute, "attr_masker/attribute"
|
10
|
+
autoload :Model, "attr_masker/model"
|
7
11
|
|
8
12
|
autoload :Error, "attr_masker/error"
|
9
13
|
autoload :Performer, "attr_masker/performer"
|
10
14
|
|
11
15
|
module Maskers
|
12
16
|
autoload :Replacing, "attr_masker/maskers/replacing"
|
13
|
-
autoload :
|
17
|
+
autoload :Simple, "attr_masker/maskers/simple"
|
14
18
|
end
|
15
19
|
|
16
20
|
require "attr_masker/railtie" if defined?(Rails)
|
17
|
-
def self.extended(base) # :nodoc:
|
18
|
-
base.class_eval do
|
19
|
-
|
20
|
-
# Only include the dangerous instance methods during the Rake task!
|
21
|
-
include InstanceMethods
|
22
|
-
attr_writer :attr_masker_options
|
23
|
-
@attr_masker_options, @masker_attributes = {}, {}
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
# Generates attr_accessors that mask attributes transparently
|
28
|
-
#
|
29
|
-
# Options (any other options you specify are passed to the masker's mask
|
30
|
-
# methods)
|
31
|
-
#
|
32
|
-
# :marshal => If set to true, attributes will be marshaled as well as masker. This is useful if you're planning
|
33
|
-
# on masking something other than a string. Defaults to false unless you're using it with ActiveRecord
|
34
|
-
# or DataMapper.
|
35
|
-
#
|
36
|
-
# :marshaler => The object to use for marshaling. Defaults to Marshal.
|
37
|
-
#
|
38
|
-
# :dump_method => The dump method name to call on the <tt>:marshaler</tt> object to. Defaults to 'dump'.
|
39
|
-
#
|
40
|
-
# :load_method => The load method name to call on the <tt>:marshaler</tt> object. Defaults to 'load'.
|
41
|
-
#
|
42
|
-
# :masker => The object to use for masking. It must respond to +#mask+. Defaults to AttrMasker::Maskers::Simple.
|
43
|
-
#
|
44
|
-
# :if => Attributes are only masker if this option evaluates to true. If you pass a symbol representing an instance
|
45
|
-
# method then the result of the method will be evaluated. Any objects that respond to <tt>:call</tt> are evaluated as well.
|
46
|
-
# Defaults to true.
|
47
|
-
#
|
48
|
-
# :unless => Attributes are only masker if this option evaluates to false. If you pass a symbol representing an instance
|
49
|
-
# method then the result of the method will be evaluated. Any objects that respond to <tt>:call</tt> are evaluated as well.
|
50
|
-
# Defaults to false.
|
51
|
-
#
|
52
|
-
# You can specify your own default options
|
53
|
-
#
|
54
|
-
# class User
|
55
|
-
# # now all attributes will be encoded and marshaled by default
|
56
|
-
# attr_masker_options.merge!(:marshal => true, :some_other_option => true)
|
57
|
-
# attr_masker :configuration
|
58
|
-
# end
|
59
|
-
#
|
60
|
-
#
|
61
|
-
# Example
|
62
|
-
#
|
63
|
-
# class User
|
64
|
-
# attr_masker :email, :credit_card
|
65
|
-
# attr_masker :configuration, :marshal => true
|
66
|
-
# end
|
67
|
-
#
|
68
|
-
# @user = User.new
|
69
|
-
# @user.masker_email # nil
|
70
|
-
# @user.email? # false
|
71
|
-
# @user.email = 'test@example.com'
|
72
|
-
# @user.email? # true
|
73
|
-
# @user.masker_email # returns the masker version of 'test@example.com'
|
74
|
-
#
|
75
|
-
# @user.configuration = { :time_zone => 'UTC' }
|
76
|
-
# @user.masker_configuration # returns the masker version of configuration
|
77
|
-
#
|
78
|
-
# See README for more examples
|
79
|
-
def attr_masker(*attributes)
|
80
|
-
options = {
|
81
|
-
:if => true,
|
82
|
-
:unless => false,
|
83
|
-
:column_name => nil,
|
84
|
-
:marshal => false,
|
85
|
-
:marshaler => Marshal,
|
86
|
-
:dump_method => "dump",
|
87
|
-
:load_method => "load",
|
88
|
-
:masker => AttrMasker::Maskers::SIMPLE,
|
89
|
-
}.merge!(attr_masker_options).merge!(attributes.last.is_a?(Hash) ? attributes.pop : {})
|
90
|
-
|
91
|
-
attributes.each do |attribute|
|
92
|
-
masker_attributes[attribute.to_sym] = options.merge(attribute: attribute.to_sym)
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
# Default options to use with calls to <tt>attr_masker</tt>
|
97
|
-
# XXX:Keep
|
98
|
-
#
|
99
|
-
# It will inherit existing options from its superclass
|
100
|
-
def attr_masker_options
|
101
|
-
@attr_masker_options ||= superclass.attr_masker_options.dup
|
102
|
-
end
|
103
|
-
|
104
|
-
# Checks if an attribute is configured with <tt>attr_masker</tt>
|
105
|
-
# XXX:Keep
|
106
|
-
#
|
107
|
-
# Example
|
108
|
-
#
|
109
|
-
# class User
|
110
|
-
# attr_accessor :name
|
111
|
-
# attr_masker :email
|
112
|
-
# end
|
113
|
-
#
|
114
|
-
# User.attr_masker?(:name) # false
|
115
|
-
# User.attr_masker?(:email) # true
|
116
|
-
def attr_masker?(attribute)
|
117
|
-
masker_attributes.has_key?(attribute.to_sym)
|
118
|
-
end
|
119
|
-
|
120
|
-
# masks a value for the attribute specified
|
121
|
-
# XXX:modify
|
122
|
-
#
|
123
|
-
# Example
|
124
|
-
#
|
125
|
-
# class User
|
126
|
-
# attr_masker :email
|
127
|
-
# end
|
128
|
-
#
|
129
|
-
# masker_email = User.mask(:email, 'test@example.com')
|
130
|
-
def mask(attribute, value, options = {})
|
131
|
-
options = masker_attributes[attribute.to_sym].merge(options)
|
132
|
-
# if options[:if] && !options[:unless] && !value.nil? && !(value.is_a?(String) && value.empty?)
|
133
|
-
if options[:if] && !options[:unless]
|
134
|
-
value = options[:marshal] ? options[:marshaler].send(options[:dump_method], value) : value.to_s
|
135
|
-
masker_value = options[:masker].call(options.merge!(value: value))
|
136
|
-
masker_value
|
137
|
-
else
|
138
|
-
value
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
# Contains a hash of masker attributes with virtual attribute names as keys
|
143
|
-
# and their corresponding options as values
|
144
|
-
# XXX:Keep
|
145
|
-
#
|
146
|
-
# Example
|
147
|
-
#
|
148
|
-
# class User
|
149
|
-
# attr_masker :email
|
150
|
-
# end
|
151
|
-
#
|
152
|
-
# User.masker_attributes # { :email => { :attribute => 'masker_email' } }
|
153
|
-
def masker_attributes
|
154
|
-
@masker_attributes ||= superclass.masker_attributes.dup
|
155
|
-
end
|
156
|
-
|
157
|
-
# Forwards calls to :mask_#{attribute} to the corresponding mask method
|
158
|
-
# if attribute was configured with attr_masker
|
159
|
-
#
|
160
|
-
# Example
|
161
|
-
#
|
162
|
-
# class User
|
163
|
-
# attr_masker :email
|
164
|
-
# end
|
165
|
-
#
|
166
|
-
# User.mask_email('SOME_masker_EMAIL_STRING')
|
167
|
-
def method_missing(method, *arguments, &block)
|
168
|
-
if method.to_s =~ /^mask_(.+)$/ && attr_masker?($1)
|
169
|
-
send(:mask, $1, *arguments)
|
170
|
-
else
|
171
|
-
super
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
module InstanceMethods
|
176
|
-
|
177
|
-
# masks a value for the attribute specified using options evaluated in the current object's scope
|
178
|
-
#
|
179
|
-
# Example
|
180
|
-
#
|
181
|
-
# class User
|
182
|
-
# attr_accessor :secret_key
|
183
|
-
# attr_masker :email
|
184
|
-
#
|
185
|
-
# def initialize(secret_key)
|
186
|
-
# self.secret_key = secret_key
|
187
|
-
# end
|
188
|
-
# end
|
189
|
-
#
|
190
|
-
# @user = User.new('some-secret-key')
|
191
|
-
# @user.mask(:email, 'test@example.com')
|
192
|
-
def mask(attribute, value=nil)
|
193
|
-
value = self.send(attribute) if value.nil?
|
194
|
-
self.class.mask(attribute, value, evaluated_attr_masker_options_for(attribute))
|
195
|
-
end
|
196
|
-
|
197
|
-
protected
|
198
|
-
|
199
|
-
# Returns attr_masker options evaluated in the current object's scope for the attribute specified
|
200
|
-
# XXX:Keep
|
201
|
-
def evaluated_attr_masker_options_for(attribute)
|
202
|
-
self.class.masker_attributes[attribute.to_sym].inject({}) do |hash, (option, value)|
|
203
|
-
if %i[if unless].include?(option)
|
204
|
-
hash.merge!(option => evaluate_attr_masker_option(value))
|
205
|
-
else
|
206
|
-
hash.merge!(option => value)
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
|
211
|
-
# Evaluates symbol (method reference) or proc (responds to call) options
|
212
|
-
# XXX:Keep
|
213
|
-
#
|
214
|
-
# If the option is not a symbol or proc then the original option is returned
|
215
|
-
def evaluate_attr_masker_option(option)
|
216
|
-
if option.is_a?(Symbol) && respond_to?(option)
|
217
|
-
send(option)
|
218
|
-
elsif option.respond_to?(:call)
|
219
|
-
option.call(self)
|
220
|
-
else
|
221
|
-
option
|
222
|
-
end
|
223
|
-
end
|
224
|
-
end
|
225
21
|
end
|
226
22
|
|
227
|
-
Object.extend AttrMasker
|
23
|
+
Object.extend AttrMasker::Model
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# (c) 2017 Ribose Inc.
|
2
|
+
#
|
3
|
+
|
4
|
+
module AttrMasker
|
5
|
+
# Holds the definition of maskable attribute.
|
6
|
+
class Attribute
|
7
|
+
attr_reader :name, :model, :options
|
8
|
+
|
9
|
+
def initialize(name, model, options)
|
10
|
+
@name = name.to_sym
|
11
|
+
@model = model
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
# Evaluates the +:if+ and +:unless+ attribute options on given instance.
|
16
|
+
# Returns +true+ or +false+, depending on whether the attribute should be
|
17
|
+
# masked for this object or not.
|
18
|
+
def should_mask?(model_instance)
|
19
|
+
not (
|
20
|
+
options.key?(:if) && !evaluate_option(:if, model_instance) ||
|
21
|
+
options.key?(:unless) && evaluate_option(:unless, model_instance)
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Mask the attribute on given model. Masking will be performed regardless
|
26
|
+
# of +:if+ and +:unless+ options. A +should_mask?+ method should be called
|
27
|
+
# separately to ensure that given object is eligible for masking.
|
28
|
+
#
|
29
|
+
# The method returns the masked value but does not modify the object's
|
30
|
+
# attribute.
|
31
|
+
#
|
32
|
+
# If +marshal+ attribute's option is +true+, the attribute value will be
|
33
|
+
# loaded before masking, and dumped to proper storage format prior
|
34
|
+
# returning.
|
35
|
+
def mask(model_instance)
|
36
|
+
value = unmarshal_data(model_instance.send(name))
|
37
|
+
masker = options[:masker]
|
38
|
+
masker_value = masker.call(value: value, model: model_instance,
|
39
|
+
attribute_name: name, masking_options: options)
|
40
|
+
model_instance.send("#{name}=", marshal_data(masker_value))
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns a hash of maskable attribute names, and respective attribute
|
44
|
+
# values. Unchanged attributes are skipped.
|
45
|
+
def masked_attributes_new_values(model_instance)
|
46
|
+
model_instance.changes.slice(*column_names).transform_values(&:second)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Evaluates option (typically +:if+ or +:unless+) on given model instance.
|
50
|
+
# That option can be either a proc (a model is passed as an only argument),
|
51
|
+
# or a symbol (a method of that name is called on model instance).
|
52
|
+
def evaluate_option(option_name, model_instance)
|
53
|
+
option = options[option_name]
|
54
|
+
|
55
|
+
if option.is_a?(Symbol)
|
56
|
+
model_instance.send(option)
|
57
|
+
elsif option.respond_to?(:call)
|
58
|
+
option.call(model_instance)
|
59
|
+
else
|
60
|
+
option
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def marshal_data(data)
|
65
|
+
return data unless options[:marshal]
|
66
|
+
|
67
|
+
options[:marshaler].send(options[:dump_method], data)
|
68
|
+
end
|
69
|
+
|
70
|
+
def unmarshal_data(data)
|
71
|
+
return data unless options[:marshal]
|
72
|
+
|
73
|
+
options[:marshaler].send(options[:load_method], data)
|
74
|
+
end
|
75
|
+
|
76
|
+
def column_names
|
77
|
+
options[:column_names] || [name]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/attr_masker/error.rb
CHANGED
@@ -1,15 +1,32 @@
|
|
1
1
|
# (c) 2017 Ribose Inc.
|
2
2
|
#
|
3
|
+
|
3
4
|
module AttrMasker
|
4
5
|
module Maskers
|
5
|
-
#
|
6
|
+
# +Replacing+ masker replaces every character of string which is being
|
7
|
+
# masked with +replacement+ one, preserving the length of the masked string
|
8
|
+
# (provided that a replacement string contains a single character, which is
|
9
|
+
# a typical case). Optionally, non-alphanumeric characters like dashes or
|
10
|
+
# spaces may be left unchanged.
|
6
11
|
#
|
7
|
-
#
|
8
|
-
#
|
12
|
+
# @example Would mask "Adam West" as "XXXXXXXXX"
|
13
|
+
# class User < ActiveRecord::Base
|
14
|
+
# m = AttrMasker::Maskers::Replacing.new(replacement: "X")
|
15
|
+
# attr_masker :name, :masker => m
|
16
|
+
# end
|
9
17
|
#
|
18
|
+
# @example Would mask "123-456-789" as "XXX-XXX-XXX"
|
19
|
+
# class User < ActiveRecord::Base
|
20
|
+
# m = AttrMasker::Maskers::Replacing.new(
|
21
|
+
# replacement: "X", alphanum_only: true)
|
22
|
+
# attr_masker :phone, :masker => m
|
23
|
+
# end
|
10
24
|
class Replacing
|
11
25
|
attr_reader :replacement, :alphanum_only
|
12
26
|
|
27
|
+
# @param replacement [String] replacement string
|
28
|
+
# @param alphanum_only [Boolean] whether to leave non-alphanumeric
|
29
|
+
# characters unchanged or not
|
13
30
|
def initialize(replacement: "*", alphanum_only: false)
|
14
31
|
replacement = "" if replacement.nil?
|
15
32
|
@replacement = replacement
|
@@ -1,12 +1,27 @@
|
|
1
|
+
# (c) 2017 Ribose Inc.
|
2
|
+
#
|
3
|
+
|
1
4
|
module AttrMasker
|
2
5
|
module Maskers
|
3
|
-
#
|
6
|
+
# +Simple+ masker replaces values with a predefined +(redacted)+ string.
|
7
|
+
# This is a default masker, which is used when no specific +:masker+ is
|
8
|
+
# passed in +attr_masker+ method call.
|
4
9
|
#
|
5
|
-
#
|
6
|
-
#
|
10
|
+
# @example Would mask "Adam West" as "(redacted)"
|
11
|
+
# class User < ActiveRecord::Base
|
12
|
+
# m = AttrMasker::Maskers::Simple.new
|
13
|
+
# attr_masker :name, :masker => m
|
14
|
+
# end
|
7
15
|
#
|
8
|
-
|
9
|
-
|
16
|
+
# @example Would mask "Adam West" as "(redacted)"
|
17
|
+
# class User < ActiveRecord::Base
|
18
|
+
# attr_masker :name
|
19
|
+
# end
|
20
|
+
class Simple
|
21
|
+
# Accepts any keyword arguments, but they all are ignored.
|
22
|
+
def call(**_opts)
|
23
|
+
"(redacted)"
|
24
|
+
end
|
10
25
|
end
|
11
26
|
end
|
12
27
|
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
# (c) 2017 Ribose Inc.
|
2
|
+
#
|
3
|
+
|
4
|
+
module AttrMasker
|
5
|
+
module Model
|
6
|
+
def self.extended(base) # :nodoc:
|
7
|
+
base.class_eval do
|
8
|
+
attr_writer :attr_masker_options
|
9
|
+
@attr_masker_options = {}
|
10
|
+
@masker_attributes = {}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Generates attr_accessors that mask attributes transparently
|
15
|
+
#
|
16
|
+
# Options (any other options you specify are passed to the masker's mask
|
17
|
+
# methods)
|
18
|
+
#
|
19
|
+
# [:masker]
|
20
|
+
# The object to use for masking. It must respond to +#mask+. Defaults to
|
21
|
+
# AttrMasker::Maskers::Simple.
|
22
|
+
#
|
23
|
+
# [:if]
|
24
|
+
# Attributes are only masker if this option evaluates to true. If you
|
25
|
+
# pass a symbol representing an instance method then the result of
|
26
|
+
# the method will be evaluated. Any objects that respond to
|
27
|
+
# <tt>:call</tt> are evaluated as well. Defaults to true.
|
28
|
+
#
|
29
|
+
# [:unless]
|
30
|
+
# Attributes are only masker if this option evaluates to false. If you
|
31
|
+
# pass a symbol representing an instance method then the result of
|
32
|
+
# the method will be evaluated. Any objects that respond to
|
33
|
+
# <tt>:call</tt> are evaluated as well. Defaults to false.
|
34
|
+
#
|
35
|
+
# [:marshal]
|
36
|
+
# If set to true, attributes will be marshaled as well as masker. This
|
37
|
+
# is useful if you're planning on masking something other than a string.
|
38
|
+
# Defaults to false unless you're using it with ActiveRecord or
|
39
|
+
# DataMapper.
|
40
|
+
#
|
41
|
+
# [:marshaler]
|
42
|
+
# The object to use for marshaling. Defaults to Marshal.
|
43
|
+
#
|
44
|
+
# [:dump_method]
|
45
|
+
# The dump method name to call on the <tt>:marshaler</tt> object to.
|
46
|
+
# Defaults to 'dump'.
|
47
|
+
#
|
48
|
+
# [:load_method]
|
49
|
+
# The load method name to call on the <tt>:marshaler</tt> object.
|
50
|
+
# Defaults to 'load'.
|
51
|
+
#
|
52
|
+
# You can specify your own default options
|
53
|
+
#
|
54
|
+
# class User
|
55
|
+
# # now all attributes will be encoded and marshaled by default
|
56
|
+
# attr_masker_options.merge!(:marshal => true, :another_option => true)
|
57
|
+
# attr_masker :configuration
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
#
|
61
|
+
# Example
|
62
|
+
#
|
63
|
+
# class User
|
64
|
+
# attr_masker :email, :credit_card
|
65
|
+
# attr_masker :configuration, :marshal => true
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# @user = User.new
|
69
|
+
# @user.masker_email # nil
|
70
|
+
# @user.email? # false
|
71
|
+
# @user.email = 'test@example.com'
|
72
|
+
# @user.email? # true
|
73
|
+
# @user.masker_email # returns the masker version of 'test@example.com'
|
74
|
+
#
|
75
|
+
# @user.configuration = { :time_zone => 'UTC' }
|
76
|
+
# @user.masker_configuration # returns the masker version of configuration
|
77
|
+
#
|
78
|
+
# See README for more examples
|
79
|
+
#--
|
80
|
+
# rubocop:disable Metrics/MethodLength
|
81
|
+
def attr_masker(*args)
|
82
|
+
default_options = {
|
83
|
+
if: true,
|
84
|
+
unless: false,
|
85
|
+
column_name: nil,
|
86
|
+
marshal: false,
|
87
|
+
marshaler: Marshal,
|
88
|
+
dump_method: "dump",
|
89
|
+
load_method: "load",
|
90
|
+
masker: AttrMasker::Maskers::Simple.new,
|
91
|
+
}
|
92
|
+
|
93
|
+
options = args.extract_options!.
|
94
|
+
reverse_merge(attr_masker_options).
|
95
|
+
reverse_merge(default_options)
|
96
|
+
|
97
|
+
args.each do |attribute_name|
|
98
|
+
attribute = Attribute.new(attribute_name, self, options)
|
99
|
+
masker_attributes[attribute.name] = attribute
|
100
|
+
end
|
101
|
+
end
|
102
|
+
# rubocop:enable Metrics/MethodLength
|
103
|
+
|
104
|
+
# Default options to use with calls to <tt>attr_masker</tt>
|
105
|
+
# XXX:Keep
|
106
|
+
#
|
107
|
+
# It will inherit existing options from its superclass
|
108
|
+
def attr_masker_options
|
109
|
+
@attr_masker_options ||= superclass.attr_masker_options.dup
|
110
|
+
end
|
111
|
+
|
112
|
+
# Checks if an attribute is configured with <tt>attr_masker</tt>
|
113
|
+
# XXX:Keep
|
114
|
+
#
|
115
|
+
# Example
|
116
|
+
#
|
117
|
+
# class User
|
118
|
+
# attr_accessor :name
|
119
|
+
# attr_masker :email
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# User.attr_masker?(:name) # false
|
123
|
+
# User.attr_masker?(:email) # true
|
124
|
+
def attr_masker?(attribute)
|
125
|
+
masker_attributes.has_key?(attribute.to_sym)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Contains a hash of masker attributes with virtual attribute names as keys
|
129
|
+
# and their corresponding options as values
|
130
|
+
# XXX:Keep
|
131
|
+
#
|
132
|
+
# Example
|
133
|
+
#
|
134
|
+
# class User
|
135
|
+
# attr_masker :email
|
136
|
+
# end
|
137
|
+
#
|
138
|
+
# User.masker_attributes # { :email => { :attribute => 'masker_email' } }
|
139
|
+
def masker_attributes
|
140
|
+
@masker_attributes ||= superclass.masker_attributes.dup
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|