activeresource 6.0.0 → 6.2.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: f2a0c925e2619c4a4204eebdaea70776a57fec944e9eefeb98982ed8cf425bbc
4
- data.tar.gz: b77c66a74c3fe0d32e16d0ce4eaaec1c772200bda9ea36b2bfba1a3cc4f58b5d
3
+ metadata.gz: 56d3dcccc623568664d512654a732adb1c7f4a254ab04e265ac1399d0e4c1967
4
+ data.tar.gz: d58a56b898820598bde4e7f285120623f91cd4abf16f5e9487b52922780b9dc1
5
5
  SHA512:
6
- metadata.gz: 9116078833a262f5a4d184a9cd080f3a29bfc5c07481cf352dd87baa735ba0dfae6159a35ac37ea1ad2e121348a33d958151a048af8dbb6e85e3a387076de3b5
7
- data.tar.gz: 02e8b7e3aa1fd89f70f67b2696d778bf0c4a5d89c4b6f779ec3ecffc6a099374c06515ec2bfcb4e7175a518f00543f16b1b691670378c2533fcf353ce82cb96f
6
+ metadata.gz: f3347abdf46c203bc277274a0f3f308e8582137a91471ae4ef2064acd7f869a1d12ce020e8031bee2ec710dfecd9933e4b5718ae3a3181116f08ccb5a5839305
7
+ data.tar.gz: c81b3391288de5f1031a6372354555d7cdad9f117e68193094b728e8e9851e35fbcea41a4b9b977b1338cad30095680935e9eb30c7ceee9e7d338ddf160ae660
data/README.md CHANGED
@@ -112,7 +112,7 @@ end
112
112
  Active Resource is built on a standard JSON or XML format for requesting and submitting resources
113
113
  over HTTP. It mirrors the RESTful routing built into Action Controller but will also work with any
114
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:
115
+ applications, it makes use of all the verbs available in the HTTP specification:
116
116
 
117
117
  * GET requests are used for finding and retrieving resources.
118
118
  * POST requests are used to create new resources.
@@ -296,6 +296,8 @@ The `payload` is a `Hash` with the following keys:
296
296
 
297
297
  * `method` as a `Symbol`
298
298
  * `request_uri` as a `String`
299
+ * `headers` as a `Hash`
300
+ * `body` as a `String` when available
299
301
  * `result` as an `Net::HTTPResponse`
300
302
 
301
303
  ## License
@@ -18,9 +18,8 @@ module ActiveResource
18
18
  end
19
19
  end
20
20
 
21
- private
22
- def klass
23
- ActiveResource::Base
24
- end
21
+ def klass
22
+ ActiveResource::Base
23
+ end
25
24
  end
26
25
  end
@@ -4,7 +4,7 @@ module ActiveResource::Associations::Builder
4
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
- self.valid_options = [:class_name]
7
+ self.valid_options = [ :class_name ]
8
8
 
9
9
  # would identify subclasses of association
10
10
  class_attribute :macro
@@ -2,7 +2,7 @@
2
2
 
3
3
  module ActiveResource::Associations::Builder
4
4
  class BelongsTo < Association
5
- self.valid_options += [:foreign_key]
5
+ self.valid_options += [ :foreign_key ]
6
6
 
7
7
  self.macro = :belongs_to
8
8
 
@@ -11,7 +11,6 @@ require "active_support/core_ext/object/blank"
11
11
  require "active_support/core_ext/object/to_query"
12
12
  require "active_support/core_ext/object/duplicable"
13
13
  require "set"
14
- require "uri"
15
14
 
16
15
  require "active_resource/connection"
17
16
  require "active_resource/formats"
@@ -213,6 +212,7 @@ module ActiveResource
213
212
  # * 301, 302, 303, 307 - ActiveResource::Redirection
214
213
  # * 400 - ActiveResource::BadRequest
215
214
  # * 401 - ActiveResource::UnauthorizedAccess
215
+ # * 402 - ActiveResource::PaymentRequired
216
216
  # * 403 - ActiveResource::ForbiddenAccess
217
217
  # * 404 - ActiveResource::ResourceNotFound
218
218
  # * 405 - ActiveResource::MethodNotAllowed
@@ -490,8 +490,8 @@ module ActiveResource
490
490
  self._site = nil
491
491
  else
492
492
  self._site = create_site_uri_from(site)
493
- self._user = URI::DEFAULT_PARSER.unescape(_site.user) if _site.user
494
- self._password = URI::DEFAULT_PARSER.unescape(_site.password) if _site.password
493
+ self._user = URI_PARSER.unescape(_site.user) if _site.user
494
+ self._password = URI_PARSER.unescape(_site.password) if _site.password
495
495
  end
496
496
  end
497
497
 
@@ -750,7 +750,7 @@ module ActiveResource
750
750
  # Default value is <tt>site.path</tt>.
751
751
  def prefix=(value = "/")
752
752
  # Replace :placeholders with '#{embedded options[:lookups]}'
753
- prefix_call = value.gsub(/:\w+/) { |key| "\#{URI::DEFAULT_PARSER.escape options[#{key}].to_s}" }
753
+ prefix_call = value.gsub(/:\w+/) { |key| "\#{URI_PARSER.escape options[#{key}].to_s}" }
754
754
 
755
755
  # Clear prefix parameters in case they have been cached
