active_attr 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of active_attr might be problematic. Click here for more details.
- data/CHANGELOG.md +15 -0
- data/README.md +83 -11
- data/active_attr.gemspec +4 -3
- data/lib/active_attr.rb +5 -0
- data/lib/active_attr/attributes.rb +72 -24
- data/lib/active_attr/block_initialization.rb +37 -0
- data/lib/active_attr/chainable_initialization.rb +6 -4
- data/lib/active_attr/dangerous_attribute_error.rb +11 -0
- data/lib/active_attr/logger.rb +46 -0
- data/lib/active_attr/mass_assignment_security.rb +42 -0
- data/lib/active_attr/query_attributes.rb +62 -0
- data/lib/active_attr/railtie.rb +10 -0
- data/lib/active_attr/unknown_attribute_error.rb +19 -0
- data/lib/active_attr/version.rb +1 -1
- data/spec/functional/active_attr/attributes_spec.rb +104 -0
- data/spec/functional/active_attr/query_attributes_spec.rb +32 -0
- data/spec/support/mass_assignment_shared_examples.rb +9 -0
- data/spec/unit/active_attr/attributes_spec.rb +73 -39
- data/spec/unit/active_attr/block_initialization_spec.rb +39 -0
- data/spec/unit/active_attr/dangerous_attribute_error_spec.rb +9 -0
- data/spec/unit/active_attr/logger_spec.rb +186 -0
- data/spec/unit/active_attr/mass_assignment_security_spec.rb +62 -0
- data/spec/unit/active_attr/query_attributes_spec.rb +225 -0
- data/spec/unit/active_attr/unknown_attribute_error_spec.rb +9 -0
- metadata +48 -17
@@ -0,0 +1,42 @@
|
|
1
|
+
require "active_attr/mass_assignment"
|
2
|
+
require "active_support/concern"
|
3
|
+
require "active_model"
|
4
|
+
|
5
|
+
module ActiveAttr
|
6
|
+
# MassAssignmentSecurity allows you to bulk set and update a blacklist or
|
7
|
+
# whitelist of attributes
|
8
|
+
#
|
9
|
+
# Including MassAssignmentSecurity extends all {ActiveAttr::MassAssignment}
|
10
|
+
# methods to honor any declared attribute permissions.
|
11
|
+
#
|
12
|
+
# @example Usage
|
13
|
+
# class Person
|
14
|
+
# include ActiveAttr::MassAssignmentSecurity
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# @since 0.3.0
|
18
|
+
module MassAssignmentSecurity
|
19
|
+
extend ActiveSupport::Concern
|
20
|
+
include MassAssignment
|
21
|
+
include ActiveModel::MassAssignmentSecurity
|
22
|
+
|
23
|
+
# Mass update a model's attributes, honoring attribute permissions
|
24
|
+
#
|
25
|
+
# @param (see MassAssignment#assign_attributes)
|
26
|
+
# @param [Hash, #[]] options Options that affect mass assignment
|
27
|
+
#
|
28
|
+
# @option options [Symbol] :as (:default) Mass assignment role
|
29
|
+
# @option options [true, false] :without_protection (false) Bypass mass
|
30
|
+
# assignment security if true
|
31
|
+
#
|
32
|
+
# @since 0.3.0
|
33
|
+
def assign_attributes(new_attributes, options={})
|
34
|
+
if new_attributes && !options[:without_protection]
|
35
|
+
mass_assignment_role = options[:as] || :default
|
36
|
+
new_attributes = sanitize_for_mass_assignment new_attributes, mass_assignment_role
|
37
|
+
end
|
38
|
+
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require "active_attr/attributes"
|
2
|
+
require "active_attr/unknown_attribute_error"
|
3
|
+
require "active_support/concern"
|
4
|
+
require "active_support/core_ext/object/blank"
|
5
|
+
|
6
|
+
module ActiveAttr
|
7
|
+
# QueryAttributes provides instance methods for querying attributes.
|
8
|
+
#
|
9
|
+
# @example Usage
|
10
|
+
# class Person
|
11
|
+
# include ActiveAttr::QueryAttributes
|
12
|
+
# attribute :name
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# person = Person.new
|
16
|
+
# person.name? #=> false
|
17
|
+
# person.name = "Chris Griego"
|
18
|
+
# person.name? #=> true
|
19
|
+
#
|
20
|
+
# @since 0.3.0
|
21
|
+
module QueryAttributes
|
22
|
+
extend ActiveSupport::Concern
|
23
|
+
include Attributes
|
24
|
+
|
25
|
+
included do
|
26
|
+
attribute_method_suffix "?"
|
27
|
+
end
|
28
|
+
|
29
|
+
# Test the presence of an attribute
|
30
|
+
#
|
31
|
+
# Similar to an ActiveRecord model, when the attribute is a zero value or
|
32
|
+
# is a string that represents false, the method returns false.
|
33
|
+
#
|
34
|
+
# @example Query an attribute
|
35
|
+
# person.query_attribute(:name)
|
36
|
+
#
|
37
|
+
# @param [String, Symbol, #to_s] name The name of the attribute to query
|
38
|
+
#
|
39
|
+
# @return [true, false] The presence of the attribute
|
40
|
+
#
|
41
|
+
# @raise [UnknownAttributeError] if the attribute is unknown
|
42
|
+
#
|
43
|
+
# @since 0.3.0
|
44
|
+
def query_attribute(name)
|
45
|
+
if respond_to? "#{name}?"
|
46
|
+
send "#{name}?"
|
47
|
+
else
|
48
|
+
raise UnknownAttributeError, "unknown attribute: #{name}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def attribute?(name)
|
55
|
+
case value = read_attribute(name)
|
56
|
+
when "false", "FALSE", "f", "F" then false
|
57
|
+
when Numeric, /^\-?[0-9]/ then !value.to_f.zero?
|
58
|
+
else value.present?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "active_attr/error"
|
2
|
+
|
3
|
+
module ActiveAttr
|
4
|
+
# This exception is raised if attempting to assign unknown attributes when
|
5
|
+
# using {Attributes}
|
6
|
+
#
|
7
|
+
# @example Rescuing an UnknownAttributeError error
|
8
|
+
# begin
|
9
|
+
# person.write_attribute(:middle_initial, "J")
|
10
|
+
# rescue ActiveAttr::UnknownAttributeError
|
11
|
+
# logger.error "attempted to write an unknown attribute"
|
12
|
+
# raise
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# @since 0.3.0
|
16
|
+
class UnknownAttributeError < NoMethodError
|
17
|
+
include Error
|
18
|
+
end
|
19
|
+
end
|
data/lib/active_attr/version.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
require "active_attr/attributes"
|
3
3
|
require "active_model"
|
4
|
+
require "factory_girl"
|
4
5
|
|
5
6
|
module ActiveAttr
|
6
7
|
describe Attributes do
|
@@ -107,5 +108,108 @@ module ActiveAttr
|
|
107
108
|
include_examples "serialization method"
|
108
109
|
end
|
109
110
|
end
|
111
|
+
|
112
|
+
context "building with FactoryGirl" do
|
113
|
+
subject { FactoryGirl.build(:person) }
|
114
|
+
|
115
|
+
before do
|
116
|
+
Object.const_set("Person", model_class)
|
117
|
+
|
118
|
+
Factory.define :person, :class => :person do |model|
|
119
|
+
model.first_name "Chris"
|
120
|
+
model.last_name "Griego"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
after do
|
125
|
+
FactoryGirl.factories.clear
|
126
|
+
Object.send :remove_const, "Person"
|
127
|
+
end
|
128
|
+
|
129
|
+
let :model_class do
|
130
|
+
Class.new do
|
131
|
+
include Attributes
|
132
|
+
attribute :first_name
|
133
|
+
attribute :last_name
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
it "builds an instance of the model class" do
|
138
|
+
should be_a_kind_of Person
|
139
|
+
end
|
140
|
+
|
141
|
+
it "sets the attributes" do
|
142
|
+
subject.first_name.should == "Chris"
|
143
|
+
subject.last_name.should == "Griego"
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
context "defining dangerous attributes" do
|
148
|
+
shared_examples "defining a dangerous attribute" do
|
149
|
+
it "defining an attribute that conflicts with #{described_class} raises DangerousAttributeError" do
|
150
|
+
expect { model_class.attribute(:write_attribute) }.to raise_error DangerousAttributeError, %{an attribute method named "write_attribute" would conflict with an existing method}
|
151
|
+
end
|
152
|
+
|
153
|
+
it "defining an attribute that conflicts with ActiveModel::AttributeMethods raises DangerousAttributeError" do
|
154
|
+
expect { model_class.attribute(:attribute_method_matchers) }.to raise_error DangerousAttributeError, %{an attribute method named "attribute_method_matchers" would conflict with an existing method}
|
155
|
+
end
|
156
|
+
|
157
|
+
it "defining an :id attribute does not raise" do
|
158
|
+
expect { model_class.attribute(:id) }.not_to raise_error
|
159
|
+
end
|
160
|
+
|
161
|
+
it "defining a :type attribute does not raise" do
|
162
|
+
expect { model_class.attribute(:type) }.not_to raise_error
|
163
|
+
end
|
164
|
+
|
165
|
+
it "defining an attribute that conflicts with Kernel raises DangerousAttributeError" do
|
166
|
+
expect { model_class.attribute(:puts) }.to raise_error DangerousAttributeError
|
167
|
+
end
|
168
|
+
|
169
|
+
it "defining an attribute that conflicts with Object raises DangerousAttributeError" do
|
170
|
+
expect { model_class.attribute(:class) }.to raise_error DangerousAttributeError
|
171
|
+
end
|
172
|
+
|
173
|
+
it "defining an attribute that conflicts with BasicObject raises DangerousAttributeError" do
|
174
|
+
expect { model_class.attribute(:instance_eval) }.to raise_error DangerousAttributeError
|
175
|
+
end
|
176
|
+
|
177
|
+
it "defining an attribute that conflicts with a properly implemented method_missing callback raises DangerousAttributeError" do
|
178
|
+
expect { model_class.attribute(:my_proper_missing_method) }.to raise_error DangerousAttributeError
|
179
|
+
end
|
180
|
+
|
181
|
+
it "defining an attribute that conflicts with a less properly implemented method_missing callback raises DangerousAttributeError" do
|
182
|
+
expect { model_class.attribute(:my_less_proper_missing_method) }.to raise_error DangerousAttributeError
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
let :dangerous_model_class do
|
187
|
+
Class.new do
|
188
|
+
include Attributes
|
189
|
+
|
190
|
+
def method_missing(method_name, *)
|
191
|
+
super if %w(my_proper_missing_method my_less_proper_missing_method).include? method_name.to_s
|
192
|
+
end
|
193
|
+
|
194
|
+
def respond_to_missing?(method_name, *)
|
195
|
+
method_name.to_s == "my_proper_missing_method" || super
|
196
|
+
end
|
197
|
+
|
198
|
+
def respond_to?(method_name, include_private=false)
|
199
|
+
super || method_name.to_s == "my_less_proper_missing_method" || (RUBY_VERSION < "1.9" && respond_to_missing?(method_name, include_private))
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context "on a model class" do
|
205
|
+
let(:model_class) { dangerous_model_class }
|
206
|
+
include_examples "defining a dangerous attribute"
|
207
|
+
end
|
208
|
+
|
209
|
+
context "on a child class" do
|
210
|
+
let(:model_class) { Class.new(dangerous_model_class) }
|
211
|
+
include_examples "defining a dangerous attribute"
|
212
|
+
end
|
213
|
+
end
|
110
214
|
end
|
111
215
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "active_attr/query_attributes"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
describe QueryAttributes do
|
6
|
+
context "defining dangerous attributes" do
|
7
|
+
shared_examples "defining a dangerous queryable attribute" do
|
8
|
+
it "defining an attribute that conflicts with ActiveModel::AttributeMethods raises DangerousAttributeError" do
|
9
|
+
expect { model_class.attribute(:attribute_method) }.to raise_error DangerousAttributeError, %{an attribute method named "attribute_method?" would conflict with an existing method}
|
10
|
+
end
|
11
|
+
|
12
|
+
it "defining an attribute that conflicts with Kernel raises DangerousAttributeError" do
|
13
|
+
expect { model_class.attribute(:block_given) }.to raise_error DangerousAttributeError
|
14
|
+
end
|
15
|
+
|
16
|
+
it "defining an attribute that conflicts with Object raises DangerousAttributeError" do
|
17
|
+
expect { model_class.attribute(:nil) }.to raise_error DangerousAttributeError
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "on a model class" do
|
22
|
+
let(:model_class) { Class.new { include QueryAttributes } }
|
23
|
+
include_examples "defining a dangerous queryable attribute"
|
24
|
+
end
|
25
|
+
|
26
|
+
context "on a child class" do
|
27
|
+
let(:model_class) { Class.new(Class.new { include QueryAttributes }) }
|
28
|
+
include_examples "defining a dangerous queryable attribute"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -46,6 +46,11 @@ shared_examples "#assign_attribures", :assign_attributes => true do
|
|
46
46
|
person
|
47
47
|
end
|
48
48
|
|
49
|
+
def mass_assign_attributes_with_options(attributes, options)
|
50
|
+
person.assign_attributes attributes, options
|
51
|
+
person
|
52
|
+
end
|
53
|
+
|
49
54
|
it "raises ArgumentError when called with three arguments" do
|
50
55
|
expect { subject.new.assign_attributes({}, {}, nil) }.to raise_error ArgumentError
|
51
56
|
end
|
@@ -79,6 +84,10 @@ shared_examples "#initialize", :initialize => true do
|
|
79
84
|
subject.new(attributes)
|
80
85
|
end
|
81
86
|
|
87
|
+
def mass_assign_attributes_with_options(attributes, options)
|
88
|
+
subject.new(attributes, options)
|
89
|
+
end
|
90
|
+
|
82
91
|
it "invokes the superclass initializer" do
|
83
92
|
subject.new.should be_initialized
|
84
93
|
end
|
@@ -4,12 +4,14 @@ require "active_attr/attributes"
|
|
4
4
|
module ActiveAttr
|
5
5
|
describe Attributes do
|
6
6
|
subject { model_class.new }
|
7
|
+
let(:last_name) { "Poweski" }
|
7
8
|
|
8
9
|
let :model_class do
|
9
10
|
Class.new do
|
10
11
|
include InitializationVerifier
|
11
12
|
include Attributes
|
12
|
-
attribute :
|
13
|
+
attribute :first_name
|
14
|
+
attribute :last_name
|
13
15
|
attribute :amount
|
14
16
|
|
15
17
|
def self.name
|
@@ -24,9 +26,17 @@ module ActiveAttr
|
|
24
26
|
super
|
25
27
|
end
|
26
28
|
|
27
|
-
def
|
29
|
+
def last_name=(value)
|
30
|
+
super(value.to_s.upcase)
|
31
|
+
end
|
32
|
+
|
33
|
+
def last_name
|
34
|
+
super || "Poweski"
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(first_name=nil)
|
28
38
|
super
|
29
|
-
write_attribute(:
|
39
|
+
write_attribute(:first_name, first_name)
|
30
40
|
end
|
31
41
|
end
|
32
42
|
end
|
@@ -43,7 +53,7 @@ module ActiveAttr
|
|
43
53
|
|
44
54
|
describe ".attribute" do
|
45
55
|
it "creates an attribute with no options" do
|
46
|
-
model_class.attributes.should include(AttributeDefinition.new(:
|
56
|
+
model_class.attributes.should include(AttributeDefinition.new(:first_name))
|
47
57
|
end
|
48
58
|
|
49
59
|
it "returns the attribute definition" do
|
@@ -51,8 +61,8 @@ module ActiveAttr
|
|
51
61
|
end
|
52
62
|
|
53
63
|
it "defines an attribute reader that calls #attribute" do
|
54
|
-
subject.should_receive(:attribute).with("
|
55
|
-
subject.
|
64
|
+
subject.should_receive(:attribute).with("first_name")
|
65
|
+
subject.first_name
|
56
66
|
end
|
57
67
|
|
58
68
|
it "defines an attribute reader that can be called via super" do
|
@@ -61,8 +71,8 @@ module ActiveAttr
|
|
61
71
|
end
|
62
72
|
|
63
73
|
it "defines an attribute writer that calls #attribute=" do
|
64
|
-
subject.should_receive(:attribute=).with("
|
65
|
-
subject.
|
74
|
+
subject.should_receive(:attribute=).with("first_name", "Ben")
|
75
|
+
subject.first_name = "Ben"
|
66
76
|
end
|
67
77
|
|
68
78
|
it "defines an attribute writer that can be called via super" do
|
@@ -70,7 +80,7 @@ module ActiveAttr
|
|
70
80
|
subject.amount = 1
|
71
81
|
end
|
72
82
|
|
73
|
-
it "defining an attribute twice does not
|
83
|
+
it "defining an attribute twice does not give the class two attribute definitions" do
|
74
84
|
Class.new do
|
75
85
|
include Attributes
|
76
86
|
attribute :name
|
@@ -93,7 +103,7 @@ module ActiveAttr
|
|
93
103
|
end
|
94
104
|
|
95
105
|
it "renders the attribute names in alphabetical order" do
|
96
|
-
model_class.inspect.should match "(amount,
|
106
|
+
model_class.inspect.should match "(amount, first_name, last_name)"
|
97
107
|
end
|
98
108
|
|
99
109
|
it "doesn't format the inspection string for attributes if the model does not have any" do
|
@@ -109,7 +119,7 @@ module ActiveAttr
|
|
109
119
|
end
|
110
120
|
|
111
121
|
it "returns false when compared to another type" do
|
112
|
-
should_not == Struct.new(:attributes).new("
|
122
|
+
should_not == Struct.new(:attributes).new("first_name" => "Ben")
|
113
123
|
end
|
114
124
|
end
|
115
125
|
|
@@ -122,32 +132,23 @@ module ActiveAttr
|
|
122
132
|
|
123
133
|
context "when an attribute is defined" do
|
124
134
|
it "returns the key value pairs" do
|
125
|
-
subject.
|
126
|
-
subject.attributes.should include("
|
135
|
+
subject.first_name = "Ben"
|
136
|
+
subject.attributes.should include("first_name" => "Ben")
|
127
137
|
end
|
128
138
|
|
129
139
|
it "returns a new Hash " do
|
130
|
-
subject.attributes.merge!("
|
131
|
-
subject.attributes.should_not include("
|
140
|
+
subject.attributes.merge!("first_name" => "Bob")
|
141
|
+
subject.attributes.should_not include("first_name" => "Bob")
|
132
142
|
end
|
133
143
|
|
134
144
|
it "returns all attributes" do
|
135
|
-
subject.attributes.keys.should =~ %w(amount
|
145
|
+
subject.attributes.keys.should =~ %w(amount first_name last_name)
|
136
146
|
end
|
137
147
|
end
|
138
148
|
|
139
149
|
context "when a getter is overridden" do
|
140
|
-
before do
|
141
|
-
subject.extend Module.new {
|
142
|
-
def name
|
143
|
-
"Benjamin"
|
144
|
-
end
|
145
|
-
}
|
146
|
-
end
|
147
|
-
|
148
150
|
it "uses the overridden implementation" do
|
149
|
-
subject.
|
150
|
-
subject.attributes.should include("name" => "Benjamin")
|
151
|
+
subject.attributes.should include("last_name" => last_name)
|
151
152
|
end
|
152
153
|
end
|
153
154
|
end
|
@@ -159,14 +160,20 @@ module ActiveAttr
|
|
159
160
|
end
|
160
161
|
|
161
162
|
describe "#inspect" do
|
162
|
-
before { subject.
|
163
|
+
before { subject.first_name = "Ben" }
|
163
164
|
|
164
165
|
it "includes the class name and all attribute values in alphabetical order by attribute name" do
|
165
|
-
subject.inspect.should == %
|
166
|
+
subject.inspect.should == %{#<Foo amount: nil, first_name: "Ben", last_name: "#{last_name}">}
|
166
167
|
end
|
167
168
|
|
168
169
|
it "doesn't format the inspection string for attributes if the model does not have any" do
|
169
|
-
attributeless.new.inspect.should == %
|
170
|
+
attributeless.new.inspect.should == %{#<Foo>}
|
171
|
+
end
|
172
|
+
|
173
|
+
context "when a getter is overridden" do
|
174
|
+
it "uses the overridden implementation" do
|
175
|
+
subject.inspect.should include %{last_name: "#{last_name}"}
|
176
|
+
end
|
170
177
|
end
|
171
178
|
end
|
172
179
|
|
@@ -174,42 +181,69 @@ module ActiveAttr
|
|
174
181
|
describe "##{method}" do
|
175
182
|
context "when an attribute is not set" do
|
176
183
|
it "returns nil" do
|
177
|
-
subject.send(method, :
|
184
|
+
subject.send(method, :first_name).should be_nil
|
178
185
|
end
|
179
186
|
end
|
180
187
|
|
181
188
|
context "when an attribute is set" do
|
182
|
-
let(:
|
189
|
+
let(:first_name) { "Bob" }
|
183
190
|
|
184
|
-
before { subject.write_attribute(:
|
191
|
+
before { subject.write_attribute(:first_name, first_name) }
|
185
192
|
|
186
193
|
it "returns the attribute using a Symbol" do
|
187
|
-
subject.send(method, :
|
194
|
+
subject.send(method, :first_name).should == first_name
|
188
195
|
end
|
189
196
|
|
190
197
|
it "returns the attribute using a String" do
|
191
|
-
subject.send(method, '
|
198
|
+
subject.send(method, 'first_name').should == first_name
|
192
199
|
end
|
193
200
|
end
|
201
|
+
|
202
|
+
context "when the getter is overridden" do
|
203
|
+
it "uses the overridden implementation" do
|
204
|
+
subject.send(method, :last_name).should == last_name
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
it "raises when getting an undefined attribute" do
|
209
|
+
expect do
|
210
|
+
subject.send(method, :initials)
|
211
|
+
end.to raise_error UnknownAttributeError, "unknown attribute: initials"
|
212
|
+
end
|
194
213
|
end
|
195
214
|
end
|
196
215
|
|
197
216
|
[:[]=, :write_attribute].each do |method|
|
198
217
|
describe "##{method}" do
|
199
218
|
it "raises ArgumentError with one argument" do
|
200
|
-
expect { subject.send(method, :
|
219
|
+
expect { subject.send(method, :first_name) }.to raise_error(ArgumentError)
|
201
220
|
end
|
202
221
|
|
203
222
|
it "raises ArgumentError with no arguments" do
|
204
223
|
expect { subject.send(method) }.to raise_error(ArgumentError)
|
205
224
|
end
|
206
225
|
|
207
|
-
it "
|
208
|
-
expect { subject.send(method, :
|
226
|
+
it "sets an attribute using a Symbol and value" do
|
227
|
+
expect { subject.send(method, :first_name, "Ben") }.to change { subject.attributes["first_name"] }.from(nil).to("Ben")
|
228
|
+
end
|
229
|
+
|
230
|
+
it "sets an attribute using a String and value" do
|
231
|
+
expect { subject.send(method, 'first_name', "Ben") }.to change { subject.attributes["first_name"] }.from(nil).to("Ben")
|
232
|
+
end
|
233
|
+
|
234
|
+
it "is able to set an attribute to nil" do
|
235
|
+
subject.first_name = "Ben"
|
236
|
+
expect { subject.send(method, :first_name, nil) }.to change { subject.attributes["first_name"] }.from("Ben").to(nil)
|
237
|
+
end
|
238
|
+
|
239
|
+
it "uses the overridden implementation when the setter is overridden" do
|
240
|
+
subject.send(method, :last_name, "poweski").should == "POWESKI"
|
209
241
|
end
|
210
242
|
|
211
|
-
it "
|
212
|
-
expect
|
243
|
+
it "raises when setting an undefined attribute" do
|
244
|
+
expect do
|
245
|
+
subject.send(method, :initials, "BP")
|
246
|
+
end.to raise_error UnknownAttributeError, "unknown attribute: initials"
|
213
247
|
end
|
214
248
|
end
|
215
249
|
end
|