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
data/lib/active_resource/base.rb
CHANGED
@@ -1,24 +1,27 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
|
14
|
-
require
|
15
|
-
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support"
|
4
|
+
require "active_support/core_ext/class/attribute_accessors"
|
5
|
+
require "active_support/core_ext/class/attribute"
|
6
|
+
require "active_support/core_ext/hash/indifferent_access"
|
7
|
+
require "active_support/core_ext/kernel/reporting"
|
8
|
+
require "active_support/core_ext/module/delegation"
|
9
|
+
require "active_support/core_ext/module/aliasing"
|
10
|
+
require "active_support/core_ext/object/blank"
|
11
|
+
require "active_support/core_ext/object/to_query"
|
12
|
+
require "active_support/core_ext/object/duplicable"
|
13
|
+
require "set"
|
14
|
+
require "uri"
|
15
|
+
|
16
|
+
require "active_resource/connection"
|
17
|
+
require "active_resource/formats"
|
18
|
+
require "active_resource/schema"
|
19
|
+
require "active_resource/log_subscriber"
|
20
|
+
require "active_resource/associations"
|
21
|
+
require "active_resource/reflection"
|
22
|
+
require "active_resource/threadsafe_attributes"
|
23
|
+
|
24
|
+
require "active_model/serializers/xml"
|
22
25
|
|
23
26
|
module ActiveResource
|
24
27
|
# ActiveResource::Base is the main class for mapping RESTful resources as models in a Rails application.
|
@@ -123,18 +126,25 @@ module ActiveResource
|
|
123
126
|
# requests. These sensitive credentials are sent unencrypted, visible to
|
124
127
|
# any onlooker, so this scheme should only be used with SSL.
|
125
128
|
#
|
126
|
-
# Digest authentication sends a
|
129
|
+
# Digest authentication sends a cryptographic hash of the username, password,
|
127
130
|
# HTTP method, URI, and a single-use secret key provided by the server.
|
128
131
|
# Sensitive credentials aren't visible to onlookers, so digest authentication
|
129
132
|
# doesn't require SSL. However, this doesn't mean the connection is secure!
|
130
133
|
# Just the username and password.
|
131
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
|
+
#
|
132
142
|
# (You really, really want to use SSL. There's little reason not to.)
|
133
143
|
#
|
134
144
|
# === Picking an authentication scheme
|
135
145
|
#
|
136
|
-
# Basic authentication is the default. To switch to digest authentication,
|
137
|
-
# 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+:
|
138
148
|
#
|
139
149
|
# class Person < ActiveResource::Base
|
140
150
|
# self.auth_type = :digest
|
@@ -153,6 +163,16 @@ module ActiveResource
|
|
153
163
|
# self.site = "https://ryan:password@api.people.com"
|
154
164
|
# end
|
155
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
|
+
#
|
156
176
|
# === Certificate Authentication
|
157
177
|
#
|
158
178
|
# You can also authenticate using an X509 certificate. <tt>See ssl_options=</tt> for all options.
|
@@ -198,7 +218,9 @@ module ActiveResource
|
|
198
218
|
# * 405 - ActiveResource::MethodNotAllowed
|
199
219
|
# * 409 - ActiveResource::ResourceConflict
|
200
220
|
# * 410 - ActiveResource::ResourceGone
|
221
|
+
# * 412 - ActiveResource::PreconditionFailed
|
201
222
|
# * 422 - ActiveResource::ResourceInvalid (rescued by save as validation errors)
|
223
|
+
# * 429 - ActiveResource::TooManyRequests
|
202
224
|
# * 401..499 - ActiveResource::ClientError
|
203
225
|
# * 500..599 - ActiveResource::ServerError
|
204
226
|
# * Other - ActiveResource::ConnectionError
|
@@ -298,17 +320,24 @@ module ActiveResource
|
|
298
320
|
##
|
299
321
|
# :singleton-method:
|
300
322
|
# The logger for diagnosing and tracing Active Resource calls.
|
301
|
-
|
323
|
+
cattr_reader :logger
|
324
|
+
|
325
|
+
def self.logger=(logger)
|
326
|
+
self._connection = nil
|
327
|
+
@@logger = logger
|
328
|
+
end
|
302
329
|
|
303
330
|
class_attribute :_format
|
304
331
|
class_attribute :_collection_parser
|
305
332
|
class_attribute :include_format_in_path
|
306
333
|
self.include_format_in_path = true
|
307
334
|
|
335
|
+
class_attribute :connection_class
|
336
|
+
self.connection_class = Connection
|
308
337
|
|
309
338
|
class << self
|
310
339
|
include ThreadsafeAttributes
|
311
|
-
threadsafe_attribute :_headers, :_connection, :_user, :_password, :_site, :_proxy
|
340
|
+
threadsafe_attribute :_headers, :_connection, :_user, :_password, :_bearer_token, :_site, :_proxy
|
312
341
|
|
313
342
|
# Creates a schema for this resource - setting the attributes that are
|
314
343
|
# known prior to fetching an instance from the remote system.
|
@@ -374,12 +403,12 @@ module ActiveResource
|
|
374
403
|
@schema ||= {}.with_indifferent_access
|
375
404
|
@known_attributes ||= []
|
376
405
|
|
377
|
-
schema_definition.attrs.each do |k,v|
|
406
|
+
schema_definition.attrs.each do |k, v|
|
378
407
|
@schema[k] = v
|
379
408
|
@known_attributes << k
|
380
409
|
end
|
381
410
|
|
382
|
-
schema
|
411
|
+
@schema
|
383
412
|
else
|
384
413
|
@schema ||= nil
|
385
414
|
end
|
@@ -396,7 +425,7 @@ module ActiveResource
|
|
396
425
|
# example:
|
397
426
|
#
|
398
427
|
# class Person < ActiveResource::Base
|
399
|
-
# schema = {'name' => :string, 'age' => :integer }
|
428
|
+
# self.schema = {'name' => :string, 'age' => :integer }
|
400
429
|
# end
|
401
430
|
#
|
402
431
|
# The keys/values can be strings or symbols. They will be converted to
|
@@ -413,7 +442,7 @@ module ActiveResource
|
|
413
442
|
raise ArgumentError, "Expected a hash" unless the_schema.kind_of? Hash
|
414
443
|
|
415
444
|
schema do
|
416
|
-
the_schema.each {|k,v| attribute(k,v) }
|
445
|
+
the_schema.each { |k, v| attribute(k, v) }
|
417
446
|
end
|
418
447
|
end
|
419
448
|
|
@@ -461,8 +490,8 @@ module ActiveResource
|
|
461
490
|
self._site = nil
|
462
491
|
else
|
463
492
|
self._site = create_site_uri_from(site)
|
464
|
-
self._user = URI.
|
465
|
-
self._password = URI.
|
493
|
+
self._user = URI::DEFAULT_PARSER.unescape(_site.user) if _site.user
|
494
|
+
self._password = URI::DEFAULT_PARSER.unescape(_site.password) if _site.password
|
466
495
|
end
|
467
496
|
end
|
468
497
|
|
@@ -514,6 +543,22 @@ module ActiveResource
|
|
514
543
|
self._password = password
|
515
544
|
end
|
516
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
|
+
|
517
562
|
def auth_type
|
518
563
|
if defined?(@auth_type)
|
519
564
|
@auth_type
|
@@ -615,7 +660,7 @@ module ActiveResource
|
|
615
660
|
# * <tt>:ssl_timeout</tt> -The SSL timeout in seconds.
|
616
661
|
def ssl_options=(options)
|
617
662
|
self._connection = nil
|
618
|
-
@ssl_options
|
663
|
+
@ssl_options = options
|
619
664
|
end
|
620
665
|
|
621
666
|
# Returns the SSL options hash.
|
@@ -632,10 +677,13 @@ module ActiveResource
|
|
632
677
|
# or not (defaults to <tt>false</tt>).
|
633
678
|
def connection(refresh = false)
|
634
679
|
if _connection_defined? || superclass == Object
|
635
|
-
self._connection =
|
680
|
+
self._connection = connection_class.new(
|
681
|
+
site, format, logger: logger
|
682
|
+
) if refresh || _connection.nil?
|
636
683
|
_connection.proxy = proxy if proxy
|
637
684
|
_connection.user = user if user
|
638
685
|
_connection.password = password if password
|
686
|
+
_connection.bearer_token = bearer_token if bearer_token
|
639
687
|
_connection.auth_type = auth_type if auth_type
|
640
688
|
_connection.timeout = timeout if timeout
|
641
689
|
_connection.open_timeout = open_timeout if open_timeout
|
@@ -648,11 +696,10 @@ module ActiveResource
|
|
648
696
|
end
|
649
697
|
|
650
698
|
def headers
|
651
|
-
self._headers ||=
|
652
|
-
|
653
|
-
self._headers = superclass.headers.merge(_headers)
|
699
|
+
self._headers ||= if superclass != Object
|
700
|
+
InheritingHash.new(superclass.headers)
|
654
701
|
else
|
655
|
-
|
702
|
+
{}
|
656
703
|
end
|
657
704
|
end
|
658
705
|
|
@@ -678,15 +725,15 @@ module ActiveResource
|
|
678
725
|
return primary_key if primary_key.is_a?(Symbol)
|
679
726
|
primary_key.dup.freeze
|
680
727
|
else
|
681
|
-
|
728
|
+
"id"
|
682
729
|
end
|
683
730
|
end
|
684
731
|
|
685
732
|
# Gets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.json</tt>)
|
686
733
|
# This method is regenerated at runtime based on what the \prefix is set to.
|
687
|
-
def prefix(options={})
|
734
|
+
def prefix(options = {})
|
688
735
|
default = site.path
|
689
|
-
default <<
|
736
|
+
default << "/" unless default[-1..-1] == "/"
|
690
737
|
# generate the actual method based on the current site path
|
691
738
|
self.prefix = default
|
692
739
|
prefix(options)
|
@@ -701,9 +748,9 @@ module ActiveResource
|
|
701
748
|
|
702
749
|
# Sets the \prefix for a resource's nested URL (e.g., <tt>prefix/collectionname/1.json</tt>).
|
703
750
|
# Default value is <tt>site.path</tt>.
|
704
|
-
def prefix=(value =
|
751
|
+
def prefix=(value = "/")
|
705
752
|
# Replace :placeholders with '#{embedded options[:lookups]}'
|
706
|
-
prefix_call = value.gsub(/:\w+/) { |key| "\#{URI.
|
753
|
+
prefix_call = value.gsub(/:\w+/) { |key| "\#{URI::DEFAULT_PARSER.escape options[#{key}].to_s}" }
|
707
754
|
|
708
755
|
# Clear prefix parameters in case they have been cached
|
709
756
|
@prefix_parameters = nil
|
@@ -720,10 +767,10 @@ module ActiveResource
|
|
720
767
|
raise
|
721
768
|
end
|
722
769
|
|
723
|
-
alias_method :set_prefix, :prefix=
|
770
|
+
alias_method :set_prefix, :prefix= # :nodoc:
|
724
771
|
|
725
|
-
alias_method :set_element_name, :element_name=
|
726
|
-
alias_method :set_collection_name, :collection_name=
|
772
|
+
alias_method :set_element_name, :element_name= # :nodoc:
|
773
|
+
alias_method :set_collection_name, :collection_name= # :nodoc:
|
727
774
|
|
728
775
|
def format_extension
|
729
776
|
include_format_in_path ? ".#{format.extension}" : ""
|
@@ -759,7 +806,37 @@ module ActiveResource
|
|
759
806
|
check_prefix_options(prefix_options)
|
760
807
|
|
761
808
|
prefix_options, query_options = split_options(prefix_options) if query_options.nil?
|
762
|
-
"#{prefix(prefix_options)}#{collection_name}/#{URI.
|
809
|
+
"#{prefix(prefix_options)}#{collection_name}/#{URI.encode_www_form_component(id.to_s)}#{format_extension}#{query_string(query_options)}"
|
810
|
+
end
|
811
|
+
|
812
|
+
# Gets the element url for the given ID in +id+. If the +query_options+ parameter is omitted, Rails
|
813
|
+
# will split from the \prefix options.
|
814
|
+
#
|
815
|
+
# ==== Options
|
816
|
+
# +prefix_options+ - A \hash to add a \prefix to the request for nested URLs (e.g., <tt>:account_id => 19</tt>
|
817
|
+
# would yield a URL like <tt>https://37s.sunrise.com/accounts/19/purchases.json</tt>).
|
818
|
+
#
|
819
|
+
# +query_options+ - A \hash to add items to the query string for the request.
|
820
|
+
#
|
821
|
+
# ==== Examples
|
822
|
+
# Post.element_url(1)
|
823
|
+
# # => https://37s.sunrise.com/posts/1.json
|
824
|
+
#
|
825
|
+
# class Comment < ActiveResource::Base
|
826
|
+
# self.site = "https://37s.sunrise.com/posts/:post_id"
|
827
|
+
# end
|
828
|
+
#
|
829
|
+
# Comment.element_url(1, :post_id => 5)
|
830
|
+
# # => https://37s.sunrise.com/posts/5/comments/1.json
|
831
|
+
#
|
832
|
+
# Comment.element_url(1, :post_id => 5, :active => 1)
|
833
|
+
# # => https://37s.sunrise.com/posts/5/comments/1.json?active=1
|
834
|
+
#
|
835
|
+
# Comment.element_url(1, {:post_id => 5}, {:active => 1})
|
836
|
+
# # => https://37s.sunrise.com/posts/5/comments/1.json?active=1
|
837
|
+
#
|
838
|
+
def element_url(id, prefix_options = {}, query_options = nil)
|
839
|
+
URI.join(site, element_path(id, prefix_options, query_options)).to_s
|
763
840
|
end
|
764
841
|
|
765
842
|
# Gets the new element path for REST resources.
|
@@ -809,7 +886,7 @@ module ActiveResource
|
|
809
886
|
"#{prefix(prefix_options)}#{collection_name}#{format_extension}#{query_string(query_options)}"
|
810
887
|
end
|
811
888
|
|
812
|
-
alias_method :set_primary_key, :primary_key=
|
889
|
+
alias_method :set_primary_key, :primary_key= # :nodoc:
|
813
890
|
|
814
891
|
# Builds a new, unsaved record using the default values from the remote server so
|
815
892
|
# that it can be used with RESTful forms.
|
@@ -852,6 +929,17 @@ module ActiveResource
|
|
852
929
|
self.new(attributes).tap { |resource| resource.save }
|
853
930
|
end
|
854
931
|
|
932
|
+
# Creates a new resource (just like <tt>create</tt>) and makes a request to the
|
933
|
+
# remote service that it be saved, but runs validations and raises
|
934
|
+
# <tt>ActiveResource::ResourceInvalid</tt>, making it equivalent to the following
|
935
|
+
# simultaneous calls:
|
936
|
+
#
|
937
|
+
# ryan = Person.new(:first => 'ryan')
|
938
|
+
# ryan.save!
|
939
|
+
def create!(attributes = {})
|
940
|
+
self.new(attributes).tap { |resource| resource.save! }
|
941
|
+
end
|
942
|
+
|
855
943
|
# Core method for finding resources. Used similarly to Active Record's +find+ method.
|
856
944
|
#
|
857
945
|
# ==== Arguments
|
@@ -916,11 +1004,18 @@ module ActiveResource
|
|
916
1004
|
options = arguments.slice!(0) || {}
|
917
1005
|
|
918
1006
|
case scope
|
919
|
-
|
920
|
-
|
921
|
-
|
922
|
-
|
923
|
-
|
1007
|
+
when :all
|
1008
|
+
find_every(options)
|
1009
|
+
when :first
|
1010
|
+
collection = find_every(options)
|
1011
|
+
collection && collection.first
|
1012
|
+
when :last
|
1013
|
+
collection = find_every(options)
|
1014
|
+
collection && collection.last
|
1015
|
+
when :one
|
1016
|
+
find_one(options)
|
1017
|
+
else
|
1018
|
+
find_single(scope, options)
|
924
1019
|
end
|
925
1020
|
end
|
926
1021
|
|
@@ -947,7 +1042,7 @@ module ActiveResource
|
|
947
1042
|
|
948
1043
|
def where(clauses = {})
|
949
1044
|
raise ArgumentError, "expected a clauses Hash, got #{clauses.inspect}" unless clauses.is_a? Hash
|
950
|
-
find(:all, :
|
1045
|
+
find(:all, params: clauses)
|
951
1046
|
end
|
952
1047
|
|
953
1048
|
|
@@ -981,7 +1076,7 @@ module ActiveResource
|
|
981
1076
|
prefix_options, query_options = split_options(options[:params])
|
982
1077
|
path = element_path(id, prefix_options, query_options)
|
983
1078
|
response = connection.head(path, headers)
|
984
|
-
response.code.to_i
|
1079
|
+
(200..206).include? response.code.to_i
|
985
1080
|
end
|
986
1081
|
# id && !find_single(id, options).nil?
|
987
1082
|
rescue ActiveResource::ResourceNotFound, ActiveResource::ResourceGone
|
@@ -989,7 +1084,6 @@ module ActiveResource
|
|
989
1084
|
end
|
990
1085
|
|
991
1086
|
private
|
992
|
-
|
993
1087
|
def check_prefix_options(prefix_options)
|
994
1088
|
p_options = HashWithIndifferentAccess.new(prefix_options)
|
995
1089
|
prefix_parameters.each do |p|
|
@@ -999,23 +1093,26 @@ module ActiveResource
|
|
999
1093
|
|
1000
1094
|
# Find every resource
|
1001
1095
|
def find_every(options)
|
1002
|
-
|
1096
|
+
params = options[:params]
|
1097
|
+
prefix_options, query_options = split_options(params)
|
1098
|
+
|
1099
|
+
response =
|
1003
1100
|
case from = options[:from]
|
1004
1101
|
when Symbol
|
1005
|
-
|
1102
|
+
get(from, params)
|
1006
1103
|
when String
|
1007
|
-
path = "#{from}#{query_string(
|
1008
|
-
|
1104
|
+
path = "#{from}#{query_string(query_options)}"
|
1105
|
+
format.decode(connection.get(path, headers).body)
|
1009
1106
|
else
|
1010
|
-
prefix_options, query_options = split_options(options[:params])
|
1011
1107
|
path = collection_path(prefix_options, query_options)
|
1012
|
-
|
1108
|
+
format.decode(connection.get(path, headers).body)
|
1013
1109
|
end
|
1014
|
-
|
1015
|
-
|
1016
|
-
|
1017
|
-
|
1018
|
-
|
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
|
1019
1116
|
end
|
1020
1117
|
|
1021
1118
|
# Find a single resource from a one-off URL
|
@@ -1076,16 +1173,16 @@ module ActiveResource
|
|
1076
1173
|
prefix_options, query_options = {}, {}
|
1077
1174
|
|
1078
1175
|
(options || {}).each do |key, value|
|
1079
|
-
next if key.blank?
|
1080
|
-
(prefix_parameters.include?(key.to_sym) ? prefix_options : query_options)[key.to_sym] = value
|
1176
|
+
next if key.blank?
|
1177
|
+
(prefix_parameters.include?(key.to_s.to_sym) ? prefix_options : query_options)[key.to_s.to_sym] = value
|
1081
1178
|
end
|
1082
1179
|
|
1083
1180
|
[ prefix_options, query_options ]
|
1084
1181
|
end
|
1085
1182
|
end
|
1086
1183
|
|
1087
|
-
attr_accessor :attributes
|
1088
|
-
attr_accessor :prefix_options
|
1184
|
+
attr_accessor :attributes # :nodoc:
|
1185
|
+
attr_accessor :prefix_options # :nodoc:
|
1089
1186
|
|
1090
1187
|
# If no schema has been defined for the class (see
|
1091
1188
|
# <tt>ActiveResource::schema=</tt>), the default automatic schema is
|
@@ -1142,13 +1239,13 @@ module ActiveResource
|
|
1142
1239
|
# not_ryan.hash # => {:not => "an ARes instance"}
|
1143
1240
|
def clone
|
1144
1241
|
# Clone all attributes except the pk and any nested ARes
|
1145
|
-
cloned =
|
1242
|
+
cloned = attributes.reject { |k, v| k == self.class.primary_key || v.is_a?(ActiveResource::Base) }.transform_values { |v| v.clone }
|
1146
1243
|
# Form the new resource - bypass initialize of resource with 'new' as that will call 'load' which
|
1147
1244
|
# attempts to convert hashes into member objects and arrays into collections of objects. We want
|
1148
1245
|
# the raw objects to be cloned so we bypass load by directly setting the attributes hash.
|
1149
1246
|
resource = self.class.new({})
|
1150
1247
|
resource.prefix_options = self.prefix_options
|
1151
|
-
resource.send :instance_variable_set,
|
1248
|
+
resource.send :instance_variable_set, "@attributes", cloned
|
1152
1249
|
resource
|
1153
1250
|
end
|
1154
1251
|
|
@@ -1323,13 +1420,13 @@ module ActiveResource
|
|
1323
1420
|
# Person.delete(guys_id)
|
1324
1421
|
# that_guy.exists? # => false
|
1325
1422
|
def exists?
|
1326
|
-
!new? && self.class.exists?(to_param, :
|
1423
|
+
!new? && self.class.exists?(to_param, params: prefix_options)
|
1327
1424
|
end
|
1328
1425
|
|
1329
1426
|
# Returns the serialized string representation of the resource in the configured
|
1330
1427
|
# serialization format specified in ActiveResource::Base.format. The options
|
1331
1428
|
# applicable depend on the configured encoding format.
|
1332
|
-
def encode(options={})
|
1429
|
+
def encode(options = {})
|
1333
1430
|
send("to_#{self.class.format.extension}", options)
|
1334
1431
|
end
|
1335
1432
|
|
@@ -1345,7 +1442,7 @@ module ActiveResource
|
|
1345
1442
|
# my_branch.reload
|
1346
1443
|
# my_branch.name # => "Wilson Road"
|
1347
1444
|
def reload
|
1348
|
-
self.load(self.class.find(to_param, :
|
1445
|
+
self.load(self.class.find(to_param, params: @prefix_options).attributes, false, true)
|
1349
1446
|
end
|
1350
1447
|
|
1351
1448
|
# A method to manually load attributes from a \hash. Recursively loads collections of
|
@@ -1370,7 +1467,11 @@ module ActiveResource
|
|
1370
1467
|
# your_supplier.load(my_attrs)
|
1371
1468
|
# your_supplier.save
|
1372
1469
|
def load(attributes, remove_root = false, persisted = false)
|
1373
|
-
|
1470
|
+
unless attributes.respond_to?(:to_hash)
|
1471
|
+
raise ArgumentError, "expected attributes to be able to convert to Hash, got #{attributes.inspect}"
|
1472
|
+
end
|
1473
|
+
|
1474
|
+
attributes = attributes.to_hash
|
1374
1475
|
@prefix_options, attributes = split_options(attributes)
|
1375
1476
|
|
1376
1477
|
if attributes.keys.size == 1
|
@@ -1382,21 +1483,21 @@ module ActiveResource
|
|
1382
1483
|
attributes.each do |key, value|
|
1383
1484
|
@attributes[key.to_s] =
|
1384
1485
|
case value
|
1385
|
-
|
1386
|
-
|
1387
|
-
|
1388
|
-
|
1389
|
-
|
1390
|
-
|
1391
|
-
|
1392
|
-
|
1393
|
-
end
|
1486
|
+
when Array
|
1487
|
+
resource = nil
|
1488
|
+
value.map do |attrs|
|
1489
|
+
if attrs.is_a?(Hash)
|
1490
|
+
resource ||= find_or_create_resource_for_collection(key)
|
1491
|
+
resource.new(attrs, persisted)
|
1492
|
+
else
|
1493
|
+
attrs.duplicable? ? attrs.dup : attrs
|
1394
1494
|
end
|
1395
|
-
|
1396
|
-
|
1397
|
-
|
1398
|
-
|
1399
|
-
|
1495
|
+
end
|
1496
|
+
when Hash
|
1497
|
+
resource = find_or_create_resource_for(key)
|
1498
|
+
resource.new(value, persisted)
|
1499
|
+
else
|
1500
|
+
value.duplicable? ? value.dup : value
|
1400
1501
|
end
|
1401
1502
|
end
|
1402
1503
|
self
|
@@ -1438,7 +1539,7 @@ module ActiveResource
|
|
1438
1539
|
# A method to determine if an object responds to a message (e.g., a method call). In Active Resource, a Person object with a
|
1439
1540
|
# +name+ attribute can answer <tt>true</tt> to <tt>my_person.respond_to?(:name)</tt>, <tt>my_person.respond_to?(:name=)</tt>, and
|
1440
1541
|
# <tt>my_person.respond_to?(:name?)</tt>.
|
1441
|
-
def
|
1542
|
+
def respond_to_missing?(method, include_priv = false)
|
1442
1543
|
method_name = method.to_s
|
1443
1544
|
if attributes.nil?
|
1444
1545
|
super
|
@@ -1453,12 +1554,20 @@ module ActiveResource
|
|
1453
1554
|
end
|
1454
1555
|
end
|
1455
1556
|
|
1456
|
-
def to_json(options={})
|
1457
|
-
super(include_root_in_json ? { :
|
1557
|
+
def to_json(options = {})
|
1558
|
+
super(include_root_in_json ? { root: self.class.element_name }.merge(options) : options)
|
1559
|
+
end
|
1560
|
+
|
1561
|
+
def to_xml(options = {})
|
1562
|
+
super({ root: self.class.element_name }.merge(options))
|
1458
1563
|
end
|
1459
1564
|
|
1460
|
-
def
|
1461
|
-
|
1565
|
+
def read_attribute_for_serialization(n)
|
1566
|
+
if !attributes[n].nil?
|
1567
|
+
attributes[n]
|
1568
|
+
elsif respond_to?(n)
|
1569
|
+
send(n)
|
1570
|
+
end
|
1462
1571
|
end
|
1463
1572
|
|
1464
1573
|
protected
|
@@ -1486,9 +1595,9 @@ module ActiveResource
|
|
1486
1595
|
end
|
1487
1596
|
|
1488
1597
|
def load_attributes_from_response(response)
|
1489
|
-
if
|
1490
|
-
(response[
|
1491
|
-
!response.body.nil? && response.body.strip.size > 0
|
1598
|
+
if response_code_allows_body?(response.code.to_i) &&
|
1599
|
+
(response["Content-Length"].nil? || response["Content-Length"] != "0") &&
|
1600
|
+
!response.body.nil? && response.body.strip.size > 0
|
1492
1601
|
load(self.class.format.decode(response.body), true, true)
|
1493
1602
|
@persisted = true
|
1494
1603
|
end
|
@@ -1496,13 +1605,17 @@ module ActiveResource
|
|
1496
1605
|
|
1497
1606
|
# Takes a response from a typical create post and pulls the ID out
|
1498
1607
|
def id_from_response(response)
|
1499
|
-
response[
|
1608
|
+
response["Location"][/\/([^\/]*?)(\.\w+)?$/, 1] if response["Location"]
|
1500
1609
|
end
|
1501
1610
|
|
1502
1611
|
def element_path(options = nil)
|
1503
1612
|
self.class.element_path(to_param, options || prefix_options)
|
1504
1613
|
end
|
1505
1614
|
|
1615
|
+
def element_url(options = nil)
|
1616
|
+
self.class.element_url(to_param, options || prefix_options)
|
1617
|
+
end
|
1618
|
+
|
1506
1619
|
def new_element_path
|
1507
1620
|
self.class.new_element_path(prefix_options)
|
1508
1621
|
end
|
@@ -1512,14 +1625,9 @@ module ActiveResource
|
|
1512
1625
|
end
|
1513
1626
|
|
1514
1627
|
private
|
1515
|
-
|
1516
|
-
def read_attribute_for_serialization(n)
|
1517
|
-
attributes[n]
|
1518
|
-
end
|
1519
|
-
|
1520
1628
|
# Determine whether the response is allowed to have a body per HTTP 1.1 spec section 4.4.1
|
1521
1629
|
def response_code_allows_body?(c)
|
1522
|
-
!((100..199).include?(c) || [204,304].include?(c))
|
1630
|
+
!((100..199).include?(c) || [204, 304].include?(c))
|
1523
1631
|
end
|
1524
1632
|
|
1525
1633
|
# Tries to find a resource for a given collection name; if it fails, then the resource is created
|
@@ -1532,7 +1640,7 @@ module ActiveResource
|
|
1532
1640
|
# if it fails, then the resource is created
|
1533
1641
|
def find_or_create_resource_in_modules(resource_name, module_names)
|
1534
1642
|
receiver = Object
|
1535
|
-
namespaces = module_names[0, module_names.size-1].map do |module_name|
|
1643
|
+
namespaces = module_names[0, module_names.size - 1].map do |module_name|
|
1536
1644
|
receiver = receiver.const_get(module_name)
|
1537
1645
|
end
|
1538
1646
|
const_args = [resource_name, false]
|
@@ -1549,7 +1657,11 @@ module ActiveResource
|
|
1549
1657
|
resource_name = name.to_s.camelize
|
1550
1658
|
|
1551
1659
|
const_args = [resource_name, false]
|
1552
|
-
|
1660
|
+
|
1661
|
+
if !const_valid?(*const_args)
|
1662
|
+
# resource_name is not a valid ruby module name and cannot be created normally
|
1663
|
+
find_or_create_resource_for(:UnnamedResource)
|
1664
|
+
elsif self.class.const_defined?(*const_args)
|
1553
1665
|
self.class.const_get(*const_args)
|
1554
1666
|
else
|
1555
1667
|
ancestors = self.class.name.to_s.split("::")
|
@@ -1565,11 +1677,20 @@ module ActiveResource
|
|
1565
1677
|
end
|
1566
1678
|
end
|
1567
1679
|
|
1680
|
+
def const_valid?(*const_args)
|
1681
|
+
self.class.const_defined?(*const_args)
|
1682
|
+
true
|
1683
|
+
rescue NameError
|
1684
|
+
false
|
1685
|
+
end
|
1686
|
+
|
1568
1687
|
# Create and return a class definition for a resource inside the current resource
|
1569
1688
|
def create_resource_for(resource_name)
|
1570
|
-
resource =
|
1689
|
+
resource = Class.new(ActiveResource::Base)
|
1571
1690
|
resource.prefix = self.class.prefix
|
1572
|
-
resource.site
|
1691
|
+
resource.site = self.class.site
|
1692
|
+
self.class.const_set(resource_name, resource)
|
1693
|
+
|
1573
1694
|
resource
|
1574
1695
|
end
|
1575
1696
|
|
@@ -1577,7 +1698,7 @@ module ActiveResource
|
|
1577
1698
|
self.class.__send__(:split_options, options)
|
1578
1699
|
end
|
1579
1700
|
|
1580
|
-
def method_missing(method_symbol, *arguments)
|
1701
|
+
def method_missing(method_symbol, *arguments) # :nodoc:
|
1581
1702
|
method_name = method_symbol.to_s
|
1582
1703
|
|
1583
1704
|
if method_name =~ /(=|\?)$/
|
@@ -1600,7 +1721,7 @@ module ActiveResource
|
|
1600
1721
|
extend ActiveModel::Naming
|
1601
1722
|
extend ActiveResource::Associations
|
1602
1723
|
|
1603
|
-
include Callbacks, CustomMethods,
|
1724
|
+
include Callbacks, CustomMethods, Validations
|
1604
1725
|
include ActiveModel::Conversion
|
1605
1726
|
include ActiveModel::Serializers::JSON
|
1606
1727
|
include ActiveModel::Serializers::Xml
|
@@ -1609,4 +1730,3 @@ module ActiveResource
|
|
1609
1730
|
|
1610
1731
|
ActiveSupport.run_load_hooks(:active_resource, Base)
|
1611
1732
|
end
|
1612
|
-
|