reactive_resource 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/Gemfile.lock CHANGED
@@ -1,20 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- reactive_resource (0.5.1)
4
+ reactive_resource (0.6.0)
5
5
  activeresource (>= 2.3.10)
6
6
 
7
7
  GEM
8
8
  remote: http://rubygems.org/
9
9
  specs:
10
- activemodel (3.0.3)
11
- activesupport (= 3.0.3)
10
+ activemodel (3.0.6)
11
+ activesupport (= 3.0.6)
12
12
  builder (~> 2.1.2)
13
- i18n (~> 0.4)
14
- activeresource (3.0.3)
15
- activemodel (= 3.0.3)
16
- activesupport (= 3.0.3)
17
- activesupport (3.0.3)
13
+ i18n (~> 0.5.0)
14
+ activeresource (3.0.6)
15
+ activemodel (= 3.0.6)
16
+ activesupport (= 3.0.6)
17
+ activesupport (3.0.6)
18
18
  addressable (2.2.2)
19
19
  builder (2.1.2)
20
20
  crack (0.1.8)
@@ -29,7 +29,6 @@ PLATFORMS
29
29
  ruby
30
30
 
31
31
  DEPENDENCIES
32
- activeresource (>= 2.3.10)
33
32
  rake
34
33
  reactive_resource!
35
34
  shoulda (~> 2.11.3)
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2011 by Justin Weiss
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -60,6 +60,7 @@ module ReactiveResource
60
60
  # address.lawyer_id = 3
61
61
  define_method("#{attribute}_id=") do |value|
62
62
  prefix_options["#{attribute}_id".intern] = value
63
+ attributes["#{attribute}_id".intern] = value
63
64
  end
64
65
 
65
66
  # address.lawyer
@@ -91,6 +91,11 @@ module ReactiveResource
91
91
  def load(attributes)
92
92
  self.class.belongs_to_with_parents.each do |belongs_to_param|
93
93
  attributes["#{belongs_to_param}_id".intern] ||= prefix_options["#{belongs_to_param}_id".intern]
94
+
95
+ # also set prefix attributes as real attributes. Otherwise,
96
+ # belongs_to attributes will be stripped out of the response
97
+ # even if we aren't actually using the association.
98
+ @attributes["#{belongs_to_param}_id".intern] = attributes["#{belongs_to_param}_id".intern]
94
99
  end
95
100
  super(attributes)
96
101
  end
@@ -109,6 +114,33 @@ module ReactiveResource
109
114
  @prefix_parameters
110
115
  end
111
116
 
117
+ # Returns a list of the belongs_to associations we will use to
118
+ # generate the full path for this resource.
119
+ def self.prefix_associations(options)
120
+ options = options.dup
121
+ used_associations = []
122
+ parent_associations = []
123
+
124
+ # Recurse to add the parent resource hierarchy. For Phone, for
125
+ # instance, this will add the '/lawyers/:id' part of the URL,
126
+ # which it knows about from the Address class.
127
+ parents.each do |parent|
128
+ parent_associations = parent.prefix_associations(options)
129
+ break unless parent_associations.empty?
130
+ end
131
+
132
+ # The association chain we're following
133
+ used_association = nil
134
+
135
+ belongs_to_associations.each do |association|
136
+ if !used_association && param_value = options.delete("#{association.attribute}_id".intern) # only take the first one
137
+ used_associations << association
138
+ break
139
+ end
140
+ end
141
+ parent_associations + used_associations
142
+ end
143
+
112
144
  # Generates the URL prefix that the belongs_to parameters and
113
145
  # associations refer to. For example, a license with params
114
146
  # :lawyer_id => 2 will return 'lawyers/2/' and a phone with params
@@ -117,23 +149,21 @@ module ReactiveResource
117
149
  def self.association_prefix(options)
118
150
  options = options.dup
119
151
  association_prefix = ''
120
- parent_prefix = ''
121
152
 
122
153
  if belongs_to_associations
123
- # Recurse to add the parent resource hierarchy. For Phone, for
124
- # instance, this will add the '/lawyers/:id' part of the URL,
125
- # which it knows about from the Address class.
126
- parents.each do |parent|
127
- parent_prefix = parent.association_prefix(options) if parent_prefix.blank?
128
- end
129
154
 
130
- belongs_to_associations.each do |association|
131
- if association_prefix.blank? && param_value = options.delete("#{association.attribute}_id".intern) # only take the first one
132
- association_prefix = "#{association.associated_class.collection_name}/#{param_value}/"
133
- end
134
- end
155
+ used_associations = prefix_associations(options)
156
+
157
+ association_prefix = used_associations.map do |association|
158
+ collection_name = association.associated_class.collection_name
159
+ value = options.delete("#{association.attribute}_id".intern)
160
+ "#{collection_name}/#{value}"
161
+ end.join('/')
162
+
163
+ # add trailing slash
164
+ association_prefix << '/' unless used_associations.empty?
135
165
  end
