activeresource 5.1.1 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e0cde8f42f06f37aa13a7098b9a1e828aac34c90971a62f4c9a1a7fcf529ce7
4
- data.tar.gz: 11e7f85a7c565de83cb5248d8ee35783b343b8714e8ed9adcf08462f8aaceeda
3
+ metadata.gz: f2a0c925e2619c4a4204eebdaea70776a57fec944e9eefeb98982ed8cf425bbc
4
+ data.tar.gz: b77c66a74c3fe0d32e16d0ce4eaaec1c772200bda9ea36b2bfba1a3cc4f58b5d
5
5
  SHA512:
6
- metadata.gz: 133f4a82c31d9c925bc1c04a304c63e03982d45aa51b97d7e9ba580a433579802fb811a32009d40ebb7fc84e947fe65c60fd658b2cebb2f2f4c724efc536f528
7
- data.tar.gz: 0f0042b3b644fe8584d34403913f2e11363a684fe438d536303871baaed2a98f26319b0e1dcd036364b42597b48e76c5713a627ea4431a2b2ec9263fce98b3a6
6
+ metadata.gz: 9116078833a262f5a4d184a9cd080f3a29bfc5c07481cf352dd87baa735ba0dfae6159a35ac37ea1ad2e121348a33d958151a048af8dbb6e85e3a387076de3b5
7
+ data.tar.gz: 02e8b7e3aa1fd89f70f67b2696d778bf0c4a5d89c4b6f779ec3ecffc6a099374c06515ec2bfcb4e7175a518f00543f16b1b691670378c2533fcf353ce82cb96f
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.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveResource::Associations::Builder
4
- class Association #:nodoc:
4
+ class Association # :nodoc:
5
5
  # providing a Class-Variable, which will have a different store of subclasses
6
6
  class_attribute :valid_options
7
7
  self.valid_options = [:class_name]
@@ -25,7 +25,6 @@ module ActiveResource::Associations::Builder
25
25
  end
26
26
 
27
27
  private
28
-
29
28
  def validate_options
30
29
  options.assert_valid_keys(self.class.valid_options)
31
30
  end
@@ -148,7 +148,7 @@ module ActiveResource::Associations
148
148
  elsif attributes.include?(method_name)
149
149
  attributes[method_name]
150
150
  elsif !new_record?
151
- instance_variable_set(ivar_name, reflection.klass.find(:all, params: { :"#{self.class.element_name}_id" => self.id }))
151
+ instance_variable_set(ivar_name, reflection.klass.find(:all, params: { "#{self.class.element_name}_id": self.id }))
152
152
  else
153
153
  instance_variable_set(ivar_name, self.class.collection_parser.new)
154
154
  end
@@ -166,7 +166,7 @@ module ActiveResource::Associations
166
166
  elsif attributes.include?(method_name)
167
167
  attributes[method_name]
168
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 }))
169
+ instance_variable_set(ivar_name, reflection.klass.find(params: { "#{self.class.element_name}_id": self.id }))
170
170
  else
171
171
  instance_variable_set(ivar_name, reflection.klass.find(:one, from: "/#{self.class.collection_name}/#{self.id}/#{method_name}#{self.class.format_extension}"))
172
172
  end
@@ -13,7 +13,6 @@ require "active_support/core_ext/object/duplicable"
13
13
  require "set"
14
14
  require "uri"
15
15
 
16
- require "active_support/core_ext/uri"
17
16
  require "active_resource/connection"
18
17
  require "active_resource/formats"
19
18
  require "active_resource/schema"
@@ -127,18 +126,25 @@ module ActiveResource
127
126
  # requests. These sensitive credentials are sent unencrypted, visible to
128
127
  # any onlooker, so this scheme should only be used with SSL.
129
128
  #
130
- # Digest authentication sends a crytographic hash of the username, password,
129
+ # Digest authentication sends a cryptographic hash of the username, password,
131
130
  # HTTP method, URI, and a single-use secret key provided by the server.
132
131
  # Sensitive credentials aren't visible to onlookers, so digest authentication
133
132
  # doesn't require SSL. However, this doesn't mean the connection is secure!
134
133
  # Just the username and password.
135
134
  #
