spectator-attr_encrypted 1.1.3
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.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +331 -0
- data/Rakefile +31 -0
- data/lib/attr_encrypted.rb +239 -0
- data/lib/attr_encrypted/adapters/active_record.rb +55 -0
- data/lib/attr_encrypted/adapters/data_mapper.rb +21 -0
- data/lib/attr_encrypted/adapters/sequel.rb +13 -0
- data/test/active_record_test.rb +100 -0
- data/test/attr_encrypted_test.rb +277 -0
- data/test/data_mapper_test.rb +52 -0
- data/test/sequel_test.rb +50 -0
- data/test/test_helper.rb +10 -0
- metadata +242 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
if defined?(ActiveRecord)
|
2
|
+
module AttrEncrypted
|
3
|
+
module Adapters
|
4
|
+
module ActiveRecord
|
5
|
+
def self.extended(base)
|
6
|
+
base.attr_encrypted_options[:encode] = true
|
7
|
+
base.eigenclass_eval { alias_method_chain :method_missing, :attr_encrypted }
|
8
|
+
end
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
# Ensures the attribute methods for db fields have been defined before calling the original
|
13
|
+
# <tt>attr_encrypted</tt> method
|
14
|
+
def attr_encrypted(*attrs)
|
15
|
+
define_attribute_methods rescue nil
|
16
|
+
super
|
17
|
+
attrs.reject { |attr| attr.is_a?(Hash) }.each { |attr| alias_method "#{attr}_before_type_cast", attr }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Allows you to use dynamic methods like <tt>find_by_email</tt> or <tt>scoped_by_email</tt> for
|
21
|
+
# encrypted attributes
|
22
|
+
#
|
23
|
+
# NOTE: This only works when the <tt>:key</tt> option is specified as a string (see the README)
|
24
|
+
#
|
25
|
+
# This is useful for encrypting fields like email addresses. Your user's email addresses
|
26
|
+
# are encrypted in the database, but you can still look up a user by email for logging in
|
27
|
+
#
|
28
|
+
# Example
|
29
|
+
#
|
30
|
+
# class User < ActiveRecord::Base
|
31
|
+
# attr_encrypted :email, :key => 'secret key'
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# User.find_by_email_and_password('test@example.com', 'testing')
|
35
|
+
# # results in a call to
|
36
|
+
# User.find_by_encrypted_email_and_password('the_encrypted_version_of_test@example.com', 'testing')
|
37
|
+
def method_missing_with_attr_encrypted(method, *args, &block)
|
38
|
+
if match = /^(find|scoped)_(all_by|by)_([_a-zA-Z]\w*)$/.match(method.to_s)
|
39
|
+
attribute_names = match.captures.last.split('_and_')
|
40
|
+
attribute_names.each_with_index do |attribute, index|
|
41
|
+
if attr_encrypted?(attribute)
|
42
|
+
args[index] = send("encrypt_#{attribute}", args[index])
|
43
|
+
attribute_names[index] = encrypted_attributes[attribute]
|
44
|
+
end
|
45
|
+
end
|
46
|
+
method = "#{match.captures[0]}_#{match.captures[1]}_#{attribute_names.join('_and_')}".to_sym
|
47
|
+
end
|
48
|
+
method_missing_without_attr_encrypted(method, *args, &block)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
ActiveRecord::Base.extend AttrEncrypted::Adapters::ActiveRecord
|
55
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
if defined?(DataMapper)
|
2
|
+
module AttrEncrypted
|
3
|
+
module Adapters
|
4
|
+
module DataMapper
|
5
|
+
def self.extended(base)
|
6
|
+
base.eigenclass_eval do
|
7
|
+
alias_method :included_without_attr_encrypted, :included
|
8
|
+
alias_method :included, :included_with_attr_encrypted
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def included_with_attr_encrypted(base)
|
13
|
+
included_without_attr_encrypted(base)
|
14
|
+
base.attr_encrypted_options[:encode] = true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
DataMapper::Resource.extend AttrEncrypted::Adapters::DataMapper
|
21
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
|
4
|
+
|
5
|
+
def create_people_table
|
6
|
+
silence_stream(STDOUT) do
|
7
|
+
ActiveRecord::Schema.define(:version => 1) do
|
8
|
+
create_table :people do |t|
|
9
|
+
t.string :encrypted_email
|
10
|
+
t.string :password
|
11
|
+
t.string :encrypted_credentials
|
12
|
+
t.string :salt
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# The table needs to exist before defining the class
|
19
|
+
create_people_table
|
20
|
+
|
21
|
+
class Person < ActiveRecord::Base
|
22
|
+
attr_encrypted :email, :key => 'a secret key'
|
23
|
+
attr_encrypted :credentials, :key => Proc.new { |user| Encryptor.encrypt(:value => user.salt, :key => 'some private key') }, :marshal => true
|
24
|
+
|
25
|
+
def initialize(attributes = nil)
|
26
|
+
super(attributes)
|
27
|
+
self.salt ||= Digest::SHA256.hexdigest((Time.now.to_i * rand(5)).to_s)
|
28
|
+
self.credentials ||= { :username => 'example', :password => 'test' }
|
29
|
+
rescue Exception => e
|
30
|
+
raise unless ['ActiveRecord::MissingAttributeError', 'ActiveModel::MissingAttributeError'].include? e.class.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class PersonWithValidation < Person
|
35
|
+
validates_presence_of :email
|
36
|
+
validates_uniqueness_of :encrypted_email
|
37
|
+
end
|
38
|
+
|
39
|
+
class ActiveRecordTest < Test::Unit::TestCase
|
40
|
+
|
41
|
+
def setup
|
42
|
+
ActiveRecord::Base.connection.tables.each { |table| ActiveRecord::Base.connection.drop_table(table) }
|
43
|
+
create_people_table
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_should_encrypt_email
|
47
|
+
@person = Person.create :email => 'test@example.com'
|
48
|
+
assert_not_nil @person.encrypted_email
|
49
|
+
assert_not_equal @person.email, @person.encrypted_email
|
50
|
+
assert_equal @person.email, Person.find(:first).email
|
51
|
+
end
|
52
|
+
|
53
|
+
def test_should_marshal_and_encrypt_credentials
|
54
|
+
@person = Person.create
|
55
|
+
assert_not_nil @person.encrypted_credentials
|
56
|
+
assert_not_equal @person.credentials, @person.encrypted_credentials
|
57
|
+
assert_equal @person.credentials, Person.find(:first).credentials
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_should_find_by_email
|
61
|
+
@person = Person.create(:email => 'test@example.com')
|
62
|
+
assert_equal @person, Person.find_by_email('test@example.com')
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_should_find_by_email_and_password
|
66
|
+
Person.create(:email => 'test@example.com', :password => 'invalid')
|
67
|
+
@person = Person.create(:email => 'test@example.com', :password => 'test')
|
68
|
+
assert_equal @person, Person.find_by_email_and_password('test@example.com', 'test')
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_should_scope_by_email
|
72
|
+
@person = Person.create(:email => 'test@example.com')
|
73
|
+
assert_equal @person, Person.scoped_by_email('test@example.com').find(:first) rescue NoMethodError
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_should_scope_by_email_and_password
|
77
|
+
Person.create(:email => 'test@example.com', :password => 'invalid')
|
78
|
+
@person = Person.create(:email => 'test@example.com', :password => 'test')
|
79
|
+
assert_equal @person, Person.scoped_by_email_and_password('test@example.com', 'test').find(:first) rescue NoMethodError
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_should_encode_by_default
|
83
|
+
assert Person.attr_encrypted_options[:encode]
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_should_validate_presence_of_email
|
87
|
+
@person = PersonWithValidation.new
|
88
|
+
assert !@person.valid?
|
89
|
+
assert ![nil, []].include?(@person.errors[:email])
|
90
|
+
end
|
91
|
+
|
92
|
+
def test_should_validate_uniqueness_of_email
|
93
|
+
@person = PersonWithValidation.new :email => 'test@example.com'
|
94
|
+
assert @person.save
|
95
|
+
@person2 = PersonWithValidation.new :email => @person.email
|
96
|
+
assert !@person2.valid?
|
97
|
+
assert ![nil, []].include?(@person2.errors[:encrypted_email])
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,277 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
require 'mocha'
|
3
|
+
|
4
|
+
class SillyEncryptor
|
5
|
+
def self.silly_encrypt(options)
|
6
|
+
(options[:value] + options[:some_arg]).reverse
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.silly_decrypt(options)
|
10
|
+
options[:value].reverse.gsub(/#{options[:some_arg]}$/, '')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class User
|
15
|
+
self.attr_encrypted_options[:key] = Proc.new { |user| user.class.to_s } # default key
|
16
|
+
|
17
|
+
attr_encrypted :email, :without_encoding, :key => 'secret key'
|
18
|
+
attr_encrypted :password, :prefix => 'crypted_', :suffix => '_test'
|
19
|
+
attr_encrypted :ssn, :key => :salt, :attribute => 'ssn_encrypted'
|
20
|
+
attr_encrypted :credit_card, :encryptor => SillyEncryptor, :encrypt_method => :silly_encrypt, :decrypt_method => :silly_decrypt, :some_arg => 'test'
|
21
|
+
attr_encrypted :with_encoding, :key => 'secret key', :encode => true
|
22
|
+
attr_encrypted :with_custom_encoding, :key => 'secret key', :encode => 'm'
|
23
|
+
attr_encrypted :with_marshaling, :key => 'secret key', :marshal => true
|
24
|
+
attr_encrypted :with_true_if, :key => 'secret key', :if => true
|
25
|
+
attr_encrypted :with_false_if, :key => 'secret key', :if => false
|
26
|
+
attr_encrypted :with_true_unless, :key => 'secret key', :unless => true
|
27
|
+
attr_encrypted :with_false_unless, :key => 'secret key', :unless => false
|
28
|
+
attr_encrypted :with_if_changed, :key => 'secret key', :if => :should_encrypt
|
29
|
+
|
30
|
+
attr_encryptor :aliased, :key => 'secret_key'
|
31
|
+
|
32
|
+
attr_accessor :salt
|
33
|
+
attr_accessor :should_encrypt
|
34
|
+
|
35
|
+
def initialize
|
36
|
+
self.salt = Time.now.to_i.to_s
|
37
|
+
self.should_encrypt = true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class Admin < User
|
42
|
+
attr_encrypted :testing
|
43
|
+
end
|
44
|
+
|
45
|
+
class SomeOtherClass
|
46
|
+
def self.call(object)
|
47
|
+
object.class
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class AttrEncryptedTest < Test::Unit::TestCase
|
52
|
+
|
53
|
+
def test_should_store_email_in_encrypted_attributes
|
54
|
+
assert User.encrypted_attributes.include?('email')
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_should_not_store_salt_in_encrypted_attributes
|
58
|
+
assert !User.encrypted_attributes.include?('salt')
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_attr_encrypted_should_return_true_for_email
|
62
|
+
assert User.attr_encrypted?('email')
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_attr_encrypted_should_return_false_for_salt
|
66
|
+
assert !User.attr_encrypted?('salt')
|
67
|
+
end
|
68
|
+
|
69
|
+
def test_should_generate_an_encrypted_attribute
|
70
|
+
assert User.new.respond_to?(:encrypted_email)
|
71
|
+
end
|
72
|
+
|
73
|
+
def test_should_generate_an_encrypted_attribute_with_a_prefix_and_suffix
|
74
|
+
assert User.new.respond_to?(:crypted_password_test)
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_should_generate_an_encrypted_attribute_with_the_attribute_option
|
78
|
+
assert User.new.respond_to?(:ssn_encrypted)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_should_not_encrypt_nil_value
|
82
|
+
assert_nil User.encrypt_email(nil)
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_should_not_encrypt_empty_string
|
86
|
+
assert_equal '', User.encrypt_email('')
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_should_encrypt_email
|
90
|
+
assert_not_nil User.encrypt_email('test@example.com')
|
91
|
+
assert_not_equal 'test@example.com', User.encrypt_email('test@example.com')
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_should_encrypt_email_when_modifying_the_attr_writer
|
95
|
+
@user = User.new
|
96
|
+
assert_nil @user.send(:encrypted_email)
|
97
|
+
@user.email = 'test@example.com'
|
98
|
+
assert_not_nil @user.send(:encrypted_email)
|
99
|
+
assert_equal User.encrypt_email('test@example.com'), @user.send(:encrypted_email)
|
100
|
+
end
|
101
|
+
|
102
|
+
def test_should_not_decrypt_nil_value
|
103
|
+
assert_nil User.decrypt_email(nil)
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_should_not_decrypt_empty_string
|
107
|
+
assert_equal '', User.decrypt_email('')
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_should_decrypt_email
|
111
|
+
encrypted_email = User.encrypt_email('test@example.com')
|
112
|
+
assert_not_equal 'test@test.com', encrypted_email
|
113
|
+
assert_equal 'test@example.com', User.decrypt_email(encrypted_email)
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_should_decrypt_email_when_reading
|
117
|
+
@user = User.new
|
118
|
+
assert_nil @user.email
|
119
|
+
@user.send(:encrypted_email=, User.encrypt_email('test@example.com'))
|
120
|
+
assert_equal 'test@example.com', @user.email
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_should_encrypt_with_encoding
|
124
|
+
assert_equal User.encrypt_with_encoding('test'), [User.encrypt_without_encoding('test')].pack('m*')
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_should_decrypt_with_encoding
|
128
|
+
encrypted = User.encrypt_with_encoding('test')
|
129
|
+
assert_equal 'test', User.decrypt_with_encoding(encrypted)
|
130
|
+
assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m')[0])
|
131
|
+
end
|
132
|
+
|
133
|
+
def test_should_encrypt_with_custom_encoding
|
134
|
+
assert_equal User.encrypt_with_encoding('test'), [User.encrypt_without_encoding('test')].pack('m')
|
135
|
+
end
|
136
|
+
|
137
|
+
def test_should_decrypt_with_custom_encoding
|
138
|
+
encrypted = User.encrypt_with_encoding('test')
|
139
|
+
assert_equal 'test', User.decrypt_with_encoding(encrypted)
|
140
|
+
assert_equal User.decrypt_with_encoding(encrypted), User.decrypt_without_encoding(encrypted.unpack('m')[0])
|
141
|
+
end
|
142
|
+
|
143
|
+
def test_should_encrypt_with_marshaling
|
144
|
+
@user = User.new
|
145
|
+
@user.with_marshaling = [1, 2, 3]
|
146
|
+
assert_not_nil @user.send(:encrypted_with_marshaling)
|
147
|
+
assert_equal User.encrypt_with_marshaling([1, 2, 3]), @user.send(:encrypted_with_marshaling)
|
148
|
+
end
|
149
|
+
|
150
|
+
def test_should_decrypt_with_marshaling
|
151
|
+
encrypted = User.encrypt_with_marshaling([1, 2, 3])
|
152
|
+
@user = User.new
|
153
|
+
assert_nil @user.with_marshaling
|
154
|
+
@user.send(:encrypted_with_marshaling=, encrypted)
|
155
|
+
assert_equal [1, 2, 3], @user.with_marshaling
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_should_use_custom_encryptor_and_crypt_method_names_and_arguments
|
159
|
+
assert_equal SillyEncryptor.silly_encrypt(:value => 'testing', :some_arg => 'test'), User.encrypt_credit_card('testing')
|
160
|
+
end
|
161
|
+
|
162
|
+
def test_should_evaluate_a_key_passed_as_a_symbol
|
163
|
+
@user = User.new
|
164
|
+
assert_nil @user.send(:ssn_encrypted)
|
165
|
+
@user.ssn = 'testing'
|
166
|
+
assert_not_nil @user.send(:ssn_encrypted)
|
167
|
+
assert_equal Encryptor.encrypt(:value => 'testing', :key => @user.salt), @user.send(:ssn_encrypted)
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_should_evaluate_a_key_passed_as_a_proc
|
171
|
+
@user = User.new
|
172
|
+
assert_nil @user.send(:crypted_password_test)
|
173
|
+
@user.password = 'testing'
|
174
|
+
assert_not_nil @user.send(:crypted_password_test)
|
175
|
+
assert_equal Encryptor.encrypt(:value => 'testing', :key => 'User'), @user.send(:crypted_password_test)
|
176
|
+
end
|
177
|
+
|
178
|
+
def test_should_use_options_found_in_the_attr_encrypted_options_attribute
|
179
|
+
@user = User.new
|
180
|
+
assert_nil @user.send(:crypted_password_test)
|
181
|
+
@user.password = 'testing'
|
182
|
+
assert_not_nil @user.send(:crypted_password_test)
|
183
|
+
assert_equal Encryptor.encrypt(:value => 'testing', :key => 'User'), @user.send(:crypted_password_test)
|
184
|
+
end
|
185
|
+
|
186
|
+
def test_should_inherit_encrypted_attributes
|
187
|
+
assert_equal User.encrypted_attributes.merge('testing' => 'encrypted_testing'), Admin.encrypted_attributes
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_should_inherit_attr_encrypted_options
|
191
|
+
assert !User.attr_encrypted_options.empty?
|
192
|
+
assert_equal User.attr_encrypted_options, Admin.attr_encrypted_options
|
193
|
+
end
|
194
|
+
|
195
|
+
def test_should_not_inherit_unrelated_attributes
|
196
|
+
assert SomeOtherClass.attr_encrypted_options.empty?
|
197
|
+
assert SomeOtherClass.encrypted_attributes.empty?
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_should_evaluate_a_symbol_option
|
201
|
+
assert_equal Object, Object.send(:evaluate_attr_encrypted_option, :class, Object.new)
|
202
|
+
end
|
203
|
+
|
204
|
+
def test_should_evaluate_a_proc_option
|
205
|
+
assert_equal Object, Object.send(:evaluate_attr_encrypted_option, proc { |object| object.class }, Object.new)
|
206
|
+
end
|
207
|
+
|
208
|
+
def test_should_evaluate_a_lambda_option
|
209
|
+
assert_equal Object, Object.send(:evaluate_attr_encrypted_option, lambda { |object| object.class }, Object.new)
|
210
|
+
end
|
211
|
+
|
212
|
+
def test_should_evaluate_a_method_option
|
213
|
+
assert_equal Object, Object.send(:evaluate_attr_encrypted_option, SomeOtherClass.method(:call), Object.new)
|
214
|
+
end
|
215
|
+
|
216
|
+
def test_should_return_a_string_option
|
217
|
+
assert_equal 'Object', Object.send(:evaluate_attr_encrypted_option, 'Object', Object.new)
|
218
|
+
end
|
219
|
+
|
220
|
+
def test_should_encrypt_with_true_if
|
221
|
+
@user = User.new
|
222
|
+
assert_nil @user.send(:encrypted_with_true_if)
|
223
|
+
@user.with_true_if = 'testing'
|
224
|
+
assert_not_nil @user.send(:encrypted_with_true_if)
|
225
|
+
assert_equal Encryptor.encrypt(:value => 'testing', :key => 'secret key'), @user.send(:encrypted_with_true_if)
|
226
|
+
end
|
227
|
+
|
228
|
+
def test_should_not_encrypt_with_false_if
|
229
|
+
@user = User.new
|
230
|
+
assert_nil @user.send(:encrypted_with_false_if)
|
231
|
+
@user.with_false_if = 'testing'
|
232
|
+
assert_not_nil @user.send(:encrypted_with_false_if)
|
233
|
+
assert_equal 'testing', @user.send(:encrypted_with_false_if)
|
234
|
+
end
|
235
|
+
|
236
|
+
def test_should_encrypt_with_false_unless
|
237
|
+
@user = User.new
|
238
|
+
assert_nil @user.send(:encrypted_with_false_unless)
|
239
|
+
@user.with_false_unless = 'testing'
|
240
|
+
assert_not_nil @user.send(:encrypted_with_false_unless)
|
241
|
+
assert_equal Encryptor.encrypt(:value => 'testing', :key => 'secret key'), @user.send(:encrypted_with_false_unless)
|
242
|
+
end
|
243
|
+
|
244
|
+
def test_should_not_encrypt_with_true_unless
|
245
|
+
@user = User.new
|
246
|
+
assert_nil @user.send(:encrypted_with_true_unless)
|
247
|
+
@user.with_true_unless = 'testing'
|
248
|
+
assert_not_nil @user.send(:encrypted_with_true_unless)
|
249
|
+
assert_equal 'testing', @user.send(:encrypted_with_true_unless)
|
250
|
+
end
|
251
|
+
|
252
|
+
def test_should_work_with_aliased_attr_encryptor
|
253
|
+
assert User.encrypted_attributes.include?('aliased')
|
254
|
+
end
|
255
|
+
|
256
|
+
def test_should_always_reset_options
|
257
|
+
@user = User.new
|
258
|
+
@user.with_if_changed = "encrypt_stuff"
|
259
|
+
@user.stubs(:instance_variable_get).returns(nil)
|
260
|
+
@user.stubs(:instance_variable_set).raises("BadStuff")
|
261
|
+
assert_raise RuntimeError do
|
262
|
+
@user.with_if_changed
|
263
|
+
end
|
264
|
+
|
265
|
+
@user = User.new
|
266
|
+
@user.should_encrypt = false
|
267
|
+
@user.with_if_changed = "not_encrypted_stuff"
|
268
|
+
assert_equal "not_encrypted_stuff", @user.with_if_changed
|
269
|
+
assert_equal "not_encrypted_stuff", @user.send(:encrypted_with_if_changed)
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_should_return_true_if_method_name_is_defined
|
273
|
+
assert User.send :has_instance_method?, :encrypted_email
|
274
|
+
assert User.send :has_instance_method?, "encrypted_email"
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|