api_resource 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. metadata +179 -123
  2. data/.document +0 -5
  3. data/.rspec +0 -5
  4. data/.travis.yml +0 -4
  5. data/Gemfile +0 -37
  6. data/Gemfile.lock +0 -190
  7. data/Guardfile +0 -27
  8. data/Rakefile +0 -49
  9. data/VERSION +0 -1
  10. data/api_resource.gemspec +0 -180
  11. data/lib/api_resource.rb +0 -130
  12. data/lib/api_resource/association_activation.rb +0 -19
  13. data/lib/api_resource/associations.rb +0 -218
  14. data/lib/api_resource/associations/association_proxy.rb +0 -116
  15. data/lib/api_resource/associations/belongs_to_remote_object_proxy.rb +0 -16
  16. data/lib/api_resource/associations/dynamic_resource_scope.rb +0 -23
  17. data/lib/api_resource/associations/generic_scope.rb +0 -68
  18. data/lib/api_resource/associations/has_many_remote_object_proxy.rb +0 -16
  19. data/lib/api_resource/associations/has_many_through_remote_object_proxy.rb +0 -13
  20. data/lib/api_resource/associations/has_one_remote_object_proxy.rb +0 -24
  21. data/lib/api_resource/associations/multi_argument_resource_scope.rb +0 -15
  22. data/lib/api_resource/associations/multi_object_proxy.rb +0 -84
  23. data/lib/api_resource/associations/related_object_hash.rb +0 -12
  24. data/lib/api_resource/associations/relation_scope.rb +0 -25
  25. data/lib/api_resource/associations/resource_scope.rb +0 -32
  26. data/lib/api_resource/associations/scope.rb +0 -132
  27. data/lib/api_resource/associations/single_object_proxy.rb +0 -82
  28. data/lib/api_resource/attributes.rb +0 -243
  29. data/lib/api_resource/base.rb +0 -717
  30. data/lib/api_resource/callbacks.rb +0 -45
  31. data/lib/api_resource/connection.rb +0 -195
  32. data/lib/api_resource/core_extensions.rb +0 -7
  33. data/lib/api_resource/custom_methods.rb +0 -117
  34. data/lib/api_resource/decorators.rb +0 -6
  35. data/lib/api_resource/decorators/caching_decorator.rb +0 -20
  36. data/lib/api_resource/exceptions.rb +0 -99
  37. data/lib/api_resource/formats.rb +0 -22
  38. data/lib/api_resource/formats/json_format.rb +0 -25
  39. data/lib/api_resource/formats/xml_format.rb +0 -36
  40. data/lib/api_resource/local.rb +0 -12
  41. data/lib/api_resource/log_subscriber.rb +0 -15
  42. data/lib/api_resource/mocks.rb +0 -277
  43. data/lib/api_resource/model_errors.rb +0 -82
  44. data/lib/api_resource/observing.rb +0 -27
  45. data/lib/api_resource/railtie.rb +0 -24
  46. data/lib/api_resource/scopes.rb +0 -48
  47. data/nohup.out +0 -63
  48. data/spec/lib/api_resource_spec.rb +0 -43
  49. data/spec/lib/associations_spec.rb +0 -751
  50. data/spec/lib/attributes_spec.rb +0 -191
  51. data/spec/lib/base_spec.rb +0 -655
  52. data/spec/lib/callbacks_spec.rb +0 -68
  53. data/spec/lib/connection_spec.rb +0 -137
  54. data/spec/lib/local_spec.rb +0 -20
  55. data/spec/lib/mocks_spec.rb +0 -45
  56. data/spec/lib/model_errors_spec.rb +0 -29
  57. data/spec/lib/prefixes_spec.rb +0 -107
  58. data/spec/spec_helper.rb +0 -82
  59. data/spec/support/mocks/association_mocks.rb +0 -63
  60. data/spec/support/mocks/error_resource_mocks.rb +0 -21
  61. data/spec/support/mocks/prefix_model_mocks.rb +0 -5
  62. data/spec/support/mocks/test_resource_mocks.rb +0 -44
  63. data/spec/support/requests/association_requests.rb +0 -31
  64. data/spec/support/requests/error_resource_requests.rb +0 -25
  65. data/spec/support/requests/prefix_model_requests.rb +0 -7
  66. data/spec/support/requests/test_resource_requests.rb +0 -38
  67. data/spec/support/test_resource.rb +0 -72
  68. data/spec/tmp/DIR +0 -0