135
+ # Another common way to authenticate requests is via bearer tokens, a scheme
136
+ # originally created as part of the OAuth 2.0 protocol (see RFC 6750).
137
+ #
138
+ # Bearer authentication sends a token, that can maybe only be a short string
139
+ # of hexadecimal characters or even a JWT Token. Similarly to the Basic
140
+ # authentication, this scheme should only be used with SSL.
141
+ #
136
142
  # (You really, really want to use SSL. There's little reason not to.)
137
143
  #
138
144
  # === Picking an authentication scheme
139
145
  #
140
- # Basic authentication is the default. To switch to digest authentication,
141
- # set +auth_type+ to +:digest+:
146
+ # Basic authentication is the default. To switch to digest or bearer token authentication,
147
+ # set +auth_type+ to +:digest+ or +:bearer+:
142
148
  #
143
149
  # class Person < ActiveResource::Base
144
150
  # self.auth_type = :digest
@@ -157,6 +163,16 @@ module ActiveResource
157
163
  # self.site = "https://ryan:password@api.people.com"
158
164
  # end
159
165
  #
166
+ # === Setting the bearer token
167
+ #
168
+ # Set +bearer_token+ on the class:
169
+ #
170
+ # class Person < ActiveResource::Base
171
+ # # Set bearer token directly:
172
+ # self.auth_type = :bearer
173
+ # self.bearer_token = "my-bearer-token"
174
+ # end
175
+ #
160
176
  # === Certificate Authentication
161
177
  #
162
178
  # You can also authenticate using an X509 certificate. <tt>See ssl_options=</tt> for all options.
@@ -202,7 +218,9 @@ module ActiveResource
202
218
  # * 405 - ActiveResource::MethodNotAllowed
203
219
  # * 409 - ActiveResource::ResourceConflict
204
220
  # * 410 - ActiveResource::ResourceGone
221
+ # * 412 - ActiveResource::PreconditionFailed
205
222
  # * 422 - ActiveResource::ResourceInvalid (rescued by save as validation errors)
223
+ # * 429 - ActiveResource::TooManyRequests
206
224
  # * 401..499 - ActiveResource::ClientError
207
225
  # * 500..599 - ActiveResource::ServerError
208
226
  # * Other - ActiveResource::ConnectionError
@@ -319,7 +337,7 @@ module ActiveResource
319
337
 
320
338
  class << self
321
339
  include ThreadsafeAttributes
322
- threadsafe_attribute :_headers, :_connection, :_user, :_password, :_site, :_proxy
340
+ threadsafe_attribute :_headers, :_connection, :_user, :_password, :_bearer_token, :_site, :_proxy
323
341
 
324
342
  # Creates a schema for this resource - setting the attributes that are
325
343
  # known prior to fetching an instance from the remote system.
@@ -407,7 +425,7 @@ module ActiveResource
407
425
  # example:
408
426
  #
409
427
  # class Person < ActiveResource::Base
410
- # schema = {'name' => :string, 'age' => :integer }
428
+ # self.schema = {'name' => :string, 'age' => :integer }
411
429
  # end
412
430
  #
413
431
  # The keys/values can be strings or symbols. They will be converted to
@@ -472,8 +490,8 @@ module ActiveResource
472
490
  self._site = nil
473
491
  else
474
492
  self._site = create_site_uri_from(site)
475
- self._user = URI.parser.unescape(_site.user) if _site.user
476
- self._password = URI.parser.unescape(_site.password) if _site.password
493
+ self._user = URI::DEFAULT_PARSER.unescape(_site.user) if _site.user
494
+ self._password = URI::DEFAULT_PARSER.unescape(_site.password) if _site.password
477
495
  end
478
496
  end
479
497
 
@@ -525,6 +543,22 @@ module ActiveResource
525
543
  self._password = password
526
544
  end
527
545
 
546
+ # Gets the \bearer_token for REST HTTP authentication.
547
+ def bearer_token
548
+ # Not using superclass_delegating_reader. See +site+ for explanation
549
+ if _bearer_token_defined?
550
+ _bearer_token
551
+ elsif superclass != Object && superclass.bearer_token
552
+ superclass.bearer_token.dup.freeze
553
+ end
554
+ end
555
+
556
+ # Sets the \bearer_token for REST HTTP authentication.
557
+ def bearer_token=(bearer_token)
558
+ self._connection = nil
559
+ self._bearer_token = bearer_token
560
+ end
561
+
528
562
  def auth_type