756
756
  @prefix_parameters = nil
@@ -886,6 +886,31 @@ module ActiveResource
886
886
  "#{prefix(prefix_options)}#{collection_name}#{format_extension}#{query_string(query_options)}"
887
887
  end
888
888
 
889
+ # Gets the collection URL for the REST resources. If the +query_options+ parameter is omitted, Rails
890
+ # will split from the +prefix_options+.
891
+ #
892
+ # ==== Options
893
+ # * +prefix_options+ - A hash to add a prefix to the request for nested URLs (e.g., <tt>:account_id => 19</tt>
894
+ # would yield a URL like <tt>/accounts/19/purchases.json</tt>).
895
+ # * +query_options+ - A hash to add items to the query string for the request.
896
+ #
897
+ # ==== Examples
898
+ # Post.collection_url
899
+ # # => https://example.com/posts.json
900
+ #
901
+ # Comment.collection_url(:post_id => 5)
902
+ # # => https://example.com/posts/5/comments.json
903
+ #
904
+ # Comment.collection_url(:post_id => 5, :active => 1)
905
+ # # => https://example.com/posts/5/comments.json?active=1
906
+ #
907
+ # Comment.collection_url({:post_id => 5}, {:active => 1})
908
+ # # => https://example.com/posts/5/comments.json?active=1
909
+ #
910
+ def collection_url(prefix_options = {}, query_options = nil)
911
+ URI.join(site, collection_path(prefix_options, query_options)).to_s
912
+ end
913
+
889
914
  alias_method :set_primary_key, :primary_key= # :nodoc:
890
915
 
891
916
  # Builds a new, unsaved record using the default values from the remote server so
@@ -1037,12 +1062,13 @@ module ActiveResource
1037
1062
  # This is an alias for find(:all). You can pass in all the same
1038
1063
  # arguments to this method as you can to <tt>find(:all)</tt>
1039
1064
  def all(*args)
1040
- find(:all, *args)
1065
+ WhereClause.new(self, *args)
1041
1066
  end
1042
1067
 
1043
1068
  def where(clauses = {})
1069
+ clauses = sanitize_forbidden_attributes(clauses)
1044
1070
  raise ArgumentError, "expected a clauses Hash, got #{clauses.inspect}" unless clauses.is_a? Hash
1045
- find(:all, params: clauses)
1071
+ all(params: clauses)
1046
1072
  end
1047
1073
 
1048
1074
 
@@ -1072,13 +1098,13 @@ module ActiveResource
1072
1098
  #
1073
1099
  # Note.exists(1349) # => false
1074
1100
  def exists?(id, options = {})
1075
- if id
1076
- prefix_options, query_options = split_options(options[:params])
1077
- path = element_path(id, prefix_options, query_options)
1078
- response = connection.head(path, headers)
1079
- (200..206).include? response.code.to_i
1080
- end
1081
- # id && !find_single(id, options).nil?
1101
+ return false unless id
1102
+
1103
+ prefix_options, query_options = split_options(options[:params])
1104
+ path = element_path(id, prefix_options, query_options)
1105
+ response = connection.head(path, headers)
1106
+
1107
+ (200..206).include?(response.code.to_i)
1082
1108
  rescue ActiveResource::ResourceNotFound, ActiveResource::ResourceGone
1083
1109
  false
1084
1110
  end
@@ -1443,6 +1469,8 @@ module ActiveResource
1443
1469
  # my_branch.name # => "Wilson Road"
1444
1470
  def reload
1445
1471
  self.load(self.class.find(to_param, params: @prefix_options).attributes, false, true)
1472
+ rescue => exception
1473
+ rescue_with_handler(exception) || raise
1446
1474
  end
1447
1475
 
1448
1476
  # A method to manually load attributes from a \hash. Recursively loads collections of
@@ -1471,6 +1499,7 @@ module ActiveResource
1471
1499
  raise ArgumentError, "expected attributes to be able to convert to Hash, got #{attributes.inspect}"
1472
1500
  end
1473
1501
 
1502
+ attributes = sanitize_forbidden_attributes(attributes)
1474
1503
  attributes = attributes.to_hash
1475
1504
  @prefix_options, attributes = split_options(attributes)
1476
1505
 
@@ -1627,7 +1656,7 @@ module ActiveResource
1627
1656
  private
1628
1657
  # Determine whether the response is allowed to have a body per HTTP 1.1 spec section 4.4.1
1629
1658
  def response_code_allows_body?(c)
1630
- !((100..199).include?(c) || [204, 304].include?(c))
1659
+ !((100..199).include?(c) || [ 204, 304 ].include?(c))
1631
1660
  end
1632
1661
 
1633
1662
  # Tries to find a resource for a given collection name; if it fails, then the resource is created
@@ -1643,7 +1672,7 @@ module ActiveResource
1643
1672
  namespaces = module_names[0, module_names.size - 1].map do |module_name|
1644
1673
  receiver = receiver.const_get(module_name)
1645
1674
  end
1646
- const_args = [resource_name, false]
1675
+ const_args = [ resource_name, false ]
1647
1676
  if namespace = namespaces.reverse.detect { |ns| ns.const_defined?(*const_args) }
1648
1677
  namespace.const_get(*const_args)
1649
1678
  else
@@ -1656,7 +1685,7 @@ module ActiveResource
1656
1685
  return reflections[name.to_sym].klass if reflections.key?(name.to_sym)
