poncho 0.0.2 → 0.0.3
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 +177 -131
- data/lib/poncho/error.rb +1 -1
- data/lib/poncho/errors.rb +1 -1
- data/lib/poncho/json_method.rb +1 -1
- data/lib/poncho/method.rb +3 -0
- data/lib/poncho/resource.rb +1 -1
- data/lib/poncho/version.rb +1 -1
- data/test/poncho/test_json_method.rb +40 -0
- metadata +4 -2
data/README.md
CHANGED
@@ -10,7 +10,9 @@ It's compatible with any rack-based framework, such as Rails or Sinatra.
|
|
10
10
|
|
11
11
|
Add this line to your application's Gemfile:
|
12
12
|
|
13
|
-
|
13
|
+
```ruby
|
14
|
+
gem 'poncho'
|
15
|
+
```
|
14
16
|
|
15
17
|
And then execute:
|
16
18
|
|
@@ -22,30 +24,32 @@ Or install it yourself as:
|
|
22
24
|
|
23
25
|
## TLDR Usage
|
24
26
|
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
```ruby
|
28
|
+
class ChargeResource < Poncho::Resource
|
29
|
+
param :amount, :type => :integer
|
30
|
+
param :currency
|
28
31
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
32
|
+
def currency
|
33
|
+
super || 'USD'
|
34
|
+
end
|
35
|
+
end
|
33
36
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
+
class ChargeCreateMethod < Poncho::JSONMethod
|
38
|
+
param :amount, :type => :integer, :required => true
|
39
|
+
param :currency, :in => ['USD', 'GBP']
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
def invoke
|
42
|
+
charge = Charge.new
|
43
|
+
charge.amount = param(:amount)
|
44
|
+
charge.currency = param(:currency)
|
45
|
+
charge.save
|
43
46
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
+
ChargeResource.new(charge)
|
48
|
+
end
|
49
|
+
end
|
47
50
|
|
48
|
-
|
51
|
+
post '/charges', &ChargeCreateMethod
|
52
|
+
```
|
49
53
|
|
50
54
|
## Getting started with Methods
|
51
55
|
|
@@ -54,27 +58,35 @@ Methods inherit from `Poncho::Method` and override `invoke`, where they perform
|
|
54
58
|
In a similar vein to Sinatra, anything returned from `invoke` is sent right back to the user. You can
|
55
59
|
return a http status code, a body string, or even a Rack response array.
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
|
61
|
+
```ruby
|
62
|
+
class ChargeListMethod < Poncho::Method
|
63
|
+
def invoke
|
64
|
+
# Some DB shizzle
|
60
65
|
|
61
|
-
|
62
|
-
|
63
|
-
|
66
|
+
200
|
67
|
+
end
|
68
|
+
end
|
69
|
+
```
|
64
70
|
|
65
71
|
To invoke the method just add it to your routes.
|
66
72
|
|
67
73
|
Using Rails:
|
68
74
|
|
69
|
-
|
75
|
+
```ruby
|
76
|
+
match '/users' => UsersListMethod, :via => :get
|
77
|
+
```
|
70
78
|
|
71
79
|
Using Sinatra:
|
72
80
|
|
73
|
-
|
81
|
+
```ruby
|
82
|
+
get '/users', &UsersListMethod
|
83
|
+
```
|
74
84
|
|
75
85
|
Or invoke manually:
|
76
86
|
|
77
|
-
|
87
|
+
```ruby
|
88
|
+
UsersListMethod.call(rack_env)
|
89
|
+
```
|
78
90
|
|
79
91
|
If you're writing a JSON API, you'll probably want to inherit the Method from `Poncho::JSONMethod` instead of
|
80
92
|
`Poncho::Method`, but we'll cover that later.
|
@@ -85,11 +97,15 @@ You can get access to the request params, via the `params` or `param(name)` meth
|
|
85
97
|
|
86
98
|
Before you can use a param though, you need to define it:
|
87
99
|
|
88
|
-
|
100
|
+
```ruby
|
101
|
+
param :param_name
|
102
|
+
```
|
89
103
|
|
90
104
|
By default, param are of type 'string'. you can choose a different type via the `:type` option:
|
91
105
|
|
92
|
-
|
106
|
+
```ruby
|
107
|
+
param :amount, :type => :integer
|
108
|
+
```
|
93
109
|
|
94
110
|
There are a bunch of predefined types, such as `:integer`, `:array`, `:boolean_string` etc, but you can
|
95
111
|
also easily define your own custom ones (covered later).
|
@@ -106,35 +122,45 @@ As well as the default type validation, Poncho lets you validate presence, forma
|
|
106
122
|
|
107
123
|
For example, to validate that a `:currency` parameter is provided, pass in the `:presence' option:
|
108
124
|
|
109
|
-
|
125
|
+
```ruby
|
126
|
+
param :currency, :presence => true
|
127
|
+
```
|
110
128
|
|
111
129
|
To validate that a currency is either 'USD' or 'GBP', use the `:in` option.
|
112
130
|
|
113
|
-
|
131
|
+
```ruby
|
132
|
+
param :currency, :in => ['USD', 'GBP']
|
133
|
+
```
|
114
134
|
|
115
135
|
The other supported validations out of the box are `:format`, `:not_in`, and `:length`:
|
116
136
|
|
117
|
-
|
118
|
-
|
137
|
+
```ruby
|
138
|
+
param :email, :format => /@/
|
139
|
+
param :password, :length => 5..20
|
140
|
+
```
|
119
141
|
|
120
142
|
## Custom Validation
|
121
143
|
|
122
144
|
You can use a custom validator via the `validate` method, passing in a block:
|
123
145
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
146
|
+
```ruby
|
147
|
+
validate do
|
148
|
+
unless param(:customer_id) ~= /\Acus_/
|
149
|
+
errors.add(:customer_id, :invalid_customer)
|
150
|
+
end
|
151
|
+
end
|
129
152
|
|
130
|
-
|
153
|
+
# Or
|
131
154
|
|
132
|
-
|
155
|
+
validates :customer_id, :customer_validate
|
156
|
+
```
|
133
157
|
|
134
158
|
Alternatively, if your validation is being used in multiple places, you can wrap it up in a class and
|
135
|
-
pass it to the `
|
159
|
+
pass it to the `validates_with` method.
|
136
160
|
|
137
|
-
|
161
|
+
```ruby
|
162
|
+
validates_with CustomValidator
|
163
|
+
```
|
138
164
|
|
139
165
|
For a good example of how to build validations, see the [
|
140
166
|
existing ones](https://github.com/stripe/poncho/tree/master/lib/poncho/validations).
|
@@ -148,72 +174,84 @@ To define a custom parameter, simply inherit from `Poncho::Param`. For example,
|
|
148
174
|
`CardHashParam`. It needs to validate input via overriding the `validate_each` method, and convert input via
|
149
175
|
overriding the `convert` method.
|
150
176
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
end
|
161
|
-
|
162
|
-
def convert(value)
|
163
|
-
value && value.symbolize_keys
|
164
|
-
end
|
165
|
-
end
|
177
|
+
```ruby
|
178
|
+
module Poncho
|
179
|
+
module Params
|
180
|
+
class CardHashParam < Param
|
181
|
+
def validate_each(method, attribute, value)
|
182
|
+
value = convert(value)
|
183
|
+
|
184
|
+
unless value.is_a?(Hash) && value.keys == [:number, :exp_month, :exp_year, :cvc]
|
185
|
+
method.errors.add(attribute, :invalid_card_hash, options.merge(:value => value))
|
166
186
|
end
|
167
187
|
end
|
168
188
|
|
189
|
+
def convert(value)
|
190
|
+
value && value.symbolize_keys
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
```
|
196
|
+
|
169
197
|
You can use custom parameters via the `:type` option.
|
170
198
|
|
171
|
-
|
199
|
+
```ruby
|
200
|
+
param :card, :type => Poncho::Params::CardHashParam
|
172
201
|
|
173
|
-
|
174
|
-
|
202
|
+
# Or the shortcut
|
203
|
+
param :card, :type => :card_hash
|
204
|
+
```
|
175
205
|
|
176
206
|
## Request & Response
|
177
207
|
|
178
208
|
You can gain access to the rack request via the `request` method, for example:
|
179
209
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
210
|
+
```ruby
|
211
|
+
def invoke
|
212
|
+
accept = request.headers['Accept']
|
213
|
+
200
|
214
|
+
end
|
215
|
+
```
|
184
216
|
|
185
217
|
The same goes for the response object:
|
186
218
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
219
|
+
```ruby
|
220
|
+
def invoke
|
221
|
+
response.body = ['Fee-fi-fo-fum']
|
222
|
+
200
|
223
|
+
end
|
224
|
+
```
|
191
225
|
|
192
226
|
There are some helper methods to set such things as the HTTP status response codes and body.
|
193
227
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
228
|
+
```ruby
|
229
|
+
def invoke
|
230
|
+
status 201
|
231
|
+
body 'Created!'
|
232
|
+
end
|
233
|
+
```
|
198
234
|
|
199
235
|
## Method filters
|
200
236
|
|
201
237
|
There are various filters you can apply to the request, for example:
|
202
238
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
239
|
+
```ruby
|
240
|
+
class MyMethod < Poncho::Method
|
241
|
+
before_validation do
|
242
|
+
# Before validation
|
243
|
+
end
|
207
244
|
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
245
|
+
before do
|
246
|
+
# Before invoke
|
247
|
+
p params
|
248
|
+
end
|
212
249
|
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
250
|
+
after do
|
251
|
+
# After invocation
|
252
|
+
end
|
253
|
+
end
|
254
|
+
```
|
217
255
|
|
218
256
|
## Error responses
|
219
257
|
|
@@ -221,15 +259,17 @@ You can provide custom responses to exceptions via the `error` class method.
|
|
221
259
|
|
222
260
|
Pass `error` a exception type or status code.
|
223
261
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
262
|
+
```ruby
|
263
|
+
class MyMethod < Poncho::Method
|
264
|
+
error MyCustomClass do
|
265
|
+
'Sorry, something went wrong.'
|
266
|
+
end
|
228
267
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
268
|
+
error 403 do
|
269
|
+
'Not authorized.'
|
270
|
+
end
|
271
|
+
end
|
272
|
+
```
|
233
273
|
|
234
274
|
## JSON APIs
|
235
275
|
|
@@ -237,13 +277,15 @@ If your API only returns JSON then Poncho has a convenient `JSONMethod` class wh
|
|
237
277
|
will ensure that all response bodies are converted into JSON and that the correct content type
|
238
278
|
header is set.
|
239
279
|
|
240
|
-
|
241
|
-
|
280
|
+
```ruby
|
281
|
+
class TokenCreateMethod < Poncho::JSONMethod
|
282
|
+
param :number, :required => true
|
242
283
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
284
|
+
def invoke
|
285
|
+
{:token => '123'}
|
286
|
+
end
|
287
|
+
end
|
288
|
+
```
|
247
289
|
|
248
290
|
`JSONMethod` also ensures that there's valid JSON error responses to 404s and 500s, as well
|
249
291
|
as returning a JSON error hash for validation errors.
|
@@ -257,45 +299,49 @@ Resources are wrappers around other classes, such as models, providing a view re
|
|
257
299
|
|
258
300
|
You can specify attributes to be returned to the client using the same `param` syntax as documented above.
|
259
301
|
|
260
|
-
|
261
|
-
|
302
|
+
```ruby
|
303
|
+
class Card
|
304
|
+
attr_reader :number
|
262
305
|
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
306
|
+
def initialize(number)
|
307
|
+
@number = number
|
308
|
+
end
|
309
|
+
end
|
267
310
|
|
268
|
-
|
269
|
-
|
270
|
-
|
311
|
+
class CardResource < Poncho::Resource
|
312
|
+
param :number
|
313
|
+
param :description
|
271
314
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
315
|
+
def number
|
316
|
+
super[-4..-1]
|
317
|
+
end
|
318
|
+
end
|
319
|
+
```
|
276
320
|
|
277
321
|
As you can see in the example above, you can override params and return a custom response.
|
278
322
|
|
279
323
|
When the `Resource` instance is converted into JSON the appropriate params will be used and serialized.
|
280
324
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
325
|
+
```ruby
|
326
|
+
class ChargeResource < Poncho::Resource
|
327
|
+
param :amount, :type => :integer
|
328
|
+
param :currency
|
329
|
+
param :card, :resource => CardResource
|
330
|
+
|
331
|
+
def currency
|
332
|
+
super || 'USD'
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
class ChargeListMethod < Poncho::JSONMethod
|
337
|
+
def invoke
|
338
|
+
[
|
339
|
+
ChargeResource.new(Charge.new(1000, 'USD')),
|
340
|
+
ChargeResource.new(Charge.new(50, 'USD'))
|
341
|
+
]
|
342
|
+
end
|
343
|
+
end
|
344
|
+
```
|
299
345
|
|
300
346
|
If a particular param points to another resource, you can use the `:type => :resource` option as demonstrated above.
|
301
347
|
|
data/lib/poncho/error.rb
CHANGED
data/lib/poncho/errors.rb
CHANGED
data/lib/poncho/json_method.rb
CHANGED
data/lib/poncho/method.rb
CHANGED
data/lib/poncho/resource.rb
CHANGED
data/lib/poncho/version.rb
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'rack/mock'
|
3
|
+
require 'poncho'
|
4
|
+
|
5
|
+
class TestJSONMethod < MiniTest::Unit::TestCase
|
6
|
+
def env(params = {})
|
7
|
+
Rack::MockRequest.env_for('http://api.com/charges', :params => params)
|
8
|
+
end
|
9
|
+
|
10
|
+
def setup
|
11
|
+
end
|
12
|
+
|
13
|
+
def test_serializes_body
|
14
|
+
method = Class.new(Poncho::JSONMethod) do
|
15
|
+
param :id, :type => :integer
|
16
|
+
param :body, :type => :string
|
17
|
+
|
18
|
+
def invoke
|
19
|
+
params
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
status, headers, body = method.call(env(:id => '1', :body => 'wem'))
|
24
|
+
assert_equal({:id => 1, :body => 'wem'}.to_json, body.body.first)
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_serializes_errors
|
28
|
+
method = Class.new(Poncho::JSONMethod) do
|
29
|
+
param :id, :type => :integer
|
30
|
+
param :body, :type => :string
|
31
|
+
|
32
|
+
def invoke
|
33
|
+
params
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
status, headers, body = method.call(env(:id => 'string', :body => 'wem'))
|
38
|
+
assert_equal({:error => {:param => 'id', :type => 'invalid_integer', :message => nil}}.to_json, body.body.first)
|
39
|
+
end
|
40
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: poncho
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-16 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: Poncho is an API to build REST APIs with a convenient DSL.
|
15
15
|
email:
|
@@ -53,6 +53,7 @@ files:
|
|
53
53
|
- lib/poncho/validator.rb
|
54
54
|
- lib/poncho/version.rb
|
55
55
|
- poncho.gemspec
|
56
|
+
- test/poncho/test_json_method.rb
|
56
57
|
- test/poncho/test_method.rb
|
57
58
|
- test/poncho/test_resource.rb
|
58
59
|
homepage: https://github.com/stripe/poncho
|
@@ -80,5 +81,6 @@ signing_key:
|
|
80
81
|
specification_version: 3
|
81
82
|
summary: Poncho is an API to build APIs
|
82
83
|
test_files:
|
84
|
+
- test/poncho/test_json_method.rb
|
83
85
|
- test/poncho/test_method.rb
|
84
86
|
- test/poncho/test_resource.rb
|