activeresource 3.2.22.5 → 5.1.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.
- checksums.yaml +5 -5
- data/MIT-LICENSE +2 -2
- data/README.rdoc +147 -49
- data/lib/active_resource.rb +12 -12
- data/lib/active_resource/active_job_serializer.rb +26 -0
- data/lib/active_resource/associations.rb +175 -0
- data/lib/active_resource/associations/builder/association.rb +33 -0
- data/lib/active_resource/associations/builder/belongs_to.rb +16 -0
- data/lib/active_resource/associations/builder/has_many.rb +14 -0
- data/lib/active_resource/associations/builder/has_one.rb +14 -0
- data/lib/active_resource/base.rb +444 -231
- data/lib/active_resource/callbacks.rb +22 -0
- data/lib/active_resource/collection.rb +94 -0
- data/lib/active_resource/connection.rb +112 -105
- data/lib/active_resource/custom_methods.rb +24 -14
- data/lib/active_resource/exceptions.rb +5 -3
- data/lib/active_resource/formats.rb +5 -3
- data/lib/active_resource/formats/json_format.rb +4 -1
- data/lib/active_resource/formats/xml_format.rb +4 -2
- data/lib/active_resource/http_mock.rb +69 -31
- data/lib/active_resource/log_subscriber.rb +14 -3
- data/lib/active_resource/observing.rb +0 -29
- data/lib/active_resource/railtie.rb +14 -3
- data/lib/active_resource/reflection.rb +78 -0
- data/lib/active_resource/schema.rb +4 -4
- data/lib/active_resource/singleton.rb +113 -0
- data/lib/active_resource/threadsafe_attributes.rb +66 -0
- data/lib/active_resource/validations.rb +56 -14
- data/lib/active_resource/version.rb +7 -5
- data/lib/activeresource.rb +3 -0
- metadata +78 -16
- data/CHANGELOG.md +0 -437
- data/examples/performance.rb +0 -70
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9e0cde8f42f06f37aa13a7098b9a1e828aac34c90971a62f4c9a1a7fcf529ce7
|
4
|
+
data.tar.gz: 11e7f85a7c565de83cb5248d8ee35783b343b8714e8ed9adcf08462f8aaceeda
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 133f4a82c31d9c925bc1c04a304c63e03982d45aa51b97d7e9ba580a433579802fb811a32009d40ebb7fc84e947fe65c60fd658b2cebb2f2f4c724efc536f528
|
7
|
+
data.tar.gz: 0f0042b3b644fe8584d34403913f2e11363a684fe438d536303871baaed2a98f26319b0e1dcd036364b42597b48e76c5713a627ea4431a2b2ec9263fce98b3a6
|
data/MIT-LICENSE
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Copyright (c) 2006-
|
1
|
+
Copyright (c) 2006-2016 David Heinemeier Hansson
|
2
2
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
4
4
|
a copy of this software and associated documentation files (the
|
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
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.
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -17,7 +17,7 @@ for ActiveResource::Base.
|
|
17
17
|
== Overview
|
18
18
|
|
19
19
|
Model classes are mapped to remote REST resources by Active Resource much the same way Active Record maps model classes to database
|
20
|
-
tables. When a request is made to a remote resource, a REST
|
20
|
+
tables. When a request is made to a remote resource, a REST JSON request is generated, transmitted, and the result
|
21
21
|
received and serialized into a usable Ruby object.
|
22
22
|
|
23
23
|
== Download and installation
|
@@ -26,9 +26,13 @@ The latest version of Active Resource can be installed with RubyGems:
|
|
26
26
|
|
27
27
|
% [sudo] gem install activeresource
|
28
28
|
|
29
|
-
|
29
|
+
Or added to a Gemfile:
|
30
30
|
|
31
|
-
|
31
|
+
gem 'activeresource'
|
32
|
+
|
33
|
+
Source code can be downloaded on GitHub
|
34
|
+
|
35
|
+
* https://github.com/rails/activeresource/tree/master/activeresource
|
32
36
|
|
33
37
|
=== Configuration and Usage
|
34
38
|
|
@@ -43,15 +47,54 @@ Now the Person class is REST enabled and can invoke REST services very similarly
|
|
43
47
|
life cycle methods that operate against a persistent store.
|
44
48
|
|
45
49
|
# Find a person with id = 1
|
46
|
-
|
50
|
+
tyler = Person.find(1)
|
47
51
|
Person.exists?(1) # => true
|
48
52
|
|
49
53
|
As you can see, the methods are quite similar to Active Record's methods for dealing with database
|
50
54
|
records. But rather than dealing directly with a database record, you're dealing with HTTP resources (which may or may not be database records).
|
51
55
|
|
56
|
+
Connection settings (`site`, `headers`, `user`, `password`, `proxy`) and the connections themselves are store in
|
57
|
+
thread-local variables to make them thread-safe, so you can also set these dynamically, even in a multi-threaded environment, for instance:
|
58
|
+
|
59
|
+
ActiveResource::Base.site = api_site_for(request)
|
60
|
+
|
61
|
+
==== Authentication
|
62
|
+
|
63
|
+
Active Resource supports the token based authentication provided by Rails through the <tt>ActionController::HttpAuthentication::Token</tt> class using custom headers.
|
64
|
+
|
65
|
+
class Person < ActiveResource::Base
|
66
|
+
self.headers['Authorization'] = 'Token token="abcd"'
|
67
|
+
end
|
68
|
+
|
69
|
+
You can also set any specific HTTP header using the same way. As mentioned above, headers are thread-safe, so you can set headers dynamically, even in a multi-threaded environment:
|
70
|
+
|
71
|
+
ActiveResource::Base.headers['Authorization'] = current_session_api_token
|
72
|
+
|
73
|
+
Global Authentication to be used across all subclasses of ActiveResource::Base should be handled using the ActiveResource::Connection class.
|
74
|
+
|
75
|
+
ActiveResource::Base.connection.auth_type = :bearer
|
76
|
+
ActiveResource::Base.connection.bearer_token = @bearer_token
|
77
|
+
|
78
|
+
class Person < ActiveResource::Base
|
79
|
+
self.connection.auth_type = :bearer
|
80
|
+
self.connection.bearer_token = @bearer_token
|
81
|
+
end
|
82
|
+
|
83
|
+
ActiveResource supports 2 options for HTTP authentication today.
|
84
|
+
|
85
|
+
1. Basic
|
86
|
+
|
87
|
+
ActiveResource::Connection.new("http://my%40email.com:%31%32%33@localhost")
|
88
|
+
# username: my@email.com password: 123
|
89
|
+
|
90
|
+
2. Bearer Token
|
91
|
+
|
92
|
+
ActiveResource::Base.connection.auth_type = :bearer
|
93
|
+
ActiveResource::Base.connection.bearer_token = @bearer_token
|
94
|
+
|
52
95
|
==== Protocol
|
53
96
|
|
54
|
-
Active Resource is built on a standard XML format for requesting and submitting resources over HTTP. It mirrors the RESTful routing
|
97
|
+
Active Resource is built on a standard JSON or XML format for requesting and submitting resources over HTTP. It mirrors the RESTful routing
|
55
98
|
built into Action Controller but will also work with any other REST service that properly implements the protocol.
|
56
99
|
REST uses HTTP, but unlike "typical" web applications, it makes use of all the verbs available in the HTTP specification:
|
57
100
|
|
@@ -65,73 +108,75 @@ for more general information on REST web services, see the article here[http://e
|
|
65
108
|
|
66
109
|
==== Find
|
67
110
|
|
68
|
-
Find requests use the GET method and expect the
|
69
|
-
for a request for a single element, the
|
111
|
+
Find requests use the GET method and expect the JSON form of whatever resource/resources is/are being requested. So,
|
112
|
+
for a request for a single element, the JSON of that item is expected in response:
|
70
113
|
|
71
114
|
# Expects a response of
|
72
115
|
#
|
73
|
-
#
|
116
|
+
# {"id":1,"first":"Tyler","last":"Durden"}
|
74
117
|
#
|
75
|
-
# for GET http://api.people.com:3000/people/1.
|
118
|
+
# for GET http://api.people.com:3000/people/1.json
|
76
119
|
#
|
77
|
-
|
120
|
+
tyler = Person.find(1)
|
78
121
|
|
79
|
-
The
|
80
|
-
|
122
|
+
The JSON document that is received is used to build a new object of type Person, with each
|
123
|
+
JSON element becoming an attribute on the object.
|
81
124
|
|
82
|
-
|
83
|
-
|
125
|
+
tyler.is_a? Person # => true
|
126
|
+
tyler.last # => 'Durden'
|
84
127
|
|
85
128
|
Any complex element (one that contains other elements) becomes its own object:
|
86
129
|
|
87
130
|
# With this response:
|
131
|
+
# {"id":1,"first":"Tyler","address":{"street":"Paper St.","state":"CA"}}
|
88
132
|
#
|
89
|
-
#
|
133
|
+
# for GET http://api.people.com:3000/people/1.json
|
90
134
|
#
|
91
|
-
|
92
|
-
#
|
93
|
-
|
94
|
-
ryan.complex # => <Person::Complex::xxxxx>
|
95
|
-
ryan.complex.attribute2 # => 'value2'
|
135
|
+
tyler = Person.find(1)
|
136
|
+
tyler.address # => <Person::Address::xxxxx>
|
137
|
+
tyler.address.street # => 'Paper St.'
|
96
138
|
|
97
139
|
Collections can also be requested in a similar fashion
|
98
140
|
|
99
141
|
# Expects a response of
|
100
142
|
#
|
101
|
-
#
|
102
|
-
#
|
103
|
-
#
|
104
|
-
#
|
143
|
+
# [
|
144
|
+
# {"id":1,"first":"Tyler","last":"Durden"},
|
145
|
+
# {"id":2,"first":"Tony","last":"Stark",}
|
146
|
+
# ]
|
105
147
|
#
|
106
|
-
# for GET http://api.people.com:3000/people.
|
148
|
+
# for GET http://api.people.com:3000/people.json
|
107
149
|
#
|
108
150
|
people = Person.all
|
109
|
-
people.first # => <Person::xxx 'first' => '
|
110
|
-
people.last # => <Person::xxx 'first' => '
|
151
|
+
people.first # => <Person::xxx 'first' => 'Tyler' ...>
|
152
|
+
people.last # => <Person::xxx 'first' => 'Tony' ...>
|
111
153
|
|
112
154
|
==== Create
|
113
155
|
|
114
|
-
Creating a new resource submits the
|
156
|
+
Creating a new resource submits the JSON form of the resource as the body of the request and expects
|
115
157
|
a 'Location' header in the response with the RESTful URL location of the newly created resource. The
|
116
158
|
id of the newly created resource is parsed out of the Location response header and automatically set
|
117
159
|
as the id of the ARes object.
|
118
160
|
|
119
|
-
#
|
161
|
+
# {"first":"Tyler","last":"Durden"}
|
120
162
|
#
|
121
163
|
# is submitted as the body on
|
122
164
|
#
|
123
|
-
#
|
165
|
+
# if include_root_in_json is not set or set to false => {"first":"Tyler"}
|
166
|
+
# if include_root_in_json is set to true => {"person":{"first":"Tyler"}}
|
167
|
+
#
|
168
|
+
# POST http://api.people.com:3000/people.json
|
124
169
|
#
|
125
170
|
# when save is called on a new Person object. An empty response is
|
126
171
|
# is expected with a 'Location' header value:
|
127
172
|
#
|
128
173
|
# Response (201): Location: http://api.people.com:3000/people/2
|
129
174
|
#
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
175
|
+
tyler = Person.new(:first => 'Tyler')
|
176
|
+
tyler.new? # => true
|
177
|
+
tyler.save # => true
|
178
|
+
tyler.new? # => false
|
179
|
+
tyler.id # => 2
|
135
180
|
|
136
181
|
==== Update
|
137
182
|
|
@@ -139,19 +184,22 @@ as the id of the ARes object.
|
|
139
184
|
with the exception that no response headers are needed -- just an empty response when the update on the
|
140
185
|
server side was successful.
|
141
186
|
|
142
|
-
#
|
187
|
+
# {"first":"Tyler"}
|
143
188
|
#
|
144
189
|
# is submitted as the body on
|
145
190
|
#
|
146
|
-
#
|
191
|
+
# if include_root_in_json is not set or set to false => {"first":"Tyler"}
|
192
|
+
# if include_root_in_json is set to true => {"person":{"first":"Tyler"}}
|
193
|
+
#
|
194
|
+
# PUT http://api.people.com:3000/people/1.json
|
147
195
|
#
|
148
196
|
# when save is called on an existing Person object. An empty response is
|
149
197
|
# is expected with code (204)
|
150
198
|
#
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
199
|
+
tyler = Person.find(1)
|
200
|
+
tyler.first # => 'Tyler'
|
201
|
+
tyler.first = 'Tyson'
|
202
|
+
tyler.save # => true
|
155
203
|
|
156
204
|
==== Delete
|
157
205
|
|
@@ -159,29 +207,79 @@ Destruction of a resource can be invoked as a class and instance method of the r
|
|
159
207
|
|
160
208
|
# A request is made to
|
161
209
|
#
|
162
|
-
# DELETE http://api.people.com:3000/people/1.
|
210
|
+
# DELETE http://api.people.com:3000/people/1.json
|
163
211
|
#
|
164
212
|
# for both of these forms. An empty response with
|
165
213
|
# is expected with response code (200)
|
166
214
|
#
|
167
|
-
|
168
|
-
|
169
|
-
|
215
|
+
tyler = Person.find(1)
|
216
|
+
tyler.destroy # => true
|
217
|
+
tyler.exists? # => false
|
170
218
|
Person.delete(2) # => true
|
171
219
|
Person.exists?(2) # => false
|
172
220
|
|
221
|
+
==== Associations
|
222
|
+
|
223
|
+
Relationships between resources can be declared using the standard association syntax
|
224
|
+
that should be familiar to anyone who uses activerecord. For example, using the
|
225
|
+
class definition below:
|
226
|
+
|
227
|
+
class Post < ActiveResource::Base
|
228
|
+
self.site = "http://blog.io"
|
229
|
+
has_many :comments
|
230
|
+
end
|
231
|
+
|
232
|
+
post = Post.find(1) # issues GET http://blog.io/posts/1.json
|
233
|
+
comments = post.comments # issues GET http://blog.io/comments.json?post_id=1
|
234
|
+
|
235
|
+
|
236
|
+
If you control the server, you may wish to include nested resources thus avoiding a
|
237
|
+
second network request. Given the resource above, if the response includes comments
|
238
|
+
in the response, they will be automatically loaded into the activeresource object.
|
239
|
+
The server-side model can be adjusted as follows to include comments in the response.
|
240
|
+
|
241
|
+
class Post < ActiveRecord::Base
|
242
|
+
has_many :comments
|
243
|
+
|
244
|
+
def as_json(options)
|
245
|
+
super.merge(:include=>[:comments])
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
==== Logging
|
250
|
+
|
251
|
+
Active Resource instruments the event `request.active_resource` when doing a request
|
252
|
+
to the remote service. You can subscribe to it by doing:
|
253
|
+
|
254
|
+
ActiveSupport::Notifications.subscribe('request.active_resource') do |name, start, finish, id, payload|
|
255
|
+
|
256
|
+
The `payload` is a `Hash` with the following keys:
|
257
|
+
|
258
|
+
* `method` as a `Symbol`
|
259
|
+
* `request_uri` as a `String`
|
260
|
+
* `result` as an `Net::HTTPResponse`
|
261
|
+
|
173
262
|
== License
|
174
263
|
|
175
|
-
Active Resource is released under the MIT license
|
264
|
+
Active Resource is released under the MIT license:
|
265
|
+
|
266
|
+
* http://www.opensource.org/licenses/MIT
|
267
|
+
|
268
|
+
== Contributing to Active Resource
|
269
|
+
|
270
|
+
Active Resource is work of many contributors. You're encouraged to submit pull requests, propose
|
271
|
+
features and discuss issues.
|
272
|
+
|
273
|
+
See {CONTRIBUTING}[https://github.com/rails/activeresource/blob/master/CONTRIBUTING.md].
|
176
274
|
|
177
275
|
== Support
|
178
276
|
|
179
|
-
API documentation is at
|
277
|
+
Full API documentation is available at
|
180
278
|
|
181
|
-
* http://
|
279
|
+
* http://rubydoc.info/gems/activeresource
|
182
280
|
|
183
281
|
Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
|
184
282
|
|
185
|
-
* https://github.com/rails/
|
283
|
+
* https://github.com/rails/activeresource/issues
|
186
284
|
|
187
285
|
You can find more usage information in the ActiveResource::Base documentation.
|
data/lib/active_resource.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
#--
|
2
|
-
# Copyright (c) 2006-
|
4
|
+
# Copyright (c) 2006-2012 David Heinemeier Hansson
|
3
5
|
#
|
4
6
|
# Permission is hereby granted, free of charge, to any person obtaining
|
5
7
|
# a copy of this software and associated documentation files (the
|
@@ -21,26 +23,24 @@
|
|
21
23
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
24
|
#++
|
23
25
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
$:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include?(activemodel_path)
|
29
|
-
|
30
|
-
require 'active_support'
|
31
|
-
require 'active_model'
|
32
|
-
require 'active_resource/exceptions'
|
33
|
-
require 'active_resource/version'
|
26
|
+
require "active_support"
|
27
|
+
require "active_model"
|
28
|
+
require "active_resource/exceptions"
|
29
|
+
require "active_resource/version"
|
34
30
|
|
35
31
|
module ActiveResource
|
36
32
|
extend ActiveSupport::Autoload
|
37
33
|
|
38
34
|
autoload :Base
|
35
|
+
autoload :Callbacks
|
39
36
|
autoload :Connection
|
40
37
|
autoload :CustomMethods
|
41
38
|
autoload :Formats
|
42
39
|
autoload :HttpMock
|
43
|
-
autoload :Observing
|
44
40
|
autoload :Schema
|
41
|
+
autoload :Singleton
|
45
42
|
autoload :Validations
|
43
|
+
autoload :Collection
|
46
44
|
end
|
45
|
+
|
46
|
+
require "active_resource/railtie" if defined?(Rails.application)
|
@@ -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
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveResource::Associations
|
4
|
+
module Builder
|
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"
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
# Specifies a one-to-many association.
|
14
|
+
#
|
15
|
+
# === Options
|
16
|
+
# [:class_name]
|
17
|
+
# Specify the class name of the association. This class name would
|
18
|
+
# be used for resolving the association class.
|
19
|
+
#
|
20
|
+
# ==== Example for [:class_name] - option
|
21
|
+
# GET /posts/123.json delivers following response body:
|
22
|
+
# {
|
23
|
+
# title: "ActiveResource now has associations",
|
24
|
+
# body: "Lorem Ipsum"
|
25
|
+
# comments: [
|
26
|
+
# {
|
27
|
+
# content: "..."
|
28
|
+
# },
|
29
|
+
# {
|
30
|
+
# content: "..."
|
31
|
+
# }
|
32
|
+
# ]
|
33
|
+
# }
|
34
|
+
# ====
|
35
|
+
#
|
36
|
+
# <tt>has_many :comments, :class_name => 'myblog/comment'</tt>
|
37
|
+
# Would resolve those comments into the <tt>Myblog::Comment</tt> class.
|
38
|
+
#
|
39
|
+
# If the response body does not contain an attribute matching the association name
|
40
|
+
# a request sent to the index action under the current resource.
|
41
|
+
# For the example above, if the comments are not present the requested path would be:
|
42
|
+
# GET /posts/123/comments.xml
|
43
|
+
def has_many(name, options = {})
|
44
|
+
Builder::HasMany.build(self, name, options)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Specifies a one-to-one association.
|
48
|
+
#
|
49
|
+
# === Options
|
50
|
+
# [:class_name]
|
51
|
+
# Specify the class name of the association. This class name would
|
52
|
+
# be used for resolving the association class.
|
53
|
+
#
|
54
|
+
# ==== Example for [:class_name] - option
|
55
|
+
# GET /posts/1.json delivers following response body:
|
56
|
+
# {
|
57
|
+
# title: "ActiveResource now has associations",
|
58
|
+
# body: "Lorem Ipsum",
|
59
|
+
# author: {
|
60
|
+
# name: "Gabby Blogger",
|
61
|
+
# }
|
62
|
+
# }
|
63
|
+
# ====
|
64
|
+
#
|
65
|
+
# <tt>has_one :author, :class_name => 'myblog/author'</tt>
|
66
|
+
# Would resolve this author into the <tt>Myblog::Author</tt> class.
|
67
|
+
#
|
68
|
+
# If the response body does not contain an attribute matching the association name
|
69
|
+
# a request is sent to a singleton path under the current resource.
|
70
|
+
# For example, if a Product class <tt>has_one :inventory</tt> calling <tt>Product#inventory</tt>
|
71
|
+
# will generate a request on /products/:product_id/inventory.json.
|
72
|
+
#
|
73
|
+
def has_one(name, options = {})
|
74
|
+
Builder::HasOne.build(self, name, options)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Specifies a one-to-one association with another class. This class should only be used
|
78
|
+
# if this class contains the foreign key.
|
79
|
+
#
|
80
|
+
# Methods will be added for retrieval and query for a single associated object, for which
|
81
|
+
# this object holds an id:
|
82
|
+
#
|
83
|
+
# [association(force_reload = false)]
|
84
|
+
# Returns the associated object. +nil+ is returned if the foreign key is +nil+.
|
85
|
+
# Throws a ActiveResource::ResourceNotFound exception if the foreign key is not +nil+
|
86
|
+
# and the resource is not found.
|
87
|
+
#
|
88
|
+
# (+association+ is replaced with the symbol passed as the first argument, so
|
89
|
+
# <tt>belongs_to :post</tt> would add among others <tt>post.nil?</tt>.
|
90
|
+
#
|
91
|
+
# === Example
|
92
|
+
#
|
93
|
+
# A Comment class declares <tt>belongs_to :post</tt>, which will add:
|
94
|
+
# * <tt>Comment#post</tt> (similar to <tt>Post.find(post_id)</tt>)
|
95
|
+
# The declaration can also include an options hash to specialize the behavior of the association.
|
96
|
+
#
|
97
|
+
# === Options
|
98
|
+
# [:class_name]
|
99
|
+
# Specify the class name for the association. Use it only if that name can't be inferred from association name.
|
100
|
+
# So <tt>belongs_to :post</tt> will by default be linked to the Post class, but if the real class name is Article,
|
101
|
+
# you'll have to specify it with this option.
|
102
|
+
# [:foreign_key]
|
103
|
+
# Specify the foreign key used for the association. By default this is guessed to be the name
|
104
|
+
# of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :post</tt>
|
105
|
+
# association will use "post_id" as the default <tt>:foreign_key</tt>. Similarly,
|
106
|
+
# <tt>belongs_to :article, :class_name => "Post"</tt> will use a foreign key
|
107
|
+
# of "article_id".
|
108
|
+
#
|
109
|
+
# Option examples:
|
110
|
+
# <tt>belongs_to :customer, :class_name => 'User'</tt>
|
111
|
+
# Creates a belongs_to association called customer which is represented through the <tt>User</tt> class.
|
112
|
+
#
|
113
|
+
# <tt>belongs_to :customer, :foreign_key => 'user_id'</tt>
|
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>
|
115
|
+
#
|
116
|
+
def belongs_to(name, options = {})
|
117
|
+
Builder::BelongsTo.build(self, name, options)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Defines the belongs_to association finder method
|
121
|
+
def defines_belongs_to_finder_method(reflection)
|
122
|
+
method_name = reflection.name
|
123
|
+
ivar_name = :"@#{method_name}"
|
124
|
+
|
125
|
+
if method_defined?(method_name)
|
126
|
+
instance_variable_set(ivar_name, nil)
|
127
|
+
remove_method(method_name)
|
128
|
+
end
|
129
|
+
|
130
|
+
define_method(method_name) do
|
131
|
+
if instance_variable_defined?(ivar_name)
|
132
|
+
instance_variable_get(ivar_name)
|
133
|
+
elsif attributes.include?(method_name)
|
134
|
+
attributes[method_name]
|
135
|
+
elsif association_id = send(reflection.foreign_key)
|
136
|
+
instance_variable_set(ivar_name, reflection.klass.find(association_id))
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def defines_has_many_finder_method(reflection)
|
142
|
+
method_name = reflection.name
|
143
|
+
ivar_name = :"@#{method_name}"
|
144
|
+
|
145
|
+
define_method(method_name) do
|
146
|
+
if instance_variable_defined?(ivar_name)
|
147
|
+
instance_variable_get(ivar_name)
|
148
|
+
elsif attributes.include?(method_name)
|
149
|
+
attributes[method_name]
|
150
|
+
elsif !new_record?
|
151
|
+
instance_variable_set(ivar_name, reflection.klass.find(:all, params: { :"#{self.class.element_name}_id" => self.id }))
|
152
|
+
else
|
153
|
+
instance_variable_set(ivar_name, self.class.collection_parser.new)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# Defines the has_one association
|
159
|
+
def defines_has_one_finder_method(reflection)
|
160
|
+
method_name = reflection.name
|
161
|
+
ivar_name = :"@#{method_name}"
|
162
|
+
|
163
|
+
define_method(method_name) do
|
164
|
+
if instance_variable_defined?(ivar_name)
|
165
|
+
instance_variable_get(ivar_name)
|
166
|
+
elsif attributes.include?(method_name)
|
167
|
+
attributes[method_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 }))
|
170
|
+
else
|
171
|
+
instance_variable_set(ivar_name, reflection.klass.find(:one, from: "/#{self.class.collection_name}/#{self.id}/#{method_name}#{self.class.format_extension}"))
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|