activeresource 3.2.22.5 → 4.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activeresource might be problematic. Click here for more details.

@@ -0,0 +1,20 @@
1
+ require 'active_support/core_ext/array/wrap'
2
+
3
+ module ActiveResource
4
+ module Callbacks
5
+ extend ActiveSupport::Concern
6
+
7
+ CALLBACKS = [
8
+ :before_validation, :after_validation, :before_save, :around_save, :after_save,
9
+ :before_create, :around_create, :after_create, :before_update, :around_update,
10
+ :after_update, :before_destroy, :around_destroy, :after_destroy
11
+ ]
12
+
13
+ included do
14
+ extend ActiveModel::Callbacks
15
+ include ActiveModel::Validations::Callbacks
16
+
17
+ define_model_callbacks :save, :create, :update, :destroy
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,85 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+ require 'active_support/inflector'
3
+
4
+ module ActiveResource # :nodoc:
5
+ class Collection # :nodoc:
6
+ include Enumerable
7
+ delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :size, :last, :first, :[], :to => :to_a
8
+
9
+ # The array of actual elements returned by index actions
10
+ attr_accessor :elements, :resource_class, :original_params
11
+
12
+ # ActiveResource::Collection is a wrapper to handle parsing index responses that
13
+ # do not directly map to Rails conventions.
14
+ #
15
+ # You can define a custom class that inherets from ActiveResource::Collection
16
+ # in order to to set the elements instance.
17
+ #
18
+ # GET /posts.json delivers following response body:
19
+ # {
20
+ # posts: [
21
+ # {
22
+ # title: "ActiveResource now has associations",
23
+ # body: "Lorem Ipsum"
24
+ # }
25
+ # {...}
26
+ # ]
27
+ # next_page: "/posts.json?page=2"
28
+ # }
29
+ #
30
+ # A Post class can be setup to handle it with:
31
+ #
32
+ # class Post < ActiveResource::Base
33
+ # self.site = "http://example.com"
34
+ # self.collection_parser = PostParser
35
+ # end
36
+ #
37
+ # And the collection parser:
38
+ #
39
+ # class PostCollection < ActiveResource::Collection
40
+ # attr_accessor :next_page
41
+ # def initialize(parsed = {})
42
+ # @elements = parsed['posts']
43
+ # @next_page = parsed['next_page']
44
+ # end
45
+ # end
46
+ #
47
+ # The result from a find method that returns multiple entries will now be a
48
+ # PostParser instance. ActiveResource::Collection includes Enumerable and
49
+ # instances can be iterated over just like an array.
50
+ # @posts = Post.find(:all) # => PostCollection:xxx
51
+ # @posts.next_page # => "/posts.json?page=2"
52
+ # @posts.map(&:id) # =>[1, 3, 5 ...]
53
+ #
54
+ # The initialize method will receive the ActiveResource::Formats parsed result
55
+ # and should set @elements.
56
+ def initialize(elements = [])
57
+ @elements = elements
58
+ end
59
+
60
+ def to_a
61
+ elements
62
+ end
63
+
64
+ def collect!
65
+ return elements unless block_given?
66
+ set = []
67
+ each { |o| set << yield(o) }
68
+ @elements = set
69
+ self
70
+ end
71
+ alias map! collect!
72
+
73
+ def first_or_create(attributes = {})
74
+ first || resource_class.create(original_params.update(attributes))
75
+ rescue NoMethodError
76
+ raise "Cannot create resource from resource type: #{resource_class.inspect}"
77
+ end
78
+
79
+ def first_or_initialize(attributes = {})
80
+ first || resource_class.new(original_params.update(attributes))
81
+ rescue NoMethodError
82
+ raise "Cannot build resource from resource type: #{resource_class.inspect}"
83
+ end
84
+ end
85
+ end
@@ -15,6 +15,7 @@ module ActiveResource
15
15
  HTTP_FORMAT_HEADER_NAMES = { :get => 'Accept',
16
16
  :put => 'Content-Type',
17
17
  :post => 'Content-Type',
18
+ :patch => 'Content-Type',
18
19
  :delete => 'Accept',
19
20
  :head => 'Accept'
20
21
  }
@@ -39,14 +40,15 @@ module ActiveResource
39
40
 
40
41
  # Set URI for remote service.
41
42
  def site=(site)
42
- @site = site.is_a?(URI) ? site : URI.parser.parse(site)
43
+ @site = site.is_a?(URI) ? site : URI.parse(site)
44
+ @ssl_options ||= {} if @site.is_a?(URI::HTTPS)
43
45
  @user = URI.parser.unescape(@site.user) if @site.user
44
46
  @password = URI.parser.unescape(@site.password) if @site.password
45
47
  end
46
48
 
47
49
  # Set the proxy for remote service.
48
50
  def proxy=(proxy)
