apify 0.3.3 → 0.4.0

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.
data/README.md CHANGED
@@ -1,14 +1,286 @@
1
- apify
1
+ Apify
2
2
  =====
3
3
 
4
- Installation
5
- ------------
4
+ Apify lets you bolt a [JSON](http://en.wikipedia.org/wiki/JSON) API onto your Rails application.
5
+ Optional features are auto-generated API documentation, request validation with [JSON Schema](http://json-schema.org/)
6
+ and a client class to consume Apify APIs from Ruby code.
7
+
8
+
9
+ Two minute quickstart
10
+ ---------------------
6
11
 
7
12
  Install the gem with
8
13
 
9
14
  sudo gem install apify
10
15
 
11
- ...
16
+ For Rails 2, add the following to your `environment.rb`:
17
+
18
+ config.gem 'apify'
19
+
20
+ For Rails 3, add the following to your `Gemfile`:
21
+
22
+ gem 'apify'
23
+
24
+ Describe your API actions in `models/api.rb`:
25
+
26
+ class Api < Apify::Api
27
+ get :ping do
28
+ respond do
29
+ { 'message' => 'pong' }
30
+ end
31
+ end
32
+ end
33
+
34
+ Create a controller to serve your API in `controllers/api_controller.rb`:
35
+
36
+ class ApiController < Apify::ApiController
37
+ api Api
38
+ end
39
+
40
+ Connect the routes to your API in `config/routes.rb`:
41
+
42
+ ActionController::Routing::Routes.draw do |map|
43
+ Api.draw_routes(map)
44
+ end
45
+
46
+ You have now exposed an API action under `http://host/api/ping`.
47
+
48
+
49
+ Protocol
50
+ --------
51
+
52
+ If you are planning to consume your Apify API with the `Apify::Client` class, you won't need to know most of this. Nonetheless, this is what you're getting into:
53
+
54
+ - Apify is about sending JSON objects (hashes) back and forth. No fancy envelope formats.
55
+ - An Apify API defines actions with a name and HTTP method (`GET, POST, PUT, DELETE`).
56
+ - API actions are accessed over HTTP. Each action gets its own route.
57
+ - API actions can take arguments. Those are serialized into a **single** HTTP parameter `args` as JSON. This is to simplify client code that consumes your API (nested params are hard).
58
+ - Successful responses are returned with a status of 200 (OK).
59
+ - The body of a successful response is always a hash, serialized as JSON.
60
+ - Requests that have errors are returned with a status of 500 (internal server error). The body of a error response is an error message in the response's content type (won't be JSON in most cases).
61
+
62
+
63
+ Defining API actions
64
+ --------------------
65
+
66
+ API actions are defined in a model such as `models/api.rb`:
67
+
68
+ - This model needs to inherit from `Apify::Api`.
69
+ - An API method has a name and a HTTP method (`GET, POST, PUT, DELETE`)
70
+ - An API action always returns a hash. If an API action returns nil, Apify will turn that into an empty hash for you.
71
+
72
+ Here is an example for a simple, reading API action:
73
+
74
+ get :ping do
75
+ respond do
76
+ { 'message' => 'pong' }
77
+ end
78
+ end
79
+
80
+ Action arguments can be accessed through the `args` hash. Its keys are always strings, never symbols.
81
+
82
+ Here is an example for an API action that takes an argument:
83
+
84
+ post :hello do
85
+ respond do
86
+ { 'message' => 'Hello ' + args['name'] }
87
+ end
88
+ end
89
+
90
+
91
+ Schemas
92
+ -------
93
+
94
+ You can describe the expected format of method arguments and response values using [JSON Schema](http://json-schema.org/). This lets you define rules like that an argument is required or must be in a given format.
95
+
96
+ Schemas are an optional feature, but providing schemas has some benefits:
97
+
98
+ - Your actions don't need to handle unexpected arguments (remember that strangers are going to call your code).
99
+ - You don't need to worry about responding with values that break your own contract. Apify will validate your own responses and raise an error if they don't validate.
100
+ - Apify can auto-generate public documentation for actions with schemas. This documentation will contain generated examples for a successful request and response and also offer your schemas for download.
101
+
102
+ You can provide a schema for arguments, a schema for response values, or both. Here is the `hello` action from the above example with schemas:
103
+
104
+ post :hello do
105
+
106
+ schema :args do
107
+ object('name' => string)
108
+ end
109
+
110
+ schema :value do
111
+ object('message' => string)
112
+ end
113
+
114
+ respond do
115
+ { 'message' => 'Hello ' + args['name'] }
116
+ end
117
+
118
+ end
119
+
120
+ Your API now allows to download the schema and an auto-generated example in JSON format:
121
+
122
+ - POST http://host/api/hello?schema=args
123
+ - POST http://host/api/hello?example=args
124
+ - POST http://host/api/hello?schema=value
125
+ - POST http://host/api/hello?example=value
126
+
127
+ Here is another example for a more complex schema:
128
+
129
+ get :contacts do
130
+ schema :value do
131
+ array(
132
+ object(
133
+ 'name' => string,
134
+ 'phone' => integer,
135
+ 'phone_type' => enum(string, 'home', 'office'),
136
+ 'email' => optional(email),
137
+ 'favorite' => boolean
138
+ )
139
+ )
140
+ end
141
+ end
142
+
143
+
144
+ Auto-generated API documentation
145
+ --------------------------------
146
+
147
+ Every Apify API comes with auto-generated HTML documentation:
148
+
149
+ - The documentation can be accessed from the `docs` action of an `ApiController`, e.g. http://host/api/docs
150
+ - The documentation contains parts of this README (protocol, instructions to use the Ruby client)
151
+ - If your actions have schemas, the documentation includes those schemas and request/response examples generated from them.
152
+
153
+ You can give your actions descriptions to make the API documentation even more useful:
154
+
155
+ post :hello do
156
+
157
+ description 'Says hello to the given name.'
158
+
159
+ respond do
160
+ { 'message' => 'Hello ' + args['name'] }
161
+ end
162
+
163
+ end
164
+
165
+
166
+
167
+ Authentication
168
+ --------------
169
+
170
+ If your API uses a single username and password for all requests, you can activate basic authentication like this:
171
+
172
+ class ApiController < ApplicationController::Base
173
+ api Api
174
+ authenticate :user => 'api', :password => 'secret'
175
+ end
176
+
177
+ If your use case is more complex, just roll your own authentication. Your `ApiController` is just a regular controller.
178
+
179
+
180
+ Consuming an API with the Ruby client
181
+ -------------------------------------
182
+
183
+ An easy way to consume an Apify API is to use the <code>Apify::Client</code> class:
184
+
185
+ - The client calls an Apify API with a Ruby hash as argument and returns the response as a Ruby hash.
186
+ - It takes care of the protocol details and lets your users focus on exchanging data.
187
+ - Your users will only need the `apify` gem. There are no dependencies on Rails.
188
+ - The auto-generated documentation contains these instructions on how to install and use the client class.
189
+
190
+ You already know how to require the `apify` gem from a Rails application. This is how you require the client class when your code is not a Rails application:
191
+
192
+ require 'rubygems'
193
+ gem 'apify'
194
+ require 'apify/client'
195
+
196
+ Here is an example for how to use the client class:
197
+
198
+ client = Apify::Client.new(:host => 'localhost:3000', :user => 'api', :password => 'secret')
199
+ client.post('/api/hello', :name => 'Jack') # { 'message' => 'Hello Jack' }
200
+
201
+ Errors can be caught and inspected like this:
202
+
203
+ begin
204
+ client.get('/api/hello', :name => 'Jack') # { 'message' => 'Hello Jack' }
205
+ rescue Apify::RequestFailed => e
206
+ puts "Oh no! The API request failed."
207
+ puts "Message: #{e.message}"
208
+ puts "Response: #{e.response_body}"
209
+ end
210
+
211
+
212
+ Dealing with dates and timestamps
213
+ ---------------------------------
214
+
215
+ Unfortunately dates and timestamps are not among the data types defined by JSON and JSON Schema. You can work around this in any way you like, but Apify supports a default approach:
216
+
217
+ - The workaround supported by Apify is to handle a date as a string formatted like "2011-05-01" and a timestamp as a string formatted like "2011-05-01 12:00:04".
218
+ - Aside from being easy to parse, you can feed such strings into a model's date or time field and ActiveRecord will convert them into `Date` and `Time` objects.
219
+ - Apify gives you helper methods `sql_date` and `sql_datetime` to support this pattern in your schemas and actions.
220
+
221
+ Here are examples for actions that deal with dates and timestamps this way:
222
+
223
+ get :now do
224
+ schema :value do
225
+ object('now' => sql_datetime)
226
+ end
227
+ respond do
228
+ { 'now' => sql_datetime(Time.now) }
229
+ end
230
+ end
231
+
232
+ get :today do
233
+ schema :value do
234
+ object('today' => sql_date)
235
+ end
236
+ respond do
237
+ { 'today' => sql_date(Date.today) }
238
+ end
239
+ end
240
+
241
+
242
+ Testing API actions
243
+ -------------------
244
+
245
+ Since your API actions end up being vanilla Ruby methods that turn argument hashes into value hashes, you can test them without special tools.
246
+
247
+ Here is an example in RSpec:
248
+
249
+ describe Api, 'hello' do
250
+
251
+ it 'should greet the given name' do
252
+ Api.post(:hello, :name => 'Jack').should == { 'message' => 'Hello Jack' }
253
+ end
254
+
255
+ it 'should require a name argument' do
256
+ expect { Api.post(:hello) }.to raise_error(Apify::Invalid)
257
+ end
258
+
259
+ end
260
+
261
+
262
+ Custom routing
263
+ --------------
264
+
265
+ You can create a route for each action of an API by adding this to your `config/routes.rb`:
266
+
267
+ ActionController::Routing::Routes.draw do |map|
268
+ Api.draw_routes(map)
269
+ end
270
+
271
+ This will create URLs like `/api/ping`, `/api/hello`, etc. You can change the `/api` prefix like this:
272
+
273
+ ActionController::Routing::Routes.draw do |map|
274
+ Api.draw_routes(map, :base_path => 'interface/v1')
275
+ end
276
+
277
+ You can also forego the automatic route generation completely and roll your own URLs:
278
+
279
+ ActionController::Routing::Routes.draw do |map|
280
+ map.connect 'api/ping', :controller => 'api', :action => 'ping', :conditions { :method => :get }
281
+ map.connect 'api/hello', :controller => 'api', :action => 'hello', :conditions { :method => :post }
282
+ end
283
+
12
284
 
13
285
  Rails 3 compatibility
14
286
  ---------------------
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.3
1
+ 0.4.0
data/apify.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{apify}
8
- s.version = "0.3.3"
8
+ s.version = "0.4.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Henning Koch"]
12
- s.date = %q{2010-08-28}
12
+ s.date = %q{2010-08-29}
13
13
  s.description = %q{Compact definition of JSON APIs for Rails applications. }
14
14
  s.email = %q{github@makandra.de}
15
15
  s.extra_rdoc_files = [
@@ -97,6 +97,7 @@ Gem::Specification.new do |s|
97
97
  "spec/app_root/lib/console_with_fixtures.rb",
98
98
  "spec/app_root/script/console",
99
99
  "spec/controllers/api_controller_spec.rb",
100
+ "spec/models/api_spec.rb",
100
101
  "spec/rcov.opts",
101
102
  "spec/spec.opts",
102
103
  "spec/spec_helper.rb"
@@ -123,6 +124,7 @@ Gem::Specification.new do |s|
123
124
  "spec/app_root/config/environment.rb",
124
125
  "spec/app_root/config/routes.rb",
125
126
  "spec/controllers/api_controller_spec.rb",
127
+ "spec/models/api_spec.rb",
126
128
  "examples/client/client.rb",
127
129
  "examples/host/app/controllers/application_controller.rb",
128
130
  "examples/host/app/controllers/api_controller.rb",
@@ -11,13 +11,19 @@ First install the Apify gem:
11
11
 
12
12
  <pre><code>sudo gem install apify</code></pre>
13
13
 
14
+ If your client code is not a Rails application:
15
+
16
+ <pre><code>require 'rubygems'
17
+ gem 'apify'
18
+ require 'apify/client'</code></pre>
19
+
14
20
  In Rails 2, add the following to your environment.rb:
15
21
 
16
- <pre><code>config.gem 'aegis'</code></pre>
22
+ <pre><code>config.gem 'apify'</code></pre>
17
23
 
18
24
  In Rails 3, add the following to your Gemfile:
19
25
 
20
- <pre><code>gem 'aegis'</code></pre>
26
+ <pre><code>gem 'apify'</code></pre>
21
27
 
22
28
  <h3>Usage</h3>
23
29
 
@@ -3,7 +3,6 @@
3
3
  <ul>
4
4
  <li>The API works by exchanging messages encoded in <a href="http://en.wikipedia.org/wiki/JSON">JSON</a>.</li>
5
5
  <li>There are no fancy message formats, it's all just sending arbitrary JSON objects back and forth.</li>
6
- <li>There are some optional features like validating requests with schemas, but you don't need to use them if you don't like.</li>
7
- <li>Building a lient that consumes the API is very straightforward. Also if you use Ruby, there is a helpful <a href="#api_client">client class</a> available.</li>
6
+ <li>Building a lient cthat consumes the API is very straightforward. Also if you use Ruby, there is a helpful <a href="#api_client">client class</a> available.</li>
8
7
  </ul>
9
8
 
@@ -3,7 +3,7 @@
3
3
  <h3>Requests</h3>
4
4
 
5
5
  <ul>
6
- <li>The API is accessed over plain HTTP.</li>
6
+ <li>The API is accessed over HTTP.</li>
7
7
  <% if authentication_configured? %>
8
8
  <li>HTTP basic authentication is used.</li>
9
9
  <% end %>
data/lib/apify/action.rb CHANGED
@@ -23,9 +23,7 @@ module Apify
23
23
  if block
24
24
  @responder = block
25
25
  else
26
- Apify::Exchange.new.tap do |exchange|
27
- exchange.respond(args, self)
28
- end
26
+ Apify::Exchange.new.respond(args, self)
29
27
  end
30
28
  end
31
29
 
data/lib/apify/api.rb CHANGED
@@ -2,7 +2,7 @@ module Apify
2
2
  class Api
3
3
  class << self
4
4
 
5
- def action(method, name, &block)
5
+ def action(method, name, args = {}, &block)
6
6
  method = method.to_sym
7
7
  name = name.to_sym
8
8
  if block
@@ -10,24 +10,25 @@ module Apify
10
10
  indexed_actions[name][method] = action
11
11
  actions << action
12
12
  else
13
- indexed_actions[name][method] or raise "Unknown API action: #{name}"
13
+ action = indexed_actions[name][method] or raise "Unknown API action: #{name}"
14
+ action.respond(args)
14
15
  end
15
16
  end
16
17
 
17
- def get(name, &block)
18
- action(:get, name, &block)
18
+ def get(*args, &block)
19
+ action(:get, *args, &block)
19
20
  end
20
21
 
21
- def post(name, &block)
22
- action(:post, name, &block)
22
+ def post(*args, &block)
23
+ action(:post, *args, &block)
23
24
  end
24
25
 
25
- def put(name, &block)
26
- action(:put, name, &block)
26
+ def put(*args, &block)
27
+ action(:put, *args, &block)
27
28
  end
28
29
 
29
- def delete(name, &block)
30
- action(:delete, name, &block)
30
+ def delete(*args, &block)
31
+ action(:delete, *args, &block)
31
32
  end
32
33
 
33
34
  def actions
@@ -59,17 +59,17 @@ module Apify
59
59
 
60
60
  def respond_with_action(action)
61
61
  args = params[:args].present? ? JSON.parse(params[:args]) : {}
62
- exchange = action.respond(args)
63
- render_api_response(exchange, "#{action.name}.json")
62
+ render_successful_request(action.respond(args), "#{action.name}.json")
63
+ rescue Exception => e
64
+ render_failed_request(e.message)
64
65
  end
65
66
 
66
- def render_api_response(exchange, filename)
67
- # p exchange.value
68
- if exchange.successful?
69
- send_data exchange.value.to_json, :status => '200', :type => 'application/json', :filename => filename
70
- else
71
- send_data exchange.value, :status => '500', :type => 'text/plain', :filename => filename
72
- end
67
+ def render_successful_request(value, filename)
68
+ send_data value.to_json, :status => '200', :type => 'application/json', :filename => filename
69
+ end
70
+
71
+ def render_failed_request(message)
72
+ send_data message, :status => '500', :type => 'text/plain'
73
73
  end
74
74
 
75
75
  def render_method_not_allowed(method)
data/lib/apify/client.rb CHANGED
@@ -36,10 +36,12 @@ module Apify
36
36
  def request(method, path, args = nil)
37
37
  url = build_url(path)
38
38
  args ||= {}
39
- json = RestClient.send(method, url, :args => args.to_json)
39
+ params = { :args => args.to_json }
40
+ [:get, :head].include?(method) and params = { :params => params }
41
+ json = RestClient.send(method, url, params)
40
42
  JSON.parse(json)
41
43
  rescue RestClient::Unauthorized => e
42
- raise Apify::RequestFailed.new("Unauthorized")
44
+ raise Apify::Unauthorized.new("Unauthorized")
43
45
  rescue RestClient::ExceptionWithResponse => e
44
46
  raise Apify::RequestFailed.new("API request failed with status #{e.http_code}", e.http_body)
45
47
  end
data/lib/apify/errors.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  module Apify
2
+
2
3
  class RequestFailed < StandardError
3
4
 
4
5
  attr_reader :response_body
@@ -9,4 +10,11 @@ module Apify
9
10
  end
10
11
 
11
12
  end
13
+
14
+ class Unauthorized < Apify::RequestFailed
15
+ end
16
+
17
+ class Invalid < Apify::RequestFailed
18
+ end
19
+
12
20
  end
@@ -6,22 +6,17 @@ module Apify
6
6
 
7
7
  def initialize
8
8
  @value = nil
9
- @error = false
10
9
  @args = nil
11
10
  end
12
11
 
13
- def successful?
14
- not @error
15
- end
16
-
17
12
  def respond(args, action)
18
- logging_errors do
19
- @args = args
20
- validate(@args, action.schema(:args), 'Invalid request args')
21
- @value = instance_eval(&action.responder) || {}
22
- validate(@value, action.schema(:value), 'Invalid response value')
23
- end
24
- successful?
13
+ # hash_class = defined?(HashWithIndifferentAccess) ? HashWithIndifferentAccess : ActiveSupport::HashWithIndifferentAccess
14
+ @args = args.stringify_keys
15
+ validate(@args, action.schema(:args), 'Invalid request args')
16
+ @value = instance_eval(&action.responder) || {}
17
+ @value.stringify_keys!
18
+ validate(@value, action.schema(:value), 'Invalid response value')
19
+ @value
25
20
  end
26
21
 
27
22
  private
@@ -31,16 +26,10 @@ module Apify
31
26
  def validate(object, schema, message_prefix)
32
27
  JSON::Schema.validate(object, schema) if schema
33
28
  rescue JSON::Schema::ValueError => e
34
- raise "#{message_prefix}: #{e.message}"
29
+ @value = nil
30
+ raise Apify::Invalid.new("#{message_prefix}: #{e.message}")
35
31
  end
36
32
 
37
- def logging_errors(&block)
38
- block.call
39
- rescue Exception => e
40
- @error = true
41
- @value = e.message
42
- end
43
-
44
33
  def sql_datetime(time)
45
34
  time.present? ? time.strftime("%Y-%m-%d %H:%M:%S") : nil
46
35
  end
@@ -4,20 +4,36 @@ describe Apify::Client do
4
4
 
5
5
  it "raise an exception if authorization is missing" do
6
6
  client = Apify::Client.new(:host => 'host')
7
- stub_http_request(:get, 'http://host/api').to_return(:status => 401, :body => 'the body')
8
- expect { client.get '/api' }.to raise_error(Apify::RequestFailed)
7
+ stub_request(:post, 'http://host/api/method').to_return(:status => 401, :body => 'the body')
8
+ expect { client.post '/api/method' }.to raise_error(Apify::RequestFailed)
9
9
  end
10
10
 
11
11
  it "raise an exception if there is a server error" do
12
12
  client = Apify::Client.new(:host => 'host')
13
- stub_http_request(:get, 'http://host/api').to_return(:status => 500, :body => 'the body')
14
- expect { client.get '/api' }.to raise_error(Apify::RequestFailed)
13
+ stub_request(:post, 'http://host/api/method').to_return(:status => 500, :body => 'the body')
14
+ expect { client.post '/api/method' }.to raise_error(Apify::RequestFailed)
15
15
  end
16
16
 
17
17
  it "should return the parsed JSON object if the request went through" do
18
18
  client = Apify::Client.new(:host => 'host')
19
- stub_http_request(:get, 'http://host/api').to_return(:status => 200, :body => '{ "key": "value" }')
20
- client.get('/api').should == { 'key' => 'value' }
19
+ stub_request(:post, 'http://host/api/method').to_return(:status => 200, :body => '{ "key": "value" }')
20
+ client.post('/api/method').should == { 'key' => 'value' }
21
+ end
22
+
23
+ it "should call API methods with arguments" do
24
+ client = Apify::Client.new(:host => 'host')
25
+ stub_request(:post, 'http://host/api/hello').to_return(:status => 200, :body => '{}')
26
+ args = { :name => 'Jack' }
27
+ client.post('/api/hello', args)
28
+ WebMock.should have_requested(:post, "http://host/api/hello").with(:body => { :args => args.to_json })
29
+ end
30
+
31
+ it "should call GET actions correctly" do
32
+ client = Apify::Client.new(:host => 'host')
33
+ args = { :name => 'Jack' }
34
+ stub_request(:get, 'http://host/api/hello').with(:query => { :args => args.to_json }).to_return(:status => 200, :body => '{}')
35
+ client.get('/api/hello', args)
36
+ WebMock.should have_requested(:get, "http://host/api/hello").with(:query => { :args => args.to_json })
21
37
  end
22
38
 
23
39
  end
@@ -12,6 +12,18 @@ class Api < Apify::Api
12
12
  end
13
13
  end
14
14
 
15
+ post :hello do
16
+ schema :args do
17
+ object('name' => string)
18
+ end
19
+ schema :value do
20
+ object('message' => string)
21
+ end
22
+ respond do
23
+ { 'message' => "Hello #{args['name']}" }
24
+ end
25
+ end
26
+
15
27
  post :echo_args do
16
28
  respond do
17
29
  args
@@ -20,19 +32,13 @@ class Api < Apify::Api
20
32
 
21
33
  post :with_args_schema do
22
34
  schema :args do
23
- { "type" => "object",
24
- "properties" => {
25
- "string_arg" => string
26
- }}
35
+ object("string_arg" => string)
27
36
  end
28
37
  end
29
38
 
30
39
  post :with_value_schema do
31
40
  schema :value do
32
- { "type" => "object",
33
- "properties" => {
34
- "string_value" => string
35
- }}
41
+ object("string_value" => string)
36
42
  end
37
43
  respond do
38
44
  args
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Convenient testing of Api models' do
4
+
5
+ it 'should allow to test response objects directly' do
6
+ Api.post(:hello, :name => 'Jack').should == { 'message' => 'Hello Jack' }
7
+ end
8
+
9
+ it 'should raise an exception on errors' do
10
+ expect { Api.post(:hello) }.to raise_error(Apify::Invalid)
11
+ expect { Api.post(:fail) }.to raise_error(StandardError)
12
+ end
13
+
14
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 3
8
- - 3
9
- version: 0.3.3
7
+ - 4
8
+ - 0
9
+ version: 0.4.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Henning Koch
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-08-28 00:00:00 +02:00
17
+ date: 2010-08-29 00:00:00 +02:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -143,6 +143,7 @@ files:
143
143
  - spec/app_root/lib/console_with_fixtures.rb
144
144
  - spec/app_root/script/console
145
145
  - spec/controllers/api_controller_spec.rb
146
+ - spec/models/api_spec.rb
146
147
  - spec/rcov.opts
147
148
  - spec/spec.opts
148
149
  - spec/spec_helper.rb
@@ -193,6 +194,7 @@ test_files:
193
194
  - spec/app_root/config/environment.rb
194
195
  - spec/app_root/config/routes.rb
195
196
  - spec/controllers/api_controller_spec.rb
197
+ - spec/models/api_spec.rb
196
198
  - examples/client/client.rb
197
199
  - examples/host/app/controllers/application_controller.rb
198
200
  - examples/host/app/controllers/api_controller.rb