api-model 0.0.3 → 0.0.4

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