49
- @proxy = proxy.is_a?(URI) ? proxy : URI.parser.parse(proxy)
51
+ @proxy = proxy.is_a?(URI) ? proxy : URI.parse(proxy)
50
52
  end
51
53
 
52
54
  # Sets the user for remote service.
@@ -70,8 +72,8 @@ module ActiveResource
70
72
  end
71
73
 
72
74
  # Hash of options applied to Net::HTTP instance when +site+ protocol is 'https'.
73
- def ssl_options=(opts={})
74
- @ssl_options = opts
75
+ def ssl_options=(options)
76
+ @ssl_options = options
75
77
  end
76
78
 
77
79
  # Executes a GET request.
@@ -86,6 +88,12 @@ module ActiveResource
86
88
  with_auth { request(:delete, path, build_request_headers(headers, :delete, self.site.merge(path))) }
87
89
  end
88
90
 
91
+ # Executes a PATCH request (see HTTP protocol documentation if unfamiliar).
92
+ # Used to update resources.
93
+ def patch(path, body = '', headers = {})
94
+ with_auth { request(:patch, path, body.to_s, build_request_headers(headers, :patch, self.site.merge(path))) }
95
+ end
96
+
89
97
  # Executes a PUT request (see HTTP protocol documentation if unfamiliar).
90
98
  # Used to update resources.
91
99
  def put(path, body = '', headers = {})
@@ -166,38 +174,28 @@ module ActiveResource
166
174
  end
167
175
 
168
176
  def configure_http(http)
169
- http = apply_ssl_options(http)
170
-
171
- # Net::HTTP timeouts default to 60 seconds.
172
- if @timeout
173
- http.open_timeout = @timeout
174
- http.read_timeout = @timeout
177
+ apply_ssl_options(http).tap do |https|
178
+ # Net::HTTP timeouts default to 60 seconds.
179
+ if defined? @timeout
180
+ https.open_timeout = @timeout
181
+ https.read_timeout = @timeout
182
+ end
175
183
  end
176
-
177
- http
178
184
  end
179
185
 
180
186
  def apply_ssl_options(http)
181
- return http unless @site.is_a?(URI::HTTPS)
187
+ http.tap do |https|
188
+ # Skip config if site is already a https:// URI.
189
+ if defined? @ssl_options
190
+ http.use_ssl = true
182
191
 
183
- http.use_ssl = true
184
- http.verify_mode = OpenSSL::SSL::VERIFY_NONE
185
- return http unless defined?(@ssl_options)
192
+ # Default to no cert verification (WTF? FIXME)
193
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
186
194
 
187
- http.ca_path = @ssl_options[:ca_path] if @ssl_options[:ca_path]
188
- http.ca_file = @ssl_options[:ca_file] if @ssl_options[:ca_file]
189
-
190
- http.cert = @ssl_options[:cert] if @ssl_options[:cert]
191
- http.key = @ssl_options[:key] if @ssl_options[:key]
192
-
193
- http.cert_store = @ssl_options[:cert_store] if @ssl_options[:cert_store]
194
- http.ssl_timeout = @ssl_options[:ssl_timeout] if @ssl_options[:ssl_timeout]
195
-
196
- http.verify_mode = @ssl_options[:verify_mode] if @ssl_options[:verify_mode]
197
- http.verify_callback = @ssl_options[:verify_callback] if @ssl_options[:verify_callback]
198
- http.verify_depth = @ssl_options[:verify_depth] if @ssl_options[:verify_depth]
199
-
200
- http
195
+ # All the SSL options have corresponding http settings.
196
+ @ssl_options.each { |key, value| http.send "#{key}=", value }
197
+ end
198
+ end
201
199
  end
202
200
 
203
201
  def default_header
@@ -2,25 +2,25 @@ require 'active_support/core_ext/object/blank'
2
2
 
3
3
  module ActiveResource
4
4
  # A module to support custom REST methods and sub-resources, allowing you to break out
5
- # of the "default" REST methods with your own custom resource requests. For example,
5
+ # of the "default" REST methods with your own custom resource requests. For example,
6
6
  # say you use Rails to expose a REST service and configure your routes with:
7
7
  #
8
8
  # map.resources :people, :new => { :register => :post },
9
9
  # :member => { :promote => :put, :deactivate => :delete }
10
10
  # :collection => { :active => :get }
11
11
  #
12
- # This route set creates routes for the following HTTP requests:
12
+ # This route set creates routes for the following HTTP requests:
13
13
  #
14
- # POST /people/new/register.json # PeopleController.register
15
- # PUT /people/1/promote.json # PeopleController.promote with :id => 1
16
- # DELETE /people/1/deactivate.json # PeopleController.deactivate with :id => 1
17
- # GET /people/active.json # PeopleController.active
14
+ # POST /people/new/register.json # PeopleController.register
15
+ # PATCH/PUT /people/1/promote.json # PeopleController.promote with :id => 1
16
+ # DELETE /people/1/deactivate.json # PeopleController.deactivate with :id => 1
17
+ # GET /people/active.json # PeopleController.active
18
18
  #