529
563
  if defined?(@auth_type)
530
564
  @auth_type
@@ -649,6 +683,7 @@ module ActiveResource
649
683
  _connection.proxy = proxy if proxy
650
684
  _connection.user = user if user
651
685
  _connection.password = password if password
686
+ _connection.bearer_token = bearer_token if bearer_token
652
687
  _connection.auth_type = auth_type if auth_type
653
688
  _connection.timeout = timeout if timeout
654
689
  _connection.open_timeout = open_timeout if open_timeout
@@ -661,11 +696,10 @@ module ActiveResource
661
696
  end
662
697
 
663
698
  def headers
664
- headers_state = self._headers || {}
665
- if superclass != Object
666
- self._headers = superclass.headers.merge(headers_state)
699
+ self._headers ||= if superclass != Object
700
+ InheritingHash.new(superclass.headers)
667
701
  else
668
- headers_state
702
+ {}
669
703
  end
670
704
  end
671
705
 
@@ -716,7 +750,7 @@ module ActiveResource
716
750
  # Default value is <tt>site.path</tt>.
717
751
  def prefix=(value = "/")
718
752
  # Replace :placeholders with '#{embedded options[:lookups]}'
719
- prefix_call = value.gsub(/:\w+/) { |key| "\#{URI.parser.escape options[#{key}].to_s}" }
753
+ prefix_call = value.gsub(/:\w+/) { |key| "\#{URI::DEFAULT_PARSER.escape options[#{key}].to_s}" }
720
754
 
721
755
  # Clear prefix parameters in case they have been cached
722
756
  @prefix_parameters = nil
@@ -733,10 +767,10 @@ module ActiveResource
733
767
  raise
734
768
  end
735
769
 
736
- alias_method :set_prefix, :prefix= #:nodoc:
770
+ alias_method :set_prefix, :prefix= # :nodoc:
737
771
 
738
- alias_method :set_element_name, :element_name= #:nodoc:
739
- alias_method :set_collection_name, :collection_name= #:nodoc:
772
+ alias_method :set_element_name, :element_name= # :nodoc:
773
+ alias_method :set_collection_name, :collection_name= # :nodoc:
740
774
 
741
775
  def format_extension
742
776
  include_format_in_path ? ".#{format.extension}" : ""
@@ -852,7 +886,7 @@ module ActiveResource
852
886
  "#{prefix(prefix_options)}#{collection_name}#{format_extension}#{query_string(query_options)}"
853
887
  end
854
888
 
855
- alias_method :set_primary_key, :primary_key= #:nodoc:
889
+ alias_method :set_primary_key, :primary_key= # :nodoc:
856
890
 
857
891
  # Builds a new, unsaved record using the default values from the remote server so
858
892
  # that it can be used with RESTful forms.
@@ -1050,7 +1084,6 @@ module ActiveResource
1050
1084
  end
1051
1085
 
1052
1086
  private
1053
-
1054
1087
  def check_prefix_options(prefix_options)
1055
1088
  p_options = HashWithIndifferentAccess.new(prefix_options)
1056
1089
  prefix_parameters.each do |p|
@@ -1060,23 +1093,26 @@ module ActiveResource
1060
1093
 
1061
1094
  # Find every resource
1062
1095
  def find_every(options)
1063
- begin
1096
+ params = options[:params]
1097
+ prefix_options, query_options = split_options(params)
1098
+
1099
+ response =
1064
1100
  case from = options[:from]
1065
1101
  when Symbol
1066
- instantiate_collection(get(from, options[:params]), options[:params])
1102
+ get(from, params)
1067
1103
  when String
1068
- path = "#{from}#{query_string(options[:params])}"
1069
- instantiate_collection(format.decode(connection.get(path, headers).body) || [], options[:params])
1104
+ path = "#{from}#{query_string(query_options)}"
1105
+ format.decode(connection.get(path, headers).body)
1070
1106
  else
1071
- prefix_options, query_options = split_options(options[:params])
1072
1107
  path = collection_path(prefix_options, query_options)
1073
- instantiate_collection((format.decode(connection.get(path, headers).body) || []), query_options, prefix_options)
1108
+ format.decode(connection.get(path, headers).body)
1074
1109
  end