1657
1686
  resource_name = name.to_s.camelize
1658
1687
 
1659
- const_args = [resource_name, false]
1688
+ const_args = [ resource_name, false ]
1660
1689
 
1661
1690
  if !const_valid?(*const_args)
1662
1691
  # resource_name is not a valid ruby module name and cannot be created normally
@@ -1718,14 +1747,16 @@ module ActiveResource
1718
1747
  end
1719
1748
 
1720
1749
  class Base
1750
+ extend ActiveModel::ForbiddenAttributesProtection
1721
1751
  extend ActiveModel::Naming
1722
1752
  extend ActiveResource::Associations
1723
1753
 
1724
- include Callbacks, CustomMethods, Validations
1754
+ include Callbacks, CustomMethods, Validations, Serialization
1725
1755
  include ActiveModel::Conversion
1756
+ include ActiveModel::ForbiddenAttributesProtection
1726
1757
  include ActiveModel::Serializers::JSON
1727
1758
  include ActiveModel::Serializers::Xml
1728
- include ActiveResource::Reflection
1759
+ include ActiveResource::Reflection, ActiveResource::Rescuable
1729
1760
  end
1730
1761
 
1731
1762
  ActiveSupport.run_load_hooks(:active_resource, Base)
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveResource
4
+ # Integrates with Active Record's
5
+ # {serialize}[link:https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize]
6
+ # method as the <tt>:coder</tt> option.
7
+ #
8
+ # Encodes Active Resource instances into a value to be stored in the
9
+ # database. Decodes values read from the database into Active Resource
10
+ # instances.
11
+ #
12
+ # class User < ActiveRecord::Base
13
+ # serialize :person, coder: ActiveResource::Coder.new(Person)
14
+ # end
15
+ #
16
+ # class Person < ActiveResource::Base
17
+ # schema do
18
+ # attribute :name, :string
19
+ # end
20
+ # end
21
+ #
22
+ # user = User.new
23
+ # user.person = Person.new name: "Matz"
24
+ # user.person.name # => "Matz"
25
+ #
26
+ # Values are loaded as persisted when decoded from data containing a
27
+ # primary key value, and new records when missing a primary key value:
28
+ #
29
+ # user.person = Person.new
30
+ # user.person.persisted? # => true
31
+ #
32
+ # user.person = Person.find(1)
33
+ # user.person.persisted? # => true
34
+ #
35
+ # By default, <tt>#dump</tt> serializes the instance to a string value by
36
+ # calling Base#encode:
37
+ #
38
+ # user.person_before_type_cast # => "{\"name\":\"Matz\"}"
39
+ #
40
+ # To customize serialization, pass the method name or a block as the second
41
+ # argument:
42
+ #
43
+ # person = Person.new name: "Matz"
44
+ #
45
+ # coder = ActiveResource::Coder.new(Person, :serializable_hash)
46
+ # coder.dump(person) # => { "name" => "Matz" }
47
+ #
48
+ # coder = ActiveResource::Coder.new(Person) { |person| person.serializable_hash }
49
+ # coder.dump(person) # => { "name" => "Matz" }
50
+ class Coder
51
+ attr_accessor :resource_class, :encoder
52
+
53
+ def initialize(resource_class, encoder_method = :encode, &block)
54
+ @resource_class = resource_class
55
+ @encoder = block || encoder_method
56
+ end
57
+
58
+ # Serializes a resource value to a value that will be stored in the database.
59
+ # Returns nil when passed nil
60
+ def dump(value)
61
+ return if value.nil?
62
+ raise ArgumentError.new("expected value to be #{resource_class}, but was #{value.class}") unless value.is_a?(resource_class)
63
+
64
+ value.yield_self(&encoder)
65
+ end
66
+
67
+ # Deserializes a value from the database to a resource instance.
68
+ # Returns nil when passed nil
69
+ def load(value)
70
+ return if value.nil?
71
+ value = resource_class.format.decode(value) if value.is_a?(String)
72
+ raise ArgumentError.new("expected value to be Hash, but was #{value.class}") unless value.is_a?(Hash)
73
+ resource_class.new(value, value[resource_class.primary_key])
74
+ end
75
+ end
76
+ end
@@ -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!, :all?]
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
 
@@ -5,7 +5,6 @@ require "active_support/core_ext/object/inclusion"
5
5
  require "net/https"
6
6
  require "date"
7
7
  require "time"
8
- require "uri"
9
8
 
10
9
  module ActiveResource
11
10
  # Class to handle connections to remote web services.
@@ -43,8 +42,8 @@ module ActiveResource
43
42
  def site=(site)
44
43
  @site = site.is_a?(URI) ? site : URI.parse(site)
45
44
  @ssl_options ||= {} if @site.is_a?(URI::HTTPS)
46
- @user = URI::DEFAULT_PARSER.unescape(@site.user) if @site.user
47
- @password = URI::DEFAULT_PARSER.unescape(@site.password) if @site.password
45
+ @user = URI_PARSER.unescape(@site.user) if @site.user
46
+ @password = URI_PARSER.unescape(@site.password) if @site.password
48
47
  end
49
48
 
50
49
  # Set the proxy for remote service.
@@ -117,9 +116,13 @@ module ActiveResource
117
116
  private
