poncho 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in poncho.gemspec
4
+ gemspec
5
+
6
+ # For testing
7
+ gem 'sinatra'
8
+ gem 'thin'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Alex MacCaw
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,302 @@
1
+ # Poncho
2
+
3
+ Poncho is an API to build APIs or, in other words, a DSL to build REST interfaces.
4
+
5
+ It'll validate input and output, coerce values and is easily extendable with custom data types.
6
+
7
+ It's compatible with any rack-based framework, such as Rails or Sinatra.
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'poncho'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install poncho
22
+
23
+ ## TLDR Usage
24
+
25
+ class ChargeResource < Poncho::Resource
26
+ param :amount, :type => :integer
27
+ param :currency
28
+
29
+ def currency
30
+ super || 'USD'
31
+ end
32
+ end
33
+
34
+ class ChargeCreateMethod < Poncho::JSONMethod
35
+ param :amount, :type => :integer, :required => true
36
+ param :currency, :in => ['USD', 'GBP']
37
+
38
+ def invoke
39
+ charge = Charge.new
40
+ charge.amount = param(:amount)
41
+ charge.currency = param(:currency)
42
+ charge.save
43
+
44
+ ChargeResource.new(charge)
45
+ end
46
+ end
47
+
48
+ post '/charges', &ChargeCreateMethod
49
+
50
+ ## Getting started with Methods
51
+
52
+ Methods inherit from `Poncho::Method` and override `invoke`, where they perform any necessary logic.
53
+
54
+ In a similar vein to Sinatra, anything returned from `invoke` is sent right back to the user. You can
55
+ return a http status code, a body string, or even a Rack response array.
56
+
57
+ class ChargeListMethod < Poncho::Method
58
+ def invoke
59
+ # Some DB shizzle
60
+
61
+ 200
62
+ end
63
+ end
64
+
65
+ To invoke the method just add it to your routes.
66
+
67
+ Using Rails:
68
+
69
+ match '/users' => UsersListMethod, :via => :get
70
+
71
+ Using Sinatra:
72
+
73
+ get '/users', &UsersListMethod
74
+
75
+ Or invoke manually:
76
+
77
+ UsersListMethod.call(rack_env)
78
+
79
+ If you're writing a JSON API, you'll probably want to inherit the Method from `Poncho::JSONMethod` instead of
80
+ `Poncho::Method`, but we'll cover that later.
81
+
82
+ ## Params
83
+
84
+ You can get access to the request params, via the `params` or `param(name)` methods.
85
+
86
+ Before you can use a param though, you need to define it:
87
+
88
+ param :param_name
89
+
90
+ By default, param are of type 'string'. you can choose a different type via the `:type` option:
91
+
92
+ param :amount, :type => :integer
93
+
94
+ There are a bunch of predefined types, such as `:integer`, `:array`, `:boolean_string` etc, but you can
95
+ also easily define your own custom ones (covered later).
96
+
97
+ Poncho will automatically validate that if a paramter is provided it is in a valid format.
98
+ Poncho will also handle type conversion for you.
99
+
100
+ So for example, in the case above, Poncho will automatically validate that the `amount` param is
101
+ indeed an Integer or an Integer string, and will coerce the parameter into an integer when you try to access it.
102
+
103
+ ## Validation
104
+
105
+ As well as the default type validation, Poncho lets you validate presence, format, length and much more!
106
+
107
+ For example, to validate that a `:currency` parameter is provided, pass in the `:presence' option:
108
+
109
+ param :currency, :presence => true
110
+
111
+ To validate that a currency is either 'USD' or 'GBP', use the `:in` option.
112
+
113
+ param :currency, :in => ['USD', 'GBP']
114
+
115
+ The other supported validations out of the box are `:format`, `:not_in`, and `:length`:
116
+
117
+ param :email, :format => /@/
118
+ param :password, :length => 5..20
119
+
120
+ ## Custom Validation
121
+
122
+ You can use a custom validator via the `validate` method, passing in a block:
123
+
124
+ validate do
125
+ unless param(:customer_id) ~= /\Acus_/
126
+ errors.add(:customer_id, :invalid_customer)
127
+ end
128
+ end
129
+
130
+ # Or
131
+
132
+ validates :customer_id, :customer_validate
133
+
134
+ 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.
136
+
137
+ validates_with CustomValidator
138
+
139
+ For a good example of how to build validations, see the [
140
+ existing ones](https://github.com/stripe/poncho/tree/master/lib/poncho/validations).
141
+
142
+ ## Custom Params
143
+
144
+ As your API grows you'll probably start to need custom parameter types. These can be useful to ensure
145
+ parameters are both valid and converted into suitable values.
146
+
147
+ To define a custom parameter, simply inherit from `Poncho::Param`. For example, let's define a new param called
148
+ `CardHashParam`. It needs to validate input via overriding the `validate_each` method, and convert input via
149
+ overriding the `convert` method.
150
+
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
166
+ end
167
+ end
168
+
169
+ You can use custom parameters via the `:type` option.
170
+
171
+ param :card, :type => Poncho::Params::CardHashParam
172
+
173
+ # Or the shortcut
174
+ param :card, :type => :card_hash
175
+
176
+ ## Request & Response
177
+
178
+ You can gain access to the rack request via the `request` method, for example:
179
+
180
+ def invoke
181
+ accept = request.headers['Accept']
182
+ 200
183
+ end
184
+
185
+ The same goes for the response object:
186
+
187
+ def invoke
188
+ response.body = ['Fee-fi-fo-fum']
189
+ 200
190
+ end
191
+
192
+ There are some helper methods to set such things as the HTTP status response codes and body.
193
+
194
+ def invoke
195
+ status 201
196
+ body 'Created!'
197
+ end
198
+
199
+ ## Method filters
200
+
201
+ There are various filters you can apply to the request, for example:
202
+
203
+ class MyMethod < Poncho::Method
204
+ before_validation do
205
+ # Before validation
206
+ end
207
+
208
+ before do
209
+ # Before invoke
210
+ p params
211
+ end
212
+
213
+ after do
214
+ # After invocation
215
+ end
216
+ end
217
+
218
+ ## Error responses
219
+
220
+ You can provide custom responses to exceptions via the `error` class method.
221
+
222
+ Pass `error` a exception type or status code.
223
+
224
+ class MyMethod < Poncho::Method
225
+ error MyCustomClass do
226
+ 'Sorry, something went wrong.'
227
+ end
228
+
229
+ error 403 do
230
+ 'Not authorized.'
231
+ end
232
+ end
233
+
234
+ ## JSON APIs
235
+
236
+ If your API only returns JSON then Poncho has a convenient `JSONMethod` class which
237
+ will ensure that all response bodies are converted into JSON and that the correct content type
238
+ header is set.
239
+
240
+ class TokenCreateMethod < Poncho::JSONMethod
241
+ param :number, :required => true
242
+
243
+ def invoke
244
+ {:token => '123'}
245
+ end
246
+ end
247
+
248
+ `JSONMethod` also ensures that there's valid JSON error responses to 404s and 500s, as well
249
+ as returning a JSON error hash for validation errors.
250
+
251
+ $ curl http://localhost:4567/tokens -d number=
252
+ {"error":{"param":"number","type":"presence"}
253
+
254
+ ## Resources
255
+
256
+ Resources are wrappers around other classes, such as models, providing a view representation of them.
257
+
258
+ You can specify attributes to be returned to the client using the same `param` syntax as documented above.
259
+
260
+ class Card
261
+ attr_reader :number
262
+
263
+ def initialize(number)
264
+ @number = number
265
+ end
266
+ end
267
+
268
+ class CardResource < Poncho::Resource
269
+ param :number
270
+ param :description
271
+
272
+ def number
273
+ super[-4..-1]
274
+ end
275
+ end
276
+
277
+ As you can see in the example above, you can override params and return a custom response.
278
+
279
+ When the `Resource` instance is converted into JSON the appropriate params will be used and serialized.
280
+
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
299
+
300
+ If a particular param points to another resource, you can use the `:type => :resource` option as demonstrated above.
301
+
302
+
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs << 'test'
6
+ t.test_files = FileList['test/**/test*.rb']
7
+ t.verbose = true
8
+ end
data/examples/app.rb ADDED
@@ -0,0 +1,80 @@
1
+ require 'sinatra'
2
+ require 'poncho'
3
+
4
+ class Card
5
+ attr_reader :number
6
+
7
+ def initialize(number)
8
+ @number = number
9
+ end
10
+ end
11
+
12
+ class Charge
13
+ attr_reader :amount, :currency
14
+
15
+ def initialize(amount, currency)
16
+ @amount = amount
17
+ @currency = currency
18
+ end
19
+
20
+ def card
21
+ Card.new('4242 4242 4242 4242')
22
+ end
23
+ end
24
+
25
+ class CardResource < Poncho::Resource
26
+ param :number
27
+ param :expiry_month
28
+
29
+ def number
30
+ super[-4..-1]
31
+ end
32
+ end
33
+
34
+ class ChargeResource < Poncho::Resource
35
+ param :amount, :type => :integer
36
+ param :currency
37
+ param :card, :resource => CardResource
38
+
39
+ def currency
40
+ super || 'USD'
41
+ end
42
+ end
43
+
44
+ class ChargeListMethod < Poncho::JSONMethod
45
+ param :page, :type => :integer
46
+
47
+ def invoke
48
+ [
49
+ ChargeResource.new(Charge.new(1000, 'USD')),
50
+ ChargeResource.new(Charge.new(50, 'USD'))
51
+ ]
52
+ end
53
+ end
54
+
55
+ class ChargeCreateMethod < Poncho::JSONMethod
56
+ include Poncho::Returns
57
+
58
+ param :amount, :type => :integer, :required => true
59
+ param :currency, :in => ['GBP', 'USD']
60
+ param :card
61
+
62
+ returns ChargeResource
63
+
64
+ def invoke
65
+ charge = Charge.new(param(:amount), param(:currency))
66
+ ChargeResource.new(charge)
67
+ end
68
+ end
69
+
70
+ class ChargeRetrieveMethod < Poncho::JSONMethod
71
+ param :id, :type => :integer
72
+
73
+ def invoke
74
+ {:id => param(:id)}
75
+ end
76
+ end
77
+
78
+ post '/charges', &ChargeCreateMethod
79
+ get '/charges', &ChargeListMethod
80
+ get '/charges/:id', &ChargeRetrieveMethod
data/lib/poncho.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'poncho/version'
2
+
3
+ module Poncho
4
+ autoload :Error, 'poncho/error'
5
+ autoload :ResourceValidationError, 'poncho/error'
6
+ autoload :ClientError, 'poncho/error'
7
+ autoload :ServerError, 'poncho/error'
8
+ autoload :ValidationError, 'poncho/error'
9
+
10
+ autoload :Errors, 'poncho/errors'
11
+ autoload :Filters, 'poncho/filters'
12
+
13
+ autoload :Validator, 'poncho/validator'
14
+ autoload :Validations, 'poncho/validations'
15
+ autoload :EachValidator, 'poncho/validator'
16
+ autoload :BlockValidator, 'poncho/validator'
17
+
18
+ autoload :Method, 'poncho/method'
19
+ autoload :JSONMethod, 'poncho/json_method'
20
+
21
+ autoload :Resource, 'poncho/resource'
22
+ autoload :Request, 'poncho/request'
23
+ autoload :Response, 'poncho/response'
24
+ autoload :Returns, 'poncho/returns'
25
+ autoload :Param, 'poncho/param'
26
+ autoload :Params, 'poncho/params'
27
+ end