api_resource 0.2.1
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/.document +5 -0
- data/.rspec +3 -0
- data/Gemfile +29 -0
- data/Gemfile.lock +152 -0
- data/Guardfile +22 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/api_resource.gemspec +154 -0
- data/lib/api_resource.rb +129 -0
- data/lib/api_resource/association_activation.rb +19 -0
- data/lib/api_resource/associations.rb +169 -0
- data/lib/api_resource/associations/association_proxy.rb +115 -0
- data/lib/api_resource/associations/belongs_to_remote_object_proxy.rb +16 -0
- data/lib/api_resource/associations/dynamic_resource_scope.rb +23 -0
- data/lib/api_resource/associations/has_many_remote_object_proxy.rb +16 -0
- data/lib/api_resource/associations/has_one_remote_object_proxy.rb +24 -0
- data/lib/api_resource/associations/multi_argument_resource_scope.rb +15 -0
- data/lib/api_resource/associations/multi_object_proxy.rb +73 -0
- data/lib/api_resource/associations/related_object_hash.rb +12 -0
- data/lib/api_resource/associations/relation_scope.rb +30 -0
- data/lib/api_resource/associations/resource_scope.rb +34 -0
- data/lib/api_resource/associations/scope.rb +107 -0
- data/lib/api_resource/associations/single_object_proxy.rb +81 -0
- data/lib/api_resource/attributes.rb +162 -0
- data/lib/api_resource/base.rb +587 -0
- data/lib/api_resource/callbacks.rb +49 -0
- data/lib/api_resource/connection.rb +171 -0
- data/lib/api_resource/core_extensions.rb +7 -0
- data/lib/api_resource/custom_methods.rb +119 -0
- data/lib/api_resource/exceptions.rb +87 -0
- data/lib/api_resource/formats.rb +14 -0
- data/lib/api_resource/formats/json_format.rb +25 -0
- data/lib/api_resource/formats/xml_format.rb +36 -0
- data/lib/api_resource/local.rb +12 -0
- data/lib/api_resource/log_subscriber.rb +15 -0
- data/lib/api_resource/mocks.rb +269 -0
- data/lib/api_resource/model_errors.rb +86 -0
- data/lib/api_resource/observing.rb +29 -0
- data/lib/api_resource/railtie.rb +22 -0
- data/lib/api_resource/scopes.rb +45 -0
- data/spec/lib/associations_spec.rb +656 -0
- data/spec/lib/attributes_spec.rb +121 -0
- data/spec/lib/base_spec.rb +504 -0
- data/spec/lib/callbacks_spec.rb +68 -0
- data/spec/lib/connection_spec.rb +76 -0
- data/spec/lib/local_spec.rb +20 -0
- data/spec/lib/mocks_spec.rb +28 -0
- data/spec/lib/model_errors_spec.rb +29 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/support/mocks/association_mocks.rb +46 -0
- data/spec/support/mocks/error_resource_mocks.rb +21 -0
- data/spec/support/mocks/test_resource_mocks.rb +43 -0
- data/spec/support/requests/association_requests.rb +14 -0
- data/spec/support/requests/error_resource_requests.rb +25 -0
- data/spec/support/requests/test_resource_requests.rb +31 -0
- data/spec/support/test_resource.rb +64 -0
- metadata +334 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
include ApiResource
|
4
|
+
|
5
|
+
describe "Attributes" do
|
6
|
+
|
7
|
+
after(:all) do
|
8
|
+
TestResource.reload_class_attributes
|
9
|
+
end
|
10
|
+
|
11
|
+
context "Defining, getting, and setting attributes" do
|
12
|
+
it "should be able to define known attributes" do
|
13
|
+
TestResource.define_attributes :attr1, :attr2
|
14
|
+
TestResource.attribute?(:attr1).should be_true
|
15
|
+
TestResource.attribute?(:attr2).should be_true
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should define methods for testing for reading and writing known attributes" do
|
19
|
+
TestResource.define_attributes :attr1, :attr2
|
20
|
+
tst = TestResource.new
|
21
|
+
tst.respond_to?(:attr1).should be_true
|
22
|
+
tst.respond_to?(:attr1=).should be_true
|
23
|
+
tst.respond_to?(:attr1?).should be_true
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should be able to set and change attributes" do
|
27
|
+
TestResource.define_attributes :attr1, :attr2
|
28
|
+
tst = TestResource.new
|
29
|
+
tst.attr1.should be_nil
|
30
|
+
tst.attr1?.should be_false
|
31
|
+
tst.attr1 = "test"
|
32
|
+
tst.attr1.should eql("test")
|
33
|
+
tst.attr1?.should be_true
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should be able to set multiple attributes at once" do
|
37
|
+
TestResource.define_attributes :attr1, :attr2, :attr3
|
38
|
+
tst = TestResource.new
|
39
|
+
tst.attr3 = "123"
|
40
|
+
|
41
|
+
tst.attributes = {:attr1 => "abc", :attr2 => "test"}
|
42
|
+
tst.attr1.should eql "abc"
|
43
|
+
tst.attr2.should eql "test"
|
44
|
+
tst.attr3.should eql "123"
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
context "Protected attributes" do
|
50
|
+
it "should allow protected attributes that cannot be changed" do
|
51
|
+
TestResource.define_protected_attributes :pattr3
|
52
|
+
lambda {
|
53
|
+
tst = TestResource.new
|
54
|
+
tst.pattr3 = "test"
|
55
|
+
}.should raise_error
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "Dirty tracking" do
|
60
|
+
context "Changes to attributes" do
|
61
|
+
it "should implement dirty tracking for attributes" do
|
62
|
+
TestResource.define_attributes :attr1, :attr2
|
63
|
+
tst = TestResource.new
|
64
|
+
tst.changed.should be_blank
|
65
|
+
tst.attr1 = "Hello"
|
66
|
+
tst.changed.include?("attr1").should be_true
|
67
|
+
tst.changes.should_not be_blank
|
68
|
+
|
69
|
+
# Set an attribute equal to itself
|
70
|
+
tst.attr2 = tst.attr2
|
71
|
+
tst.changes.include?("attr2").should be_false
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
context "Resetting and marking attributes current" do
|
77
|
+
|
78
|
+
before(:each) do
|
79
|
+
TestResource.define_attributes :attr1, :attr2
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should be able to mark any list of attributes as current (unchanged)" do
|
83
|
+
tst = TestResource.new
|
84
|
+
tst.attr1 = "Hello"
|
85
|
+
tst.changed.should_not be_blank
|
86
|
+
tst.set_attributes_as_current :attr1, :attr2
|
87
|
+
tst.changed.should be_blank
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should be able to mark all the attributes as current if none are given" do
|
91
|
+
tst = TestResource.new
|
92
|
+
tst.attr1 = "attr1"
|
93
|
+
tst.attr2 = "attr2"
|
94
|
+
tst.changed.should_not be_blank
|
95
|
+
tst.set_attributes_as_current
|
96
|
+
tst.changed.should be_blank
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should be able to reset any list of attributes" do
|
100
|
+
tst = TestResource.new
|
101
|
+
tst.attr1 = "attr1"
|
102
|
+
tst.reset_attribute_changes :attr1
|
103
|
+
tst.attr1.should be_nil
|
104
|
+
tst.changed.should be_blank
|
105
|
+
end
|
106
|
+
|
107
|
+
it "should be able to reset all the attributes if none are given" do
|
108
|
+
tst = TestResource.new
|
109
|
+
tst.attr1 = "attr1"
|
110
|
+
tst.attr2 = "attr2"
|
111
|
+
|
112
|
+
tst.reset_attribute_changes
|
113
|
+
tst.attr1.should be_nil
|
114
|
+
tst.attr2.should be_nil
|
115
|
+
tst.changed.should be_blank
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
@@ -0,0 +1,504 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
require 'json'
|
3
|
+
|
4
|
+
include ApiResource
|
5
|
+
|
6
|
+
describe "Base" do
|
7
|
+
|
8
|
+
after(:all) do
|
9
|
+
TestResource.reload_class_attributes
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "Loading data from a hash" do
|
13
|
+
|
14
|
+
describe "Determining Attributes, Scopes, and Associations from the server" do
|
15
|
+
|
16
|
+
it "should determine it's attributes when the class loads" do
|
17
|
+
tst = TestResource.new
|
18
|
+
tst.attribute?(:name).should be_true
|
19
|
+
tst.attribute?(:age).should be_true
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should determine it's associations when the class loads" do
|
23
|
+
tst = TestResource.new
|
24
|
+
tst.association?(:has_many_objects).should be_true
|
25
|
+
tst.association?(:belongs_to_object).should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should be able to determine scopes when the class loads" do
|
29
|
+
tst = TestResource.new
|
30
|
+
tst.scope?(:paginate).should be_true
|
31
|
+
tst.scope?(:active).should be_true
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
context "Attributes" do
|
36
|
+
before(:all) do
|
37
|
+
TestResource.define_attributes :attr1, :attr2
|
38
|
+
TestResource.define_protected_attributes :attr3
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should set attributes for the data loaded from a hash" do
|
42
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2"})
|
43
|
+
tst.attr1?.should be_true
|
44
|
+
tst.attr1.should eql("attr1")
|
45
|
+
tst.attr1 = "test"
|
46
|
+
tst.attr1.should eql("test")
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should create protected attributes for unknown attributes trying to be loaded" do
|
50
|
+
tst = TestResource.new({:attr1 => "attr1", :attr3 => "attr3"})
|
51
|
+
tst.attr3?.should be_true
|
52
|
+
tst.attr3.should eql("attr3")
|
53
|
+
lambda {
|
54
|
+
tst.attr3 = "test"
|
55
|
+
}.should raise_error
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context "Associations" do
|
60
|
+
before(:all) do
|
61
|
+
TestResource.has_many :has_many_objects
|
62
|
+
TestResource.has_one :has_one_object
|
63
|
+
TestResource.belongs_to :belongs_to_object
|
64
|
+
end
|
65
|
+
|
66
|
+
after(:all) do
|
67
|
+
TestResource.related_objects.each do |key,val|
|
68
|
+
val.clear
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "MultiObjectProxy" do
|
73
|
+
|
74
|
+
it "should create a MultiObjectProxy for has_many associations" do
|
75
|
+
tst = TestResource.new({:has_many_objects => []})
|
76
|
+
tst.has_many_objects.should be_a(Associations::MultiObjectProxy)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should throw an error if a has many association is not nil or an array or a hash" do
|
80
|
+
TestResource.new({:has_many_objects => nil})
|
81
|
+
lambda {
|
82
|
+
TestResource.new({:has_many_objects => "invalid"})
|
83
|
+
}.should raise_error
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should properly load the data from the provided array or hash" do
|
87
|
+
tst = TestResource.new({:has_many_objects => [{:service_uri => '/path'}]})
|
88
|
+
tst.has_many_objects.remote_path.should eql('/path')
|
89
|
+
tst = TestResource.new({:has_many_objects => {:service_uri => '/path'}})
|
90
|
+
tst.has_many_objects.remote_path.should eql('/path')
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
context "SingleObjectProxy" do
|
96
|
+
|
97
|
+
it "should create a SingleObjectProxy for belongs to and has_one associations" do
|
98
|
+
tst = TestResource.new(:belongs_to_object => {}, :has_one_object => {})
|
99
|
+
tst.belongs_to_object.should be_a(Associations::SingleObjectProxy)
|
100
|
+
tst.has_one_object.should be_a(Associations::SingleObjectProxy)
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should throw an error if a belongs_to or has_many association is not a hash or nil" do
|
104
|
+
lambda {
|
105
|
+
TestResource.new(:belongs_to_object => [])
|
106
|
+
}.should raise_error
|
107
|
+
lambda {
|
108
|
+
TestResource.new(:has_one_object => [])
|
109
|
+
}.should raise_error
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should properly load data from the provided hash" do
|
113
|
+
tst = TestResource.new(:has_one_object => {:service_uri => "/path"})
|
114
|
+
tst.has_one_object.remote_path.should eql('/path')
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "Request parameters and paths" do
|
122
|
+
|
123
|
+
after(:each) do
|
124
|
+
TestResource.element_name = TestResource.model_name.element
|
125
|
+
TestResource.collection_name = TestResource.element_name.to_s.pluralize
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should set the element name and collection name by default" do
|
129
|
+
TestResource.element_name.should eql("test_resource")
|
130
|
+
TestResource.collection_name.should eql("test_resources")
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should inherit element name and collection name from its parent class if using SCI" do
|
134
|
+
ChildTestResource.ancestors.should include TestResource
|
135
|
+
ChildTestResource.collection_name.should eql "test_resources"
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should be able to set the element and collection names to anything" do
|
139
|
+
TestResource.element_name = "element"
|
140
|
+
TestResource.collection_name = "elements"
|
141
|
+
TestResource.element_name.should eql("element")
|
142
|
+
TestResource.collection_name.should eql("elements")
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should propery generate collection paths and element paths with the new names and the default format json" do
|
146
|
+
TestResource.element_name = "element"
|
147
|
+
TestResource.collection_name = "elements"
|
148
|
+
TestResource.new_element_path.should eql("/elements/new.json")
|
149
|
+
TestResource.collection_path.should eql("/elements.json")
|
150
|
+
TestResource.element_path(1).should eql("/elements/1.json")
|
151
|
+
TestResource.element_path(1, :active => true).should eql("/elements/1.json?active=true")
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should be able to set the format" do
|
155
|
+
TestResource.format.extension.to_sym.should eql(:json)
|
156
|
+
TestResource.format = :xml
|
157
|
+
TestResource.format.extension.to_sym.should eql(:xml)
|
158
|
+
TestResource.format = :json
|
159
|
+
end
|
160
|
+
|
161
|
+
it "should be able to set an http timeout" do
|
162
|
+
TestResource.timeout = 5
|
163
|
+
TestResource.timeout.should eql(5)
|
164
|
+
TestResource.connection.timeout.should eql(5)
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "Serialization" do
|
170
|
+
|
171
|
+
before(:all) do
|
172
|
+
TestResource.reload_class_attributes
|
173
|
+
TestResource.has_many :has_many_objects
|
174
|
+
TestResource.define_attributes :attr1, :attr2
|
175
|
+
TestResource.include_root_in_json = true
|
176
|
+
end
|
177
|
+
|
178
|
+
before(:each) do
|
179
|
+
TestResource.include_root_in_json = false
|
180
|
+
end
|
181
|
+
|
182
|
+
context "JSON" do
|
183
|
+
|
184
|
+
it "should be able to serialize itself without the root" do
|
185
|
+
TestResource.include_root_in_json = false
|
186
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2"})
|
187
|
+
hash = JSON.parse(tst.to_json)
|
188
|
+
hash["attr1"].should eql("attr1")
|
189
|
+
hash["attr2"].should eql("attr2")
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should be able to serialize itself with the root" do
|
193
|
+
TestResource.include_root_in_json = true
|
194
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2"})
|
195
|
+
hash = JSON.parse(tst.to_json)
|
196
|
+
hash["test_resource"].should_not be_nil
|
197
|
+
end
|
198
|
+
|
199
|
+
it "should not include associations by default" do
|
200
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2", :has_many_objects => []})
|
201
|
+
hash = JSON.parse(tst.to_json)
|
202
|
+
hash["has_many_objects"].should be_nil
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should include associations passed given in the include_associations array" do
|
206
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2", :has_many_objects => []})
|
207
|
+
hash = JSON.parse(tst.to_json(:include_associations => [:has_many_objects]))
|
208
|
+
hash["has_many_objects"].should_not be_nil
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should not include unknown attributes unless they are passed in via the include_extras array" do
|
212
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2", :attr3 => "attr3"})
|
213
|
+
hash = JSON.parse(tst.to_json)
|
214
|
+
hash["attr3"].should be_nil
|
215
|
+
hash = JSON.parse(tst.to_json(:include_extras => [:attr3]))
|
216
|
+
hash["attr3"].should_not be_nil
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should ignore fields set under the except option" do
|
220
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2", :attr3 => "attr3"})
|
221
|
+
hash = JSON.parse(tst.to_json(:except => [:attr1]))
|
222
|
+
hash["attr1"].should be_nil
|
223
|
+
end
|
224
|
+
|
225
|
+
context "Nested Objects" do
|
226
|
+
before(:all) do
|
227
|
+
TestResource.has_many(:has_many_objects)
|
228
|
+
end
|
229
|
+
after(:all) do
|
230
|
+
TestResource.reload_class_attributes
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should include the id of nested objects in the serialization" do
|
234
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2", :has_many_objects => [{:name => "123", :id => "1"}]})
|
235
|
+
hash = JSON.parse(tst.to_json(:include_associations => [:has_many_objects]))
|
236
|
+
hash["has_many_objects"].first["id"].should_not be nil
|
237
|
+
end
|
238
|
+
it "should include the id of nested objects in the serialization" do
|
239
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2", :has_many_objects => [{:name => "123"}]})
|
240
|
+
hash = JSON.parse(tst.to_json(:include_associations => [:has_many_objects]))
|
241
|
+
hash["has_many_objects"].first.keys.should_not include "id"
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
context "XML" do
|
247
|
+
|
248
|
+
it "should only be able to serialize itself with the root" do
|
249
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2"})
|
250
|
+
hash = Hash.from_xml(tst.to_xml)
|
251
|
+
hash["test_resource"].should_not be_nil
|
252
|
+
end
|
253
|
+
|
254
|
+
it "should properly serialize associations if they are included" do
|
255
|
+
tst = TestResource.new({:attr1 => "attr1", :attr2 => "attr2", :has_many_objects => []})
|
256
|
+
hash = Hash.from_xml(tst.to_xml(:include_associations => [:has_many_objects]))
|
257
|
+
hash["test_resource"]["has_many_objects"].should eql([])
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
describe "Finding Data" do
|
264
|
+
|
265
|
+
before(:all) do
|
266
|
+
TestResource.reload_class_attributes
|
267
|
+
end
|
268
|
+
|
269
|
+
it "should be able to find all" do
|
270
|
+
resources = TestResource.find(:all)
|
271
|
+
resources.size.should eql(5)
|
272
|
+
resources.each{|r| r.should be_a TestResource}
|
273
|
+
end
|
274
|
+
|
275
|
+
it "should be able to find first or last" do
|
276
|
+
res = TestResource.first
|
277
|
+
res.should be_a TestResource
|
278
|
+
res.name.should_not be_blank
|
279
|
+
res.age.should_not be_blank
|
280
|
+
|
281
|
+
res = TestResource.last
|
282
|
+
res.should be_a TestResource
|
283
|
+
res.name.should_not be_blank
|
284
|
+
res.age.should_not be_blank
|
285
|
+
end
|
286
|
+
|
287
|
+
it "should be able to find by id" do
|
288
|
+
res = TestResource.find(2)
|
289
|
+
res.should be_a TestResource
|
290
|
+
res.id.to_i.should eql(2)
|
291
|
+
end
|
292
|
+
|
293
|
+
end
|
294
|
+
|
295
|
+
describe "Saving Data" do
|
296
|
+
|
297
|
+
before(:all) do
|
298
|
+
TestResource.include_root_in_json = true
|
299
|
+
TestResource.reload_class_attributes
|
300
|
+
end
|
301
|
+
|
302
|
+
context "Creating new records" do
|
303
|
+
|
304
|
+
before(:all) do
|
305
|
+
TestResource.has_many :has_many_objects
|
306
|
+
end
|
307
|
+
|
308
|
+
it "should be able to post new data via the create method" do
|
309
|
+
tr = TestResource.create({:name => "Ethan", :age => 20})
|
310
|
+
tr.id.should_not be_blank
|
311
|
+
end
|
312
|
+
|
313
|
+
it "should be able to post new data via the save method" do
|
314
|
+
tr = TestResource.build({:name => "Ethan", :age => 20})
|
315
|
+
tr.save.should be_true
|
316
|
+
tr.id.should_not be_blank
|
317
|
+
end
|
318
|
+
|
319
|
+
context("Override create to return the json") do
|
320
|
+
|
321
|
+
before(:all) do
|
322
|
+
TestResource.send(:alias_method, :old_create, :create)
|
323
|
+
TestResource.send(:alias_method, :old_save, :save)
|
324
|
+
|
325
|
+
TestResource.send(:define_method, :create) do |*args|
|
326
|
+
opts = args.extract_options!
|
327
|
+
# When we create we should not include any blank attributes unless they are associations
|
328
|
+
except = self.class.include_blank_attributes_on_create ? {} : self.attributes.select{|k,v| v.blank?}
|
329
|
+
opts[:except] = opts[:except] ? opts[:except].concat(except.keys).uniq.symbolize_array : except.keys.symbolize_array
|
330
|
+
opts[:include_associations] = opts[:include_associations] ? opts[:include_associations].concat(args) : []
|
331
|
+
opts[:include_extras] ||= []
|
332
|
+
encode(opts)
|
333
|
+
end
|
334
|
+
TestResource.send(:define_method, :save) do |*args|
|
335
|
+
new? ? create(*args) : update(*args)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
after(:all) do
|
340
|
+
TestResource.send(:alias_method, :create, :old_create)
|
341
|
+
TestResource.send(:alias_method, :save, :old_save)
|
342
|
+
end
|
343
|
+
|
344
|
+
it "should be able to include associations when saving if they are specified" do
|
345
|
+
tr = TestResource.build(:name => "Ethan", :age => 20)
|
346
|
+
hash = JSON.parse(tr.save)
|
347
|
+
hash['test_resource']['has_many_objects'].should be_nil
|
348
|
+
hash = JSON.parse(tr.save(:include_associations => [:has_many_objects]))
|
349
|
+
hash['test_resource']['has_many_objects'].should eql([])
|
350
|
+
end
|
351
|
+
|
352
|
+
it "should not include nil attributes when creating by default" do
|
353
|
+
tr = TestResource.build(:name => "Ethan")
|
354
|
+
hash = JSON.parse(tr.save)
|
355
|
+
hash['test_resource']['age'].should be_nil
|
356
|
+
hash['test_resource']['name'].should eql("Ethan")
|
357
|
+
end
|
358
|
+
|
359
|
+
it "should include nil attributes if they are passed in through the include_extras" do
|
360
|
+
tr = TestResource.build(:name => "Ethan")
|
361
|
+
hash = JSON.parse(tr.save(:include_extras => [:age]))
|
362
|
+
hash['test_resource'].key?('age').should be_true
|
363
|
+
end
|
364
|
+
|
365
|
+
it "should include nil attributes when creating if include_nil_attributes_on_create is true" do
|
366
|
+
TestResource.include_blank_attributes_on_create = true
|
367
|
+
tr = TestResource.build(:name => "Ethan")
|
368
|
+
hash = JSON.parse(tr.save)
|
369
|
+
hash['test_resource'].key?('age').should be_true
|
370
|
+
TestResource.include_blank_attributes_on_create = false
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
context "Updating old records" do
|
376
|
+
before(:all) do
|
377
|
+
TestResource.reload_class_attributes
|
378
|
+
TestResource.has_many :has_many_objects
|
379
|
+
|
380
|
+
TestResource.send(:alias_method, :old_update, :update)
|
381
|
+
TestResource.send(:alias_method, :old_save, :save)
|
382
|
+
|
383
|
+
TestResource.send(:define_method, :update) do |*args|
|
384
|
+
opts = args.extract_options!
|
385
|
+
# When we create we should not include any blank attributes
|
386
|
+
except = self.class.attribute_names - self.changed.symbolize_array
|
387
|
+
changed_associations = self.changed.symbolize_array.select{|item| self.association?(item)}
|
388
|
+
opts[:except] = opts[:except] ? opts[:except].concat(except).uniq.symbolize_array : except.symbolize_array
|
389
|
+
opts[:include_associations] = opts[:include_associations] ? opts[:include_associations].concat(args).concat(changed_associations).uniq : changed_associations.concat(args)
|
390
|
+
opts[:include_extras] ||= []
|
391
|
+
opts[:except] = [:id] if self.class.include_all_attributes_on_update
|
392
|
+
encode(opts)
|
393
|
+
end
|
394
|
+
TestResource.send(:define_method, :save) do |*args|
|
395
|
+
new? ? create(*args) : update(*args)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
after(:all) do
|
400
|
+
TestResource.send(:alias_method, :update, :old_update)
|
401
|
+
TestResource.send(:alias_method, :save, :old_save)
|
402
|
+
end
|
403
|
+
|
404
|
+
it "should be able to put updated data via the update method" do
|
405
|
+
tr = TestResource.new(:id => 1, :name => "Ethan")
|
406
|
+
tr.should_not be_new
|
407
|
+
# Thus we know we are calling update
|
408
|
+
tr.age = 6
|
409
|
+
hash = JSON.parse(tr.update)
|
410
|
+
hash['test_resource']['age'].should eql(6)
|
411
|
+
|
412
|
+
hash = JSON.parse(tr.save)
|
413
|
+
hash['test_resource']['age'].should eql(6)
|
414
|
+
end
|
415
|
+
|
416
|
+
it "should only include changed attributes when updating" do
|
417
|
+
tr = TestResource.new(:id => 1, :name => "Ethan")
|
418
|
+
tr.should_not be_new
|
419
|
+
# Thus we know we are calling update
|
420
|
+
tr.age = 6
|
421
|
+
hash = JSON.parse(tr.save)
|
422
|
+
hash['test_resource']['name'].should be_nil
|
423
|
+
end
|
424
|
+
|
425
|
+
it "should include changed associations without specification" do
|
426
|
+
tr = TestResource.new(:id => 1, :name => "Ethan")
|
427
|
+
tr.has_many_objects = [HasManyObject.new]
|
428
|
+
hash = JSON.parse(tr.save)
|
429
|
+
hash['test_resource']['has_many_objects'].should_not be_blank
|
430
|
+
end
|
431
|
+
|
432
|
+
it "should include unchanged associations if they are specified" do
|
433
|
+
tr = TestResource.new(:id => 1, :name => "Ethan")
|
434
|
+
hash = JSON.parse(tr.save(:has_many_objects))
|
435
|
+
hash['test_resource']['has_many_objects'].should eql([])
|
436
|
+
end
|
437
|
+
|
438
|
+
it "should include all attributes if include_all_attributes_on_update is true" do
|
439
|
+
TestResource.include_all_attributes_on_update = true
|
440
|
+
tr = TestResource.new(:id => 1, :name => "Ethan")
|
441
|
+
hash = JSON.parse(tr.save)
|
442
|
+
hash['test_resource']['name'].should eql("Ethan")
|
443
|
+
hash['test_resource'].key?('age').should be_true
|
444
|
+
TestResource.include_all_attributes_on_update = false
|
445
|
+
end
|
446
|
+
|
447
|
+
it "should provide an update_attributes method to set attrs and save" do
|
448
|
+
|
449
|
+
tr = TestResource.new(:id => 1, :name => "Ethan")
|
450
|
+
hash = JSON.parse(tr.update_attributes(:name => "Dan"))
|
451
|
+
hash['test_resource']['name'].should eql "Dan"
|
452
|
+
|
453
|
+
end
|
454
|
+
|
455
|
+
|
456
|
+
end
|
457
|
+
|
458
|
+
end
|
459
|
+
|
460
|
+
describe "Deleting data" do
|
461
|
+
it "should be able to delete an id from the class method" do
|
462
|
+
TestResource.delete(1).should be_true
|
463
|
+
end
|
464
|
+
|
465
|
+
it "should be able to destroy itself as an instance" do
|
466
|
+
tr = TestResource.new(:id => 1, :name => "Ethan")
|
467
|
+
tr.destroy.should be_true
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
describe "Random methods" do
|
472
|
+
|
473
|
+
it "should know if it is persisted" do
|
474
|
+
tr = TestResource.new(:id => 1, :name => "Ethan")
|
475
|
+
tr.persisted?.should be_true
|
476
|
+
tr = TestResource.new(:name => "Ethan")
|
477
|
+
tr.persisted?.should be_false
|
478
|
+
end
|
479
|
+
|
480
|
+
end
|
481
|
+
|
482
|
+
describe "Inheritable Accessors" do
|
483
|
+
|
484
|
+
it "should copy the default values down to any level of subclass" do
|
485
|
+
|
486
|
+
class Child < TestResource
|
487
|
+
end
|
488
|
+
|
489
|
+
Child.site.should eql(TestResource.site)
|
490
|
+
Child.site.should_not be_blank
|
491
|
+
end
|
492
|
+
|
493
|
+
end
|
494
|
+
|
495
|
+
describe "Inflections" do
|
496
|
+
|
497
|
+
it "should be able to singularize and pluralize words ending in ess" do
|
498
|
+
"address".singularize.should eql("address")
|
499
|
+
"address".pluralize.should eql("addresses")
|
500
|
+
end
|
501
|
+
|
502
|
+
end
|
503
|
+
|
504
|
+
end
|