19
19
  # Using this module, Active Resource can use these custom REST methods just like the
20
20
  # standard methods.
21
21
  #
22
22
  # class Person < ActiveResource::Base
23
- # self.site = "http://37s.sunrise.i:3000"
23
+ # self.site = "https://37s.sunrise.com"
24
24
  # end
25
25
  #
26
26
  # Person.new(:name => 'Ryan').post(:register) # POST /people/new/register.json
@@ -63,6 +63,10 @@ module ActiveResource
63
63
  connection.post(custom_method_collection_url(custom_method_name, options), body, headers)
64
64
  end
65
65
 
66
+ def patch(custom_method_name, options = {}, body = '')
67
+ connection.patch(custom_method_collection_url(custom_method_name, options), body, headers)
68
+ end
69
+
66
70
  def put(custom_method_name, options = {}, body = '')
67
71
  connection.put(custom_method_collection_url(custom_method_name, options), body, headers)
68
72
  end
@@ -81,7 +85,7 @@ module ActiveResource
81
85
  module ClassMethods
82
86
  def custom_method_collection_url(method_name, options = {})
83
87
  prefix_options, query_options = split_options(options)
84
- "#{prefix(prefix_options)}#{collection_name}/#{method_name}.#{format.extension}#{query_string(query_options)}"
88
+ "#{prefix(prefix_options)}#{collection_name}/#{method_name}#{format_extension}#{query_string(query_options)}"
85
89
  end
86
90
  end
87
91
 
@@ -98,6 +102,10 @@ module ActiveResource
98
102
  end
99
103
  end
100
104
 
105
+ def patch(method_name, options = {}, body = '')
106
+ connection.patch(custom_method_element_url(method_name, options), body, self.class.headers)
107
+ end
108
+
101
109
  def put(method_name, options = {}, body = '')
102
110
  connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
103
111
  end
@@ -109,11 +117,11 @@ module ActiveResource
109
117
 
110
118
  private
111
119
  def custom_method_element_url(method_name, options = {})
112
- "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
120
+ "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}#{self.class.format_extension}#{self.class.__send__(:query_string, options)}"
113
121
  end
114
122
 
115
123
  def custom_method_new_element_url(method_name, options = {})
116
- "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
124
+ "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}#{self.class.format_extension}#{self.class.__send__(:query_string, options)}"
117
125
  end
118
126
  end
119
127
  end
@@ -4,7 +4,7 @@ require 'active_support/core_ext/object/inclusion'
4
4
  module ActiveResource
5
5
  class InvalidRequestError < StandardError; end #:nodoc:
6
6
 
7
- # One thing that has always been a pain with remote web services is testing. The HttpMock
7
+ # One thing that has always been a pain with remote web services is testing. The HttpMock
8
8
  # class makes it easy to test your Active Resource models by creating a set of mock responses to specific
9
9
  # requests.
10
10
  #
@@ -15,17 +15,17 @@ module ActiveResource
15
15
  #
16
16
  # mock.http_method(path, request_headers = {}, body = nil, status = 200, response_headers = {})
17
17
  #
18
- # * <tt>http_method</tt> - The HTTP method to listen for. This can be +get+, +post+, +put+, +delete+ or
18
+ # * <tt>http_method</tt> - The HTTP method to listen for. This can be +get+, +post+, +patch+, +put+, +delete+ or
19
19
  # +head+.
20
20
  # * <tt>path</tt> - A string, starting with a "/", defining the URI that is expected to be
21
21
  # called.
22
- # * <tt>request_headers</tt> - Headers that are expected along with the request. This argument uses a
23
- # hash format, such as <tt>{ "Content-Type" => "application/json" }</tt>. This mock will only trigger
22
+ # * <tt>request_headers</tt> - Headers that are expected along with the request. This argument uses a
23
+ # hash format, such as <tt>{ "Content-Type" => "application/json" }</tt>. This mock will only trigger
24
24
  # if your tests sends a request with identical headers.
25
- # * <tt>body</tt> - The data to be returned. This should be a string of Active Resource parseable content,
25
+ # * <tt>body</tt> - The data to be returned. This should be a string of Active Resource parseable content,
26
26
  # such as Json.
27
27
  # * <tt>status</tt> - The HTTP response code, as an integer, to return with the response.
28
- # * <tt>response_headers</tt> - Headers to be returned with the response. Uses the same hash format as
28
+ # * <tt>response_headers</tt> - Headers to be returned with the response. Uses the same hash format as
29
29
  # <tt>request_headers</tt> listed above.
