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,22 +0,0 @@
1
- module ApiResource
2
- module Formats
3
- autoload :XmlFormat, 'api_resource/formats/xml_format'
4
- autoload :JsonFormat, 'api_resource/formats/json_format'
5
-
6
- # Lookup the format class from a mime type reference symbol. Example:
7
- #
8
- # ActiveResource::Formats[:xml] # => ActiveResource::Formats::XmlFormat
9
- # ActiveResource::Formats[:json] # => ActiveResource::Formats::JsonFormat
10
- def self.[](mime_type_reference)
11
- format_name = ActiveSupport::Inflector.camelize(mime_type_reference.to_s) + "Format"
12
- begin
13
- ApiResource::Formats.const_get(format_name)
14
- rescue NameError => e
15
- raise BadFormat.new("#{mime_type_reference} is not a valid format")
16
- end
17
- end
18
-
19
- class BadFormat < StandardError
20
- end
21
- end
22
- end
@@ -1,25 +0,0 @@
1
- require 'active_support/json'
2
- require 'json'
3
- module ApiResource
4
- module Formats
5
- module JsonFormat
6
- extend self
7
-
8
- def extension
9
- "json"
10
- end
11
-
12
- def mime_type
13
- "application/json"
14
- end
15
-
16
- def encode(hash, options = nil)
17
- JSON.dump(hash, options)
18
- end
19
-
20
- def decode(json)
21
- JSON.parse(json)
22
- end
23
- end
24
- end
25
- end
@@ -1,36 +0,0 @@
1
- require 'active_support/core_ext/hash/conversions'
2
-
3
- module ApiResource
4
- module Formats
5
- module XmlFormat
6
- extend self
7
-
8
- def extension
9
- "xml"
10
- end
11
-
12
- def mime_type
13
- "application/xml"
14
- end
15
-
16
- def encode(hash, options={})
17
- hash.to_xml(options)
18
- end
19
-
20
- def decode(xml)
21
- from_xml_data(Hash.from_xml(xml))
22
- end
23
-
24
- private
25
- # Manipulate from_xml Hash, because xml_simple is not exactly what we
26
- # want for Active Resource.
27
- def from_xml_data(data)
28
- if data.is_a?(Hash) && data.keys.size == 1
29
- data.values.first
30
- else
31
- data
32
- end
33
- end
34
- end
35
- end
36
- end
@@ -1,12 +0,0 @@
1
- module ApiResource
2
- class Local < Base
3
- # nothing to do here
4
- def self.set_class_attributes_upon_load
5
- true
6
- end
7
- # shouldn't do anything here either -
8
- def self.reload_class_attributes
9
- true
10
- end
11
- end
12
- end
@@ -1,15 +0,0 @@
1
- module ApiResource
2
- class LogSubscriber < ActiveSupport::LogSubscriber
3
- def request(event)
4
- result = event[:payload]
5
- info "#{event.payload[:method].to_s.upcase} #{even.payload[:request_uri]}"
6
- info "--> %d %s %d (%.1fms)" % [result.code, result.message, result.body.to_s.length, event.duration]
7
- end
8
-
9
- def logger
10
- Rails.logger
11
- end
12
- end
13
- end
14
-
15
- ApiResource::LogSubscriber.attach_to :api_resource
@@ -1,277 +0,0 @@
1
- require 'api_resource'
2
-
3
- module ApiResource
4
-
5
- module Mocks
6
-
7
- @@endpoints = {}
8
- @@path = nil
9
-
10
- # A simple interface class to change the new connection to look like the
11
- # old activeresource connection
12
- class Interface
13
-
14
- def initialize(path)
15
- @path = path
16
- end
17
-
18
- def get(*args, &block)
19
- Connection.send(:get, @path, *args, &block)
20
- end
21
- def post(*args, &block)
22
- Connection.send(:post, @path, *args, &block)
23
- end
24
- def put(*args, &block)
25
- Connection.send(:put, @path, *args, &block)
26
- end
27
- def delete(*args, &block)
28
- Connection.send(:delete, @path, *args, &block)
29
- end
30
- def head(*args, &block)
31
- Connection.send(:head, @path, *args, &block)
32
- end
33
- end
34
-
35
- # set ApiResource's http
36
- def self.init
37
- ::ApiResource::Connection.class_eval do
38
- private
39
- alias_method :http_without_mock, :http
40
- def http(path)
41
- Interface.new(path)
42
- end
43
- end
44
- end
45
-
46
- # set ApiResource's http
47
- def self.remove
48
- ::ApiResource::Connection.class_eval do
49
- private
50
- alias_method :http, :http_without_mock
51
- end
52
- end
53
-
54
- # clear out the defined mocks
55
- def self.clear_endpoints
56
- ret = @@endpoints
57
- @@endpoints = {}
58
- ret
59
- end
60
- # re-set the endpoints
61
- def self.set_endpoints(new_endpoints)
62
- @@endpoints = new_endpoints
63
- end
64
- # return the defined endpoints
65
- def self.endpoints
66
- @@endpoints
67
- end
68
- def self.define(&block)
69
- instance_eval(&block) if block_given?
70
- end
71
- # define an endpoint for the mock
72
- def self.endpoint(path, &block)
73
- path, format = path.split(".")
74
- @@endpoints[path] ||= []
75
- with_path_and_format(path, format) do
76
- instance_eval(&block) if block_given?
77
- end
78
- end
79
- # find a matching response
80
- def self.find_response(request)
81
- # these are stored as [[Request, Response], [Request, Response]]
82
- responses_and_params = self.responses_for_path(request.path)
83
- ret = (responses_and_params[:responses] || []).select{|pair| pair.first.match?(request)}
84
- raise Exception.new("More than one response matches #{request}") if ret.length > 1
85
- return ret.first ? {:response => ret.first[1], :params => responses_and_params[:params]} : nil
86
- end
87
-
88
- def self.paths_match?(known_path, entered_path)
89
- PathString.paths_match?(known_path, entered_path)
90
- end
91
-
92
- # This method assumes that the two are matching paths
93
- # if they aren't the behavior is undefined
94
- def self.extract_params(known_path, entered_path)
95
- PathString.extract_params(known_path, entered_path)
96
- end
97
-
98
- # returns a hash {:responses => [[Request, Response],[Request,Response]], :params => {...}}
99
- # if there is no match returns nil
100
- def self.responses_for_path(path)
101
- path = path.split("?").first
102
- path = path.split(/\./).first
103
- # The obvious case
104
- if @@endpoints[path]
105
- return {:responses => @@endpoints[path], :params => {}}
106
- end
107
- # parameter names prefixed with colons should match parts
108
- # of the path and push those parameters into the response
109
- @@endpoints.keys.each do |possible_path|
110
- if self.paths_match?(possible_path, path)
111
- return {:responses => @@endpoints[possible_path], :params => self.extract_params(possible_path, path)}
112
- end
113
- end
114
-
115
- return {:responses => nil, :params => nil}
116
- end
117
-
118
-
119
- private
120
- def self.with_path_and_format(path, format, &block)
121
- @@path, @@format = path, format
122
- ret = yield
123
- @@path, @@format = nil, nil
124
- ret
125
- end
126
- # define the
127
- [:post, :put, :get, :delete, :head].each do |verb|
128
- instance_eval <<-EOE, __FILE__, __LINE__ + 1
129
- def #{verb}(response_body, opts = {}, &block)
130
-
131
- raise Exception.new("Must be called from within an endpoint block") unless @@path
132
- opts = opts.reverse_merge({:status_code => 200, :response_headers => {}, :params => {}})
133
-
134
- @@endpoints[@@path] << [MockRequest.new(:#{verb}, @@path, :params => opts[:params], :format => @@format), MockResponse.new(response_body, :status_code => opts[:status_code], :headers => opts[:response_headers], :format => @@format, &block)]
135
- end
136
- EOE
137
- end
138
-
139
- class MockResponse
140
- attr_reader :body, :headers, :code, :format, :block
141
- def initialize(body, opts = {}, &block)
142
- opts = opts.reverse_merge({:headers => {}, :status_code => 200})
143
- @body = body
144
- @headers = opts[:headers]
145
- @code = opts[:status_code]
146
- @format = (opts[:format] || :json)
147
- @block = block if block_given?
148
- end
149
- def []=(key, val)
150
- @headers[key] = val
151
- end
152
- def [](key)
153
- @headers[key]
154
- end
155
-
156
- def body
157
- raise Exception.new("Body must respond to to_#{self.format}") unless @body.respond_to?("to_#{self.format}")
158
- @body.send("to_#{self.format}")
159
- end
160
-
161
- def body_as_object
162
- return @body
163
- end
164
-
165
- def generate_response(params)
166
- @body = @body.instance_exec(params, &self.block) if self.block
167
- end
168
- end
169
-
170
- class MockRequest
171
- attr_reader :method, :path, :body, :headers, :params, :format, :query
172
-
173
- def initialize(method, path, opts = {})
174
- @method = method.to_sym
175
-
176
- # set the normalized path, format and query string
177
- @path, @query = path.split("?")
178
- @path, @format = @path.split(".")
179
-
180
- # if we have params, it is a MockRequest definition
181
- if opts[:params]
182
- @params = opts[:params]
183
- # otherwise, we need to check either the query string or the body
184
- # depending on the http verb
185
- else
186
- case @method
187
- when :post, :put
188
- @params = JSON.parse(opts[:body] || "")
189
- when :get, :delete, :head
190
- @params = sorted_params(@query || "")
191
- end
192
- end
193
- @body = opts[:body]
194
- @headers = opts[:headers] || {}
195
- @headers["Content-Length"] = @body.blank? ? "0" : @body.size.to_s
196
- end
197
-
198
- #
199
- def sorted_params(data)
200
- ret = {}
201
- data.split("&").each do |entry|
202
- key, val = entry.split("=")
203
- if val.to_s =~ /^\d+$/
204
- ret[key] = val.to_i
205
- elsif val =~ /^[\d\.]+$/
206
- ret[key] = val.to_f
207
- elsif val == "true"
208
- ret[key] = true
209
- elsif val == "false"
210
- ret[key] = false
211
- else
212
- ret[key] = val || ""
213
- end
214
- end
215
- ret
216
- end
217
-
218
- # because of the context these come from, we can assume that the path already matches
219
- def match?(request)
220
- return false unless self.method == request.method
221
- return false unless self.format == request.format || request.format.nil? || self.format.nil?
222
- Comparator.diff(self.params, request.params) == {}
223
- end
224
- # string representation
225
- def to_s
226
- "#{self.method.upcase} #{self.format} #{self.path} #{self.params}"
227
- end
228
- end
229
- class Connection
230
-
231
- cattr_accessor :requests
232
- self.requests = []
233
-
234
- # body? methods
235
- { true => %w(post put),
236
- false => %w(get delete head) }.each do |has_body, methods|
237
- methods.each do |method|
238
- # def post(path, body, headers)
239
- # request = ApiResource::Request.new(:post, path, body, headers)
240
- # self.class.requests << request
241
- # if response = LifebookerClient::Mocks.find_response(request)
242
- # response
243
- # else
244
- # raise InvalidRequestError.new("Could not find a response
245
- # recorded for #{request.to_s} - Responses recorded are: -
246
- # #{inspect_responses}")
247
- # end
248
- # end
249
- instance_eval <<-EOE, __FILE__, __LINE__ + 1
250
- def #{method}(path, #{'body, ' if has_body}headers)
251
- opts = {:headers => headers}
252
- #{"opts[:body] = body" if has_body}
253
- request = MockRequest.new(:#{method}, path, opts)
254
- self.requests << request
255
- if response = Mocks.find_response(request)
256
- response[:response].tap{|resp|
257
- resp.generate_response(
258
- request.params
259
- .with_indifferent_access
260
- .merge(response[:params].with_indifferent_access)
261
- )
262
- }
263
- else
264
- raise ApiResource::ResourceNotFound.new(
265
- MockResponse.new({}, {:headers => {"Content-type" => "application/json"}, :status_code => 404}),
266
- :message => "\nCould not find a response recorded for \#{request.pretty_inspect}\n" +
267
- "Potential Responses Are:\n" +
268
- "\#{Array.wrap(Mocks.responses_for_path(request.path)[:responses]).collect(&:first).pretty_inspect}"
269
- )
270
- end
271
- end
272
- EOE
273
- end
274
- end
275
- end
276
- end
277
- end
@@ -1,82 +0,0 @@
1
- module ApiResource
2
-
3
- class Errors < ::ActiveModel::Errors
4
-
5
- def from_array(messages, save_cache = false)
6
- clear unless save_cache
7
- humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.humanize => attr_name) }
8
- messages.each do |message|
9
- attr_message = humanized_attributes.keys.detect do |attr_name|
10
- if message[0,attr_name.size + 1] == "#{attr_name} "
11
- add humanized_attributes[attr_name], message[(attr_name.size + 1)..-1]
12
- end
13
- end
14
- end
15
- end
16
-
17
- def from_hash(messages, save_cache = false)
18
- clear unless save_cache
19
- messages.each do |attr, message_array|
20
- message_array.each do |message|
21
- add attr, message
22
- end
23
- end
24
- end
25
-
26
- end
27
-
28
- module ModelErrors
29
-
30
- extend ActiveSupport::Concern
31
- include ActiveModel::Validations
32
-
33
- included do
34
- # this is required here because this module will be above Base in the inheritance chain
35
- alias_method_chain :save, :validations
36
- end
37
-
38
- def save_with_validations(*args)
39
- # we want to leave the original intact
40
- options = args.clone.extract_options!
41
-
42
- perform_validation = options.blank? ? true : options[:validate]
43
-
44
- @remote_errors = nil
45
- if perform_validation && valid? || !perform_validation
46
- save_without_validations(*args)
47
- true
48
- else
49
- false
50
- end
51
- rescue ApiResource::UnprocessableEntity => error
52
- @remote_errors = error
53
- load_remote_errors(@remote_errors, true)
54
- false
55
- end
56
-
57
- def load_remote_errors(remote_errors, save_cache = false)
58
- error_data = self.class.format.decode(remote_errors.response.body)['errors'] || {}
59
- if error_data.is_a?(Hash)
60
- self.errors.from_hash(error_data)
61
- elsif error_data.is_a?(Array)
62
- self.errors.from_array(error_data)
63
- else
64
- raise Exception.new
65
- end
66
- rescue Exception
67
- raise "Invalid response for invalid object: expected an array or hash got #{remote_errors}"
68
- end
69
-
70
- # This method runs any local validations but not remote ones
71
- def valid?
72
- super
73
- errors.empty?
74
- end
75
-
76
- def errors
77
- @errors ||= ApiResource::Errors.new(self)
78
- end
79
-
80
- end
81
-
82
- end