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