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 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
- gem 'poncho'
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
- class ChargeResource < Poncho::Resource
26
- param :amount, :type => :integer
27
- param :currency
27
+ ```ruby
28
+ class ChargeResource < Poncho::Resource
29
+ param :amount, :type => :integer
30
+ param :currency
28
31
 
29
- def currency
30
- super || 'USD'
31
- end
32
- end
32
+ def currency
33
+ super || 'USD'
34
+ end
35
+ end
33
36
 
34
- class ChargeCreateMethod < Poncho::JSONMethod
35
- param :amount, :type => :integer, :required => true
36
- param :currency, :in => ['USD', 'GBP']
37
+ class ChargeCreateMethod < Poncho::JSONMethod
38
+ param :amount, :type => :integer, :required => true
39
+ param :currency, :in => ['USD', 'GBP']
37
40
 
38
- def invoke
39
- charge = Charge.new
40
- charge.amount = param(:amount)
41
- charge.currency = param(:currency)
42
- charge.save
41
+ def invoke
42
+ charge = Charge.new
43
+ charge.amount = param(:amount)
44
+ charge.currency = param(:currency)
45
+ charge.save
43
46
 
44
- ChargeResource.new(charge)
45
- end
46
- end
47
+ ChargeResource.new(charge)
48
+ end
49
+ end
47
50
 
48
- post '/charges', &ChargeCreateMethod
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
- class ChargeListMethod < Poncho::Method
58
- def invoke
59
- # Some DB shizzle
61
+ ```ruby
62
+ class ChargeListMethod < Poncho::Method
63
+ def invoke
64
+ # Some DB shizzle
60
65
 
61
- 200
62
- end
63
- end
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
- match '/users' => UsersListMethod, :via => :get
75
+ ```ruby
76
+ match '/users' => UsersListMethod, :via => :get
77
+ ```
70
78
 
71
79
  Using Sinatra:
72
80
 
73
- get '/users', &UsersListMethod
81
+ ```ruby
82
+ get '/users', &UsersListMethod
83
+ ```
74
84
 
75
85
  Or invoke manually:
76
86
 
77
- UsersListMethod.call(rack_env)
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
- param :param_name
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
- param :amount, :type => :integer
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
- param :currency, :presence => true
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
- param :currency, :in => ['USD', 'GBP']
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
- param :email, :format => /@/
118
- param :password, :length => 5..20
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
- validate do
125
- unless param(:customer_id) ~= /\Acus_/
126
- errors.add(:customer_id, :invalid_customer)
127
- end
128
- end
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
- # Or
153
+ # Or
131
154
 
132
- validates :customer_id, :customer_validate
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 `validate_with` method.
159
+ pass it to the `validates_with` method.
136
160
 
137
- validates_with CustomValidator
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
- module Poncho
152
- module Params
153
- class CardHashParam < Param
154
- def validate_each(method, attribute, value)
155
- value = convert(value)
156
-
157
- unless value.is_a?(Hash) && value.keys == [:number, :exp_month, :exp_year, :cvc]
158
- method.errors.add(attribute, :invalid_card_hash, options.merge(:value => value))
159
- end
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
- param :card, :type => Poncho::Params::CardHashParam
199
+ ```ruby
200
+ param :card, :type => Poncho::Params::CardHashParam
172
201
 
173
- # Or the shortcut
174
- param :card, :type => :card_hash
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
- def invoke
181
- accept = request.headers['Accept']
182
- 200
183
- end
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
- def invoke
188
- response.body = ['Fee-fi-fo-fum']
189
- 200
190
- end
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
- def invoke
195
- status 201
196
- body 'Created!'
197
- end
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
- class MyMethod < Poncho::Method
204
- before_validation do
205
- # Before validation
206
- end
239
+ ```ruby
240
+ class MyMethod < Poncho::Method
241
+ before_validation do
242
+ # Before validation
243
+ end
207
244
 
208
- before do
209
- # Before invoke
210
- p params
211
- end
245
+ before do
246
+ # Before invoke
247
+ p params
248
+ end
212
249
 
213
- after do
214
- # After invocation
215
- end
216
- end
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
- class MyMethod < Poncho::Method
225
- error MyCustomClass do
226
- 'Sorry, something went wrong.'
227
- end
262
+ ```ruby
263
+ class MyMethod < Poncho::Method
264
+ error MyCustomClass do
265
+ 'Sorry, something went wrong.'
266
+ end
228
267
 
229
- error 403 do
230
- 'Not authorized.'
231
- end
232
- end
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
- class TokenCreateMethod < Poncho::JSONMethod
241
- param :number, :required => true
280
+ ```ruby
281
+ class TokenCreateMethod < Poncho::JSONMethod
282
+ param :number, :required => true
242
283
 
243
- def invoke
244
- {:token => '123'}
245
- end
246
- end
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
- class Card
261
- attr_reader :number
302
+ ```ruby
303
+ class Card
304
+ attr_reader :number
262
305
 
263
- def initialize(number)
264
- @number = number
265
- end
266
- end
306
+ def initialize(number)
307
+ @number = number
308
+ end
309
+ end
267
310
 
268
- class CardResource < Poncho::Resource
269
- param :number
270
- param :description
311
+ class CardResource < Poncho::Resource
312
+ param :number
313
+ param :description
271
314
 
272
- def number
273
- super[-4..-1]
274
- end
275
- end
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
- class ChargeResource < Poncho::Resource
282
- param :amount, :type => :integer
283
- param :currency
284
- param :card, :resource => CardResource
285
-
286
- def currency
287
- super || 'USD'
288
- end
289
- end
290
-
291
- class ChargeListMethod < Poncho::JSONMethod
292
- def invoke
293
- [
294
- ChargeResource.new(Charge.new(1000, 'USD')),
295
- ChargeResource.new(Charge.new(50, 'USD'))
296
- ]
297
- end
298
- end
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
@@ -1,6 +1,6 @@
1
1
  module Poncho
2
2
  class Error < StandardError
3
- def to_json
3
+ def to_json(*)
4
4
  as_json.to_json
5
5
  end
6
6
 
data/lib/poncho/errors.rb CHANGED
@@ -118,7 +118,7 @@ module Poncho
118
118
  }
119
119
  end
120
120
 
121
- def to_json(options=nil)
121
+ def to_json(*)
122
122
  as_json.to_json
123
123
  end
124
124
 
@@ -10,7 +10,7 @@ module Poncho
10
10
  json ServerError.new
11
11
  end
12
12
 
13
- error 403 do
13
+ error 400 do
14
14
  json InvalidRequestError.new
15
15
  end
16
16
 
data/lib/poncho/method.rb CHANGED
@@ -223,6 +223,9 @@ module Poncho
223
223
  end
224
224
 
225
225
  def handle_exception!(error)
226
+ # Exception raised in error handling
227
+ raise error if env['poncho.error']
228
+
226
229
  env['poncho.error'] = error
227
230
 
228
231
  status error.respond_to?(:code) ? Integer(error.code) : 500
@@ -33,7 +33,7 @@ module Poncho
33
33
  [to_json]
34
34
  end
35
35
 
36
- def to_json(options = nil)
36
+ def to_json(*)
37
37
  as_json.to_json
38
38
  end
39
39
 
@@ -1,3 +1,3 @@
1
1
  module Poncho
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -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.2
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-02 00:00:00.000000000 Z
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