1075
- rescue ActiveResource::ResourceNotFound
1076
- # Swallowing ResourceNotFound exceptions and return nil - as per
1077
- # ActiveRecord.
1078
- nil
1079
- end
1110
+
1111
+ instantiate_collection(response || [], query_options, prefix_options)
1112
+ rescue ActiveResource::ResourceNotFound
1113
+ # Swallowing ResourceNotFound exceptions and return nil - as per
1114
+ # ActiveRecord.
1115
+ nil
1080
1116
  end
1081
1117
 
1082
1118
  # Find a single resource from a one-off URL
@@ -1145,8 +1181,8 @@ module ActiveResource
1145
1181
  end
1146
1182
  end
1147
1183
 
1148
- attr_accessor :attributes #:nodoc:
1149
- attr_accessor :prefix_options #:nodoc:
1184
+ attr_accessor :attributes # :nodoc:
1185
+ attr_accessor :prefix_options # :nodoc:
1150
1186
 
1151
1187
  # If no schema has been defined for the class (see
1152
1188
  # <tt>ActiveResource::schema=</tt>), the default automatic schema is
@@ -1203,7 +1239,7 @@ module ActiveResource
1203
1239
  # not_ryan.hash # => {:not => "an ARes instance"}
1204
1240
  def clone
1205
1241
  # Clone all attributes except the pk and any nested ARes
1206
- cloned = Hash[attributes.reject { |k, v| k == self.class.primary_key || v.is_a?(ActiveResource::Base) }.map { |k, v| [k, v.clone] }]
1242
+ cloned = attributes.reject { |k, v| k == self.class.primary_key || v.is_a?(ActiveResource::Base) }.transform_values { |v| v.clone }
1207
1243
  # Form the new resource - bypass initialize of resource with 'new' as that will call 'load' which
1208
1244
  # attempts to convert hashes into member objects and arrays into collections of objects. We want
1209
1245
  # the raw objects to be cloned so we bypass load by directly setting the attributes hash.
@@ -1589,7 +1625,6 @@ module ActiveResource
1589
1625
  end
1590
1626
 
1591
1627
  private
1592
-
1593
1628
  # Determine whether the response is allowed to have a body per HTTP 1.1 spec section 4.4.1
1594
1629
  def response_code_allows_body?(c)
1595
1630
  !((100..199).include?(c) || [204, 304].include?(c))
@@ -1651,9 +1686,11 @@ module ActiveResource
1651
1686
 
1652
1687
  # Create and return a class definition for a resource inside the current resource
1653
1688
  def create_resource_for(resource_name)
1654
- resource = self.class.const_set(resource_name, Class.new(ActiveResource::Base))
1689
+ resource = Class.new(ActiveResource::Base)
1655
1690
  resource.prefix = self.class.prefix
1656
- resource.site = self.class.site
1691
+ resource.site = self.class.site
1692
+ self.class.const_set(resource_name, resource)
1693
+
1657
1694
  resource
1658
1695
  end
1659
1696
 
@@ -1661,7 +1698,7 @@ module ActiveResource
1661
1698
  self.class.__send__(:split_options, options)
1662
1699
  end
1663
1700
 
1664
- def method_missing(method_symbol, *arguments) #:nodoc:
1701
+ def method_missing(method_symbol, *arguments) # :nodoc:
1665
1702
  method_name = method_symbol.to_s
1666
1703
 
1667
1704
  if method_name =~ /(=|\?)$/
@@ -5,7 +5,7 @@ require "active_support/inflector"
5
5
 
6
6
  module ActiveResource # :nodoc:
7
7
  class Collection # :nodoc:
8
- SELF_DEFINE_METHODS = [:to_a, :collect!, :map!]
8
+ SELF_DEFINE_METHODS = [:to_a, :collect!, :map!, :all?]
9
9
  include Enumerable
10
10
  delegate :to_yaml, :all?, *(Array.instance_methods(false) - SELF_DEFINE_METHODS), to: :to_a
11
11
 
@@ -15,7 +15,7 @@ module ActiveResource # :nodoc:
15
15
  # ActiveResource::Collection is a wrapper to handle parsing index responses that
16
16
  # do not directly map to Rails conventions.
17
17
  #
18
- # You can define a custom class that inherets from ActiveResource::Collection
18
+ # You can define a custom class that inherits from ActiveResource::Collection
19
19
  # in order to to set the elements instance.
20
20
  #
21
21
  # GET /posts.json delivers following response body: