api_resource 0.6.3 → 0.6.4
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/.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
|