api_resource 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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