118
117
  # Makes a request to the remote service.
119
118
  def request(method, path, *arguments)
119
+ body, headers = arguments
120
+ headers, body = body, nil if headers.nil?
120
121
  result = ActiveSupport::Notifications.instrument("request.active_resource") do |payload|
121
122
  payload[:method] = method
122
123
  payload[:request_uri] = "#{site.scheme}://#{site.host}:#{site.port}#{path}"
124
+ payload[:headers] = headers
125
+ payload[:body] = body
123
126
  payload[:result] = http.send(method, path, *arguments)
124
127
  end
125
128
  handle_response(result)
@@ -127,6 +130,8 @@ module ActiveResource
127
130
  raise TimeoutError.new(e.message)
128
131
  rescue OpenSSL::SSL::SSLError => e
129
132
  raise SSLError.new(e.message)
133
+ rescue Errno::ECONNREFUSED => e
134
+ raise ConnectionRefusedError.new(e.message)
130
135
  end
131
136
 
132
137
  # Handles response and error codes from the remote service.
@@ -140,6 +145,8 @@ module ActiveResource
140
145
  raise(BadRequest.new(response))
141
146
  when 401
142
147
  raise(UnauthorizedAccess.new(response))
148
+ when 402
149
+ raise(PaymentRequired.new(response))
143
150
  when 403
144
151
  raise(ForbiddenAccess.new(response))
145
152
  when 404
@@ -173,8 +180,8 @@ module ActiveResource
173
180
 
174
181
  def new_http
175
182
  if @proxy
176
- user = URI::DEFAULT_PARSER.unescape(@proxy.user) if @proxy.user
177
- password = URI::DEFAULT_PARSER.unescape(@proxy.password) if @proxy.password
183
+ user = URI_PARSER.unescape(@proxy.user) if @proxy.user
184
+ password = URI_PARSER.unescape(@proxy.password) if @proxy.password
178
185
  Net::HTTP.new(@site.host, @site.port, @proxy.host, @proxy.port, user, password)
179
186
  else
180
187
  Net::HTTP.new(@site.host, @site.port)
@@ -211,7 +218,7 @@ module ActiveResource
211
218
 
212
219
  # Builds headers for request to remote service.
213
220
  def build_request_headers(headers, http_method, uri)
214
- authorization_header(http_method, uri).update(default_header).update(http_format_header(http_method)).update(headers)
221
+ authorization_header(http_method, uri).update(default_header).update(http_format_header(http_method)).update(headers.to_hash)
215
222
  end
216
223
 
217
224
  def response_auth_header
@@ -233,7 +240,7 @@ module ActiveResource
233
240
  if auth_type == :digest
234
241
  { "Authorization" => digest_auth_header(http_method, uri) }
235
242
  else
236
- { "Authorization" => "Basic " + ["#{@user}:#{@password}"].pack("m").delete("\r\n") }
243
+ { "Authorization" => "Basic " + [ "#{@user}:#{@password}" ].pack("m").delete("\r\n") }
237
244
  end
238
245
  elsif @bearer_token
239
246
  { "Authorization" => "Bearer #{@bearer_token}" }
@@ -252,7 +259,7 @@ module ActiveResource
252
259
  ha2 = Digest::MD5.hexdigest("#{http_method.to_s.upcase}:#{request_uri}")
253
260
 
254
261
  params["cnonce"] = client_nonce
255
- request_digest = Digest::MD5.hexdigest([ha1, params["nonce"], "0", params["cnonce"], params["qop"], ha2].join(":"))
262
+ request_digest = Digest::MD5.hexdigest([ ha1, params["nonce"], "0", params["cnonce"], params["qop"], ha2 ].join(":"))
256
263
  "Digest #{auth_attributes_for(uri, request_digest, params)}"
257
264
  end
258
265
 
@@ -278,7 +285,7 @@ module ActiveResource
278
285
  %Q(nonce="#{params['nonce']}"),
279
286
  'nc="0"',
280
287
  %Q(cnonce="#{params['cnonce']}"),
281
- %Q(response="#{request_digest}")]
288
+ %Q(response="#{request_digest}") ]
282
289
 
283
290
  auth_attrs << %Q(opaque="#{params['opaque']}") unless params["opaque"].blank?
284
291
  auth_attrs.join(", ")
@@ -291,7 +298,7 @@ module ActiveResource
291
298
  def legitimize_auth_type(auth_type)
292
299
  return :basic if auth_type.nil?
293
300
  auth_type = auth_type.to_sym
294
- auth_type.in?([:basic, :digest, :bearer]) ? auth_type : :basic
301
+ auth_type.in?([ :basic, :digest, :bearer ]) ? auth_type : :basic
295
302
  end
296
303
  end
297
304
  end
@@ -119,7 +119,7 @@ module ActiveResource
119
119
 
120
120
  private
121
121
  def custom_method_element_url(method_name, options = {})
122
- "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}#{self.class.format_extension}#{self.class.__send__(:query_string, options)}"
122
+ "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{URI.encode_www_form_component(id.to_s)}/#{method_name}#{self.class.format_extension}#{self.class.__send__(:query_string, options)}"
123
123
  end
124
124
 
125
125
  def custom_method_new_element_url(method_name, options = {})
@@ -35,6 +35,9 @@ module ActiveResource
35
35
  def to_s; @message ; end
