api-model 0.0.3 → 0.0.4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 75b2ddb171371b895e8ecf655cfd69a56368738b
4
- data.tar.gz: a0165241ef3855d10e044d9cdc8b821e3114a6a9
3
+ metadata.gz: fd7712e1eb0b5faac320b38518575bfbdeaf61b8
4
+ data.tar.gz: 2ea94f183ad7de875863ff989327dc414d2dc54a
5
5
  SHA512:
6
- metadata.gz: c377a0408bcc44930c807b07ef4274bf15ab5ddb6db7e790c421e39c8ec4dcfeaf71496c3fd0d57188c0d6821b32646a5b8c96dafdf8e9166ff4e00e34510fb4
7
- data.tar.gz: 480a76b37a1800abcbccc9da0365cda8a8332af301e5e997d0b281a451403e7339a7a93433dd9ea814ca4b6113aa26b5fe287b1a5e2a3b8ff1e6aa6937cbd8a8
6
+ metadata.gz: f1e74e2574025ccf61e563ca0631e855211ff6953cb36a2d4986ef8c301c8477cda1913421c66d103dc225db2f10653693feec8706cbbd989dae3a60e351f585
7
+ data.tar.gz: 68171597b2cb8448a10237485904d3353012196cc911d6c1cf09648e0e0e1d7f84bb5957e29ce51b4648fd995cd095bf9467c70ff26cae40ff1d385be17c4eae
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- api-model (0.0.3)
4
+ api-model (0.0.4)
5
5
  activemodel
6
6
  activesupport
7
7
  hashie
@@ -29,7 +29,7 @@ GEM
29
29
  ethon (0.6.1)
30
30
  ffi (>= 1.3.0)
31
31
  mime-types (~> 1.18)
32
- ffi (1.9.0)
32
+ ffi (1.9.3)
33
33
  hashie (2.0.5)
34
34
  i18n (0.6.9)
35
35
  method_source (0.8.2)
