activeresource 4.1.0 → 6.0.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.
- checksums.yaml +5 -5
- data/MIT-LICENSE +20 -0
- data/README.md +324 -0
- data/lib/active_resource/active_job_serializer.rb +26 -0
- data/lib/active_resource/associations/builder/association.rb +6 -6
- data/lib/active_resource/associations/builder/belongs_to.rb +5 -3
- data/lib/active_resource/associations/builder/has_many.rb +4 -2
- data/lib/active_resource/associations/builder/has_one.rb +5 -3
- data/lib/active_resource/associations.rb +23 -20
- data/lib/active_resource/base.rb +233 -113
- data/lib/active_resource/callbacks.rb +3 -1
- data/lib/active_resource/collection.rb +21 -12
- data/lib/active_resource/connection.rb +78 -81
- data/lib/active_resource/custom_methods.rb +8 -6
- data/lib/active_resource/exceptions.rb +17 -5
- data/lib/active_resource/formats/json_format.rb +4 -1
- data/lib/active_resource/formats/xml_format.rb +4 -2
- data/lib/active_resource/formats.rb +5 -3
- data/lib/active_resource/http_mock.rb +23 -27
- data/lib/active_resource/inheriting_hash.rb +15 -0
- data/lib/active_resource/log_subscriber.rb +14 -3
- data/lib/active_resource/railtie.rb +10 -10
- data/lib/active_resource/reflection.rb +11 -10
- data/lib/active_resource/schema.rb +6 -3
- data/lib/active_resource/singleton.rb +25 -28
- data/lib/active_resource/threadsafe_attributes.rb +35 -31
- data/lib/active_resource/validations.rb +18 -15
- data/lib/active_resource/version.rb +6 -4
- data/lib/active_resource.rb +8 -7
- data/lib/activeresource.rb +3 -1
- metadata +41 -24
- data/README.rdoc +0 -231
- data/lib/active_resource/observing.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f2a0c925e2619c4a4204eebdaea70776a57fec944e9eefeb98982ed8cf425bbc
|
4
|
+
data.tar.gz: b77c66a74c3fe0d32e16d0ce4eaaec1c772200bda9ea36b2bfba1a3cc4f58b5d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9116078833a262f5a4d184a9cd080f3a29bfc5c07481cf352dd87baa735ba0dfae6159a35ac37ea1ad2e121348a33d958151a048af8dbb6e85e3a387076de3b5
|
7
|
+
data.tar.gz: 02e8b7e3aa1fd89f70f67b2696d778bf0c4a5d89c4b6f779ec3ecffc6a099374c06515ec2bfcb4e7175a518f00543f16b1b691670378c2533fcf353ce82cb96f
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2006-2016 David Heinemeier Hansson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,324 @@
|
|
1
|
+
# Active Resource
|
2
|
+
|
3
|
+
Active Resource (ARes) connects business objects and Representational State Transfer (REST)
|
4
|
+
web services. It implements object-relational mapping for REST web services to provide transparent
|
5
|
+
proxying capabilities between a client (Active Resource) and a RESTful service (which is provided by
|
6
|
+
Simply RESTful routing in `ActionController::Resources`).
|
7
|
+
|
8
|
+
## Philosophy
|
9
|
+
|
10
|
+
Active Resource attempts to provide a coherent wrapper object-relational mapping for REST
|
11
|
+
web services. It follows the same philosophy as Active Record, in that one of its prime aims
|
12
|
+
is to reduce the amount of code needed to map to these resources. This is made possible
|
13
|
+
by relying on a number of code- and protocol-based conventions that make it easy for Active Resource
|
14
|
+
to infer complex relations and structures. These conventions are outlined in detail in the documentation
|
15
|
+
for `ActiveResource::Base`.
|
16
|
+
|
17
|
+
## Overview
|
18
|
+
|
19
|
+
Model classes are mapped to remote REST resources by Active Resource much the same way Active Record maps
|
20
|
+
model classes to database tables. When a request is made to a remote resource, a REST JSON request is
|
21
|
+
generated, transmitted, and the result received and serialized into a usable Ruby object.
|
22
|
+
|
23
|
+
## Download and installation
|
24
|
+
|
25
|
+
The latest version of Active Resource can be installed with RubyGems:
|
26
|
+
|
27
|
+
```
|
28
|
+
gem install activeresource
|
29
|
+
```
|
30
|
+
|
31
|
+
Or added to a Gemfile:
|
32
|
+
|
33
|
+
```ruby
|
34
|
+
gem 'activeresource'
|
35
|
+
```
|
36
|
+
|
37
|
+
Source code can be downloaded on GitHub
|
38
|
+
|
39
|
+
* https://github.com/rails/activeresource/tree/main
|
40
|
+
|
41
|
+
### Configuration and Usage
|
42
|
+
|
43
|
+
Putting Active Resource to use is very similar to Active Record. It's as simple as creating a model class
|
44
|
+
that inherits from `ActiveResource::Base` and providing a `site` class variable to it:
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class Person < ActiveResource::Base
|
48
|
+
self.site = "http://api.people.com:3000"
|
49
|
+
end
|
50
|
+
```
|
51
|
+
|
52
|
+
Now the Person class is REST enabled and can invoke REST services very similarly to how Active Record invokes
|
53
|
+
life cycle methods that operate against a persistent store.
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
# Find a person with id = 1
|
57
|
+
tyler = Person.find(1)
|
58
|
+
Person.exists?(1) # => true
|
59
|
+
```
|
60
|
+
|
61
|
+
As you can see, the methods are quite similar to Active Record's methods for dealing with database
|
62
|
+
records. But rather than dealing directly with a database record, you're dealing with HTTP resources
|
63
|
+
(which may or may not be database records).
|
64
|
+
|
65
|
+
Connection settings (`site`, `headers`, `user`, `password`, `bearer_token`, `proxy`) and the connections
|
66
|
+
themselves are store in thread-local variables to make them thread-safe, so you can also set these
|
67
|
+
dynamically, even in a multi-threaded environment, for instance:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
ActiveResource::Base.site = api_site_for(request)
|
71
|
+
```
|
72
|
+
### Authentication
|
73
|
+
|
74
|
+
Active Resource supports the token based authentication provided by Rails through the
|
75
|
+
`ActionController::HttpAuthentication::Token` class using custom headers.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
class Person < ActiveResource::Base
|
79
|
+
self.headers['Authorization'] = 'Token token="abcd"'
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
You can also set any specific HTTP header using the same way. As mentioned above, headers are
|
84
|
+
thread-safe, so you can set headers dynamically, even in a multi-threaded environment:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
ActiveResource::Base.headers['Authorization'] = current_session_api_token
|
88
|
+
```
|
89
|
+
|
90
|
+
Active Resource supports 2 options for HTTP authentication today.
|
91
|
+
|
92
|
+
1. Basic
|
93
|
+
```ruby
|
94
|
+
class Person < ActiveResource::Base
|
95
|
+
self.user = 'my@email.com'
|
96
|
+
self.password = '123'
|
97
|
+
end
|
98
|
+
# username: my@email.com password: 123
|
99
|
+
```
|
100
|
+
|
101
|
+
2. Bearer Token
|
102
|
+
```ruby
|
103
|
+
class Person < ActiveResource::Base
|
104
|
+
self.auth_type = :bearer
|
105
|
+
self.bearer_token = 'my-token123'
|
106
|
+
end
|
107
|
+
# Bearer my-token123
|
108
|
+
```
|
109
|
+
|
110
|
+
### Protocol
|
111
|
+
|
112
|
+
Active Resource is built on a standard JSON or XML format for requesting and submitting resources
|
113
|
+
over HTTP. It mirrors the RESTful routing built into Action Controller but will also work with any
|
114
|
+
other REST service that properly implements the protocol. REST uses HTTP, but unlike "typical" web
|
115
|
+
applications, it makes use of all the verubys available in the HTTP specification:
|
116
|
+
|
117
|
+
* GET requests are used for finding and retrieving resources.
|
118
|
+
* POST requests are used to create new resources.
|
119
|
+
* PUT requests are used to update existing resources.
|
120
|
+
* DELETE requests are used to delete resources.
|
121
|
+
|
122
|
+
For more information on how this protocol works with Active Resource, see the `ActiveResource::Base` documentation;
|
123
|
+
for more general information on REST web services, see the article
|
124
|
+
[here](http://en.wikipedia.org/wiki/Representational_State_Transfer).
|
125
|
+
|
126
|
+
### Find
|
127
|
+
|
128
|
+
Find requests use the GET method and expect the JSON form of whatever resource/resources is/are
|
129
|
+
being requested. So, for a request for a single element, the JSON of that item is expected in
|
130
|
+
response:
|
131
|
+
|
132
|
+
```ruby
|
133
|
+
# Expects a response of
|
134
|
+
#
|
135
|
+
# {"id":1,"first":"Tyler","last":"Durden"}
|
136
|
+
#
|
137
|
+
# for GET http://api.people.com:3000/people/1.json
|
138
|
+
#
|
139
|
+
tyler = Person.find(1)
|
140
|
+
```
|
141
|
+
|
142
|
+
The JSON document that is received is used to build a new object of type Person, with each
|
143
|
+
JSON element becoming an attribute on the object.
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
tyler.is_a? Person # => true
|
147
|
+
tyler.last # => 'Durden'
|
148
|
+
```
|
149
|
+
|
150
|
+
Any complex element (one that contains other elements) becomes its own object:
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
# With this response:
|
154
|
+
# {"id":1,"first":"Tyler","address":{"street":"Paper St.","state":"CA"}}
|
155
|
+
#
|
156
|
+
# for GET http://api.people.com:3000/people/1.json
|
157
|
+
#
|
158
|
+
tyler = Person.find(1)
|
159
|
+
tyler.address # => <Person::Address::xxxxx>
|
160
|
+
tyler.address.street # => 'Paper St.'
|
161
|
+
```
|
162
|
+
|
163
|
+
Collections can also be requested in a similar fashion
|
164
|
+
|
165
|
+
```ruby
|
166
|
+
# Expects a response of
|
167
|
+
#
|
168
|
+
# [
|
169
|
+
# {"id":1,"first":"Tyler","last":"Durden"},
|
170
|
+
# {"id":2,"first":"Tony","last":"Stark",}
|
171
|
+
# ]
|
172
|
+
#
|
173
|
+
# for GET http://api.people.com:3000/people.json
|
174
|
+
#
|
175
|
+
people = Person.all
|
176
|
+
people.first # => <Person::xxx 'first' => 'Tyler' ...>
|
177
|
+
people.last # => <Person::xxx 'first' => 'Tony' ...>
|
178
|
+
```
|
179
|
+
|
180
|
+
### Create
|
181
|
+
|
182
|
+
Creating a new resource submits the JSON form of the resource as the body of the request and expects
|
183
|
+
a 'Location' header in the response with the RESTful URL location of the newly created resource. The
|
184
|
+
id of the newly created resource is parsed out of the Location response header and automatically set
|
185
|
+
as the id of the ARes object.
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
# {"first":"Tyler","last":"Durden"}
|
189
|
+
#
|
190
|
+
# is submitted as the body on
|
191
|
+
#
|
192
|
+
# if include_root_in_json is not set or set to false => {"first":"Tyler"}
|
193
|
+
# if include_root_in_json is set to true => {"person":{"first":"Tyler"}}
|
194
|
+
#
|
195
|
+
# POST http://api.people.com:3000/people.json
|
196
|
+
#
|
197
|
+
# when save is called on a new Person object. An empty response is
|
198
|
+
# is expected with a 'Location' header value:
|
199
|
+
#
|
200
|
+
# Response (201): Location: http://api.people.com:3000/people/2
|
201
|
+
#
|
202
|
+
tyler = Person.new(:first => 'Tyler')
|
203
|
+
tyler.new? # => true
|
204
|
+
tyler.save # => true
|
205
|
+
tyler.new? # => false
|
206
|
+
tyler.id # => 2
|
207
|
+
```
|
208
|
+
|
209
|
+
### Update
|
210
|
+
|
211
|
+
'save' is also used to update an existing resource and follows the same protocol as creating a resource
|
212
|
+
with the exception that no response headers are needed -- just an empty response when the update on the
|
213
|
+
server side was successful.
|
214
|
+
|
215
|
+
```ruby
|
216
|
+
# {"first":"Tyler"}
|
217
|
+
#
|
218
|
+
# is submitted as the body on
|
219
|
+
#
|
220
|
+
# if include_root_in_json is not set or set to false => {"first":"Tyler"}
|
221
|
+
# if include_root_in_json is set to true => {"person":{"first":"Tyler"}}
|
222
|
+
#
|
223
|
+
# PUT http://api.people.com:3000/people/1.json
|
224
|
+
#
|
225
|
+
# when save is called on an existing Person object. An empty response is
|
226
|
+
# is expected with code (204)
|
227
|
+
#
|
228
|
+
tyler = Person.find(1)
|
229
|
+
tyler.first # => 'Tyler'
|
230
|
+
tyler.first = 'Tyson'
|
231
|
+
tyler.save # => true
|
232
|
+
```
|
233
|
+
|
234
|
+
### Delete
|
235
|
+
|
236
|
+
Destruction of a resource can be invoked as a class and instance method of the resource.
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
# A request is made to
|
240
|
+
#
|
241
|
+
# DELETE http://api.people.com:3000/people/1.json
|
242
|
+
#
|
243
|
+
# for both of these forms. An empty response with
|
244
|
+
# is expected with response code (200)
|
245
|
+
#
|
246
|
+
tyler = Person.find(1)
|
247
|
+
tyler.destroy # => true
|
248
|
+
tyler.exists? # => false
|
249
|
+
Person.delete(2) # => true
|
250
|
+
Person.exists?(2) # => false
|
251
|
+
```
|
252
|
+
|
253
|
+
### Associations
|
254
|
+
|
255
|
+
Relationships between resources can be declared using the standard association syntax
|
256
|
+
that should be familiar to anyone who uses Active Record. For example, using the
|
257
|
+
class definition below:
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
class Post < ActiveResource::Base
|
261
|
+
self.site = "http://blog.io"
|
262
|
+
has_many :comments
|
263
|
+
end
|
264
|
+
|
265
|
+
post = Post.find(1) # issues GET http://blog.io/posts/1.json
|
266
|
+
comments = post.comments # issues GET http://blog.io/comments.json?post_id=1
|
267
|
+
```
|
268
|
+
|
269
|
+
In this case, the `Comment` model would have to be implemented as Active Resource, too.
|
270
|
+
|
271
|
+
If you control the server, you may wish to include nested resources thus avoiding a
|
272
|
+
second network request. Given the resource above, if the response includes comments
|
273
|
+
in the response, they will be automatically loaded into the Active Resource object.
|
274
|
+
The server-side model can be adjusted as follows to include comments in the response.
|
275
|
+
|
276
|
+
```ruby
|
277
|
+
class Post < ActiveRecord::Base
|
278
|
+
has_many :comments
|
279
|
+
|
280
|
+
def as_json(options)
|
281
|
+
super.merge(:include=>[:comments])
|
282
|
+
end
|
283
|
+
end
|
284
|
+
```
|
285
|
+
|
286
|
+
### Logging
|
287
|
+
|
288
|
+
Active Resource instruments the event `request.active_resource` when doing a request
|
289
|
+
to the remote service. You can subscribe to it by doing:
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
ActiveSupport::Notifications.subscribe('request.active_resource') do |name, start, finish, id, payload|
|
293
|
+
```
|
294
|
+
|
295
|
+
The `payload` is a `Hash` with the following keys:
|
296
|
+
|
297
|
+
* `method` as a `Symbol`
|
298
|
+
* `request_uri` as a `String`
|
299
|
+
* `result` as an `Net::HTTPResponse`
|
300
|
+
|
301
|
+
## License
|
302
|
+
|
303
|
+
Active Resource is released under the MIT license:
|
304
|
+
|
305
|
+
* http://www.opensource.org/licenses/MIT
|
306
|
+
|
307
|
+
## Contributing to Active Resource
|
308
|
+
|
309
|
+
Active Resource is work of many contributors. You're encouraged to submit pull requests, propose
|
310
|
+
features and discuss issues.
|
311
|
+
|
312
|
+
See [CONTRIBUTING](https://github.com/rails/activeresource/blob/main/CONTRIBUTING.md).
|
313
|
+
|
314
|
+
## Support
|
315
|
+
|
316
|
+
Full API documentation is available at
|
317
|
+
|
318
|
+
* http://rubydoc.info/gems/activeresource
|
319
|
+
|
320
|
+
Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
|
321
|
+
|
322
|
+
* https://github.com/rails/activeresource/issues
|
323
|
+
|
324
|
+
You can find more usage information in the ActiveResource::Base documentation.
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveResource
|
4
|
+
class ActiveJobSerializer < ActiveJob::Serializers::ObjectSerializer
|
5
|
+
def serialize(resource)
|
6
|
+
super(
|
7
|
+
"class" => resource.class.name,
|
8
|
+
"persisted" => resource.persisted?,
|
9
|
+
"prefix_options" => resource.prefix_options.as_json,
|
10
|
+
"attributes" => resource.attributes.as_json
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
def deserialize(hash)
|
15
|
+
hash["class"].constantize.new(hash["attributes"]).tap do |resource|
|
16
|
+
resource.persisted = hash["persisted"]
|
17
|
+
resource.prefix_options = hash["prefix_options"]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
def klass
|
23
|
+
ActiveResource::Base
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
|
-
|
2
|
-
class Association #:nodoc:
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
3
|
+
module ActiveResource::Associations::Builder
|
4
|
+
class Association # :nodoc:
|
4
5
|
# providing a Class-Variable, which will have a different store of subclasses
|
5
6
|
class_attribute :valid_options
|
6
7
|
self.valid_options = [:class_name]
|
@@ -24,9 +25,8 @@ module ActiveResource::Associations::Builder
|
|
24
25
|
end
|
25
26
|
|
26
27
|
private
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
28
|
+
def validate_options
|
29
|
+
options.assert_valid_keys(self.class.valid_options)
|
30
|
+
end
|
31
31
|
end
|
32
32
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveResource::Associations::Builder
|
2
4
|
class BelongsTo < Association
|
3
5
|
self.valid_options += [:foreign_key]
|
4
6
|
|
@@ -7,8 +9,8 @@ module ActiveResource::Associations::Builder
|
|
7
9
|
def build
|
8
10
|
validate_options
|
9
11
|
reflection = model.create_reflection(self.class.macro, name, options)
|
10
|
-
model.defines_belongs_to_finder_method(reflection
|
11
|
-
|
12
|
+
model.defines_belongs_to_finder_method(reflection)
|
13
|
+
reflection
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveResource::Associations::Builder
|
2
4
|
class HasMany < Association
|
3
5
|
self.macro = :has_many
|
4
6
|
|
5
7
|
def build
|
6
8
|
validate_options
|
7
9
|
model.create_reflection(self.class.macro, name, options).tap do |reflection|
|
8
|
-
model.defines_has_many_finder_method(reflection
|
10
|
+
model.defines_has_many_finder_method(reflection)
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveResource::Associations::Builder
|
2
4
|
class HasOne < Association
|
3
5
|
self.macro = :has_one
|
4
|
-
|
6
|
+
|
5
7
|
def build
|
6
8
|
validate_options
|
7
9
|
model.create_reflection(self.class.macro, name, options).tap do |reflection|
|
8
|
-
model.defines_has_one_finder_method(reflection
|
10
|
+
model.defines_has_one_finder_method(reflection)
|
9
11
|
end
|
10
12
|
end
|
11
13
|
end
|
@@ -1,10 +1,11 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module ActiveResource::Associations
|
3
4
|
module Builder
|
4
|
-
autoload :Association,
|
5
|
-
autoload :HasMany,
|
6
|
-
autoload :HasOne,
|
7
|
-
autoload :BelongsTo,
|
5
|
+
autoload :Association, "active_resource/associations/builder/association"
|
6
|
+
autoload :HasMany, "active_resource/associations/builder/has_many"
|
7
|
+
autoload :HasOne, "active_resource/associations/builder/has_one"
|
8
|
+
autoload :BelongsTo, "active_resource/associations/builder/belongs_to"
|
8
9
|
end
|
9
10
|
|
10
11
|
|
@@ -65,7 +66,7 @@ module ActiveResource::Associations
|
|
65
66
|
# Would resolve this author into the <tt>Myblog::Author</tt> class.
|
66
67
|
#
|
67
68
|
# If the response body does not contain an attribute matching the association name
|
68
|
-
# a request is sent to a
|
69
|
+
# a request is sent to a singleton path under the current resource.
|
69
70
|
# For example, if a Product class <tt>has_one :inventory</tt> calling <tt>Product#inventory</tt>
|
70
71
|
# will generate a request on /products/:product_id/inventory.json.
|
71
72
|
#
|
@@ -89,15 +90,15 @@ module ActiveResource::Associations
|
|
89
90
|
#
|
90
91
|
# === Example
|
91
92
|
#
|
92
|
-
# A Comment class
|
93
|
+
# A Comment class declares <tt>belongs_to :post</tt>, which will add:
|
93
94
|
# * <tt>Comment#post</tt> (similar to <tt>Post.find(post_id)</tt>)
|
94
95
|
# The declaration can also include an options hash to specialize the behavior of the association.
|
95
96
|
#
|
96
97
|
# === Options
|
97
98
|
# [:class_name]
|
98
|
-
# Specify the class name for the association. Use it only if that name
|
99
|
+
# Specify the class name for the association. Use it only if that name can't be inferred from association name.
|
99
100
|
# So <tt>belongs_to :post</tt> will by default be linked to the Post class, but if the real class name is Article,
|
100
|
-
# you'll have to specify it with
|
101
|
+
# you'll have to specify it with this option.
|
101
102
|
# [:foreign_key]
|
102
103
|
# Specify the foreign key used for the association. By default this is guessed to be the name
|
103
104
|
# of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :post</tt>
|
@@ -112,12 +113,13 @@ module ActiveResource::Associations
|
|
112
113
|
# <tt>belongs_to :customer, :foreign_key => 'user_id'</tt>
|
113
114
|
# Creates a belongs_to association called customer which would be resolved by the foreign_key <tt>user_id</tt> instead of <tt>customer_id</tt>
|
114
115
|
#
|
115
|
-
def belongs_to(name, options={})
|
116
|
+
def belongs_to(name, options = {})
|
116
117
|
Builder::BelongsTo.build(self, name, options)
|
117
118
|
end
|
118
119
|
|
119
120
|
# Defines the belongs_to association finder method
|
120
|
-
def defines_belongs_to_finder_method(
|
121
|
+
def defines_belongs_to_finder_method(reflection)
|
122
|
+
method_name = reflection.name
|
121
123
|
ivar_name = :"@#{method_name}"
|
122
124
|
|
123
125
|
if method_defined?(method_name)
|
@@ -130,13 +132,14 @@ module ActiveResource::Associations
|
|
130
132
|
instance_variable_get(ivar_name)
|
131
133
|
elsif attributes.include?(method_name)
|
132
134
|
attributes[method_name]
|
133
|
-
elsif association_id = send(
|
134
|
-
instance_variable_set(ivar_name,
|
135
|
+
elsif association_id = send(reflection.foreign_key)
|
136
|
+
instance_variable_set(ivar_name, reflection.klass.find(association_id))
|
135
137
|
end
|
136
138
|
end
|
137
139
|
end
|
138
140
|
|
139
|
-
def defines_has_many_finder_method(
|
141
|
+
def defines_has_many_finder_method(reflection)
|
142
|
+
method_name = reflection.name
|
140
143
|
ivar_name = :"@#{method_name}"
|
141
144
|
|
142
145
|
define_method(method_name) do
|
@@ -145,7 +148,7 @@ module ActiveResource::Associations
|
|
145
148
|
elsif attributes.include?(method_name)
|
146
149
|
attributes[method_name]
|
147
150
|
elsif !new_record?
|
148
|
-
instance_variable_set(ivar_name,
|
151
|
+
instance_variable_set(ivar_name, reflection.klass.find(:all, params: { "#{self.class.element_name}_id": self.id }))
|
149
152
|
else
|
150
153
|
instance_variable_set(ivar_name, self.class.collection_parser.new)
|
151
154
|
end
|
@@ -153,7 +156,8 @@ module ActiveResource::Associations
|
|
153
156
|
end
|
154
157
|
|
155
158
|
# Defines the has_one association
|
156
|
-
def defines_has_one_finder_method(
|
159
|
+
def defines_has_one_finder_method(reflection)
|
160
|
+
method_name = reflection.name
|
157
161
|
ivar_name = :"@#{method_name}"
|
158
162
|
|
159
163
|
define_method(method_name) do
|
@@ -161,12 +165,11 @@ module ActiveResource::Associations
|
|
161
165
|
instance_variable_get(ivar_name)
|
162
166
|
elsif attributes.include?(method_name)
|
163
167
|
attributes[method_name]
|
164
|
-
elsif
|
165
|
-
instance_variable_set(ivar_name,
|
168
|
+
elsif reflection.klass.respond_to?(:singleton_name)
|
169
|
+
instance_variable_set(ivar_name, reflection.klass.find(params: { "#{self.class.element_name}_id": self.id }))
|
166
170
|
else
|
167
|
-
instance_variable_set(ivar_name,
|
171
|
+
instance_variable_set(ivar_name, reflection.klass.find(:one, from: "/#{self.class.collection_name}/#{self.id}/#{method_name}#{self.class.format_extension}"))
|
168
172
|
end
|
169
173
|
end
|
170
174
|
end
|
171
|
-
|
172
175
|
end
|