@@ -1,45 +0,0 @@
1
- module ApiResource
2
-
3
- module Callbacks
4
-
5
- extend ActiveSupport::Concern
6
-
7
- included do
8
-
9
- extend ActiveModel::Callbacks
10
-
11
- define_model_callbacks :save, :create, :update, :destroy
12
-
13
- [:save, :create, :update, :destroy].each do |action|
14
- alias_method_chain action, :callbacks
15
- end
16
-
17
- end
18
-
19
- def save_with_callbacks(*args)
20
- run_callbacks :save do
21
- save_without_callbacks(*args)
22
- end
23
- end
24
-
25
- def create_with_callbacks(*args)
26
- run_callbacks :create do
27
- create_without_callbacks(*args)
28
- end
29
- end
30
-
31
- def update_with_callbacks(*args)
32
- run_callbacks :update do
33
- update_without_callbacks(*args)
34
- end
35
- end
36
-
37
- def destroy_with_callbacks(*args)
38
- run_callbacks :destroy do
39
- destroy_without_callbacks(*args)
40
- end
41
- end
42
-
43
- end
44
-
45
- end
@@ -1,195 +0,0 @@
1
- require 'active_support/core_ext/benchmark'
2
- require 'rest_client'
3
- require 'net/https'
4
- require 'date'
5
- require 'time'
6
- require 'uri'
7
-
8
- module ApiResource
9
- # Class to handle connections to remote web services.
10
- # This class is used by ActiveResource::Base to interface with REST
11
- # services.
12
- class Connection
13
-
14
- HTTP_FORMAT_HEADER_NAMES = {
15
- :get => 'Accept',
16
- :put => 'Content-Type',
17
- :post => 'Content-Type',
18
- :delete => 'Accept',
19
- :head => 'Accept'
20
- }
21
-
22
- attr_reader :site, :user, :password, :auth_type, :timeout, :proxy, :ssl_options
23
- attr_accessor :format, :headers
24
-
25
- class << self
26
- def requests
27
- @@requests ||= []
28
- end
29
- end
30
-
31
- # The +site+ parameter is required and will set the +site+
32
- # attribute to the URI for the remote resource service.
33
- def initialize(site, format = ApiResource::Formats::JsonFormat, headers)
34
- raise ArgumentError, 'Missing site URI' unless site
35
- @user = @password = nil
36
- @uri_parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
37
- self.site = site
38
- self.format = format
39
- self.headers = headers
40
- end
41
-
42
- # Set URI for remote service.
43
- def site=(site)
44
- @site = site.is_a?(URI) ? site : @uri_parser.parse(site)
45
- @user = @uri_parser.unescape(@site.user) if @site.user
46
- @password = @uri_parser.unescape(@site.password) if @site.password
47
- end
48
-
49
- # Sets the number of seconds after which HTTP requests to the remote service should time out.
50
- def timeout=(timeout)
51
- @timeout = timeout
52
- end
53
-
54
- def get(path, headers = self.headers)
55
- # our site and headers for this request
56
- site = self.site.merge(path)
57
- headers = build_request_headers(headers, :get, site)
58
-
59
- self.with_caching(path, headers) do
60
- format.decode(request(:get, path, headers))
61
- end
62
- end
63
-
64
- def delete(path, headers = self.headers)
65
- request(:delete, path, build_request_headers(headers, :delete, self.site.merge(path)))
66
- return true
67
- end
68
-
69
- def head(path, headers = self.headers)
70
- request(:head, path, build_request_headers(headers, :head, self.site.merge(path)))
71
- end
72
-
73
-
74
- def put(path, body = {}, headers = self.headers)
75
- # If there's a file to send then we can't use JSON or XML
76
- if !body.is_a?(String) && RestClient::Payload.has_file?(body)
77
- format.decode(request(:put, path, body, build_request_headers(headers, :put, self.site.merge(path))))
78
- else
79
- format.decode(request(:put, path, body, build_request_headers(headers, :put, self.site.merge(path))))
80
- end
81
- end
82
-
83
- def post(path, body = {}, headers = self.headers)
84
- if !body.is_a?(String) && RestClient::Payload.has_file?(body)
85
- format.decode(request(:post, path, body, build_request_headers(headers, :post, self.site.merge(path))))
86
- else
87
- format.decode(request(:post, path, body, build_request_headers(headers, :post, self.site.merge(path))))
88
- end
89
- end
90
-
91
- protected
92
-
93
- def cache_key(path, headers)
94
- key = Digest::MD5.hexdigest([path, headers].to_s)
95
- return "a-#{key}-#{ApiResource::Base.ttl}"
96
- end
97
-
98
- def with_caching(path, data = {}, &block)
99
- if ApiResource::Base.ttl.to_f > 0.0
100
- key = self.cache_key(path, data)
101
- ApiResource.cache.fetch(key, :expires_in => ApiResource::Base.ttl) do
102
- yield
103
- end
104
- else
105
- yield
106
- end
107
- end
108
-
109
- private
110
- # Makes a request to the remote service.
111
- def request(method, path, *arguments)
112
- handle_response(path) do
113
- ActiveSupport::Notifications.instrument("request.api_resource") do |payload|
114
-
115
- # debug logging
116
- ApiResource.logger.debug("#{method.to_s.upcase} #{path}")
117
-
118
- payload[:method] = method
119
- payload[:request_uri] = "#{site.scheme}://#{site.host}:#{site.port}#{path}"
120
- payload[:result] = http(path).send(method, *arguments)
121
- end
122
- end
123
- end
124
-
125
- # Handles response and error codes from the remote service.
126
- def handle_response(path, &block)
127
- begin
128
- result = yield
129
- rescue RestClient::RequestTimeout
130
- raise ApiResource::RequestTimeout.new("Request Time Out - Accessing #{path}}")
131
- rescue Exception => error
132
- if error.respond_to?(:http_code)
133
- ApiResource.logger.error("#{self} accessing #{path}")
134
- ApiResource.logger.error(error.message)
135
- result = error.response
136
- else
137
- raise ApiResource::ConnectionError.new(nil, :message => "Unknown error #{error}")
138
- end
139
- end
140
- return propogate_response_or_error(result, result.code)
141
- end
142
-
143
- def propogate_response_or_error(response, code)
144
- case code.to_i
145
- when 301,302
146
- raise ApiResource::Redirection.new(response)
147
- when 200..400
148
- response.body
149
- when 400
150
- raise ApiResource::BadRequest.new(response)
151
- when 401
152
- raise ApiResource::UnauthorizedAccess.new(response)
153
- when 403
154
- raise ApiResource::ForbiddenAccess.new(response)
155
- when 404
156
- raise ApiResource::ResourceNotFound.new(response)
157
- when 405
158
- raise ApiResource::MethodNotAllowed.new(response)
159
- when 406
160
- raise ApiResource::NotAccepatable.new(response)
161
- when 409
162
- raise ApiResource::ResourceNotFound.new(response)
163
- when 410
164
- raise ApiResource::ResourceGone.new(response)
165
- when 422
166
- raise ApiResource::UnprocessableEntity.new(response)
167
- when 401..499
168
- raise ApiResource::ClientError.new(response)
169
- when 500..600
170
- raise ApiResource::ServerError.new(response)
171
- else
172
- raise ApiResource::ConnectionError.new(response, :message => "Unknown response code: #{code}")
173
- end
174
- end
175
-
176
- # Creates new Net::HTTP instance for communication with the
177
- # remote service and resources.
178
- def http(path)
179
- unless path =~ /\./
180
- path += ".#{self.format.extension}"
181
- end
182
- RestClient::Resource.new("#{site.scheme}://#{site.host}:#{site.port}#{path}", {:timeout => ApiResource::Base.timeout, :open_timeout => ApiResource::Base.open_timeout})
183
- end
184
-
185
- def build_request_headers(headers, verb, uri)
186
- http_format_header(verb).update(headers)
187
- end
188
-
189
- def http_format_header(verb)
190
- {}.tap do |ret|
191
- ret[HTTP_FORMAT_HEADER_NAMES[verb]] = format.mime_type
192
- end
193
- end
194
- end
195
- end
@@ -1,7 +0,0 @@
1
- class Array
2
-
3
- def symbolize_array
4
- self.collect{|item| item.to_s.to_sym}
5
- end
6
-
7
- end
@@ -1,117 +0,0 @@
1
- require 'active_support/core_ext/object/blank'
2
-
3
- module ApiResource
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,
6
- # say you use Rails to expose a REST service and configure your routes with:
7
- #
8
- # map.resources :people, :new => { :register => :post },
9
- # :member => { :promote => :put, :deactivate => :delete }
10
- # :collection => { :active => :get }
11
- #
12
- # This route set creates routes for the following HTTP requests:
13
- #
14
- # POST /people/new/register.xml # PeopleController.register
15
- # PUT /people/1/promote.xml # PeopleController.promote with :id => 1
16
- # DELETE /people/1/deactivate.xml # PeopleController.deactivate with :id => 1
17
- # GET /people/active.xml # PeopleController.active
18
- #
19
- # Using this module, Active Resource can use these custom REST methods just like the
20
- # standard methods.
21
- #
22
- # class Person < ActiveResource::Base
23
- # self.site = "http://37s.sunrise.i:3000"
24
- # end
25
- #
26
- # Person.new(:name => 'Ryan).post(:register) # POST /people/new/register.xml
27
- # # => { :id => 1, :name => 'Ryan' }
28
- #
29
- # Person.find(1).put(:promote, :position => 'Manager') # PUT /people/1/promote.xml
30
- # Person.find(1).delete(:deactivate) # DELETE /people/1/deactivate.xml
31
- #
32
- # Person.get(:active) # GET /people/active.xml
33
- # # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
34
- #
35
- module CustomMethods
36
- extend ActiveSupport::Concern
37
-
38
- included do
39
- class << self
40
- alias :orig_delete :delete
41
-
42
- # Invokes a GET to a given custom REST method. For example:
43
- #
44
- # Person.get(:active) # GET /people/active.xml
45
- # # => [{:id => 1, :name => 'Ryan'}, {:id => 2, :name => 'Joe'}]
46
- #
47
- # Person.get(:active, :awesome => true) # GET /people/active.xml?awesome=true
48
- # # => [{:id => 1, :name => 'Ryan'}]
49
- #
50
- # Note: the objects returned from this method are not automatically converted
51
- # into ActiveResource::Base instances - they are ordinary Hashes. If you are expecting
52
- # ActiveResource::Base instances, use the <tt>find</tt> class method with the
53
- # <tt>:from</tt> option. For example:
54
- #
55
- # Person.find(:all, :from => :active)
56
- def get(custom_method_name, options = {})
57
- connection.get(custom_method_collection_url(custom_method_name, options), headers)
58
- end
59
-
60
- def post(custom_method_name, options = {}, body = '')
61
- connection.post(custom_method_collection_url(custom_method_name, options), body, headers)
62
- end
63
-
64
- def put(custom_method_name, options = {}, body = '')
65
- connection.put(custom_method_collection_url(custom_method_name, options), body, headers)
66
- end
67
-
68
- def delete(custom_method_name, options = {})
69
- # Need to jump through some hoops to retain the original class 'delete' method
70
- if custom_method_name.is_a?(Symbol)
71
- connection.delete(custom_method_collection_url(custom_method_name, options), headers)
72
- else
73
- orig_delete(custom_method_name, options)
74
- end
75
- end
76
- end
77
- end
78
-
79
- module ClassMethods
80
- def custom_method_collection_url(method_name, options = {})
81
- prefix_options, query_options = split_options(options)
82
- "#{prefix(prefix_options)}#{collection_name}/#{method_name}.#{format.extension}#{query_string(query_options)}"
83
- end
84
- end
85
-
86
- def get(method_name, options = {})
87
- connection.get(custom_method_element_url(method_name, options), self.class.headers)
88
- end
89
-
90
- def post(method_name, options = {}, body = nil)
91
- request_body = body.blank? ? encode : body
92
- if new?
93
- connection.post(custom_method_new_element_url(method_name, options), request_body, self.class.headers)
94
- else
95
- connection.post(custom_method_element_url(method_name, options), request_body, self.class.headers)
96
- end
97
- end
98
-
99
- def put(method_name, options = {}, body = '')
100
- connection.put(custom_method_element_url(method_name, options), body, self.class.headers)
101
- end
102
-
103
- def delete(method_name, options = {})
104
- connection.delete(custom_method_element_url(method_name, options), self.class.headers)
105
- end
106
-
107
-
108
- private
109
- def custom_method_element_url(method_name, options = {})
110
- "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/#{id}/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
111
- end
112
-
113
- def custom_method_new_element_url(method_name, options = {})
114
- "#{self.class.prefix(prefix_options)}#{self.class.collection_name}/new/#{method_name}.#{self.class.format.extension}#{self.class.__send__(:query_string, options)}"
115
- end
116
- end
117
- end
@@ -1,6 +0,0 @@
1
- module ApiResource
2
- module Decorators
3
- extend ActiveSupport::Autoload
4
- autoload :CachingDecorator
5
- end
6
- end
@@ -1,20 +0,0 @@
1
- module ApiResource
2
- module Decorators
3
- class CachingDecorator
4
- attr_reader :owner
5
- attr_reader :ttl
6
-
7
- def initialize(owner, ttl)
8
- @owner = owner
9
- @ttl = ttl
10
- end
11
-
12
-
13
- def method_missing(method_name, *args, &block)
14
- ApiResource.with_ttl(self.ttl) do
15
- self.owner.send(method_name, *args, &block)
16
- end
17
- end
18
- end
19
- end
20
- end
@@ -1,99 +0,0 @@
1
- module ApiResource
2
- class ConnectionError < StandardError # :nodoc:
3
-
4
- cattr_accessor :http_code
5
-
6
- attr_reader :response
7
-
8
- def initialize(response, options = {})
9
- @response = response
10
- @message = options[:message]
11
- @path = options[:path]
12
- end
13
-
14
- def to_s
15
- message = "Failed."
16
-
17
- if response.respond_to?(:code)
18
- message << " Response code = #{response.code}."
19
- end
20
-
21
- if response.respond_to?(:body)
22
- begin
23
- body = JSON.parse(response.body).pretty_inspect
24
- rescue
25
- body = "INVALID JSON: #{response.body}"
26
- end
27
- message << "\nResponse message = #{body}."
28
- end
29
-
30
- message << "\n#{@message}"
31
- message << "\n#{@path}"
32
- end
33
-
34
- def http_code
35
- self.class.http_code
36
- end
37
-
38
- end
39
-
40
- # Raised when a Timeout::Error occurs.
41
- class RequestTimeout < ConnectionError
42
- def initialize(message)
43
- @message = message
44
- end
45
- def to_s; @message ;end
46
- end
47
-
48
- # Raised when a OpenSSL::SSL::SSLError occurs.
49
- class SSLError < ConnectionError
50
- def initialize(message)
51
- @message = message
52
- end
53
- def to_s; @message ;end
54
- end
55
-
56
- # 3xx Redirection
57
- class Redirection < ConnectionError # :nodoc:
58
- def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
59
- end
60
-
61
- # 4xx Client Error
62
- class ClientError < ConnectionError; end # :nodoc:
63
-
64
- # 400 Bad Request
65
- class BadRequest < ClientError; self.http_code = 400; end # :nodoc
66
-
67
- # 401 Unauthorized
68
- class UnauthorizedAccess < ClientError; self.http_code = 401; end # :nodoc
69
-
70
- # 403 Forbidden
71
- class ForbiddenAccess < ClientError; self.http_code = 403; end # :nodoc
72
-
73
- # 404 Not Found
74
- class ResourceNotFound < ClientError; self.http_code = 404; end # :nodoc:
75
-
76
- # 406 Not Acceptable
77
- class NotAcceptable < ClientError; self.http_code = 406; end
78
-
79
- # 409 Conflict
80
- class ResourceConflict < ClientError; self.http_code = 409; end # :nodoc:
81
-
82
- # 410 Gone
83
- class ResourceGone < ClientError; self.http_code = 410; end # :nodoc:
84
-
85
- class UnprocessableEntity < ClientError; self.http_code = 422; end
86
-
87
- # 5xx Server Error
88
- class ServerError < ConnectionError; self.http_code = 400; end # :nodoc:
89
-
90
- # 405 Method Not Allowed
91
- class MethodNotAllowed < ClientError # :nodoc:
92
-
93
- self.http_code = 405
94
-
95
- def allowed_methods
96
- @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
97
- end
98
- end
99
- end