36
36
  end
37
37
 
38
+ # Raised when a Errno::ECONNREFUSED occurs.
39
+ class ConnectionRefusedError < Errno::ECONNREFUSED; end
40
+
38
41
  # 3xx Redirection
39
42
  class Redirection < ConnectionError # :nodoc:
40
43
  def to_s
@@ -57,6 +60,10 @@ module ActiveResource
57
60
  class UnauthorizedAccess < ClientError # :nodoc:
58
61
  end
59
62
 
63
+ # 402 Payment Required
64
+ class PaymentRequired < ClientError # :nodoc:
65
+ end
66
+
60
67
  # 403 Forbidden
61
68
  class ForbiddenAccess < ClientError # :nodoc:
62
69
  end
@@ -10,7 +10,11 @@ module ActiveResource
10
10
  # ActiveResource::Formats[:xml] # => ActiveResource::Formats::XmlFormat
11
11
  # ActiveResource::Formats[:json] # => ActiveResource::Formats::JsonFormat
12
12
  def self.[](mime_type_reference)
13
- ActiveResource::Formats.const_get(ActiveSupport::Inflector.camelize(mime_type_reference.to_s) + "Format")
13
+ case mime_type_reference.to_s
14
+ when "xml" then XmlFormat
15
+ when "json" then JsonFormat
16
+ else ActiveResource::Formats.const_get(ActiveSupport::Inflector.camelize(mime_type_reference.to_s) + "Format")
17
+ end
14
18
  end
15
19
 
16
20
  def self.remove_root(data)
@@ -58,12 +58,12 @@ module ActiveResource
58
58
  end
59
59
 
60
60
  [ :post, :patch, :put, :get, :delete, :head ].each do |method|
61
- # def post(path, request_headers = {}, body = nil, status = 200, response_headers = {})
62
- # @responses[Request.new(:post, path, nil, request_headers)] = Response.new(body || "", status, response_headers)
61
+ # def post(path, request_headers = {}, body = nil, status = 200, response_headers = {}, options: {})
62
+ # @responses[Request.new(:post, path, nil, request_headers, options)] = Response.new(body || "", status, response_headers)
63
63
  # end
64
64
  module_eval <<-EOE, __FILE__, __LINE__ + 1