data/README.md CHANGED
@@ -1,4 +1,237 @@
1
1
  [![Code Climate](https://codeclimate.com/github/iZettle/api-model.png)](https://codeclimate.com/github/iZettle/api-model)
2
2
  [![Build Status](https://travis-ci.org/iZettle/api-model.png?branch=master)](https://travis-ci.org/iZettle/api-model)
3
3
 
4
- Api Model README
4
+ API Model
5
+ =========
6
+
7
+ API model is a simple wrapper for interacting with external APIs. It tries to make
8
+ it very simple and easy to make API calls and map the responses into objects.
9
+
10
+ A really simple example
11
+ -----------------------
12
+
13
+ To turn any class into an API model, it must inherit ApiModel::Base. If you want to
14
+ make attributes which will get automatically set from api responses, you can define them
15
+ as properties..
16
+
17
+ ``` ruby
18
+ class MyModel < ApiModel::Base
19
+ property :name
20
+ end
21
+ ```
22
+
23
+ Then, let's say the API endpoint /foo returned JSON which looks like `{ "name": "Bar" }`...
24
+
25
+ ```ruby
26
+ example = MyModel.get_json "/foo"
27
+ example.name #=> "Bar"
28
+ ```
29
+
30
+ Request types and params
31
+ ------------------------
32
+
33
+ There's a couple of convenience methods to make it simpler to send GET and POST requests,
34
+ or you can send other request types:
35
+
36
+ ```ruby
37
+ # Params will be sent as url params, and options is used for other things which
38
+ # can control ApiModel (such as custom builders)
39
+ get_json url, params, options
40
+
41
+ # The request body will be turned into json if it is a hash, otherwise it
42
+ # should be a string. Options are handled the same as get.
43
+ post_json url, request_body, options
44
+
45
+ # Works the same as the ones above, except if you want to pass params or body,
46
+ # they need to be within the options hash.
47
+ call_api :put, url, options
48
+ ```
49
+
50
+ Model properties
51
+ ----------------
52
+
53
+ The properties which you can define on models are extended from the [Hashie](https://github.com/intridea/hashie#trash)
54
+ gem. You can use them to define simple attributes, but also for converting attributes from one name to another, or for
55
+ transforming the values as they are set. This is useful for dealing with APIs which use a different naming scheme
56
+ than you are using, or if you need to modify values as they come in.
57
+
58
+ ### Translation
59
+
60
+ ```ruby
61
+ class MyModel < ApiModel::Base
62
+ property :full_name, from: :fullName
63
+ end
64
+
65
+ MyModel.new(fullName: "Hello").full_name # => "Hello"
66
+ ```
67
+
68
+ ### Transformation
69
+
70
+ ```ruby
71
+ class MyModel < ApiModel::Base
72
+ property :created_at, from: :timestamp, with: lambda { |t| Time.at(t) }
73
+ end
74
+
75
+ MyModel.new(timestamp: 1387550991).created_at # => 2013-12-20 15:49:51 +0100
76
+ ```
77
+
78
+ ### Defaults
79
+
80
+ ```ruby
81
+ class MyModel < ApiModel::Base
82
+ property :name, default: "FooBar"
83
+ end
84
+
85
+ MyModel.new.name # => "FooBar"
86
+ ```
87
+
88
+ For more information, check out the [Hashie::Trash docs](https://github.com/intridea/hashie#trash).
89
+
90
+ Building objects from responses
91
+ -------------------------------
92
+
93
+ If an API response begins with a hash, it is assumed that it represents a single object and so will be used
94
+ to try and build a single object. Likewise, if it is an array, it is assumed to be a collection of objects. For example:
95
+
96
+ ```ruby
97
+ # GET /foo returns { "name": "Foo" }
98
+ MyModel.get_json("/foo") # => #<MyModel:0x007 @name="Foo">
99
+
100
+ # GET /bar returns [{ "name": "Foo" }, { "name": "Bar" }]
101
+ MyModel.get_json("/bar") # => [#<MyModel:0x007 @name="Foo">, #<MyModel:0x007 @name="Bar">]
102
+ ```
103
+
104
+ You can override the default builder either on a per-call basis using the `:builder` option. The class which you
105
+ use as a builder should respond to `#build`, with the instance hash as an argument:
106
+
107
+ ```ruby
108
+ class MyCustomBuilder
109
+ def build(params)
110
+ # build something with params...
111
+ end
112
+ end
113
+
114
+ MyModel.get_json "/foo", { some_param: "bar" }, builder: MyCustomBuilder.new
115
+ ```
116
+
117
+ Configuring API Model
118
+ ---------------------
119
+
120
+ You can configure API model in a number of places; globally using `ApiModel::Base.api_config`, per-model
121
+ using `MyModel.api_config`, and per-api call by passing in options in the options hash (although some
122
+ configuration options may not be available on the per-api call technique).
123
+
124
+ ### API Host
125
+
126
+ ```ruby
127
+ ApiModel::Base.api_config do |config|
128
+ config.api_host = "http:://someserver.com"
129
+ end
130
+ ```
131
+
132
+ This will set the root of all api calls so that you can just use paths in your models instead of having
133
+ to refer to the full url all the time.
134
+
135
+ ### JSON root
136
+
137
+ ```ruby
138
+ ApiModel::Base.api_config do |config|
139
+ config.json_root = "data.posts"
140
+ end
141
+ ```
142
+
143
+ If the API response which you receive is deeply nested and you want to cut out some levels of nesting, you
144
+ can use `json_root` to set which key objects should be built from.
145
+
146
+ You can dig down multiple levels by separating keys with a period. With the example above, say the server
147
+ was returning JSON which looked like `{"data":{"posts":{"name":"Foo"}}}`, it would behave as if the
148
+ response was really just `{"name":"Foo"}`.
149
+
150
+ ### Builder
151
+
152
+ ```ruby
153
+ ApiModel::Base.api_config do |config|
154
+ config.builder = MyCustomBuilder.new
155
+ end
156
+ ```
157
+
158
+ Sets a custom builder for all API calls. See [building objects from responses](#building-objects-from-responses)
159
+ for more details on how custom builders should behave.
160
+
161
+ ### Parser
162
+
163
+ ```ruby
164
+ ApiModel::Base.api_config do |config|
165
+ config.parser = MyCustomParser.new
166
+ end
167
+ ```
168
+
169
+ ApiModel is built on the assumption that most modern APIs are JSON-based, but if you need to interact with
170
+ an API which returns something other than JSON, you can set custom parsers to deal with objectifying responses
171
+ before they are sent to builder classes. The parser should work in the same way as a custom builder, except it needs
172
+ to respond to `#parse`, with the raw response body as an argument.
173
+
174
+ ### Raise on not found or unauthenticated
175
+
176
+ ```ruby
177
+ ApiModel::Base.api_config do |config|
178
+ config.raise_on_not_found = true
179
+ config.raise_on_unauthenticated = true
180
+ end
181
+ ```
182
+
183
+ This will cause any API requests which return a 404 status to raise an ApiModel::NotFoundError exception, and requests
184
+ which return a 401 to raise an ApiModel::UnauthenticatedError exception. Both default to `false`.
185
+
186
+ ### Cache strategy & settings
187
+
188
+ ```ruby
189
+ ApiModel::Base.api_config do |config|
190
+ config.cache_strategy = MyCustomCacheStrategy
191
+ config.cache_settings = { any_custom_settings: 123 }
192
+ end
193
+ ```
194
+
195
+ Currently, ApiModel has no built-in cache strategy, but provides the interface for you to insert your own caching
196
+ strategy. On each API call, the cache strategy class will be initialized with two arguments; the cache id, which
197
+ is generated from the path and params, and the `cache_settings` which you can define on the config object as
198
+ shown above. It will then call `#cache` with the ApiModel response block. So your custom cache class needs to look
199
+ something like this:
200
+
201
+ ```ruby
202
+ class MyCustomCacheStrategy
203
+ attr_accessor :id, :options
204
+
205
+ def initialize(id, options)
206
+ @id = id
207
+ @options = options
208
+ end
209
+
210
+ def cache(&block)
211
+ # here you can check whether you want to actually call the api by running
212
+ # block.call, or want to find and return your cached response.
213
+ end
214
+ end
215
+ ```
216
+
217
+ ### Headers
218
+
219
+ ```ruby
220
+ ApiModel::Base.api_config do |config|
221
+ config.headers = { some_custom_header: "foo" }
222
+ end
223
+ ```
224
+
225
+ Adds custom headers to the requests. By default, ApiModel will add these headers:
226
+
227
+ ```ruby
228
+ { "Content-Type" => "application/json; charset=utf-8", "Accept" => "application/json" }
229
+ ```
230
+
231
+ These can of course be overridden by just re-defining them in the headers config:
232
+
233
+ ```ruby
234
+ ApiModel::Base.api_config do |config|
235
+ config.headers = { "Content-Type" => "application/soap+xml" }
236
+ end
237
+ ```
@@ -2,14 +2,13 @@ $:.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.0.3"
5
+ s.version = "0.0.4"
6
6
  s.authors = ["Damien Timewell"]
7
7
  s.email = ["mail@damientimewell.com"]
8
8
  s.homepage = "https://github.com/iZettle/api-model"
9
9
  s.summary = "A simple way of interacting with rest APIs"
10
- s.description = "A simple way of interacting with rest APIs"
10
+ s.description = "API model is a simple wrapper for interacting with external APIs. It tries to make it very simple and easy to make API calls and map the responses into objects."
11
11
 
12
- # s.add_dependency 'redis'
13
12
  s.add_dependency 'activesupport'
14
13
  s.add_dependency 'activemodel'
15
14
  s.add_dependency 'typhoeus'
@@ -3,12 +3,15 @@ require 'active_support'
3
3
  require 'active_support/core_ext'
4
4
  require 'logger'
5
5
  require 'hashie'
6
+ require 'ostruct'
6
7
 
7
8
  require 'api_model/initializer'
8
9
  require 'api_model/http_request'
9
10
  require 'api_model/response'
10
11
  require 'api_model/rest_methods'
11
12
  require 'api_model/configuration'
13
+ require 'api_model/cache_stategies/no_cache'
14
+ require 'api_model/response_parser/json'
12
15
 
13
16
  module ApiModel
14
17
  Log = Logger.new STDOUT
@@ -0,0 +1,14 @@
1
+ module ApiModel
2
+ module CacheStrategies
3
+ class NoCache
4
+
5
+ def initialize(*args)
6
+ end
7
+
8
+ def cache(&block)
9
+ yield
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -2,7 +2,8 @@ module ApiModel
2
2
  class Configuration
3
3
  include Initializer
4
4
 
5
- attr_accessor :host, :json_root, :headers, :raise_on_unauthenticated, :raise_on_not_found
5
+ attr_accessor :host, :json_root, :headers, :raise_on_unauthenticated, :cache_settings,
6
+ :raise_on_not_found, :cache_strategy, :parser, :builder
6
7
 
7
8
  def self.from_inherited_config(config)
8
9
  new config.instance_values.reject {|k,v| v.blank? }
@@ -12,6 +13,19 @@ module ApiModel
12
13
  @headers ||= {}
13
14
  @headers.reverse_merge "Content-Type" => "application/json; charset=utf-8", "Accept" => "application/json"
14
15
  end
16
+
17
+ def cache_strategy
18
+ @cache_strategy ||= ApiModel::CacheStrategies::NoCache
19
+ end
20
+
21
+ def parser
22
+ @parser ||= ApiModel::ResponseParser::Json.new
23
+ end
24
+
25
+ def cache_settings
26
+ @cache_settings ||= {}
27
+ @cache_settings.reverse_merge duration: 30.seconds, timeout: 2.seconds
28
+ end
15
29
  end
16
30
 
17
31
  module ConfigurationMethods
@@ -2,12 +2,16 @@ module ApiModel
2
2
  class HttpRequest
3
3
  include ApiModel::Initializer
4
4
 
5
- attr_accessor :path, :method, :options, :api_call, :builder, :config
5
+ attr_accessor :path, :method, :options, :api_call, :builder, :config, :cache_id
6
6
 
7
7
  after_initialize :set_default_options
8
8
 
9
9
  define_model_callbacks :run
10
10
 
11
+ def config
12
+ @config ||= Configuration.new
13
+ end
14
+
11
15
  def run
12
16
  run_callbacks :run do
13
17
  self.api_call = Typhoeus.send method, full_path, options
@@ -14,14 +14,18 @@ module ApiModel
14
14
  @_config = config || Configuration.new
15
15
  end
16
16
 
17
+ def metadata
18
+ @metadata ||= OpenStruct.new
19
+ end
20
+
17
21
  def build_objects
18
22
  raise UnauthenticatedError if @_config.raise_on_unauthenticated && http_response.api_call.response_code == 401
19
23
  raise NotFoundError if @_config.raise_on_not_found && http_response.api_call.response_code == 404
20
- return if json_response_body.nil?
24
+ return if response_body.nil?
21
25
 
22
26
  if response_build_hash.is_a? Array
23
27
  self.objects = response_build_hash.collect{ |hash| build http_response.builder, hash }
24
- elsif response_build_hash.is_a? Hash
28
+ else
25
29
  self.objects = self.build http_response.builder, response_build_hash
26
30
  end
27
31
 
@@ -36,11 +40,8 @@ module ApiModel
36
40
  end
37
41
  end
38
42
 
39
- def json_response_body
40
- @json_response_body ||= JSON.parse http_response.api_call.body
41
- rescue JSON::ParserError
42
- Log.info "Could not parse JSON response: #{http_response.api_call.body}"
43
- return nil
43
+ def response_body
44
+ @response_body ||= @_config.parser.parse http_response.api_call.body
44
45
  end
45
46
 
46
47
  # Define common methods which should never be called on this abstract class, and should always be
@@ -59,7 +60,7 @@ module ApiModel
59
60
 
60
61
  private
61
62
 
62
- # If the model config defines a json root, use it on the json_response_body
63
+ # If the model config defines a json root, use it on the response_body
63
64
  # to dig down in to the hash.
64
65
  #
65
66
  # The root for a deeply nested hash will come in as a string with key names split
@@ -67,14 +68,14 @@ module ApiModel
67
68
  def response_build_hash
68
69
  if @_config.json_root.present?
69
70
  begin
70
- @_config.json_root.split(".").inject(json_response_body) do |hash,key|
71
+ @_config.json_root.split(".").inject(response_body) do |hash,key|
71
72
  hash.fetch(key)
72
73
  end
73
74
  rescue
74
- raise ResponseBuilderError, "Could not find key #{@_config.json_root} in:\n#{json_response_body}"
75
+ raise ResponseBuilderError, "Could not find key #{@_config.json_root} in:\n#{response_body}"
75
76
  end
76
77
  else
77
- json_response_body
78
+ response_body
78
79
  end
79
80
  end
80
81
 
@@ -0,0 +1,14 @@
1
+ module ApiModel
2
+ module ResponseParser
3
+ class Json
4
+
5
+ def parse(body)
6
+ JSON.parse body
7
+ rescue JSON::ParserError
8
+ Log.info "Could not parse JSON response: #{body}"
9
+ return nil
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -11,10 +11,24 @@ module ApiModel
11
11
  end
12
12
 
13
13
  def call_api(method, path, options={})
14
- request = HttpRequest.new path: path, method: method, config: api_model_configuration
15
- request.builder = options.delete(:builder) || self
16
- request.options.merge! options
17
- request.run.build_objects
14
+ cache cache_id(path, options) do
15
+ request = HttpRequest.new path: path, method: method, config: api_model_configuration
16
+ request.builder = options.delete(:builder) || api_model_configuration.builder || self
17
+ request.options.merge! options
18
+ request.run.build_objects
19
+ end
20
+ end
21
+
22
+ def cache_id(path, options={})
23
+ return @cache_id if @cache_id
24
+ p = (options[:params] || {}).collect{ |k,v| "#{k}#{v}" }.join("")
25
+ "#{path}#{p}"
26
+ end
27
+
28
+ def cache(path, &block)
29
+ api_model_configuration.cache_strategy.new(path, api_model_configuration.cache_settings).cache do
30
+ block.call
31
+ end
18
32
  end
19
33
 
20
34
  end
@@ -127,4 +127,10 @@ describe ApiModel do
127
127
  end
128
128
  end
129
129
 
130
+ describe "cache_id" do
131
+ it 'should use options and the request path to create an identifier for the cache' do
132
+ BlogPost.cache_id("/box", params: { foo: "bar" }).should eq "/boxfoobar"
133
+ end
134
+ end
135
+
130
136
  end
@@ -1,11 +1,13 @@
1
1
  require 'spec_helper'
2
2
  require 'support/mock_models/banana'
3
3
  require 'support/mock_models/multiple_hosts'
4
+ require 'support/mock_models/blog_post'
4
5
 
5
6
  describe ApiModel, "Configuration" do
6
7
 
7
8
  after(:each) do
8
9
  Banana.reset_api_configuration
10
+ BlogPost.reset_api_configuration
9
11
  end
10
12
 
11
13
  describe "api_host" do
@@ -60,6 +62,52 @@ describe ApiModel, "Configuration" do
60
62
  end
61
63
  end
62
64
 
65
+ describe "cache_strategy" do
66
+ it 'should default to NoCache' do
67
+ ApiModel::Base.api_model_configuration.cache_strategy.should eq ApiModel::CacheStrategies::NoCache
68
+ end
69
+ end
70
+
71
+ describe "parser" do
72
+ it 'should default to the internal Json parser' do
73
+ ApiModel::Base.api_model_configuration.parser.should be_an_instance_of ApiModel::ResponseParser::Json
74
+ end
75
+
76
+ it 'should be used when handling api responses' do
77
+ ApiModel::ResponseParser::Json.any_instance.should_receive(:parse).with("{\"name\":\"foo\"}")
78
+ VCR.use_cassette('posts') { BlogPost.get_json "http://api-model-specs.com/single_post"}
79
+ end
80
+
81
+ class CustomParser
82
+ def parse(body)
83
+ { name: "Hello world" }
84
+ end
85
+ end
86
+
87
+ it 'should be possible to set a custom parser' do
88
+ BlogPost.api_config { |config| config.parser = CustomParser.new }
89
+ CustomParser.any_instance.should_receive(:parse).with("{\"name\":\"foo\"}")
90
+ VCR.use_cassette('posts') { BlogPost.get_json "http://api-model-specs.com/single_post"}
91
+ end
92
+ end
93
+
94
+ describe "builder" do
95
+ it 'should defult to nil' do
96
+ ApiModel::Base.api_model_configuration.builder.should be_nil
97
+ end
98
+
99
+ class CustomBuilder
100
+ def build(response)
101
+ end
102
+ end
103
+
104
+ it 'should be possible to set a custom builder' do
105
+ BlogPost.api_config { |config| config.builder = CustomBuilder.new }
106
+ CustomBuilder.any_instance.should_receive(:build).with({ "name" => "foo"})
107
+ VCR.use_cassette('posts') { BlogPost.get_json "http://api-model-specs.com/single_post"}
108
+ end
109
+ end
110
+
63
111
  it 'should not unset other config values when you set a new one' do
64
112
  ApiModel::Base.api_config { |c| c.host = "foo.com" }
65
113
  Banana.api_config { |c| c.json_root = "banana" }
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApiModel::ResponseParser::Json do
4
+
5
+ it "should produce a hash given valid json" do
6
+ ApiModel::ResponseParser::Json.new.parse("{\"name\":\"foo\"}")["name"].should eq "foo"
7
+ end
8
+
9
+ it "should catch errors from parsing invalid json" do
10
+ ApiModel::Log.should_receive(:info).with "Could not parse JSON response: blah"
11
+
12
+ expect {
13
+ ApiModel::ResponseParser::Json.new.parse("blah")
14
+ }.to_not raise_error
15
+ end
16
+
17
+ end
@@ -11,19 +11,7 @@ describe ApiModel::Response do
11
11
  end
12
12
 
13
13
  describe "parsing the json body" do
14
- it "should produce a hash given valid json" do
15
- valid_response.json_response_body.should be_a(Hash)
16
- valid_response.json_response_body["name"].should eq "foo"
17
- end
18
-
19
- it "should catch errors from parsing invalid json" do
20
- valid_response.stub_chain(:http_response, :api_call, :body).and_return "blah"
21
- ApiModel::Log.should_receive(:info).with "Could not parse JSON response: blah"
22
14
 
23
- expect {
24
- valid_response.json_response_body
25
- }.to_not raise_error
26
- end
27
15
  end
28
16
 
29
17
  describe "using a custom json root on the response body" do
@@ -87,17 +75,17 @@ describe ApiModel::Response do
87
75
 
88
76
  describe "#build_objects" do
89
77
  let(:single_object) do
90
- valid_response.stub(:json_response_body).and_return name: "foo"
78
+ valid_response.stub(:response_body).and_return name: "foo"
91
79
  valid_response.build_objects
92
80
  end
93
81
 
94
82
  let(:array_of_objects) do
95
- valid_response.stub(:json_response_body).and_return [{name: "foo"}, {name: "bar"}]
83
+ valid_response.stub(:response_body).and_return [{name: "foo"}, {name: "bar"}]
96
84
  valid_response.build_objects
97
85
  end
98
86
 
99
87
  let(:empty_response) do
100
- valid_response.stub(:json_response_body).and_return nil
88
+ valid_response.stub(:response_body).and_return nil
101
89
  valid_response.build_objects
102
90
  end
103
91
 
@@ -118,8 +106,8 @@ describe ApiModel::Response do
118
106
  single_object.http_response.should be_a(ApiModel::HttpRequest)
119
107
  end
120
108
 
121
- it "should include the #json_response_body" do
122
- single_object.json_response_body.should eq name: "foo"
109
+ it "should include the #response_body" do
110
+ single_object.response_body.should eq name: "foo"
123
111
  end
124
112
 
125
113
  it 'should return nil if the api returns an empty body' do
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.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Damien Timewell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-18 00:00:00.000000000 Z
11
+ date: 2013-12-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -122,7 +122,9 @@ dependencies:
122
122
  - - '='
123
123
  - !ruby/object:Gem::Version
124
124
  version: 1.15.0
125
- description: A simple way of interacting with rest APIs
125
+ description: API model is a simple wrapper for interacting with external APIs. It
126
+ tries to make it very simple and easy to make API calls and map the responses into
127
+ objects.
126
128
  email:
127
129
  - mail@damientimewell.com
128
130
  executables: []
@@ -137,15 +139,18 @@ files:
137
139
  - README.md
138
140
  - api-model.gemspec
139
141
  - lib/api-model.rb
142
+ - lib/api_model/cache_stategies/no_cache.rb
140
143
  - lib/api_model/configuration.rb
141
144
  - lib/api_model/http_request.rb
142
145
  - lib/api_model/initializer.rb
143
146
  - lib/api_model/response.rb
147
+ - lib/api_model/response_parser/json.rb
144
148
  - lib/api_model/rest_methods.rb
145
149
  - spec/api-model/api_model_spec.rb
146
150
  - spec/api-model/configuration_spec.rb
147
151
  - spec/api-model/http_request_spec.rb
148
152
  - spec/api-model/initializer_spec.rb
153
+ - spec/api-model/json_response_parser_spec.rb
149
154
  - spec/api-model/response_spec.rb
150
155
  - spec/spec_helper.rb
151
156
  - spec/support/fixtures/cars.yml
@@ -185,6 +190,7 @@ test_files:
185
190
  - spec/api-model/configuration_spec.rb
186
191
  - spec/api-model/http_request_spec.rb
187
192
  - spec/api-model/initializer_spec.rb
193
+ - spec/api-model/json_response_parser_spec.rb
188
194
  - spec/api-model/response_spec.rb
189
195
  - spec/spec_helper.rb
190
196
  - spec/support/fixtures/cars.yml