30
30
  #
31
31
  # In order for a mock to deliver its content, the incoming request must match by the <tt>http_method</tt>,
@@ -55,7 +55,7 @@ module ActiveResource
55
55
  @responses = responses
56
56
  end
57
57
 
58
- [ :post, :put, :get, :delete, :head ].each do |method|
58
+ [ :post, :patch, :put, :get, :delete, :head ].each do |method|
59
59
  # def post(path, request_headers = {}, body = nil, status = 200, response_headers = {})
60
60
  # @responses[Request.new(:post, path, nil, request_headers)] = Response.new(body || "", status, response_headers)
61
61
  # end
@@ -133,7 +133,7 @@ module ActiveResource
133
133
  #
134
134
  # === Example
135
135
  #
136
- # Request.new(:#{method}, path, nil, request_headers)
136
+ # Request.new(method, path, nil, request_headers)
137
137
  #
138
138
  # @matz = { :person => { :id => 1, :name => "Matz" } }.to_json
139
139
  #
@@ -217,7 +217,7 @@ module ActiveResource
217
217
  end
218
218
 
219
219
  # body? methods
220
- { true => %w(post put),
220
+ { true => %w(post patch put),
221
221
  false => %w(get delete head) }.each do |has_body, methods|
222
222
  methods.each do |method|
223
223
  # def post(path, body, headers)
@@ -292,11 +292,8 @@ module ActiveResource
292
292
  @body = nil
293
293
  end
294
294
 
295
- if @body.nil?
296
- self['Content-Length'] = "0"
297
- else
298
- self['Content-Length'] = body.size.to_s
299
- end
295
+ self['Content-Length'] = @body.nil? ? "0" : body.size.to_s
296
+
300
297
  end
301
298
 
302
299
  # Returns true if code is 2xx,
@@ -1,3 +1,5 @@
1
+ require 'rails/observers/active_model/observing'
2
+
1
3
  module ActiveResource
2
4
  module Observing
3
5
  extend ActiveSupport::Concern
@@ -0,0 +1,77 @@
1
+ require 'active_support/core_ext/class/attribute'
2
+ require 'active_support/core_ext/module/deprecation'
3
+
4
+ module ActiveResource
5
+ # = Active Resource reflection
6
+ #
7
+ # Associations in ActiveResource would be used to resolve nested attributes
8
+ # in a response with correct classes.
9
+ # Now they could be specified over Associations with the options :class_name
10
+ module Reflection # :nodoc:
11
+ extend ActiveSupport::Concern
12
+
13
+ included do
14
+ class_attribute :reflections
15
+ self.reflections = {}
16
+ end
17
+
18
+ module ClassMethods
19
+ def create_reflection(macro, name, options)
20
+ reflection = AssociationReflection.new(macro, name, options)
21
+ self.reflections = self.reflections.merge(name => reflection)
22
+ reflection
23
+ end
24
+ end
25
+
26
+
27
+ class AssociationReflection
28
+
29
+ def initialize(macro, name, options)
30
+ @macro, @name, @options = macro, name, options
31
+ end
32
+
33
+ # Returns the name of the macro.
34
+ #
35
+ # <tt>has_many :clients</tt> returns <tt>:clients</tt>
36
+ attr_reader :name
37
+
38
+ # Returns the macro type.
39
+ #
40
+ # <tt>has_many :clients</tt> returns <tt>:has_many</tt>
41
+ attr_reader :macro
42
+
43
+ # Returns the hash of options used for the macro.
44
+ #
45
+ # <tt>has_many :clients</tt> returns +{}+
46
+ attr_reader :options
47
+
48
+ # Returns the class for the macro.
49
+ #
50
+ # <tt>has_many :clients</tt> returns the Client class
51
+ def klass
52
+ @klass ||= class_name.constantize
53
+ end
54
+
55
+ # Returns the class name for the macro.
56
+ #
57
+ # <tt>has_many :clients</tt> returns <tt>'Client'</tt>
58
+ def class_name
59
+ @class_name ||= derive_class_name
60
+ end
61
+
62
+ # Returns the foreign_key for the macro.
63
+ def foreign_key
64
+ @foreign_key ||= self.options[:foreign_key] || "#{self.name.to_s.downcase}_id"
65
+ end
66
+
67
+ private
68
+ def derive_class_name
69
+ return (options[:class_name] ? options[:class_name].to_s : name.to_s).classify
70
+ end
71
+
72
+ def derive_foreign_key
73
+ return options[:foreign_key] ? options[:foreign_key].to_s : "#{name.to_s.downcase}_id"
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,5 +1,3 @@
1
- require 'active_resource/exceptions'
2
-
3
1
  module ActiveResource # :nodoc:
4
2
  class Schema # :nodoc:
5
3
  # attributes can be known to be one of these types. They are easy to