65
- def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {})
66
- request = Request.new(:#{method}, path, nil, request_headers)
65
+ def #{method}(path, request_headers = {}, body = nil, status = 200, response_headers = {}, options = {})
66
+ request = Request.new(:#{method}, path, nil, request_headers, options)
67
67
  response = Response.new(body || "", status, response_headers)
68
68
 
69
69
  delete_duplicate_responses(request)
@@ -241,11 +241,11 @@ module ActiveResource
241
241
  end
242
242
 
243
243
  # body? methods
244
- { true => %w(post patch put),
245
- false => %w(get delete head) }.each do |has_body, methods|
244
+ { true => %w[post patch put],
245
+ false => %w[get delete head] }.each do |has_body, methods|
246
246
  methods.each do |method|
247
- # def post(path, body, headers)
248
- # request = ActiveResource::Request.new(:post, path, body, headers)
247
+ # def post(path, body, headers, options = {})
248
+ # request = ActiveResource::Request.new(:post, path, body, headers, options)
249
249
  # self.class.requests << request
250
250
  # if response = self.class.responses.assoc(request)
251
251
  # response[1]
@@ -254,8 +254,8 @@ module ActiveResource
254
254
  # end
255
255
  # end
256
256
  module_eval <<-EOE, __FILE__, __LINE__ + 1
257
- def #{method}(path, #{'body, ' if has_body}headers)
258
- request = ActiveResource::Request.new(:#{method}, path, #{has_body ? 'body, ' : 'nil, '}headers)
257
+ def #{method}(path, #{'body, ' if has_body}headers, options = {})
258
+ request = ActiveResource::Request.new(:#{method}, path, #{has_body ? 'body, ' : 'nil, '}headers, options)
259
259
  self.class.requests << request
260
260
  if response = self.class.responses.assoc(request)
261
261
  response[1]
@@ -279,19 +279,34 @@ module ActiveResource
279
279
  class Request
280
280
  attr_accessor :path, :method, :body, :headers
281
281
 
282
- def initialize(method, path, body = nil, headers = {})
283
- @method, @path, @body, @headers = method, path, body, headers
282
+ def initialize(method, path, body = nil, headers = {}, options = {})
283
+ @method, @path, @body, @headers, @options = method, path, body, headers, options
284
284
  end
285
285
 
286
286
  def ==(req)
287
- path == req.path && method == req.method && headers_match?(req)
287
+ same_path?(req) && method == req.method && headers_match?(req)
288
288
  end
289
289
 
290
290
  def to_s
291
291
  "<#{method.to_s.upcase}: #{path} [#{headers}] (#{body})>"
292
292
  end
293
293
 
294
+ # Removes query parameters from the path.
295
+ #
296
+ # @return [String] the path without query parameters
297
+ def remove_query_params_from_path
298
+ path.split("?").first
299
+ end
300
+
294
301
  private
302
+ def same_path?(req)
303
+ if @options && @options[:omit_query_in_path]
304
+ remove_query_params_from_path == req.remove_query_params_from_path
305
+ else
306
+ path == req.path
307
+ end
308
+ end
309
+
295
310
  def headers_match?(req)
296
311
  # Ignore format header on equality if it's not defined
297
312
  format_header = ActiveResource::Connection::HTTP_FORMAT_HEADER_NAMES[method]
@@ -360,11 +375,11 @@ module ActiveResource
360
375
  end
361
376
 
362
377
  def unstub_http?
363
- HttpMock.net_connection_enabled? && defined?(@http) && @http.kind_of?(HttpMock)
378
+ HttpMock.net_connection_enabled? && (!defined?(@http) || @http.kind_of?(HttpMock))
364
379
  end
365
380
 
366
381
  def stub_http?
367
- HttpMock.net_connection_disabled? && defined?(@http) && @http.kind_of?(Net::HTTP)
382
+ HttpMock.net_connection_disabled? && (!defined?(@http) || @http.kind_of?(Net::HTTP))
368
383
  end
369
384
  end
370
385
  end
@@ -11,5 +11,24 @@ module ActiveResource
11
11
  def [](key)
12
12
  super || @parent_hash[key]
13
13
  end
14
+
15
+ # Merges the flattened parent hash (if it's an InheritingHash)
16
+ # with ourself
17
+ def to_hash
18
+ @parent_hash.to_hash.merge(self)
19
+ end
20
+
21
+ # So we can see the merged object in IRB or the Rails console
22
+ def pretty_print(pp)
23
+ pp.pp_hash to_hash
24
+ end
25
+
26
+ def inspect
27
+ to_hash.inspect
28
+ end
29
+
30
+ def to_s
31
+ inspect
32
+ end
14
33
  end
15
34
  end
@@ -14,7 +14,7 @@ module ActiveResource
14
14
  log_level_method = code.to_i < 400 ? :info : :error
15
15
 
16
16
  send log_level_method, "#{event.payload[:method].to_s.upcase} #{event.payload[:request_uri]}"
17
- send log_level_method, "--> %d %s %d (%.1fms)" % [code, message, body.to_s.length, event.duration]
17
+ send log_level_method, "--> %d %s %d (%.1fms)" % [ code, message, body.to_s.length, event.duration ]
18
18
  end
19
19
 
20
20
  def logger
@@ -5,6 +5,8 @@ require "rails"
5
5
 
6
6
  module ActiveResource
7
7
  class Railtie < Rails::Railtie
8
+ config.eager_load_namespaces << ActiveResource
9
+
8
10
  config.active_resource = ActiveSupport::OrderedOptions.new
9
11
 
10
12
  initializer "active_resource.set_configs" do |app|
@@ -21,5 +23,21 @@ module ActiveResource
21
23
  app.config.active_job.custom_serializers << ActiveResource::ActiveJobSerializer
22
24
  end
23
25
  end
26
+
27
+ initializer "active_resource.deprecator" do |app|
28
+ if app.respond_to?(:deprecators)
29
+ app.deprecators[:active_resource] = ActiveResource.deprecator
30
+ end
31
+ end
32
+
33
+ initializer "active_resource.logger" do
34
+ ActiveSupport.on_load(:active_resource) { self.logger ||= ::Rails.logger }
35
+ end
36
+
37
+ initializer "active_resource.http_mock" do
38
+ ActiveSupport.on_load(:active_support_test_case) do
39
+ teardown { ActiveResource::HttpMock.reset! }
40
+ end
41
+ end
24
42
  end
25
43
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveResource
4
+ # = Active Resource \Rescuable
5
+ #
6
+ # Provides
7
+ # {rescue_from}[rdoc-ref:ActiveSupport::Rescuable::ClassMethods#rescue_from]
8
+ # for resources. Wraps calls over the network to handle configured errors.
9
+ module Rescuable
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ include ActiveSupport::Rescuable
14
+
15
+ around_save :handle_exceptions
16
+ around_destroy :handle_exceptions
17
+ end
18
+
19
+ private
20
+ def handle_exceptions
21
+ yield
22
+ rescue => exception
23
+ rescue_with_handler(exception) || raise
24
+ end
25
+ end
26
+ end
@@ -4,7 +4,7 @@ module ActiveResource # :nodoc:
4
4
  class Schema # :nodoc:
5
5
  # attributes can be known to be one of these types. They are easy to
6
6
  # cast to/from.
7
- KNOWN_ATTRIBUTE_TYPES = %w( string text integer float decimal datetime timestamp time date binary boolean )
7
+ KNOWN_ATTRIBUTE_TYPES = %w[ string text integer float decimal datetime timestamp time date binary boolean ]
8
8
 
9
9
  # An array of attribute definitions, representing the attributes that
10
10
  # have been defined.
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveResource
4
+ # Compatibilitiy with Active Record's
5
+ # {serialize}[link:https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html#method-i-serialize]
6
+ # method as the <tt>:coder</tt> option.
7
+ #
8
+ # === Writing to String columns
9
+ #
10
+ # Encodes Active Resource instances into a string to be stored in the
11
+ # database. Decodes strings read from the database into Active Resource
12
+ # instances.
13
+ #
14
+ # class User < ActiveRecord::Base
15
+ # serialize :person, coder: Person
16
+ # end
17
+ #
18
+ # class Person < ActiveResource::Base
19
+ # schema do
20
+ # attribute :name, :string
21
+ # end
22
+ # end
23
+ #
24
+ # user = User.new
25
+ # user.person = Person.new name: "Matz"
26
+ #
27
+ # Writing string values incorporates the Base.format:
28
+ #
29
+ # Person.format = :json
30
+ #
31
+ # user.person = Person.new name: "Matz"
32
+ # user.person_before_type_cast # => "{\"name\":\"Matz\"}"
33
+ #
34
+ # Person.format = :xml
35
+ #
36
+ # user.person = Person.new name: "Matz"
37
+ # user.person_before_type_cast # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?><person><name>Matz</name></person>"
38
+ #
39
+ # Instances are loaded as persisted when decoded from data containing a
40
+ # primary key value, and new records when missing a primary key value:
41
+ #
42
+ # user.person = Person.new
43
+ # user.person.persisted? # => false
44
+ #
45
+ # user.person = Person.find(1)
46
+ # user.person.persisted? # => true
47
+ #
48
+ # === Writing to JSON and JSONB columns
49
+ #
50
+ # class User < ActiveRecord::Base
51
+ # serialize :person, coder: ActiveResource::Coder.new(Person, :serializable_hash)
52
+ # end
53
+ #
54
+ # class Person < ActiveResource::Base
55
+ # schema do
56
+ # attribute :name, :string
57
+ # end
58
+ # end
59
+ #
60
+ # user = User.new
61
+ # user.person = Person.new name: "Matz"
62
+ # user.person.name # => "Matz"
63
+ #
64
+ # user.person_before_type_cast # => {"name"=>"Matz"}
65
+ module Serialization
66
+ extend ActiveSupport::Concern
67
+
68
+ included do
69
+ class_attribute :coder, instance_accessor: false, instance_predicate: false
70
+ end
71
+
72
+ module ClassMethods
73
+ delegate :dump, :load, to: :coder
74
+
75
+ def inherited(subclass) # :nodoc:
76
+ super
77
+ subclass.coder = Coder.new(subclass)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -15,7 +15,7 @@ module ActiveResource
15
15
  # or not (by passing true).
16
16
  def from_array(messages, save_cache = false)
17
17
  clear unless save_cache
18
- humanized_attributes = Hash[@base.known_attributes.map { |attr_name| [attr_name.humanize, attr_name] }]
18
+ humanized_attributes = Hash[@base.known_attributes.map { |attr_name| [ attr_name.humanize, attr_name ] }]
19
19
  messages.each do |message|
20
20
  attr_message = humanized_attributes.keys.sort_by { |a| -a.length }.detect do |attr_name|
21
21
  if message[0, attr_name.size + 1] == "#{attr_name} "
@@ -57,7 +57,7 @@ module ActiveResource
57
57
  errors = decoded["errors"] || {}
58
58
  if errors.kind_of?(Array)
59
59
  # 3.2.1-style with array of strings
60
- ActiveSupport::Deprecation.warn("Returning errors as an array of strings is deprecated.")
60
+ ActiveResource.deprecator.warn("Returning errors as an array of strings is deprecated.")
61
61
  from_array errors, save_cache
62
62
  else
63
63
  # 3.2.2+ style
@@ -65,7 +65,7 @@ module ActiveResource
65
65
  end
66
66
  else
67
67
  # <3.2-style respond_with - lacks 'errors' key
68
- ActiveSupport::Deprecation.warn('Returning errors as a hash without a root "errors" key is deprecated.')
68
+ ActiveResource.deprecator.warn('Returning errors as a hash without a root "errors" key is deprecated.')
69
69
  from_hash decoded, save_cache
70
70
  end
71
71
  end
@@ -160,7 +160,7 @@ module ActiveResource
160
160
  # my_person.valid?
161
161
  # # => false
162
162
  #
163
- def valid?
163
+ def valid?(context = nil)
164
164
  run_callbacks :validate do
165
165
  super
166
166
  load_remote_errors(@remote_errors, true) if defined?(@remote_errors) && @remote_errors.present?
@@ -3,10 +3,10 @@
3
3
  module ActiveResource
4
4
  module VERSION # :nodoc:
5
5
  MAJOR = 6
6
- MINOR = 0
6
+ MINOR = 2
7
7
  TINY = 0
8
8
  PRE = nil
9
9
 
10
- STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
10
+ STRING = [ MAJOR, MINOR, TINY, PRE ].compact.join(".")
11
11
  end
12
12
  end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveResource
4
+ class WhereClause < BasicObject # :nodoc:
5
+ delegate_missing_to :resources
6
+
7
+ def initialize(resource_class, options = {})
8
+ @resource_class = resource_class
9
+ @options = options
10
+ @resources = nil
11
+ @loaded = false
12
+ end
13
+
14
+ def where(clauses = {})
15
+ all(params: clauses)
16
+ end
17
+
18
+ def all(options = {})
19
+ WhereClause.new(@resource_class, @options.deep_merge(options))
20
+ end
21
+
22
+ def load
23
+ unless @loaded
24
+ @resources = @resource_class.find(:all, @options)
25
+ @loaded = true
26
+ end
27
+
28
+ self
29
+ end
30
+
31
+ def reload
32
+ reset
33
+ load
34
+ end
35
+
36
+ private
37
+ def resources
38
+ load
39
+ @resources
40
+ end
41
+
42
+ def reset
43
+ @resources = nil
44
+ @loaded = false
45
+ end
46
+ end
47
+ end
@@ -23,6 +23,8 @@
23
23
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
24
  #++
25
25
 
26
+ require "uri"
27
+
26
28
  require "active_support"
27
29
  require "active_model"
28
30
  require "active_resource/exceptions"
@@ -31,17 +33,35 @@ require "active_resource/version"
31
33
  module ActiveResource
32
34
  extend ActiveSupport::Autoload
33
35
 
36
+ URI_PARSER = defined?(URI::RFC2396_PARSER) ? URI::RFC2396_PARSER : URI::RFC2396_Parser.new
37
+
34
38
  autoload :Base
35
39
  autoload :Callbacks
40
+ autoload :Coder
36
41
  autoload :Connection
37
42
  autoload :CustomMethods
38
43
  autoload :Formats
39
44
  autoload :HttpMock
45
+ autoload :Rescuable
40
46
  autoload :Schema
47
+ autoload :Serialization
41
48
  autoload :Singleton
42
49
  autoload :InheritingHash
43
50
  autoload :Validations
44
51
  autoload :Collection
52
+ eager_autoload do
53
+ autoload :WhereClause
54
+ end
55
+
56
+ if ActiveSupport::VERSION::STRING >= "7.1"
57
+ def self.deprecator
58
+ @deprecator ||= ActiveSupport::Deprecation.new(VERSION::STRING, "ActiveResource")
59
+ end
60
+ else
61
+ def self.deprecator
62
+ ActiveSupport::Deprecation
63
+ end
64
+ end
45
65
  end
46
66
 
47
67
  require "active_resource/railtie" if defined?(Rails.application)
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activeresource
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0
4
+ version: 6.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Heinemeier Hansson
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2022-01-10 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activesupport
@@ -16,28 +15,28 @@ dependencies:
16
15
  requirements:
17
16
  - - ">="
18
17
  - !ruby/object:Gem::Version
19
- version: '6.0'
18
+ version: '7.0'
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - ">="
25
24
  - !ruby/object:Gem::Version
26
- version: '6.0'
25
+ version: '7.0'
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: activemodel
29
28
  requirement: !ruby/object:Gem::Requirement
30
29
  requirements:
31
30
  - - ">="
32
31
  - !ruby/object:Gem::Version
33
- version: '6.0'
32
+ version: '7.0'
34
33
  type: :runtime
35
34
  prerelease: false
36
35
  version_requirements: !ruby/object:Gem::Requirement
37
36
  requirements:
38
37
  - - ">="
39
38
  - !ruby/object:Gem::Version
40
- version: '6.0'
39
+ version: '7.0'
41
40
  - !ruby/object:Gem::Dependency
42
41
  name: activemodel-serializers-xml
43
42
  requirement: !ruby/object:Gem::Requirement
@@ -112,6 +111,7 @@ files:
112
111
  - lib/active_resource/associations/builder/has_one.rb
113
112
  - lib/active_resource/base.rb
114
113
  - lib/active_resource/callbacks.rb
114
+ - lib/active_resource/coder.rb
115
115
  - lib/active_resource/collection.rb
116
116
  - lib/active_resource/connection.rb
117
117
  - lib/active_resource/custom_methods.rb
@@ -124,22 +124,24 @@ files:
124
124
  - lib/active_resource/log_subscriber.rb
125
125
  - lib/active_resource/railtie.rb
126
126
  - lib/active_resource/reflection.rb
127
+ - lib/active_resource/rescuable.rb
127
128
  - lib/active_resource/schema.rb
129
+ - lib/active_resource/serialization.rb
128
130
  - lib/active_resource/singleton.rb
129
131
  - lib/active_resource/threadsafe_attributes.rb
130
132
  - lib/active_resource/validations.rb
131
133
  - lib/active_resource/version.rb
134
+ - lib/active_resource/where_clause.rb
132
135
  - lib/activeresource.rb
133
136
  homepage: http://www.rubyonrails.org
134
137
  licenses:
135
138
  - MIT
136
139
  metadata:
137
140
  bug_tracker_uri: https://github.com/rails/activeresource/issues
138
- changelog_uri: https://github.com/rails/activeresource/releases/tag/v6.0.0
141
+ changelog_uri: https://github.com/rails/activeresource/releases/tag/v6.2.0
139
142
  documentation_uri: http://rubydoc.info/gems/activeresource
140
- source_code_uri: https://github.com/rails/activeresource/tree/v6.0.0
143
+ source_code_uri: https://github.com/rails/activeresource/tree/v6.2.0
141
144
  rubygems_mfa_required: 'true'
142
- post_install_message:
143
145
  rdoc_options: []
144
146
  require_paths:
145
147
  - lib
@@ -147,15 +149,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
147
149
  requirements:
148
150
  - - ">="
149
151
  - !ruby/object:Gem::Version
150
- version: 2.6.0
152
+ version: 2.7.0
151
153
  required_rubygems_version: !ruby/object:Gem::Requirement
152
154
  requirements:
153
155
  - - ">="
154
156
  - !ruby/object:Gem::Version
155
157
  version: '0'
156
158
  requirements: []
157
- rubygems_version: 3.2.32
158
- signing_key:
159
+ rubygems_version: 3.6.7
159
160
  specification_version: 4
160
161
  summary: REST modeling framework (part of Rails).
161
162
  test_files: []