api_resource 0.6.3 → 0.6.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +2 -1
- data/Gemfile.lock +1 -1
- data/api_resource.gemspec +1 -1
- data/lib/api_resource/association_activation.rb +5 -1
- data/lib/api_resource/associations.rb +21 -54
- data/lib/api_resource/associations/association_proxy.rb +57 -4
- data/lib/api_resource/associations/belongs_to_remote_object_proxy.rb +1 -0
- data/lib/api_resource/associations/has_many_remote_object_proxy.rb +38 -5
- data/lib/api_resource/associations/has_one_remote_object_proxy.rb +25 -6
- data/lib/api_resource/associations/multi_object_proxy.rb +17 -9
- data/lib/api_resource/base.rb +8 -1
- data/lib/api_resource/conditions/abstract_condition.rb +1 -1
- data/lib/api_resource/conditions/association_condition.rb +1 -1
- data/lib/api_resource/conditions/multi_object_association_condition.rb +3 -1
- data/lib/api_resource/version.rb +1 -1
- data/spec/lib/associations/has_many_remote_object_proxy_spec.rb +21 -0
- data/spec/lib/associations_spec.rb +61 -20
- data/spec/lib/base_spec.rb +65 -9
- data/spec/spec_helper.rb +2 -2
- data/spec/support/mocks/association_mocks.rb +14 -0
- data/spec/support/requests/test_resource_requests.rb +9 -1
- metadata +4 -18
data/.rspec
CHANGED
data/Gemfile.lock
CHANGED
data/api_resource.gemspec
CHANGED
@@ -31,7 +31,7 @@ Gem::Specification.new do |gem|
|
|
31
31
|
gem.add_development_dependency "flog"
|
32
32
|
gem.add_development_dependency "hash_dealer"
|
33
33
|
gem.add_development_dependency "rb-fsevent"
|
34
|
-
gem.add_development_dependency "ruby-debug19"
|
34
|
+
# gem.add_development_dependency "ruby-debug19"
|
35
35
|
gem.add_development_dependency "simplecov"
|
36
36
|
# stuff that seems like crap
|
37
37
|
gem.add_development_dependency "sqlite3"
|
@@ -5,7 +5,11 @@ module ApiResource
|
|
5
5
|
included do
|
6
6
|
class_attribute :association_types
|
7
7
|
# our default association types
|
8
|
-
self.association_types = {
|
8
|
+
self.association_types = {
|
9
|
+
:belongs_to => :belongs_to_remote,
|
10
|
+
:has_one => :has_one_remote,
|
11
|
+
:has_many => :has_many_remote
|
12
|
+
}
|
9
13
|
end
|
10
14
|
|
11
15
|
module ClassMethods
|
@@ -90,7 +90,7 @@ module ApiResource
|
|
90
90
|
klass.related_objects = klass.related_objects.merge(:#{assoc} => klass.related_objects[:#{assoc}].merge(arg.to_sym => klass_name))
|
91
91
|
end
|
92
92
|
# We need to define reader and writer methods here
|
93
|
-
define_association_as_attribute(:#{assoc}, arg)
|
93
|
+
define_association_as_attribute(:#{assoc}, arg, options)
|
94
94
|
end
|
95
95
|
end
|
96
96
|
|
@@ -184,8 +184,10 @@ module ApiResource
|
|
184
184
|
self.related_objects[:scopes] = self.related_objects[:scopes].clone
|
185
185
|
end
|
186
186
|
|
187
|
-
def define_association_as_attribute(assoc_type, assoc_name)
|
188
|
-
id_method_name = association_foreign_key_field(
|
187
|
+
def define_association_as_attribute(assoc_type, assoc_name, opts)
|
188
|
+
id_method_name = association_foreign_key_field(
|
189
|
+
assoc_name, assoc_type
|
190
|
+
)
|
189
191
|
|
190
192
|
# set up dirty tracking for associations, but only for ApiResource
|
191
193
|
# these methods are also used for ActiveRecord
|
@@ -194,59 +196,24 @@ module ApiResource
|
|
194
196
|
define_attribute_method(assoc_name)
|
195
197
|
define_attribute_method(id_method_name)
|
196
198
|
end
|
197
|
-
|
198
|
-
#
|
199
|
-
|
200
|
-
|
201
|
-
#
|
202
|
-
|
203
|
-
self.class_eval <<-EOE, __FILE__, __LINE__ + 1
|
204
|
-
def #{assoc_name}
|
205
|
-
@attributes_cache[:#{assoc_name}] ||= begin
|
206
|
-
klass = Associations::#{self.association_types[assoc_type.to_sym].to_s.classify}ObjectProxy
|
207
|
-
instance = klass.new(
|
208
|
-
self.association_class('#{assoc_name}'), self
|
209
|
-
)
|
210
|
-
if @attributes[:#{assoc_name}].present?
|
211
|
-
instance.internal_object = @attributes[:#{assoc_name}]
|
212
|
-
end
|
213
|
-
instance
|
214
|
-
end
|
215
|
-
end
|
216
|
-
def #{assoc_name}=(val, force = true)
|
217
|
-
if !force
|
218
|
-
#{assoc_name}_will_change!
|
219
|
-
elsif self.#{assoc_name}.internal_object != val
|
220
|
-
#{assoc_name}_will_change!
|
221
|
-
end
|
222
|
-
# This should not force a load
|
223
|
-
self.#{assoc_name}.internal_object = val
|
224
|
-
end
|
225
|
-
def #{assoc_name}?
|
226
|
-
self.#{assoc_name}.internal_object.present?
|
227
|
-
end
|
228
|
-
|
229
|
-
def #{id_method_name}
|
230
|
-
@attributes_cache[:#{id_method_name}] ||= begin
|
231
|
-
if @attributes.has_key?("#{id_method_name}")
|
232
|
-
@attributes["#{id_method_name}"]
|
233
|
-
elsif self.#{assoc_name}.collection?
|
234
|
-
self.#{assoc_name}.collect(&:id)
|
235
|
-
else
|
236
|
-
self.#{assoc_name}? ? self.#{assoc_name}.id : nil
|
237
|
-
end
|
238
|
-
end
|
239
|
-
end
|
199
|
+
|
200
|
+
# a module to contain our generated methods
|
201
|
+
cattr_accessor :api_resource_generated_methods
|
202
|
+
self.api_resource_generated_methods = Module.new
|
203
|
+
# include our anonymous module
|
204
|
+
include self.api_resource_generated_methods
|
240
205
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
@attributes_cache[:#{id_method_name}] = val
|
246
|
-
write_attribute(:#{id_method_name}, val)
|
247
|
-
end
|
206
|
+
# we let our concrete classes define this behavior
|
207
|
+
type = self.association_types[assoc_type.to_sym].to_s.classify
|
208
|
+
klass = "::ApiResource::Associations::#{type}ObjectProxy"
|
209
|
+
klass = klass.constantize
|
248
210
|
|
249
|
-
|
211
|
+
# gives us the namespaced classname
|
212
|
+
opts[:class_name] = self.find_namespaced_class_name(
|
213
|
+
opts[:class_name] || assoc_name.to_s.classify
|
214
|
+
)
|
215
|
+
|
216
|
+
klass.define_association_as_attribute(self, assoc_name, opts)
|
250
217
|
end
|
251
218
|
|
252
219
|
def find_namespaced_class_name(klass)
|
@@ -12,10 +12,63 @@ module ApiResource
|
|
12
12
|
attr_reader :owner, :klass, :finder_opts
|
13
13
|
|
14
14
|
# TODO: add the other load forcing methods here for collections
|
15
|
-
delegate :[], :[]=, :<<, :first, :second, :last, :blank?, :nil?,
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
delegate :[], :[]=, :<<, :first, :second, :last, :blank?, :nil?,
|
16
|
+
:include?, :push, :pop, :+, :concat, :flatten, :flatten!, :compact,
|
17
|
+
:compact!, :empty?, :fetch, :map, :reject, :reject!, :reverse,
|
18
|
+
:select, :select!, :size, :sort, :sort!, :uniq, :uniq!, :to_a,
|
19
|
+
:sample, :slice, :slice!, :count, :present?,
|
20
|
+
:to => :internal_object
|
21
|
+
|
22
|
+
# define association methods on the class
|
23
|
+
def self.define_association_as_attribute(klass, assoc_name, opts = {})
|
24
|
+
|
25
|
+
id_method_name = self.foreign_key_name(assoc_name)
|
26
|
+
associated_class = opts[:class_name] || assoc_name.to_s.classify
|
27
|
+
|
28
|
+
klass.api_resource_generated_methods.module_eval <<-EOE, __FILE__, __LINE__ + 1
|
29
|
+
def #{assoc_name}
|
30
|
+
@attributes_cache[:#{assoc_name}] ||= begin
|
31
|
+
instance = #{self}.new(#{associated_class}, self)
|
32
|
+
if @attributes[:#{assoc_name}].present?
|
33
|
+
instance.internal_object = @attributes[:#{assoc_name}]
|
34
|
+
end
|
35
|
+
instance
|
36
|
+
end
|
37
|
+
end
|
38
|
+
def #{assoc_name}=(val, force = true)
|
39
|
+
if !force
|
40
|
+
#{assoc_name}_will_change!
|
41
|
+
elsif self.#{assoc_name}.internal_object != val
|
42
|
+
#{assoc_name}_will_change!
|
43
|
+
end
|
44
|
+
# This should not force a load
|
45
|
+
self.#{assoc_name}.internal_object = val
|
46
|
+
end
|
47
|
+
|
48
|
+
def #{assoc_name}?
|
49
|
+
self.#{assoc_name}.internal_object.present?
|
50
|
+
end
|
51
|
+
|
52
|
+
# writer is the same for everyone
|
53
|
+
def #{id_method_name}=(val, force = false)
|
54
|
+
unless @attributes_cache[:#{id_method_name}] == val
|
55
|
+
#{id_method_name}_will_change!
|
56
|
+
end
|
57
|
+
@attributes_cache[:#{id_method_name}] = val
|
58
|
+
write_attribute(:#{id_method_name}, val)
|
59
|
+
end
|
60
|
+
|
61
|
+
EOE
|
62
|
+
end
|
63
|
+
|
64
|
+
protected
|
65
|
+
|
66
|
+
# return a foreign key name from an association
|
67
|
+
def self.foreign_key_name(assoc_name)
|
68
|
+
assoc_name.to_s.singularize.foreign_key
|
69
|
+
end
|
70
|
+
|
71
|
+
public
|
19
72
|
|
20
73
|
def initialize(klass, owner, opts = {})
|
21
74
|
|
@@ -1,11 +1,44 @@
|
|
1
1
|
module ApiResource
|
2
2
|
module Associations
|
3
3
|
class HasManyRemoteObjectProxy < MultiObjectProxy
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
|
5
|
+
# defines a method to get the foreign key
|
6
|
+
def self.define_association_as_attribute(klass, assoc_name, opts = {})
|
7
|
+
id_method_name = self.foreign_key_name(assoc_name)
|
8
|
+
|
9
|
+
klass.api_resource_generated_methods.module_eval <<-EOE, __FILE__, __LINE__ + 1
|
10
|
+
|
11
|
+
def #{id_method_name}
|
12
|
+
@attributes_cache[:#{id_method_name}] ||= begin
|
13
|
+
# check our attributes first, then go to the remote
|
14
|
+
@attributes[:#{id_method_name}] || self.#{assoc_name}.collect(
|
15
|
+
&:id
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
EOE
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
# gets the foreign key name for a given association
|
25
|
+
# e.g. service_ids
|
26
|
+
def self.foreign_key_name(assoc_name)
|
27
|
+
super(assoc_name).pluralize
|
28
|
+
end
|
29
|
+
|
30
|
+
public
|
31
|
+
|
32
|
+
def internal_object
|
33
|
+
# if we don't have a remote path and we do have and id,
|
34
|
+
# we set it before we call the internal object
|
35
|
+
# this lets us dynamically generate the correct path
|
36
|
+
if self.remote_path.blank? && self.owner.try(:id).present?
|
37
|
+
self.remote_path = self.klass.collection_path(
|
38
|
+
self.owner.class.to_s.foreign_key => self.owner.id
|
39
|
+
)
|
40
|
+
end
|
41
|
+
super
|
9
42
|
end
|
10
43
|
end
|
11
44
|
end
|
@@ -2,18 +2,37 @@ module ApiResource
|
|
2
2
|
module Associations
|
3
3
|
class HasOneRemoteObjectProxy < SingleObjectProxy
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
# defines a method to get the id of the associated objecgt
|
6
|
+
def self.define_association_as_attribute(klass, assoc_name, opts = {})
|
7
|
+
id_method_name = self.foreign_key_name(assoc_name)
|
8
|
+
|
9
|
+
klass.api_resource_generated_methods.module_eval <<-EOE, __FILE__, __LINE__ + 1
|
10
|
+
def #{id_method_name}
|
11
|
+
self.#{assoc_name}? ? self.#{assoc_name}.id : nil
|
12
|
+
end
|
13
|
+
EOE
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def internal_object
|
18
|
+
# if we don't have a remote path and we do have and id,
|
19
|
+
# we set it before we call the internal object
|
20
|
+
# this lets us dynamically generate the correct path
|
21
|
+
if self.remote_path.blank? && self.owner.try(:id).present?
|
22
|
+
self.remote_path = self.klass.collection_path(
|
23
|
+
self.owner.class.to_s.foreign_key => self.owner.id
|
24
|
+
)
|
25
|
+
end
|
26
|
+
super
|
10
27
|
end
|
11
28
|
|
12
29
|
protected
|
13
30
|
|
14
31
|
# because of how this works we use a multi object proxy and return the first element
|
15
32
|
def to_condition
|
16
|
-
ApiResource::Conditions::MultiObjectAssociationCondition.new(
|
33
|
+
ApiResource::Conditions::MultiObjectAssociationCondition.new(
|
34
|
+
self.klass, self.remote_path
|
35
|
+
)
|
17
36
|
end
|
18
37
|
|
19
38
|
def load(opts = {})
|
@@ -25,13 +25,18 @@ module ApiResource
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def internal_object
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
end
|
28
|
+
|
29
|
+
# if we don't have a remote path or any data so we set it to a
|
30
|
+
# blank array
|
31
|
+
if self.remote_path.blank? && @internal_object.blank?
|
32
|
+
return @internal_object ||= []
|
34
33
|
end
|
34
|
+
|
35
|
+
# if we aren't loaded and we don't have data added load here
|
36
|
+
if !self.loaded? && @internal_object.blank?
|
37
|
+
@internal_object = self.load
|
38
|
+
end
|
39
|
+
@internal_object
|
35
40
|
end
|
36
41
|
|
37
42
|
def internal_object=(contents)
|
@@ -52,9 +57,10 @@ module ApiResource
|
|
52
57
|
return @internal_object = self.klass.instantiate_collection(
|
53
58
|
contents
|
54
59
|
)
|
55
|
-
# we have only provided the resource definition
|
60
|
+
# we have only provided the resource definition - that's the same
|
61
|
+
# as a blank array in this case
|
56
62
|
elsif contents.nil?
|
57
|
-
return @internal_object =
|
63
|
+
return @internal_object = []
|
58
64
|
else
|
59
65
|
raise ArgumentError.new(
|
60
66
|
"#{contents} must be a #{self.klass}, #{self.class}, " +
|
@@ -82,7 +88,9 @@ module ApiResource
|
|
82
88
|
def to_condition
|
83
89
|
obj = nil
|
84
90
|
obj = self.internal_object if self.loaded?
|
85
|
-
ApiResource::Conditions::MultiObjectAssociationCondition.new(
|
91
|
+
ApiResource::Conditions::MultiObjectAssociationCondition.new(
|
92
|
+
self.klass, self.remote_path, obj
|
93
|
+
)
|
86
94
|
end
|
87
95
|
|
88
96
|
def load(opts = {})
|
data/lib/api_resource/base.rb
CHANGED
@@ -558,8 +558,15 @@ module ApiResource
|
|
558
558
|
|
559
559
|
# also add in the _id fields that are changed
|
560
560
|
ret = self.association_names.inject(ret) do |accum, assoc_name|
|
561
|
+
|
562
|
+
# get the id method for the association
|
561
563
|
id_method = self.class.association_foreign_key_field(assoc_name)
|
562
|
-
|
564
|
+
|
565
|
+
# only do this if they are not prefix_attribute_names
|
566
|
+
# and we have changes
|
567
|
+
if !self.prefix_attribute_names.include?(id_method.to_sym) &&
|
568
|
+
self.changes[id_method].present?
|
569
|
+
|
563
570
|
accum[id_method] = self.changes[id_method].last
|
564
571
|
end
|
565
572
|
accum
|
@@ -7,7 +7,9 @@ module ApiResource
|
|
7
7
|
protected
|
8
8
|
|
9
9
|
def instantiate_finder
|
10
|
-
ApiResource::Finders::MultiObjectAssociationFinder.new(
|
10
|
+
ApiResource::Finders::MultiObjectAssociationFinder.new(
|
11
|
+
self.klass, self, @internal_object
|
12
|
+
)
|
11
13
|
end
|
12
14
|
|
13
15
|
end
|
data/lib/api_resource/version.rb
CHANGED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module ApiResource
|
4
|
+
module Associations
|
5
|
+
|
6
|
+
describe HasManyRemoteObjectProxy do
|
7
|
+
|
8
|
+
context "#<<" do
|
9
|
+
|
10
|
+
it "implements the shift operator" do
|
11
|
+
tr = TestResource.new
|
12
|
+
tr.has_many_objects << HasManyObject.new
|
13
|
+
|
14
|
+
tr.has_many_objects.length.should be 1
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -360,17 +360,17 @@ describe "Associations" do
|
|
360
360
|
end
|
361
361
|
|
362
362
|
it "should include the foreign_key_id when saving" do
|
363
|
-
tr = TestResource.new
|
363
|
+
tr = TestResource.new.tap do |tr|
|
364
|
+
tr.stubs(:id => 123)
|
365
|
+
end
|
364
366
|
tr.has_many_object_ids = [4]
|
365
367
|
hsh = tr.serializable_hash
|
366
368
|
hsh[:has_many_object_ids].should eql([4])
|
367
369
|
end
|
368
370
|
|
369
|
-
it "should
|
370
|
-
tr = TestResource.
|
371
|
-
tr.has_many_object_ids
|
372
|
-
hsh = tr.serializable_hash
|
373
|
-
hsh[:has_many_object_ids].should eql([5])
|
371
|
+
it "should handle loading attributes from the remote" do
|
372
|
+
tr = TestResource.instantiate_record({:has_many_object_ids => [3]})
|
373
|
+
tr.has_many_object_ids.should eql([3])
|
374
374
|
end
|
375
375
|
|
376
376
|
it "should not try to load if the foreign key is nil" do
|
@@ -682,17 +682,38 @@ describe "Associations" do
|
|
682
682
|
before(:all) do
|
683
683
|
TestResource.has_one(:has_one_object)
|
684
684
|
TestResource.belongs_to(:belongs_to_object)
|
685
|
+
HasManyObject.reload_class_attributes
|
686
|
+
BelongsToObject.reload_class_attributes
|
687
|
+
HasOneObject.reload_class_attributes
|
685
688
|
end
|
686
689
|
after(:all) do
|
687
690
|
TestResource.reload_class_attributes
|
688
691
|
end
|
689
692
|
|
690
|
-
it "should assign associations to the correct
|
691
|
-
|
692
|
-
|
693
|
+
it "should assign associations to the correct
|
694
|
+
type on initialization" do
|
695
|
+
|
696
|
+
tr = TestResource.new(
|
697
|
+
:has_one_object => {:color => "Blue"},
|
698
|
+
:belongs_to_object => {:zip => "11201"},
|
699
|
+
:has_many_objects => [{:name => "Dan"}]
|
700
|
+
)
|
701
|
+
|
702
|
+
tr.has_one_object.internal_object.should be_instance_of(
|
703
|
+
HasOneObject
|
704
|
+
)
|
705
|
+
tr.has_one_object.color.should eql("Blue")
|
706
|
+
|
707
|
+
tr.belongs_to_object.internal_object.should be_instance_of(
|
708
|
+
BelongsToObject
|
709
|
+
)
|
710
|
+
tr.belongs_to_object.zip.should eql("11201")
|
693
711
|
|
694
|
-
|
695
|
-
tr.
|
712
|
+
|
713
|
+
tr.has_many_objects.internal_object.first.should be_instance_of(
|
714
|
+
HasManyObject
|
715
|
+
)
|
716
|
+
tr.has_many_objects.first.name.should eql("Dan")
|
696
717
|
|
697
718
|
end
|
698
719
|
|
@@ -706,15 +727,12 @@ describe "Associations" do
|
|
706
727
|
end
|
707
728
|
|
708
729
|
it "should be able to reload a single-object association" do
|
709
|
-
|
710
|
-
HasOneObject.connection.stubs(:get => nil)
|
711
|
-
|
730
|
+
|
712
731
|
tr = TestResource.new()
|
713
|
-
|
714
732
|
tr.has_one_object = {:color => "Blue"}
|
715
733
|
|
716
734
|
tr.has_one_object.reload
|
717
|
-
tr.has_one_object.
|
735
|
+
tr.has_one_object.should be_nil
|
718
736
|
end
|
719
737
|
|
720
738
|
end
|
@@ -740,16 +758,38 @@ describe "Associations" do
|
|
740
758
|
end
|
741
759
|
|
742
760
|
it "should be able to reload a multi-object association" do
|
743
|
-
|
744
|
-
|
761
|
+
|
762
|
+
# do this to load the resource definition
|
763
|
+
TestResource.reload_resource_definition
|
764
|
+
HasManyObject.reload_resource_definition
|
745
765
|
|
746
|
-
tr = TestResource.new
|
766
|
+
tr = TestResource.new
|
767
|
+
tr.has_many_objects = [{:name => "Dan"}]
|
747
768
|
|
748
769
|
tr.has_many_objects.reload
|
749
|
-
|
750
770
|
tr.has_many_objects.should be_blank
|
751
771
|
end
|
752
772
|
|
773
|
+
it "should be able to override service_uri for a
|
774
|
+
multi-object association" do
|
775
|
+
|
776
|
+
tr = TestResource.new
|
777
|
+
tr.has_many_objects = [{:service_uri => "/a/b/c"}]
|
778
|
+
|
779
|
+
tr.has_many_objects.remote_path.should eql("/a/b/c")
|
780
|
+
|
781
|
+
end
|
782
|
+
|
783
|
+
it "should be able to override service_uri for a multi-object
|
784
|
+
association when loaded with instantiate_record" do
|
785
|
+
|
786
|
+
tr = TestResource.instantiate_record(
|
787
|
+
:has_many_objects => [{:service_uri => "/a/b/c"}]
|
788
|
+
)
|
789
|
+
|
790
|
+
tr.has_many_objects.remote_path.should eql("/a/b/c")
|
791
|
+
end
|
792
|
+
|
753
793
|
end
|
754
794
|
|
755
795
|
context "ActiveModel" do
|
@@ -853,6 +893,7 @@ describe "Associations" do
|
|
853
893
|
[{"name" => "testing", "id" => 22}]
|
854
894
|
)
|
855
895
|
# load the test resource
|
896
|
+
tar.has_many_objects.internal_object
|
856
897
|
tar.has_many_objects.first.name.should eql "testing"
|
857
898
|
tar.has_many_object_ids.should eql([22])
|
858
899
|
end
|
data/spec/lib/base_spec.rb
CHANGED
@@ -61,6 +61,40 @@ describe "Base" do
|
|
61
61
|
|
62
62
|
end
|
63
63
|
|
64
|
+
|
65
|
+
context "Prefixes" do
|
66
|
+
|
67
|
+
before(:all) do
|
68
|
+
TestResource.prefix = "/belongs_to_objects/:belongs_to_object_id/"
|
69
|
+
end
|
70
|
+
|
71
|
+
after(:all) do
|
72
|
+
TestResource.prefix = "/"
|
73
|
+
end
|
74
|
+
|
75
|
+
context "#create" do
|
76
|
+
|
77
|
+
it "should place prefix data in the URL and remove it from
|
78
|
+
the parameters" do
|
79
|
+
|
80
|
+
TestResource.connection.expects(:post).with(
|
81
|
+
"/belongs_to_objects/22/test_resources.json", JSON.unparse(
|
82
|
+
:test_resource => {
|
83
|
+
:name => "Dan"
|
84
|
+
}
|
85
|
+
),
|
86
|
+
TestResource.headers
|
87
|
+
)
|
88
|
+
|
89
|
+
TestResource.create(:belongs_to_object_id => 22, :name => "Dan")
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
|
64
98
|
context "Comparison" do
|
65
99
|
|
66
100
|
context "&group_by" do
|
@@ -521,8 +555,17 @@ describe "Base" do
|
|
521
555
|
|
522
556
|
it "should include nil attributes when creating if include_nil_attributes_on_create is true" do
|
523
557
|
ApiResource::Connection.any_instance.expects(:post).with(
|
524
|
-
"/test_resources.json",
|
525
|
-
|
558
|
+
"/test_resources.json", JSON.unparse(
|
559
|
+
:test_resource => {
|
560
|
+
:name => "Ethan",
|
561
|
+
:age => nil,
|
562
|
+
:is_active => nil,
|
563
|
+
:belongs_to_object_id => nil,
|
564
|
+
:custom_name_id => nil,
|
565
|
+
:bday => nil,
|
566
|
+
:roles => []
|
567
|
+
}
|
568
|
+
),
|
526
569
|
TestResource.headers
|
527
570
|
)
|
528
571
|
|
@@ -577,16 +620,19 @@ describe "Base" do
|
|
577
620
|
JSON.unparse({
|
578
621
|
:test_resource => {
|
579
622
|
:name => "Ethan",
|
580
|
-
:has_many_objects => [{}]
|
623
|
+
:has_many_objects => [{:name => "Test"}]
|
581
624
|
}
|
582
625
|
}),
|
583
626
|
TestResource.headers
|
584
627
|
)
|
585
628
|
|
586
|
-
tr = TestResource.new(
|
629
|
+
tr = TestResource.new(
|
630
|
+
:name => "Ethan",
|
631
|
+
:has_many_objects => [{:id => 12, :name => "Dan"}]
|
632
|
+
)
|
587
633
|
tr.stubs(:id => 1)
|
588
634
|
|
589
|
-
tr.has_many_objects = [HasManyObject.new]
|
635
|
+
tr.has_many_objects = [HasManyObject.new(:name => "Test")]
|
590
636
|
tr.save
|
591
637
|
end
|
592
638
|
|
@@ -603,9 +649,10 @@ describe "Base" do
|
|
603
649
|
TestResource.headers
|
604
650
|
)
|
605
651
|
|
606
|
-
tr = TestResource.new(:name => "Ethan")
|
652
|
+
tr = TestResource.new(:name => "Ethan", :has_many_objects => [])
|
607
653
|
tr.stubs(:id => 1)
|
608
|
-
|
654
|
+
|
655
|
+
tr.save(:include_associations => [:has_many_objects])
|
609
656
|
end
|
610
657
|
|
611
658
|
|
@@ -688,8 +735,17 @@ describe "Base" do
|
|
688
735
|
it "should include all attributes if include_all_attributes_on_update is true" do
|
689
736
|
|
690
737
|
ApiResource::Connection.any_instance.expects(:put).with(
|
691
|
-
"/test_resources/1.json",
|
692
|
-
|
738
|
+
"/test_resources/1.json", JSON.unparse(
|
739
|
+
:test_resource => {
|
740
|
+
:name => "Ethan",
|
741
|
+
:age => nil,
|
742
|
+
:is_active => nil,
|
743
|
+
:belongs_to_object_id => nil,
|
744
|
+
:custom_name_id => nil,
|
745
|
+
:bday => nil,
|
746
|
+
:roles => []
|
747
|
+
}
|
748
|
+
),
|
693
749
|
TestResource.headers
|
694
750
|
)
|
695
751
|
begin
|
data/spec/spec_helper.rb
CHANGED
@@ -39,6 +39,13 @@ Mocks.define do
|
|
39
39
|
}
|
40
40
|
})
|
41
41
|
end
|
42
|
+
|
43
|
+
endpoint("/has_one_objects.json") do
|
44
|
+
get(
|
45
|
+
[HashDealer.roll(:has_one_object)],
|
46
|
+
:params => {:test_resource_id => 1}.matcher
|
47
|
+
)
|
48
|
+
end
|
42
49
|
|
43
50
|
endpoint("/has_many_objects/new") do
|
44
51
|
get({
|
@@ -47,6 +54,13 @@ Mocks.define do
|
|
47
54
|
}
|
48
55
|
})
|
49
56
|
end
|
57
|
+
|
58
|
+
endpoint("/has_many_objects.json") do
|
59
|
+
get(
|
60
|
+
[HashDealer.roll(:has_many_object)],
|
61
|
+
:params => {:test_resource_id => 1}.matcher
|
62
|
+
)
|
63
|
+
end
|
50
64
|
|
51
65
|
endpoint("/belongs_to_objects/new") do
|
52
66
|
get({
|
@@ -1,7 +1,15 @@
|
|
1
1
|
HashDealer.define(:new_test_object) do
|
2
2
|
attributes({
|
3
3
|
:protected => [:id, :protected_attr],
|
4
|
-
:public => [
|
4
|
+
:public => [
|
5
|
+
:name,
|
6
|
+
:age,
|
7
|
+
:is_active,
|
8
|
+
:belongs_to_object_id,
|
9
|
+
:custom_name_id,
|
10
|
+
[:bday, :date],
|
11
|
+
[:roles, :array]
|
12
|
+
]
|
5
13
|
})
|
6
14
|
scopes({
|
7
15
|
:active => {},
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api_resource
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2013-04-
|
14
|
+
date: 2013-04-16 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rake
|
@@ -221,22 +221,6 @@ dependencies:
|
|
221
221
|
- - ! '>='
|
222
222
|
- !ruby/object:Gem::Version
|
223
223
|
version: '0'
|
224
|
-
- !ruby/object:Gem::Dependency
|
225
|
-
name: ruby-debug19
|
226
|
-
requirement: !ruby/object:Gem::Requirement
|
227
|
-
none: false
|
228
|
-
requirements:
|
229
|
-
- - ! '>='
|
230
|
-
- !ruby/object:Gem::Version
|
231
|
-
version: '0'
|
232
|
-
type: :development
|
233
|
-
prerelease: false
|
234
|
-
version_requirements: !ruby/object:Gem::Requirement
|
235
|
-
none: false
|
236
|
-
requirements:
|
237
|
-
- - ! '>='
|
238
|
-
- !ruby/object:Gem::Version
|
239
|
-
version: '0'
|
240
224
|
- !ruby/object:Gem::Dependency
|
241
225
|
name: simplecov
|
242
226
|
requirement: !ruby/object:Gem::Requirement
|
@@ -440,6 +424,7 @@ files:
|
|
440
424
|
- nohup.out
|
441
425
|
- spec/lib/api_resource_spec.rb
|
442
426
|
- spec/lib/associations/association_scope_spec.rb
|
427
|
+
- spec/lib/associations/has_many_remote_object_proxy_spec.rb
|
443
428
|
- spec/lib/associations_spec.rb
|
444
429
|
- spec/lib/attributes_spec.rb
|
445
430
|
- spec/lib/base_spec.rb
|
@@ -500,6 +485,7 @@ summary: ActiveRecord for restful APIs
|
|
500
485
|
test_files:
|
501
486
|
- spec/lib/api_resource_spec.rb
|
502
487
|
- spec/lib/associations/association_scope_spec.rb
|
488
|
+
- spec/lib/associations/has_many_remote_object_proxy_spec.rb
|
503
489
|
- spec/lib/associations_spec.rb
|
504
490
|
- spec/lib/attributes_spec.rb
|
505
491
|
- spec/lib/base_spec.rb
|