api-model 0.1.2 → 0.1.3
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.
- 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
|