attr_masker 0.1.0 → 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|