active_attr 0.2.2 → 0.3.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.
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
|