grape 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of grape might be problematic. Click here for more details.

@@ -0,0 +1,68 @@
1
+ 0.2.1 (7/11/2012)
2
+ =================
3
+
4
+ * [#186](https://github.com/intridea/grape/issues/186): Fix: helpers allow multiple calls with modules and blocks - [@ppadron](https://github.com/ppadron).
5
+ * [#188](https://github.com/intridea/grape/pull/188): Fix: multi-method routes append '(.:format)' only once - [@kainosnoema](https://github.com/kainosnoema).
6
+ * [#64](https://github.com/intridea/grape/issues/64), [#180](https://github.com/intridea/grape/pull/180): Added support to get request bodies as parameters - [@bobbytables](https://github.com/bobbytables).
7
+ * [#175](https://github.com/intridea/grape/pull/175): Added support for API versioning based on a request parameter - [@jackcasey](https://github.com/jackcasey).
8
+ * [#168](https://github.com/intridea/grape/pull/168): Fix: Formatter can parse symbol keys in the headers hash - [@netmask](https://github.com/netmask).
9
+ * [#169](https://github.com/intridea/grape/pull/169): Silence multi_json deprecation warnings - [@whiteley](https://github.com/whiteley).
10
+ * [#166](https://github.com/intridea/grape/pull/166): Added support for `redirect`, including permanent and temporary - [@allenwei](https://github.com/allenwei).
11
+ * [#159](https://github.com/intridea/grape/pull/159): Added `:requirements` to routes, allowing to use reserved characters in paths - [@gaiottino](https://github.com/gaiottino).
12
+ * [#156](https://github.com/intridea/grape/pull/156): Added support for adding formatters to entities - [@bobbytables](https://github.com/bobbytables).
13
+ * [#183](https://github.com/intridea/grape/pull/183): Added ability to include documentation in entities - [@flah00](https://github.com/flah00).
14
+ * [#189](https://github.com/intridea/grape/pull/189): `HEAD` requests no longer return a body - [@stephencelis](https://github.com/stephencelis).
15
+ * [#97](https://github.com/intridea/grape/issues/97): Allow overriding `Content-Type` - [@dblock](https://github.com/dblock).
16
+
17
+ 0.2.0 (3/28/2012)
18
+ =================
19
+
20
+ * Added support for inheriting exposures from entities - [@bobbytables](https://github.com/bobbytables).
21
+ * Extended formatting with `default_format` - [@dblock](https://github.com/dblock).
22
+ * Added support for cookies - [@lukaszsliwa](https://github.com/lukaszsliwa).
23
+ * Added support for declaring additional content-types - [@joeyAghion](https://github.com/joeyAghion).
24
+ * Added support for HTTP PATCH - [@LTe](https://github.com/LTe).
25
+ * Added support for describing, documenting and reflecting APIs - [@dblock](https://github.com/dblock).
26
+ * Added support for anchoring and vendoring - [@jwkoelewijn](https://github.com/jwkoelewijn).
27
+ * Added support for HTTP OPTIONS - [@grimen](https://github.com/grimen).
28
+ * Added support for silencing logger - [@evansj](https://github.com/evansj).
29
+ * Added support for helper modules - [@freelancing-god](https://github.com/freelancing-god).
30
+ * Added support for Accept header-based versioning - [@jch](https://github.com/jch), [@rodzyn](https://github.com/rodzyn).
31
+ * Added support for mounting APIs and other Rack applications within APIs - [@mbleigh](https://github.com/mbleigh).
32
+ * Added entities, multiple object representations - [@mbleigh](https://github.com/mbleigh).
33
+ * Added ability to handle XML in the incoming request body - [@jwillis](https://github.com/jwillis).
34
+ * Added support for a configurable logger - [@mbleigh](https://github.com/mbleigh).
35
+ * Added support for before and after filters - [@mbleigh](https://github.com/mbleigh).
36
+ * Extended `rescue_from`, which can now take a block - [@dblock](https://github.com/dblock).
37
+
38
+
39
+ 0.1.5 (6/14/2011)
40
+ ==================
41
+
42
+ * Extended exception handling to all exceptions - [@dblock](https://github.com/dblock).
43
+ * Added support for returning JSON objects from within error blocks - [@dblock](https://github.com/dblock).
44
+ * Added support for handling incoming JSON in body - [@tedkulp](https://github.com/tedkulp).
45
+ * Added support for HTTP digest authentication - [@daddz](https://github.com/daddz).
46
+
47
+ 0.1.4 (4/8/2011)
48
+ ==================
49
+
50
+ * Allow multiple definitions of the same endpoint under multiple versions - [@chrisrhoden](https://github.com/chrisrhoden).
51
+ * Added support for multipart URL parameters - [@mcastilho](https://github.com/mcastilho).
52
+ * Added support for custom formatters - [@spraints](https://github.com/spraints).
53
+
54
+ 0.1.3 (1/10/2011)
55
+ ==================
56
+
57
+ * Added support for JSON format in route matching - [@aiwilliams](https://github.com/aiwilliams).
58
+ * Added suport for custom middleware - [@mbleigh](https://github.com/mbleigh).
59
+
60
+ 0.1.1 (11/14/2010)
61
+ ==================
62
+
63
+ * Endpoints properly reset between each request - [@mbleigh](https://github.com/mbleigh).
64
+
65
+ 0.1.0 (11/13/2010)
66
+ ==================
67
+
68
+ * Initial public release - [@mbleigh](https://github.com/mbleigh).
data/README.markdown CHANGED
@@ -77,9 +77,18 @@ class Twitter::API < Grape::API
77
77
  end
78
78
  ```
79
79
 
80
+ Optionally, you can define requirements for your named route parameters using regular expressions. The route will match only if
81
+ all requirements are met.
82
+
83
+ ```ruby
84
+ get '/show/:id', :requirements => { :id => /[0-9]*/ } do
85
+ Tweet.find(params[:id])
86
+ end
87
+ ```
88
+
80
89
  ## Mounting
81
90
 
82
- The above sample creates a Rack application that can be run from a rackup *config.ru* file
91
+ The above sample creates a Rack application that can be run from a rackup *config.ru* file
83
92
  with `rackup`:
84
93
 
85
94
  ``` ruby
@@ -110,12 +119,16 @@ end
110
119
 
111
120
  ## Versioning
112
121
 
113
- There are two stragies in which clients can reach your API's endpoints: `:header`
114
- and `:path`. The default strategy is `:header`.
122
+ There are three strategies in which clients can reach your API's endpoints: `:header`, `:path` and `:param`. The default strategy is `:header`.
123
+
115
124
 
116
- version 'v1', :using => :header
125
+ ### Header
126
+
127
+ ```ruby
128
+ version 'v1', :using => :header
129
+ ```
117
130
 
118
- Using this versioning strategy, clients should pass the desired version in the HTTP Accept head.
131
+ Using this versioning strategy, clients should pass the desired version in the HTTP Accept head.
119
132
 
120
133
  curl -H Accept=application/vnd.twitter-v1+json http://localhost:9292/statuses/public_timeline
121
134
 
@@ -124,23 +137,59 @@ supplied. This behavior is similar to routing in Rails. To circumvent this defau
124
137
  one could use the `:strict` option. When this option is set to `true`, a `404 Not found` error
125
138
  is returned when no correct Accept header is supplied.
126
139
 
127
- version 'v1', :using => :path
140
+ ### Path
141
+
142
+ ``` ruby
143
+ version 'v1', :using => :path
144
+ ```
128
145
 
129
146
  Using this versioning strategy, clients should pass the desired version in the URL.
130
147
 
131
148
  curl -H http://localhost:9292/v1/statuses/public_timeline
132
149
 
133
- Serialization takes place automatically.
150
+ Serialization takes place automatically.
151
+
152
+ ### Param
153
+
154
+ ```ruby
155
+ version 'v1', :using => :param
156
+ ```
157
+
158
+ Using this versioning strategy, clients should pass the desired version as a request parameter, either in the URL query string or in the request body.
159
+
160
+ curl -H http://localhost:9292/events?apiver=v1
161
+
162
+ The default name for the query parameter is 'apiver' but can be specified using the :parameter option.
163
+
164
+ ```ruby
165
+ version 'v1', :using => :param, :parameter => "v"
166
+ ```
167
+
168
+ curl -H http://localhost:9292/events?v=v1
134
169
 
135
170
  ## Parameters
136
171
 
137
- Parameters are available through the `params` hash object. This includes `GET` and `POST` parameters,
172
+ Parameters are available through the `params` hash object. This includes `GET` and `POST` parameters,
138
173
  along with any named parameters you specify in your route strings.
139
174
 
140
175
  ```ruby
141
- get do
176
+ get do
142
177
  Article.order(params[:sort_by])
143
- end
178
+ end
179
+ ```
180
+
181
+ Parameters are also populated from the request body on POST and PUT for JSON and XML content-types.
182
+
183
+ The Request:
184
+
185
+ ```curl -d '{"some_key": "some_value"}' 'http://localhost:9292/json_endpoint' -H Content-Type:application/json -v```
186
+
187
+ The Grape Endpoint:
188
+
189
+ ```ruby
190
+ post '/json_endpoint' do
191
+ params[:some_key]
192
+ end
144
193
  ```
145
194
 
146
195
  ## Headers
@@ -148,10 +197,10 @@ along with any named parameters you specify in your route strings.
148
197
  Headers are available through the `env` hash object.
149
198
 
150
199
  ```ruby
151
- get do
200
+ get do
152
201
  error! 'Unauthorized', 401 unless env['HTTP_SECRET_PASSWORD'] == 'swordfish'
153
202
  ...
154
- end
203
+ end
155
204
  ```
156
205
 
157
206
  ## Helpers
@@ -213,6 +262,19 @@ cookies[:counter] = {
213
262
  }
214
263
  cookies[:counter][:value] +=1
215
264
  ```
265
+ ## Redirect
266
+
267
+ You can redirect to a new url
268
+
269
+ ``` ruby
270
+ redirect "/new_url"
271
+ ```
272
+
273
+ use permanent redirect
274
+
275
+ ``` ruby
276
+ redirect "/new_url", :permanent => true
277
+ ```
216
278
 
217
279
  ## Raising Errors
218
280
 
@@ -292,6 +354,51 @@ class Twitter::API < Grape::API
292
354
  end
293
355
  ```
294
356
 
357
+ ## Logging
358
+
359
+ `Grape::API` provides a `logger` method which by default will return an instance of the `Logger`
360
+ class from Ruby's standard library.
361
+
362
+ To log messages from within an endpoint, you need to define a helper to make the logger
363
+ available in the endpoint context:
364
+
365
+ ``` ruby
366
+ class API < Grape::API
367
+ helpers do
368
+ def logger
369
+ API.logger
370
+ end
371
+ end
372
+ get '/hello' do
373
+ logger.info "someone said hello"
374
+ "hey there"
375
+ end
376
+ end
377
+ ```
378
+
379
+ You can also set your own logger:
380
+
381
+ ``` ruby
382
+ class MyLogger
383
+ def warning(message)
384
+ puts "this is a warning: #{message}"
385
+ end
386
+ end
387
+
388
+ class API < Grape::API
389
+ logger MyLogger.new
390
+ helpers do
391
+ def logger
392
+ API.logger
393
+ end
394
+ end
395
+ get '/hello' do
396
+ logger.warning "someone said hello"
397
+ "hey there"
398
+ end
399
+ end
400
+ ```
401
+
295
402
  ## Content-Types
296
403
 
297
404
  By default, Grape supports _XML_, _JSON_, _Atom_, _RSS_, and _text_ content-types.
@@ -319,9 +426,20 @@ class Twitter::API < Grape::API
319
426
  end
320
427
  ```
321
428
 
429
+ You can override the content-type by setting the `Content-Type` header.
430
+
431
+ ``` ruby
432
+ class API < Grape::API
433
+ get '/script' do
434
+ content_type "application/javascript"
435
+ "var x = 1;"
436
+ end
437
+ end
438
+ ```
439
+
322
440
  ## Writing Tests
323
441
 
324
- You can test a Grape API with RSpec by making HTTP requests and examining the response.
442
+ You can test a Grape API with RSpec by making HTTP requests and examining the response.
325
443
 
326
444
  ### Writing Tests with Rack
327
445
 
@@ -349,7 +467,7 @@ describe Twitter::API do
349
467
  it "returns a status by id" do
350
468
  status = Status.create!
351
469
  get "/api/v1/statuses/#{status.id}"
352
- last_resonse.body.should == status.to_json
470
+ last_response.body.should == status.to_json
353
471
  end
354
472
  end
355
473
  end
@@ -390,11 +508,122 @@ RSpec.configure do |config|
390
508
  end
391
509
  ```
392
510
 
511
+ ## Reusable Responses with Entities
512
+
513
+ Entities are a reusable means for converting Ruby objects to API responses.
514
+ Entities can be used to conditionally include fields, nest other entities, and build
515
+ ever larger responses, using inheritance.
516
+
517
+ ### Defining Entities
518
+
519
+ Entities inherit from Grape::Entity, and define a simple DSL. Exposures can use
520
+ runtime options to determine which fields should be visible, these options are
521
+ available to :if, :unless, and :proc. The option keys :version and :collection
522
+ will always be defined. The :version key is defined as api.version. The
523
+ :collection key is boolean, and defined as true if the object presented is an
524
+ array.
525
+
526
+ * `expose SYMBOLS`
527
+ * define a list of fields which will always be exposed
528
+ * `expose SYMBOLS, HASH`
529
+ * HASH keys include :if, :unless, :proc, :as, :using, :format_with, :documentation
530
+ * :if and :unless accept hashes (passed during runtime) or procs (arguments are object and options)
531
+ * `expose SYMBOL, {:format_with => :formatter}`
532
+ * expose a value, formatting it first
533
+ * :format_with can only be applied to one exposure at a time
534
+ * `expose SYMBOL, {:as => "alias"}`
535
+ * Expose a value, changing its hash key from SYMBOL to alias
536
+ * :as can only be applied to one exposure at a time
537
+ * `expose SYMBOL BLOCK`
538
+ * block arguments are object and options
539
+ * expose the value returned by the block
540
+ * block can only be applied to one exposure at a time
541
+
542
+ ``` ruby
543
+ module API
544
+ module Entities
545
+ class User < Grape::Entity
546
+ expose :first_name, :last_name
547
+ expose :field, :documentation => {:type => "string", :desc => "words go here"}
548
+ expose :email, :if => {:type => :full}
549
+ expose :user_type, user_id, :if => lambda{|user,options| user.confirmed?}
550
+ expose(:name){|user,options| [user.first_name, user.last_name].join(' ')}
551
+ expose :latest_status, :using => API::Status, :as => :status
552
+ end
553
+ end
554
+ end
555
+
556
+ module API
557
+ module Entities
558
+ class UserDetailed < API::Entities::User
559
+ expose :account_id
560
+ end
561
+ end
562
+ end
563
+ ```
564
+
565
+ ### Using Entities
566
+
567
+ Once an entity is defined, it can be used within endpoints, by calling #present. The #present
568
+ method accepts two arguments, the object to be presented and the options associated with it. The
569
+ options hash must always include :with, which defines the entity to expose.
570
+
571
+ If the entity includes documentation it can be included in an endpoint's description.
572
+
573
+ ``` ruby
574
+ module API
575
+ class Users < Grape::API
576
+ version 'v1'
577
+
578
+ desc 'User index', {
579
+ :object_fields => API::Entities::User.documentation
580
+ }
581
+ get '/users' do
582
+ @users = User.all
583
+ type = current_user.admin? ? :full : :default
584
+ present @users, with: API::Entities::User, :type => type
585
+ end
586
+ end
587
+ end
588
+ ```
589
+
590
+ ### Caveats
591
+
592
+ Entities with duplicate exposure names and conditions will silently overwrite one another.
593
+ In the following example, when object#check equals "foo", only afield will be exposed.
594
+ However, when object#check equals "bar" both bfield and foo will be exposed.
595
+
596
+ ```ruby
597
+ module API
598
+ module Entities
599
+ class User < Grape::Entity
600
+ expose :afield, :foo, :if => lambda{|object,options| object.check=="foo"}
601
+ expose :bfield, :foo, :if => lambda{|object,options| object.check=="bar"}
602
+ end
603
+ end
604
+ end
605
+ ```
606
+
607
+ This can be problematic, when you have mixed collections. Using #respond_to? is safer.
608
+
609
+ ```ruby
610
+ module API
611
+ module Entities
612
+ class User < Grape::Entity
613
+ expose :afield, :if => lambda{|object,options| object.check=="foo"}
614
+ expose :bfield, :if => lambda{|object,options| object.check=="bar"}
615
+ expose :foo, :if => lambda{object,options| object.respond_to?(:foo)}
616
+ end
617
+ end
618
+ end
619
+ ```
620
+
393
621
  ## Describing and Inspecting an API
394
622
 
395
623
  Grape lets you add a description to an API along with any other optional
396
624
  elements that can also be inspected at runtime.
397
- This can be useful for generating documentation.
625
+ This can be useful for generating documentation. If the response
626
+ requires documentation, consider using an entity.
398
627
 
399
628
  ``` ruby
400
629
  class TwitterAPI < Grape::API
@@ -432,13 +661,13 @@ Parameters can also be tagged to the method declaration itself.
432
661
 
433
662
  ``` ruby
434
663
  class StringAPI < Grape::API
435
- get "split/:string", { :params => [ "token" ], :optional_params => [ "limit" ] } do
664
+ get "split/:string", { :params => { "token" => "a token" }, :optional_params => { "limit" => "the limit" } } do
436
665
  params[:string].split(params[:token], (params[:limit] || 0))
437
666
  end
438
667
  end
439
668
 
440
- StringAPI::routes[0].route_params # yields an array [ "string", "token" ]
441
- StringAPI::routes[0].route_optional_params # yields an array [ "limit" ]
669
+ StringAPI::routes[0].route_params # yields a hash {"string" => "", "token" => "a token"}
670
+ StringAPI::routes[0].route_optional_params # yields a hash {"limit" => "the limit"}
442
671
  ```
443
672
 
444
673
  It's possible to retrieve the information about the current route from within an API call with `route`.
@@ -452,6 +681,33 @@ class MyAPI < Grape::API
452
681
  end
453
682
  ```
454
683
 
684
+ You can use this information to create a helper that will check if the request has
685
+ all required parameters:
686
+
687
+ ``` ruby
688
+ class MyAPI < Grape::API
689
+
690
+ helpers do
691
+ def validate_request!
692
+ # skip validation if no parameter is declared
693
+ return unless route.route_params
694
+ route.route_params.each do |k, v|
695
+ if !params.has_key? k
696
+ error!("Missing field: #{k}", 400)
697
+ end
698
+ end
699
+ end
700
+ end
701
+
702
+ before { validate_request! }
703
+
704
+ desc "creates a new item resource", :params => { :name => 'name is a required parameter' }
705
+ post :items do
706
+ ...
707
+ end
708
+ end
709
+ ```
710
+
455
711
  ## Anchoring
456
712
 
457
713
  Grape by default anchors all request paths, which means that the request URL