friendly-attributes 0.7.1.1 → 0.7.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,210 @@
1
+ require 'spec_helper'
2
+
3
+ describe FriendlyAttributes::DetailsDelegator do
4
+ DetailsDelegator = FriendlyAttributes::DetailsDelegator
5
+
6
+ let(:details_delegator) { FriendlyAttributes::DetailsDelegator.new(friendly_model, ar_model, attributes, options, &initializer) }
7
+ let(:initializer) { proc {} }
8
+ let(:attributes) { {} }
9
+ let(:options) { {} }
10
+
11
+ let(:friendly_model) { mock_friendly_model }
12
+ let(:ar_model) { Class.new { include ActiveRecordFake } }
13
+
14
+ let(:ar_instance) { ar_model.new(:id => 42) }
15
+ let(:friendly_instance) { mock(friendly_model) }
16
+
17
+ describe "class methods" do
18
+ describe ".friendly_model_name" do
19
+ it { DetailsDelegator.friendly_model_name(UserDetails).should == :user_details }
20
+ end
21
+
22
+ describe ".friendly_model_ivar" do
23
+ it { DetailsDelegator.friendly_model_ivar(:user_details).should == :@user_details_ivar }
24
+ end
25
+
26
+ describe ".friendly_model_reader" do
27
+ it { DetailsDelegator.friendly_model_reader(:user_details).should == :load_user_details }
28
+ it { DetailsDelegator.friendly_model_reader(UserDetails).should == :load_user_details }
29
+ end
30
+ end
31
+
32
+ describe "initialization" do
33
+ shared_examples_for "DetailsDelegator initialization" do
34
+ context "DetailsDelegator attr_readers" do
35
+ subject { details_delegator }
36
+
37
+ before(:each) do
38
+ details_delegator
39
+ end
40
+
41
+ its(:active_record_model) { should == ar_model }
42
+ its(:friendly_model) { should == friendly_model }
43
+ its(:attributes) { should == attributes }
44
+ end
45
+
46
+ context "#setup_delegated_attributes" do
47
+ let(:attributes) do
48
+ {
49
+ String => :foo,
50
+ Integer => [:bar, :baz]
51
+ }
52
+ end
53
+
54
+ it "delegates the attributes passed in the options" do
55
+ details_delegator.should_receive(:delegated_attribute).with(:foo, String)
56
+ details_delegator.should_receive(:delegated_attribute).with(:bar, Integer)
57
+ details_delegator.should_receive(:delegated_attribute).with(:baz, Integer)
58
+ details_delegator.setup_delegated_attributes
59
+ end
60
+ end
61
+
62
+ context "the Friendly model" do
63
+ before(:each) do
64
+ details_delegator
65
+ end
66
+
67
+ it "includes Friendly::Document" do
68
+ friendly_model.ancestors.should include(Friendly::Document)
69
+ end
70
+
71
+ context "with defaults" do
72
+ it "adds the active_record_id attribute" do
73
+ friendly_model.attributes.should include(:active_record_id)
74
+ end
75
+
76
+ it "adds an index to active_record_id" do
77
+ friendly_model.storage_proxy.index_for_fields([:active_record_id]).should be_an_instance_of(Friendly::Index)
78
+ end
79
+
80
+ it "sets the active_record_key" do
81
+ friendly_model.active_record_key.should == :active_record_id
82
+ end
83
+ end
84
+
85
+ context "with options" do
86
+ let(:options) { { :active_record_key => :user_id } }
87
+
88
+ it "adds the active_record_id attribute" do
89
+ friendly_model.attributes.should include(:user_id)
90
+ end
91
+
92
+ it "adds an index to active_record_id" do
93
+ friendly_model.storage_proxy.index_for_fields([:user_id]).should be_an_instance_of(Friendly::Index)
94
+ end
95
+
96
+ it "sets the active_record_key" do
97
+ friendly_model.active_record_key.should == :user_id
98
+ end
99
+ end
100
+ end
101
+
102
+ context "the ActiveRecord model" do
103
+ it "installs the update_friendly_models callback after_save" do
104
+ ar_model.should_receive(:after_save).with(:update_friendly_models)
105
+ details_delegator
106
+ end
107
+
108
+ it "installs the destroy_friendly_models callback after_destroy" do
109
+ ar_model.should_receive(:after_destroy).with(:destroy_friendly_models)
110
+ details_delegator
111
+ end
112
+
113
+ describe "friendly model reader method" do
114
+ before(:each) do
115
+ details_delegator
116
+ end
117
+
118
+ it "is defined" do
119
+ ar_instance.should respond_to(DetailsDelegator.friendly_model_reader(friendly_model))
120
+ end
121
+
122
+ it "finds or builds and memoizes the associated Friendly model" do
123
+ ar_instance.should_receive(:find_or_build_and_memoize_details).with(friendly_model)
124
+ ar_instance.send(DetailsDelegator.friendly_model_reader(friendly_model))
125
+ end
126
+ end
127
+
128
+ describe "cattr_accessor friendly_attributes_configuration" do
129
+ context "when no Configuration exists" do
130
+ it "creates and assigns a new Configuration with the delegator added" do
131
+ details_delegator
132
+
133
+ ar_model.friendly_attributes_configuration.should be_an_instance_of(FriendlyAttributes::Configuration)
134
+ ar_model.friendly_attributes_configuration.friendly_models.should == [friendly_model]
135
+ end
136
+ end
137
+
138
+ context "when Configuration already exists" do
139
+ let(:existing_details_delegator) { FriendlyAttributes::DetailsDelegator.new(other_friendly_model, ar_model, attributes, options, &initializer) }
140
+ let(:other_friendly_model) { mock_friendly_model }
141
+
142
+ before(:each) do
143
+ existing_details_delegator
144
+ end
145
+
146
+ it "adds to the existing configuration" do
147
+ existing_configuration = ar_model.friendly_attributes_configuration
148
+
149
+ expect do
150
+ details_delegator
151
+ end.to change { ar_model.friendly_attributes_configuration.friendly_models }.
152
+ from([other_friendly_model]).
153
+ to([other_friendly_model, friendly_model])
154
+
155
+ ar_model.friendly_attributes_configuration.should == existing_configuration
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ context "missing initialization block" do
163
+ let(:details_delegator) { FriendlyAttributes::DetailsDelegator.new(friendly_model, ar_model, attributes, options) }
164
+ it_should_behave_like "DetailsDelegator initialization"
165
+ end
166
+ end
167
+
168
+ describe "#delegated_method" do
169
+ before(:each) do
170
+ details_delegator
171
+ details_delegator.delegated_method(:some_method)
172
+ ar_instance.stub(DetailsDelegator.friendly_model_reader(friendly_model) => friendly_instance)
173
+ end
174
+
175
+ it "delegates the method to Friendly model" do
176
+ bar = mock
177
+ friendly_instance.should_receive(:some_method).with(:foo).and_return(bar)
178
+ ar_instance.some_method(:foo).should == bar
179
+ end
180
+ end
181
+
182
+ describe "#delegated_attribute" do
183
+ before(:each) do
184
+ details_delegator
185
+ details_delegator.delegated_attribute(:some_attribute, String)
186
+ ar_instance.stub(DetailsDelegator.friendly_model_reader(friendly_model) => friendly_instance)
187
+ end
188
+
189
+ it "adds an attribute to the Friendly model" do
190
+ friendly_model.attributes.should include(:some_attribute)
191
+ friendly_model.attributes[:some_attribute].type.should == String
192
+ end
193
+
194
+ it "delegates the reader to Friendly model" do
195
+ bar = mock
196
+ friendly_instance.should_receive(:some_attribute).and_return(bar)
197
+ ar_instance.some_attribute.should == bar
198
+ end
199
+
200
+ it "delegates the writer to Friendly model" do
201
+ bar = mock
202
+ friendly_instance.should_receive(:some_attribute=).with(:value)
203
+ ar_instance.some_attribute = :value
204
+ end
205
+
206
+ it "adds the attribute to the configuration" do
207
+ ar_instance.friendly_attributes_configuration.attributes.should == { :some_attribute => friendly_model }
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,240 @@
1
+ require 'spec_helper'
2
+
3
+ describe FriendlyAttributes::InstanceMethods do
4
+ DetailsDelegator = FriendlyAttributes::DetailsDelegator unless defined?(DetailsDelegator)
5
+
6
+ let(:object) { klass.new.tap { |o| o.stub(:friendly_attributes_configuration => configuration) } }
7
+ let(:configuration) { mock(FriendlyAttributes::Configuration) }
8
+ let(:klass) {
9
+ Class.new {
10
+ include ActiveRecordFake
11
+ include FriendlyAttributes::InstanceMethods
12
+ }
13
+ }
14
+ let(:user_details) { double(UserDetails).as_null_object }
15
+ let(:user_second_details) { double(UserSecondDetails).as_null_object }
16
+ let(:friendly_model_instances) { [user_details, user_second_details] }
17
+
18
+ describe "reading and writing Friendly attributes" do
19
+ let(:object_details) { double(FriendlyAttributes::Base, :foo => foo, :foo= => foo) }
20
+ let(:foo) { mock("object_details.foo") }
21
+
22
+ before(:each) do
23
+ object.stub(:friendly_instance_for_attribute => object_details)
24
+ end
25
+
26
+ describe "#read_friendly_attribute" do
27
+ it "finds the FriendlyAttributes instance for the specified attribute" do
28
+ object.
29
+ should_receive(:friendly_instance_for_attribute).
30
+ with(:foo).
31
+ and_return(object_details)
32
+
33
+ object.read_friendly_attribute(:foo)
34
+ end
35
+
36
+ it "reads the attribute from the FriendlyAttributes instance" do
37
+ object_details.should_receive(:foo).and_return(foo)
38
+ object.read_friendly_attribute(:foo).should == foo
39
+ end
40
+ end
41
+
42
+ describe "#write_friendly_attribute" do
43
+ it "finds the FriendlyAttributes instance for the specified attribute" do
44
+ object.
45
+ should_receive(:friendly_instance_for_attribute).
46
+ with(:foo).
47
+ and_return(object_details)
48
+
49
+ object.write_friendly_attribute(:foo, "bar")
50
+ end
51
+
52
+ it "writes the attribute on the FriendlyAttributes instance" do
53
+ object_details.should_receive(:foo=).with("bar").and_return(foo)
54
+ object.write_friendly_attribute(:foo, "bar").should == foo
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+
61
+ describe "#friendly_instance_for_attribute" do
62
+ it "reads the value for the friendly model from the instance" do
63
+ configuration.should_receive(:model_for_attribute).with(:foo).and_return(UserDetails)
64
+ object.should_receive(DetailsDelegator.friendly_model_reader(UserDetails)).and_return(user_details)
65
+ object.friendly_instance_for_attribute(:foo).should == user_details
66
+ end
67
+ end
68
+
69
+ describe "#update_friendly_models" do
70
+ it "updates each loaded friendly model instance if needed" do
71
+ object.should_receive(:present_friendly_instances).and_return(friendly_model_instances)
72
+ friendly_model_instances.each do |instance|
73
+ instance.should_receive(:update_if_changed_with_model).with(object.id)
74
+ end
75
+ object.update_friendly_models
76
+ end
77
+ end
78
+
79
+ describe "#destroy_friendly_details" do
80
+ it "loads and destroys all associated friendly models" do
81
+ object.should_receive(:all_friendly_instances).and_return(friendly_model_instances)
82
+ friendly_model_instances.each do |instance|
83
+ instance.should_receive(:destroy).and_return(true)
84
+ end
85
+ object.destroy_friendly_models.should be_true
86
+ end
87
+ end
88
+
89
+ describe "#friendly_instance_present?" do
90
+ subject { object.friendly_instance_present?(friendly_model) }
91
+ let(:friendly_model) { UserDetails }
92
+
93
+ context "when friendly_details ivar is present" do
94
+ before(:each) do
95
+ object.instance_variable_set(DetailsDelegator.friendly_model_ivar(friendly_model), mock)
96
+ end
97
+
98
+ it { should be_true }
99
+ end
100
+
101
+ context "when friendly_details ivar is not present" do
102
+ it { should be_false }
103
+ end
104
+ end
105
+
106
+ describe "#friendly_instance_presence" do
107
+ subject { object.friendly_instance_presence(friendly_model) }
108
+ let(:friendly_model) { UserDetails }
109
+
110
+ context "when present" do
111
+ before(:each) do
112
+ object.instance_variable_set(DetailsDelegator.friendly_model_ivar(friendly_model), mock)
113
+ end
114
+
115
+ it "returns the instance using the model reader" do
116
+ object.should_receive(DetailsDelegator.friendly_model_reader(friendly_model)).and_return(user_details)
117
+ subject.should == user_details
118
+ end
119
+ end
120
+
121
+ context "when not present" do
122
+ it "returns nil" do
123
+ subject.should be_nil
124
+ end
125
+ end
126
+ end
127
+
128
+ describe "#all_friendly_instances" do
129
+ let(:friendly_models) { [UserDetails, UserSecondDetails] }
130
+
131
+ it "loads and returns all the Friendly model instances associated with the record" do
132
+ configuration.should_receive(:friendly_models).and_return(friendly_models)
133
+ object.should_receive(DetailsDelegator.friendly_model_reader(UserDetails)).and_return(user_details)
134
+ object.should_receive(DetailsDelegator.friendly_model_reader(UserSecondDetails)).and_return(user_second_details)
135
+ object.all_friendly_instances.should == [user_details, user_second_details]
136
+ end
137
+ end
138
+
139
+ describe "#present_friendly_instances" do
140
+ let(:friendly_models) { [UserDetails, UserSecondDetails] }
141
+
142
+ it "returns all the loaded Friendly model instances associated with the record" do
143
+ configuration.should_receive(:friendly_models).and_return(friendly_models)
144
+ object.should_receive(:friendly_instance_presence).with(UserDetails).and_return(user_details)
145
+ object.should_receive(:friendly_instance_presence).with(UserSecondDetails).and_return(nil)
146
+ object.present_friendly_instances.should == [user_details]
147
+ end
148
+ end
149
+
150
+ describe "#friendly_details_build_options" do
151
+ context "when friendly_details_build_options is not defined" do
152
+ it "returns and empty Hash" do
153
+ object.friendly_details_build_options.should == { }
154
+ object.friendly_details_build_options(UserDetails).should == { }
155
+ end
156
+ end
157
+
158
+ context "when friendly_details_build_options is defined" do
159
+ let(:foo) { mock("default friendly details build option") }
160
+
161
+ before(:each) do
162
+ _foo = foo
163
+
164
+ klass.class_eval do
165
+ define_method(:friendly_details_build_options) do |klass|
166
+ { :foo => _foo }
167
+ end
168
+ end
169
+ end
170
+
171
+ it "returns the options specified by friendly_details_build_options" do
172
+ object.friendly_details_build_options(UserSecondDetails).should == { :foo => foo }
173
+ end
174
+ end
175
+ end
176
+
177
+ describe "#changed?" do
178
+ subject { active_record.changed? }
179
+ let(:active_record) { User.create(:name => "Stan", :email => "stan@example.com") }
180
+
181
+ context "when the ActiveRecord model has changed," do
182
+ before(:each) do
183
+ active_record.email = "eric@example.com"
184
+ end
185
+
186
+ context "and the Friendly model has changed," do
187
+ before(:each) do
188
+ active_record.name = "Eric"
189
+ end
190
+
191
+ it { should be_true }
192
+ end
193
+
194
+ context "and the Friendly model has not changed" do
195
+ it { should be_true }
196
+ end
197
+ end
198
+
199
+ context "when the ActiveRecord model has not changed," do
200
+ context "and the Friendly model has changed" do
201
+ before(:each) do
202
+ active_record.name = "Eric"
203
+ end
204
+
205
+ it { should be_true }
206
+ end
207
+
208
+ context "and the Friendly model has not changed" do
209
+ it { should be_false }
210
+ end
211
+ end
212
+ end
213
+
214
+ describe "#find_or_build_and_memoize_details" do
215
+ let(:user) { @user }
216
+ let(:build_options) { mock(Hash) }
217
+ let(:user_details) { double(UserDetails) }
218
+
219
+ before(:each) do
220
+ @user = User.find(User.create(:name => "Stan", :email => "stan@example.com").id)
221
+ end
222
+
223
+ it "finds (or builds a new instance with build options) and memoizes the associated Friendly instance" do
224
+ user.
225
+ should_receive(:friendly_details_build_options).
226
+ with(UserDetails).
227
+ once.
228
+ and_return(build_options)
229
+
230
+ UserDetails.
231
+ should_receive(:find_or_build_by_active_record_id).
232
+ with(user.id, build_options).
233
+ once.
234
+ and_return(user_details)
235
+
236
+ user.find_or_build_and_memoize_details(UserDetails).should == user_details
237
+ user.find_or_build_and_memoize_details(UserDetails).should == user_details
238
+ end
239
+ end
240
+ end
@@ -0,0 +1,152 @@
1
+ require 'spec_helper'
2
+
3
+ describe FriendlyAttributes do
4
+ use_database_cleanup
5
+
6
+ describe "definition" do
7
+ it { User.should have_friendly_attributes(String, :name, :through => UserDetails) }
8
+ it { User.should have_friendly_attributes(Integer, :second_int, :through => UserSecondDetails) }
9
+ it { User.new.should have_friendly_attributes(Integer, :shoe_size, :birth_year, :through => UserDetails) }
10
+ it { User.new.should have_friendly_attributes(Friendly::Boolean, :subscribed, :through => UserDetails) }
11
+ it { User.should_not have_friendly_attributes(String, :foo, :through => UserDetails) }
12
+ end
13
+
14
+ describe "creating" do
15
+ context "with Friendly attributes" do
16
+ context "when using just one Friendly model" do
17
+ let(:user) { User.create!(:name => "Stan", :email => "stan@example.com", :birth_year => 1984, :subscribed => true) }
18
+
19
+ it "creates only the required Friendly model" do
20
+ expect do
21
+ expect do
22
+ user
23
+ end.to change { UserDetails.count({}) }
24
+ end.to_not change { UserSecondDetails.count({}) }
25
+ end
26
+
27
+ it "sets the appropriate attributes on the ActiveRecord model and associated Friendly model" do
28
+ user_detail = user.load_user_details
29
+ user_detail.name.should == "Stan"
30
+ user_detail.birth_year.should == 1984
31
+ user_detail.shoe_size.should == 42
32
+ user_detail.subscribed.should be_true
33
+ end
34
+ end
35
+
36
+ context "when using multiple associated Friendly models" do
37
+ let(:user) { User.create!(:name => "Stan", :email => "stan@example.com", :birth_year => 1984, :second_int => 120, :subscribed => true) }
38
+
39
+ it "creates all the required Friendly models" do
40
+ expect do
41
+ expect do
42
+ user
43
+ end.to change { UserDetails.count({}) }
44
+ end.to change { UserSecondDetails.count({}) }
45
+ end
46
+
47
+ it "sets the appropriate attributes on the ActiveRecord model and associated Friendly model" do
48
+ user_detail = user.load_user_details
49
+ user_detail.name.should == "Stan"
50
+ user_detail.birth_year.should == 1984
51
+ user_detail.shoe_size.should == 42
52
+ user_detail.subscribed.should be_true
53
+
54
+ user_second_detail = user.load_user_second_details
55
+ user_second_detail.second_int.should == 120
56
+ end
57
+ end
58
+ end
59
+
60
+ context "without Friendly attributes" do
61
+ let(:user) { User.create(:email => "stan@example.com") }
62
+
63
+ it "does not create an associated Friendly model" do
64
+ expect do
65
+ user
66
+ end.to_not change { UserDetails.count({}) }
67
+ end
68
+ end
69
+ end
70
+
71
+ describe "reading" do
72
+ let(:user) { User.create!(:name => "Stan", :email => "stan@example.com", :birth_year => 1984, :subscribed => true) }
73
+
74
+ it "through the attr_reader" do
75
+ user.name.should == "Stan"
76
+ end
77
+
78
+ it "through read_friendly_attribute" do
79
+ user.read_friendly_attribute(:name).should == "Stan"
80
+ user.read_friendly_attribute(:birth_year).should == 1984
81
+ end
82
+ end
83
+
84
+ describe "updating" do
85
+ context "with Friendly attributes" do
86
+ let(:user) { User.create(:name => "Stan", :email => "stan@example.com", :second_int => 200) }
87
+
88
+ it "updates the attributes" do
89
+ user.update_attributes(:name => "Eric", :second_int => 123)
90
+ UserDetails.first({}).name.should == "Eric"
91
+ UserSecondDetails.first({}).second_int.should == 123
92
+ end
93
+ end
94
+
95
+ context "without Friendly attributes" do
96
+ let(:user) { User.create(:email => "stan@example.com") }
97
+
98
+ it "does not create an associated Details model, if no delegated attributes are updated" do
99
+ expect do
100
+ user.update_attributes(:email => "eric@example.com")
101
+ end.to_not change { UserDetails.count({}) }
102
+ end
103
+
104
+ it "creates an associated Details model, if delegated attributes are updated" do
105
+ expect do
106
+ user.update_attributes(:name => "Eric")
107
+ end.to change { UserDetails.count({}) }
108
+ user.name.should == "Eric"
109
+ end
110
+ end
111
+
112
+ context "with nested attributes" do
113
+ let(:user) { User.create(:name => "Stan Marsh", :email => "smarsh@example.com") }
114
+ let(:parent) { Parent.create(:users => [user]) }
115
+
116
+ before(:each) do
117
+ parent
118
+ parent.update_attributes(:users_attributes => users_attributes)
119
+ user.reload
120
+ end
121
+
122
+ context "when only changing Friendly attributes" do
123
+ let(:users_attributes) { { "0" => { "id" => user.id, "name" => "Eric Cartman" } } }
124
+
125
+ it "updates Friendly attributes through nested association" do
126
+ user.name.should == "Eric Cartman"
127
+ end
128
+ end
129
+
130
+ context "when changing both Friendly attributes and ActiveRecord attributes" do
131
+ let(:users_attributes) { { "0" => { "id" => user.id, "name" => "Eric Cartman", "email" => "eric@example.com" } } }
132
+
133
+ it "updates Friendly attributes through nested association" do
134
+ user.email.should == "eric@example.com"
135
+ user.name.should == "Eric Cartman"
136
+ end
137
+ end
138
+ end
139
+ end
140
+
141
+ describe "destroying" do
142
+ let(:user) { User.create(:name => "Stan", :email => "stan@example.com") }
143
+
144
+ it "destroys the associated Details model" do
145
+ user # create it first
146
+
147
+ expect do
148
+ user.destroy
149
+ end.to change { UserDetails.count({}) }.by(-1)
150
+ end
151
+ end
152
+ end