136
- parent_prefix + association_prefix
166
+ association_prefix
137
167
  end
138
168
 
139
169
  class << self
@@ -213,6 +243,27 @@ module ReactiveResource
213
243
  end
214
244
  end
215
245
 
246
+ # In order to support two-way belongs_to associations in a
247
+ # reasonable way, we duplicate all of the prefix options as real
248
+ # attributes (so license.lawyer_id will set both the lawyer_id
249
+ # attribute and the lawyer_id prefix option). When we're ready to
250
+ # save, we should take all the parameters that we're already
251
+ # sending as prefix options and remove them from the model's
252
+ # attributes so we don't send duplicates down the wire. This way,
253
+ # if you have an object that belongs_to :lawyer and belongs_to
254
+ # :phone (in that order), setting lawyer_id and phone_id will
255
+ # treat lawyer_id as a prefix option and phone_id as a normal
256
+ # attribute.
257
+ def save
258
+ if self.class.belongs_to_associations
259
+ used_attributes = self.class.prefix_associations(prefix_options).map {|association| association.attribute}
260
+ used_attributes.each do |attribute|
261
+ attributes.delete("#{attribute}_id".intern)
262
+ end
263
+ end
264
+ super
265
+ end
266
+
216
267
  # belongs_to in ReactiveResource works a little differently than
217
268
  # ActiveRecord. Because we have to deal with full class hierachies
218
269
  # in order to generate the full URL (as mentioned in
@@ -1,4 +1,4 @@
1
1
  module ReactiveResource
2
2
  # The current version of ReactiveResource
3
- VERSION = "0.5.1"
3
+ VERSION = "0.6.0"
4
4
  end
data/test/test_objects.rb CHANGED
@@ -28,6 +28,19 @@ class ReactiveResource::Address < ReactiveResource::Base
28
28
  has_many :phones
29
29
  end
30
30
 
31
+ class ReactiveResource::Phone < ReactiveResource::Base
32
+ belongs_to :address
33
+ end
34
+
35
+ class ReactiveResource::Post < ReactiveResource::Base
36
+ has_one :lawyer_post
37
+ end
38
+
39
+ class ReactiveResource::LawyerPost < ReactiveResource::Base
40
+ belongs_to :lawyer
41
+ belongs_to :post
42
+ end
43
+
31
44
  module ChildResource
32
45
 
33
46
  class Address < ReactiveResource::Address
@@ -39,10 +52,6 @@ module ChildResource
39
52
  end
40
53
  end
41
54
 
42
- class ReactiveResource::Phone < ReactiveResource::Base
43
- belongs_to :address
44
- end
45
-
46
55
  module ActiveResource
47
56
  module Formats
48
57
  module NoFormatFormat
@@ -166,4 +166,20 @@ class ReactiveResource::BaseTest < Test::Unit::TestCase
166
166
  assert_requested(:get, "https://api.avvo.com/api/1/no_extensions/test")
167
167
  end
168
168
  end
169
+
170
+ context "A resource with a two-way belongs to association" do
171
+ should "convert unused prefix options to attributes" do
172
+ stub_request(:post, "https://api.avvo.com/api/1/lawyers/1/lawyer_posts.json").
173
+ with(:body => {"lawyer_post" => {"post_id" => 2}})
174
+ @object = ReactiveResource::LawyerPost.new(:lawyer_id => 1, :post_id => 2).save
175
+ assert_requested(:post, "https://api.avvo.com/api/1/lawyers/1/lawyer_posts.json")
176
+ end
177
+
178
+ should "set the attributes correctly when it's created" do
179
+ @object = ReactiveResource::LawyerPost.new(:lawyer_id => 1, :post_id => 2)
180
+ assert_not_nil @object.attributes[:post_id]
181
+ assert_not_nil @object.attributes[:lawyer_id]
182
+ end
183
+ end
184
+
169
185
  end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 5
8
- - 1
9
- version: 0.5.1
7
+ - 6
8
+ - 0
9
+ version: 0.6.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Justin Weiss
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2011-01-28 00:00:00 -08:00
17
+ date: 2011-04-13 00:00:00 -07:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -75,6 +75,7 @@ files:
75
75
  - .gitignore
76
76
  - Gemfile
77
77
  - Gemfile.lock
78
+ - LICENSE
78
79
  - README.rdoc
79
80
  - Rakefile
80
81
  - lib/reactive_resource.rb
@@ -104,7 +105,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
104
105
  requirements:
105
106
  - - ">="
106
107
  - !ruby/object:Gem::Version
107
- hash: 4176716010993460221
108
+ hash: -2570327779021755250
108
109
  segments:
109
110
  - 0
110
111
  version: "0"
@@ -113,7 +114,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
113
114
  requirements:
114
115
  - - ">="
115
116
  - !ruby/object:Gem::Version
116
- hash: 4176716010993460221
117
+ hash: -2570327779021755250
117
118
  segments:
118
119
  - 0
119
120
  version: "0"