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 +276 -4
- data/VERSION +1 -1
- data/apify.gemspec +4 -2
- data/app/views/apify/api/_client.html.erb +8 -2
- data/app/views/apify/api/_overview.html.erb +1 -2
- data/app/views/apify/api/_protocol.html.erb +1 -1
- data/lib/apify/action.rb +1 -3
- data/lib/apify/api.rb +11 -10
- data/lib/apify/api_controller.rb +9 -9
- data/lib/apify/client.rb +4 -2
- data/lib/apify/errors.rb +8 -0
- data/lib/apify/exchange.rb +9 -20
- data/spec/apify/client_spec.rb +22 -6
- data/spec/app_root/app/models/api.rb +14 -8
- data/spec/models/api_spec.rb +14 -0
- metadata +6 -4
data/README.md
CHANGED
@@ -1,14 +1,286 @@
|
|
1
|
-
|
1
|
+
Apify
|
2
2
|
=====
|
3
3
|
|
4
|
-
|
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.
|
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.
|
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-
|
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 '
|
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 '
|
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>
|
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
|
|
data/lib/apify/action.rb
CHANGED
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(
|
18
|
-
action(:get,
|
18
|
+
def get(*args, &block)
|
19
|
+
action(:get, *args, &block)
|
19
20
|
end
|
20
21
|
|
21
|
-
def post(
|
22
|
-
action(:post,
|
22
|
+
def post(*args, &block)
|
23
|
+
action(:post, *args, &block)
|
23
24
|
end
|
24
25
|
|
25
|
-
def put(
|
26
|
-
action(:put,
|
26
|
+
def put(*args, &block)
|
27
|
+
action(:put, *args, &block)
|
27
28
|
end
|
28
29
|
|
29
|
-
def delete(
|
30
|
-
action(:delete,
|
30
|
+
def delete(*args, &block)
|
31
|
+
action(:delete, *args, &block)
|
31
32
|
end
|
32
33
|
|
33
34
|
def actions
|
data/lib/apify/api_controller.rb
CHANGED
@@ -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
|
-
|
63
|
-
|
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
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
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::
|
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
data/lib/apify/exchange.rb
CHANGED
@@ -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
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
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
|
data/spec/apify/client_spec.rb
CHANGED
@@ -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
|
-
|
8
|
-
expect { client.
|
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
|
-
|
14
|
-
expect { client.
|
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
|
-
|
20
|
-
client.
|
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
|
-
|
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
|
-
|
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
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
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-
|
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
|