api-model 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -2
- data/README.md +6 -4
- data/api-model.gemspec +1 -1
- data/lib/api-model.rb +3 -0
- data/lib/api_model/assignment.rb +20 -0
- data/lib/api_model/configuration.rb +1 -1
- data/lib/api_model/http_request.rb +2 -1
- data/lib/api_model/initializer.rb +1 -12
- data/lib/api_model/instance_methods.rb +1 -16
- data/lib/api_model/response.rb +5 -0
- data/spec/api-model/api_model_spec.rb +20 -8
- data/spec/api-model/response_spec.rb +22 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/fixtures/errors.yml +27 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b46c6c4b3e683a5e7c5a3577611b72fb7b398e8e
|
4
|
+
data.tar.gz: 89a47c86aecc1c2b66e1566511b2939335d2e385
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dab983c5d688a24d9916f7a2a73073a3b63e59d8cae7296e3b4eb554081f031f03b375690768258507fb1d07c7ceacb7779cd3f3a50ca780b87866627eb2c804
|
7
|
+
data.tar.gz: fb188b0fed0aeb683e9c9c3e84fe979e79d0045c766e81b115c69bbf03c951ca396289955154e5de92ab17d8b4f31831a7a2ef2f280ae8b23b1226ed0309d6e8
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
api-model (0.1.
|
4
|
+
api-model (0.1.3)
|
5
5
|
activemodel
|
6
6
|
activesupport
|
7
7
|
hashie
|
@@ -35,7 +35,7 @@ GEM
|
|
35
35
|
method_source (0.8.2)
|
36
36
|
mime-types (1.25.1)
|
37
37
|
minitest (4.7.5)
|
38
|
-
multi_json (1.8.
|
38
|
+
multi_json (1.8.4)
|
39
39
|
pry (0.9.12.2)
|
40
40
|
coderay (~> 1.0.5)
|
41
41
|
method_source (~> 0.8)
|
data/README.md
CHANGED
@@ -143,7 +143,7 @@ configuration options may not be available on the per-api call technique).
|
|
143
143
|
|
144
144
|
```ruby
|
145
145
|
ApiModel::Base.api_config do |config|
|
146
|
-
config.
|
146
|
+
config.host = "http:://someserver.com"
|
147
147
|
end
|
148
148
|
```
|
149
149
|
|
@@ -189,17 +189,19 @@ an API which returns something other than JSON, you can set custom parsers to de
|
|
189
189
|
before they are sent to builder classes. The parser should work in the same way as a custom builder, except it needs
|
190
190
|
to respond to `#parse`, with the raw response body as an argument.
|
191
191
|
|
192
|
-
###
|
192
|
+
### Raising exceptions
|
193
193
|
|
194
194
|
```ruby
|
195
195
|
ApiModel::Base.api_config do |config|
|
196
196
|
config.raise_on_not_found = true
|
197
197
|
config.raise_on_unauthenticated = true
|
198
|
+
config.raise_on_server_error = true
|
198
199
|
end
|
199
200
|
```
|
200
201
|
|
201
|
-
This will cause any API requests which return a 404 status to raise an ApiModel::NotFoundError exception,
|
202
|
-
which return
|
202
|
+
This will cause any API requests which return a 404 status to raise an ApiModel::NotFoundError exception,
|
203
|
+
requests which return 500 to raise an ApiModel::ServerError exception, and requests which return a 401
|
204
|
+
to raise an ApiModel::UnauthenticatedError exception. All default to `false`.
|
203
205
|
|
204
206
|
### Cache strategy & settings
|
205
207
|
|
data/api-model.gemspec
CHANGED
@@ -2,7 +2,7 @@ $:.push File.expand_path("../lib", __FILE__)
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = "api-model"
|
5
|
-
s.version = "0.1.
|
5
|
+
s.version = "0.1.3"
|
6
6
|
s.authors = ["Damien Timewell"]
|
7
7
|
s.email = ["mail@damientimewell.com"]
|
8
8
|
s.homepage = "https://github.com/iZettle/api-model"
|
data/lib/api-model.rb
CHANGED
@@ -6,6 +6,7 @@ require 'hashie'
|
|
6
6
|
require 'typhoeus'
|
7
7
|
require 'ostruct'
|
8
8
|
|
9
|
+
require 'api_model/assignment'
|
9
10
|
require 'api_model/initializer'
|
10
11
|
require 'api_model/http_request'
|
11
12
|
require 'api_model/response'
|
@@ -22,6 +23,7 @@ module ApiModel
|
|
22
23
|
class ResponseBuilderError < StandardError; end
|
23
24
|
class UnauthenticatedError < StandardError; end
|
24
25
|
class NotFoundError < StandardError; end
|
26
|
+
class ServerError < StandardError; end
|
25
27
|
|
26
28
|
if defined?(Rails)
|
27
29
|
class Railtie < Rails::Railtie
|
@@ -40,6 +42,7 @@ module ApiModel
|
|
40
42
|
extend ActiveModel::Callbacks
|
41
43
|
|
42
44
|
extend ClassMethods
|
45
|
+
include Assignment
|
43
46
|
include ConfigurationMethods
|
44
47
|
include InstanceMethods
|
45
48
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ApiModel
|
2
|
+
module Assignment
|
3
|
+
|
4
|
+
# Convenience method to change attributes on an instance en-masse using a hash. This is
|
5
|
+
# useful for when an api response includes changed attributes and you want to update the current
|
6
|
+
# instance with the changes.
|
7
|
+
def update_attributes(values={})
|
8
|
+
return unless values.present?
|
9
|
+
|
10
|
+
values.each do |key,value|
|
11
|
+
begin
|
12
|
+
public_send "#{key}=", value
|
13
|
+
rescue
|
14
|
+
Log.debug "Could not set #{key} on #{self.class.name}"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -3,7 +3,7 @@ module ApiModel
|
|
3
3
|
include Initializer
|
4
4
|
|
5
5
|
attr_accessor :host, :json_root, :headers, :raise_on_unauthenticated, :cache_settings,
|
6
|
-
:raise_on_not_found, :cache_strategy, :parser, :builder
|
6
|
+
:raise_on_not_found, :cache_strategy, :parser, :builder, :raise_on_server_error
|
7
7
|
|
8
8
|
def self.from_inherited_config(config)
|
9
9
|
new config.instance_values.reject {|k,v| v.blank? }
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ApiModel
|
2
2
|
class HttpRequest
|
3
|
-
include
|
3
|
+
include Initializer
|
4
4
|
|
5
5
|
attr_accessor :path, :method, :options, :api_call, :builder, :config, :cache_id
|
6
6
|
|
@@ -14,6 +14,7 @@ module ApiModel
|
|
14
14
|
|
15
15
|
def run
|
16
16
|
run_callbacks :run do
|
17
|
+
Log.debug "#{method.to_s.upcase} #{full_path} #{options}"
|
17
18
|
self.api_call = Typhoeus.send method, full_path, options
|
18
19
|
Response.new self, config
|
19
20
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module ApiModel
|
2
2
|
module Initializer
|
3
|
+
include ApiModel::Assignment
|
3
4
|
extend ActiveSupport::Concern
|
4
5
|
|
5
6
|
included do
|
@@ -13,17 +14,5 @@ module ApiModel
|
|
13
14
|
end
|
14
15
|
end
|
15
16
|
|
16
|
-
def update_attributes(values={})
|
17
|
-
return unless values.present?
|
18
|
-
|
19
|
-
values.each do |key,value|
|
20
|
-
begin
|
21
|
-
public_send "#{key}=", value
|
22
|
-
rescue
|
23
|
-
Log.debug "Could not set #{key} on #{self.class.name}"
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
17
|
end
|
29
18
|
end
|
@@ -30,21 +30,6 @@ module ApiModel
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
-
# Convenience method to change attributes on an instance en-masse using a hash. This is
|
34
|
-
# useful for when an api response includes changed attributes and you want to update the current
|
35
|
-
# instance with the changes.
|
36
|
-
def update_attributes_from_hash(values={})
|
37
|
-
return unless values.present?
|
38
|
-
|
39
|
-
values.each do |key,value|
|
40
|
-
begin
|
41
|
-
public_send "#{key}=", value
|
42
|
-
rescue
|
43
|
-
Log.debug "Could not set #{key} on #{self.class.name}"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
33
|
# Sends a request to the api to update a resource. If the response was successful, then it will
|
49
34
|
# update the instance with any changes which the API has returned. If not, it will set ActiveModel
|
50
35
|
# errors.
|
@@ -68,7 +53,7 @@ module ApiModel
|
|
68
53
|
|
69
54
|
if response_success
|
70
55
|
run_callbacks :successful_save do
|
71
|
-
|
56
|
+
update_attributes response.response_body
|
72
57
|
end
|
73
58
|
else
|
74
59
|
run_callbacks :unsuccessful_save do
|
data/lib/api_model/response.rb
CHANGED
@@ -21,6 +21,7 @@ module ApiModel
|
|
21
21
|
def build_objects
|
22
22
|
raise UnauthenticatedError if @_config.raise_on_unauthenticated && http_response.api_call.response_code == 401
|
23
23
|
raise NotFoundError if @_config.raise_on_not_found && http_response.api_call.response_code == 404
|
24
|
+
raise ServerError if @_config.raise_on_server_error && http_response.api_call.response_code == 500
|
24
25
|
return if response_body.nil?
|
25
26
|
|
26
27
|
if response_build_hash.is_a? Array
|
@@ -44,6 +45,10 @@ module ApiModel
|
|
44
45
|
@response_body ||= @_config.parser.parse http_response.api_call.body
|
45
46
|
end
|
46
47
|
|
48
|
+
def successful?
|
49
|
+
http_response.api_call.success?
|
50
|
+
end
|
51
|
+
|
47
52
|
# Define common methods which should never be called on this abstract class, and should always be
|
48
53
|
# passed down to the #objects
|
49
54
|
FALL_THROUGH_METHODS.each do |transparent_method|
|
@@ -121,9 +121,7 @@ describe ApiModel do
|
|
121
121
|
end
|
122
122
|
|
123
123
|
describe "with a single object which has properties which are undefined" do
|
124
|
-
let
|
125
|
-
VCR.use_cassette('cars') { Car.get_json "http://cars.com/new_model" }
|
126
|
-
end
|
124
|
+
let(:new_car) { VCR.use_cassette('cars') { Car.get_json "http://cars.com/new_model" } }
|
127
125
|
|
128
126
|
it "should not raise an exception" do
|
129
127
|
expect {
|
@@ -164,19 +162,19 @@ describe ApiModel do
|
|
164
162
|
it 'should change an existing attribute' do
|
165
163
|
car.name = "Chevvy"
|
166
164
|
expect {
|
167
|
-
car.
|
165
|
+
car.update_attributes name: "Ford"
|
168
166
|
}.to change{ car.name }.from("Chevvy").to("Ford")
|
169
167
|
end
|
170
168
|
|
171
169
|
it 'should set an attribute if unset' do
|
172
170
|
expect {
|
173
|
-
car.
|
171
|
+
car.update_attributes number_of_doors: 2
|
174
172
|
}.to change{ car.number_of_doors }.from(nil).to(2)
|
175
173
|
end
|
176
174
|
|
177
175
|
it 'should log if the attribute is not defined' do
|
178
176
|
ApiModel::Log.should_receive(:debug).with "Could not set age on Car"
|
179
|
-
car.
|
177
|
+
car.update_attributes age: 2
|
180
178
|
end
|
181
179
|
end
|
182
180
|
|
@@ -201,8 +199,8 @@ describe ApiModel do
|
|
201
199
|
VCR.use_cassette('posts') { blog_post.save "/post/2", name: "foobarbaz" }
|
202
200
|
end
|
203
201
|
|
204
|
-
it 'should use #
|
205
|
-
blog_post.should_receive(:
|
202
|
+
it 'should use #update_attributes using the response body to update the instance' do
|
203
|
+
blog_post.should_receive(:update_attributes).with "name" => "foobarbaz"
|
206
204
|
VCR.use_cassette('posts') { blog_post.save "/post/2", name: "foobarbaz" }
|
207
205
|
end
|
208
206
|
|
@@ -247,4 +245,18 @@ describe ApiModel do
|
|
247
245
|
end
|
248
246
|
end
|
249
247
|
|
248
|
+
describe "successful?" do
|
249
|
+
let(:new_car) { VCR.use_cassette('cars') { Car.get_json "http://cars.com/new_model" } }
|
250
|
+
|
251
|
+
it 'should be true if the api call was successful' do
|
252
|
+
new_car.stub_chain(:http_response, :api_call, :success?).and_return true
|
253
|
+
new_car.successful?.should be_true
|
254
|
+
end
|
255
|
+
|
256
|
+
it 'should be false if the api call was not successful' do
|
257
|
+
new_car.stub_chain(:http_response, :api_call, :success?).and_return false
|
258
|
+
new_car.successful?.should be_false
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
250
262
|
end
|
@@ -169,6 +169,28 @@ describe ApiModel::Response do
|
|
169
169
|
}.to_not raise_error
|
170
170
|
end
|
171
171
|
end
|
172
|
+
|
173
|
+
describe "for requests which return a 500" do
|
174
|
+
let :api_request do
|
175
|
+
VCR.use_cassette('errors') do
|
176
|
+
BlogPost.get_json "http://api-model-specs.com/server_error"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'should raise an ApiModel::ServerError if raise_on_server_error is true' do
|
181
|
+
BlogPost.api_config { |c| c.raise_on_server_error = true }
|
182
|
+
expect {
|
183
|
+
api_request
|
184
|
+
}.to raise_error(ApiModel::ServerError)
|
185
|
+
end
|
186
|
+
|
187
|
+
it 'should not raise an ApiModel::ServerError if raise_on_server_error is false' do
|
188
|
+
BlogPost.api_config { |c| c.raise_on_server_error = false }
|
189
|
+
expect {
|
190
|
+
api_request
|
191
|
+
}.to_not raise_error
|
192
|
+
end
|
193
|
+
end
|
172
194
|
end
|
173
195
|
|
174
196
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -10,6 +10,10 @@ VCR.configure do |c|
|
|
10
10
|
c.hook_into :webmock # or :fakeweb
|
11
11
|
end
|
12
12
|
|
13
|
+
# Disable STDOUT logging during tests
|
14
|
+
ApiModel.send :remove_const, :Log
|
15
|
+
ApiModel::Log = Logger.new('/dev/null')
|
16
|
+
|
13
17
|
RSpec.configure do |config|
|
14
18
|
|
15
19
|
# Reset any config changes after each spec
|
@@ -54,4 +54,31 @@ http_interactions:
|
|
54
54
|
http_version:
|
55
55
|
recorded_at: Thu, 28 Nov 2013 16:02:20 GMT
|
56
56
|
|
57
|
+
- request:
|
58
|
+
method: get
|
59
|
+
uri: http://api-model-specs.com/server_error
|
60
|
+
headers:
|
61
|
+
User-Agent:
|
62
|
+
- Typhoeus - https://github.com/typhoeus/typhoeus
|
63
|
+
response:
|
64
|
+
status:
|
65
|
+
code: 500
|
66
|
+
message: OK
|
67
|
+
headers:
|
68
|
+
Server:
|
69
|
+
- nginx/1.4.1
|
70
|
+
Date:
|
71
|
+
- Thu, 28 Nov 2013 16:02:56 GMT
|
72
|
+
Content-Type:
|
73
|
+
- text/plain; charset=utf-8
|
74
|
+
Content-Length:
|
75
|
+
- '248'
|
76
|
+
Connection:
|
77
|
+
- keep-alive
|
78
|
+
body:
|
79
|
+
encoding: UTF-8
|
80
|
+
string: "Oh no, something went wrong"
|
81
|
+
http_version:
|
82
|
+
recorded_at: Thu, 28 Nov 2013 16:02:20 GMT
|
83
|
+
|
57
84
|
recorded_with: VCR 2.8.0
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: api-model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Damien Timewell
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-01-
|
11
|
+
date: 2014-01-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -139,6 +139,7 @@ files:
|
|
139
139
|
- README.md
|
140
140
|
- api-model.gemspec
|
141
141
|
- lib/api-model.rb
|
142
|
+
- lib/api_model/assignment.rb
|
142
143
|
- lib/api_model/builder/hash.rb
|
143
144
|
- lib/api_model/cache_stategy/no_cache.rb
|
144
145
|
- lib/api_model/class_methods.rb
|