Skip to content

Commit dfb1f24

Browse files
committed
Merge remote branch 'upstream/master'
2 parents 4d94477 + 03fe2d5 commit dfb1f24

File tree

19 files changed

+551
-102
lines changed

19 files changed

+551
-102
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ tmtags
1616

1717
## VIM
1818
*.swp
19+
*.swo
1920

2021
## RUBYMINE
2122
.idea

CHANGELOG.markdown

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,24 @@
1-
Next Release
2-
============
1+
0.2.2 (Next Release)
2+
====================
33

4-
* [#201](https://github.com/intridea/grape/pull/201): Added custom exceptions to Grape. Updated validations to use ValidationError that can be rescued. - [@adamgotterer](https://github.com/adamgotterer).
5-
* [#211](https://github.com/intridea/grape/pull/211): Updates to validation and coercion: Fix #211 and force order of operations for presence and coercion - [@adamgotterer](https://github.com/adamgotterer).
4+
Features
5+
--------
6+
7+
* [#201](https://github.com/intridea/grape/pull/201), [#236](https://github.com/intridea/grape/pull/236), [#221](https://github.com/intridea/grape/pull/221): Added coercion and validations support to `params` DSL - [@schmurfy](https://github.com/schmurfy), [@tim-vandecasteele](https://github.com/tim-vandecasteele), [@adamgotterer](https://github.com/adamgotterer).
8+
* [#204](https://github.com/intridea/grape/pull/204): Added ability to declare shared `params` at `namespace` level - [@tim-vandecasteele](https://github.com/tim-vandecasteele).
9+
* [#234](https://github.com/intridea/grape/pull/234): Added a DSL for creating entities via mixin - [@mbleigh](https://github.com/mbleigh).
10+
* [#240](https://github.com/intridea/grape/pull/240): Define API response format from a query string `format` parameter, if specified - [@neetiraj](https://github.com/neetiraj).
11+
12+
Fixes
13+
-----
14+
15+
* [#248](https://github.com/intridea/grape/pull/248): Fix: API `version` returns last version set - [@narkoz](https://github.com/narkoz).
16+
* [#242](https://github.com/intridea/grape/issues/242): Fix: permanent redirect status should be `301`, was `304` - [@adamgotterer](https://github.com/adamgotterer).
17+
* [#211](https://github.com/intridea/grape/pull/211): Fix: custom validations are no longer triggered when optional and parameter is not present - [@adamgotterer](https://github.com/adamgotterer).
618
* [#210](https://github.com/intridea/grape/pull/210): Fix: `Endpoint#body_params` causing undefined method 'size' - [@adamgotterer](https://github.com/adamgotterer).
7-
* [#201](https://github.com/intridea/grape/pull/201): Rewritten `params` DSL, including support for coercion and validations - [@schmurfy](https://github.com/schmurfy).
819
* [#205](https://github.com/intridea/grape/pull/205): Fix: Corrected parsing of empty JSON body on POST/PUT - [@tim-vandecasteele](https://github.com/tim-vandecasteele).
920
* [#181](https://github.com/intridea/grape/pull/181): Fix: Corrected JSON serialization of nested hashes containing `Grape::Entity` instances - [@benrosenblum](https://github.com/benrosenblum).
1021
* [#203](https://github.com/intridea/grape/pull/203): Added a check to `Entity#serializable_hash` that verifies an entity exists on an object - [@adamgotterer](https://github.com/adamgotterer).
11-
* [#204](https://github.com/intridea/grape/pull/204): Added ability to declare shared parameters at namespace level - [@tim-vandecasteele](https://github.com/tim-vandecasteele).
1222
* [#208](https://github.com/intridea/grape/pull/208): `Entity#serializable_hash` must also check if attribute is generated by a user supplied block - [@ppadron](https://github.com/ppadron).
1323

1424
0.2.1 (7/11/2012)

README.markdown

Lines changed: 119 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22

33
## What is Grape?
44

5-
Grape is a REST-like API micro-framework for Ruby. It's built to complement
6-
existing web application frameworks such as Rails and Sinatra by providing a
7-
simple DSL to easily develop RESTful APIs. It has built-in support for common
8-
conventions, including multiple formats, subdomain/prefix restriction, content
9-
negotiation, versioning and much more.
5+
Grape is a REST-like API micro-framework for Ruby. It's designed to run on Rack
6+
or complement existing web application frameworks such as Rails and Sinatra by
7+
providing a simple DSL to easily develop RESTful APIs. It has built-in support
8+
for common conventions, including multiple formats, subdomain/prefix restriction,
9+
content negotiation, versioning and much more.
1010

1111
[![Build Status](http://travis-ci.org/intridea/grape.png?branch=master)](http://travis-ci.org/intridea/grape)
1212

@@ -41,6 +41,7 @@ the context of recreating parts of the Twitter API.
4141
``` ruby
4242
class Twitter::API < Grape::API
4343
version 'v1', :using => :header, :vendor => 'twitter'
44+
format :json
4445

4546
helpers do
4647
def current_user
@@ -214,12 +215,17 @@ end
214215

215216
## Parameter Validation and Coercion
216217

217-
You can define validations and coercion options for your parameters using `params`.
218+
You can define validations and coercion options for your parameters using a `params` block.
218219

219220
```ruby
220221
params do
221222
requires :id, type: Integer
222223
optional :name, type: String, regexp: /^[a-z]+$/
224+
225+
group :user do
226+
requires :first_name
227+
requires :last_name
228+
end
223229
end
224230
get ':id' do
225231
# params[:id] is an Integer
@@ -229,6 +235,9 @@ end
229235
When a type is specified an implicit validation is done after the coercion to ensure
230236
the output type is the one declared.
231237

238+
Parameters can be nested using `group`. In the above example, this means both
239+
`params[:user][:first_name]` and `params[:user][:last_name]` are required next to `params[:id]`.
240+
232241
### Namespace Validation and Coercion
233242
Namespaces allow parameter definitions and apply to every method within the namespace.
234243

@@ -252,22 +261,23 @@ end
252261

253262
### Custom Validators
254263
```ruby
255-
class doit < Grape::Validations::Validator
264+
class AlphaNumeric < Grape::Validations::Validator
256265
def validate_param!(attr_name, params)
257-
unless params[attr_name] == 'im custom'
258-
throw :error, :status => 400, :message => "#{attr_name}: is not custom!"
266+
unless params[attr_name] =~ /^[[:alnum:]]+$/
267+
throw :error, :status => 400, :message => "#{attr_name}: must consist of alpha-numeric characters"
259268
end
260269
end
261270
end
262271
```
263272

264273
```ruby
265274
params do
266-
requires :name, :doit => true
275+
requires :username, :alpha_numeric => true
267276
end
268277
```
269278

270-
You can also create custom classes that take additional parameters
279+
You can also create custom classes that take parameters.
280+
271281
```ruby
272282
class Length < Grape::Validations::SingleOptionValidator
273283
def validate_param!(attr_name, params)
@@ -284,7 +294,21 @@ params do
284294
end
285295
```
286296

297+
### Validation Errors
298+
299+
When validation and coercion erros occur an exception of type `ValidationError` is raised.
300+
If the exception goes uncaught it will respond with a status of 400 and an error message.
301+
You can rescue a `ValidationError` and respond with a custom response.
287302

303+
```ruby
304+
rescue_from ValidationError do |e|
305+
Rack::Response.new({
306+
'status' => e.status,
307+
'message' => e.message,
308+
'param' => e.param
309+
}.to_json, e.status)
310+
end
311+
```
288312

289313
## Headers
290314

@@ -318,7 +342,7 @@ end
318342
## Helpers
319343

320344
You can define helper methods that your endpoints can use with the `helpers`
321-
macro by either giving a block or a module:
345+
macro by either giving a block or a module.
322346

323347
``` ruby
324348
module MyHelpers
@@ -347,7 +371,7 @@ end
347371

348372
## Cookies
349373

350-
You can set, get and delete your cookies very simply using `cookies` method:
374+
You can set, get and delete your cookies very simply using `cookies` method.
351375

352376
``` ruby
353377
class API < Grape::API
@@ -363,7 +387,7 @@ class API < Grape::API
363387
end
364388
```
365389

366-
To set more than value use hash-based syntax:
390+
To set more than value use hash-based syntax.
367391

368392
``` ruby
369393
cookies[:counter] = {
@@ -377,7 +401,7 @@ cookies[:counter][:value] +=1
377401

378402
## Redirecting
379403

380-
You can redirect to a new url temporarily or permanently.
404+
You can redirect to a new url temporarily (302) or permanently (301).
381405

382406
``` ruby
383407
redirect "/new_url"
@@ -405,7 +429,7 @@ error!({ "error" => "unexpected error", "detail" => "missing widget" }, 500)
405429
## Exception Handling
406430

407431
Grape can be told to rescue all exceptions and instead return them in
408-
text or json formats.
432+
txt or json formats.
409433

410434
``` ruby
411435
class Twitter::API < Grape::API
@@ -516,7 +540,7 @@ By default, Grape supports _XML_, _JSON_, _Atom_, _RSS_, and _text_ content-type
516540
Serialization takes place automatically.
517541

518542
Your API can declare additional types to support. Response format is determined by the
519-
request's extension or `Accept` header.
543+
request's extension, an explicit `format` parameter in the query string, or `Accept` header.
520544

521545
``` ruby
522546
class Twitter::API < Grape::API
@@ -527,7 +551,8 @@ end
527551
You can also set the default format. The order for choosing the format is the following.
528552

529553
* Use the file extension, if specified. If the file is .json, choose the JSON format.
530-
* Use the format, if specified by the `format` option.
554+
* Use the value of the `format` parameter in the query string, if specified.
555+
* Use the format set by the `format` option, if specified.
531556
* Attempt to find an acceptable format from the `Accept` header.
532557
* Use the default format, if specified by the `default_format` option.
533558
* Default to `:txt` otherwise.
@@ -560,22 +585,22 @@ ever larger responses, using inheritance.
560585

561586
Entities inherit from Grape::Entity, and define a simple DSL. Exposures can use
562587
runtime options to determine which fields should be visible, these options are
563-
available to :if, :unless, and :proc. The option keys :version and :collection
564-
will always be defined. The :version key is defined as api.version. The
565-
:collection key is boolean, and defined as true if the object presented is an
588+
available to `:if`, `:unless`, and `:proc`. The option keys `:version` and `:collection`
589+
will always be defined. The `:version` key is defined as `api.version`. The
590+
`:collection` key is boolean, and defined as `true` if the object presented is an
566591
array.
567592

568593
* `expose SYMBOLS`
569594
* define a list of fields which will always be exposed
570595
* `expose SYMBOLS, HASH`
571-
* HASH keys include :if, :unless, :proc, :as, :using, :format_with, :documentation
572-
* :if and :unless accept hashes (passed during runtime) or procs (arguments are object and options)
573-
* `expose SYMBOL, {:format_with => :formatter}`
596+
* HASH keys include `:if`, `:unless`, `:proc`, `:as`, `:using`, `:format_with`, `:documentation`
597+
* `:if` and `:unless` accept hashes (passed during runtime) or procs (arguments are object and options)
598+
* `expose SYMBOL, { :format_with => :formatter }`
574599
* expose a value, formatting it first
575-
* :format_with can only be applied to one exposure at a time
576-
* `expose SYMBOL, {:as => "alias"}`
600+
* `:format_with` can only be applied to one exposure at a time
601+
* `expose SYMBOL, { :as => "alias" }`
577602
* Expose a value, changing its hash key from SYMBOL to alias
578-
* :as can only be applied to one exposure at a time
603+
* `:as` can only be applied to one exposure at a time
579604
* `expose SYMBOL BLOCK`
580605
* block arguments are object and options
581606
* expose the value returned by the block
@@ -586,10 +611,10 @@ module API
586611
module Entities
587612
class User < Grape::Entity
588613
expose :first_name, :last_name
589-
expose :field, :documentation => {:type => "string", :desc => "words go here"}
590-
expose :email, :if => {:type => :full}
591-
expose :user_type, user_id, :if => lambda{|user,options| user.confirmed?}
592-
expose(:name){|user,options| [user.first_name, user.last_name].join(' ')}
614+
expose :field, :documentation => { :type => "string", :desc => "words go here" }
615+
expose :email, :if => { :type => :full }
616+
expose :user_type, user_id, :if => lambda{ |user,options| user.confirmed? }
617+
expose(:name) { |user,options| [ user.first_name, user.last_name ].join(' ')}
593618
expose :latest_status, :using => API::Status, :as => :status
594619
end
595620
end
@@ -604,11 +629,32 @@ module API
604629
end
605630
```
606631

632+
#### Using the Exposure DSL
633+
634+
Grape ships with a DSL to easily define entities within the context
635+
of an existing class:
636+
637+
```ruby
638+
class User
639+
include Grape::Entity::DSL
640+
641+
entity :name, :email do
642+
expose :advanced, if: :conditional
643+
end
644+
end
645+
```
646+
647+
The above will automatically create a `User::Entity` class and
648+
define properties on it according to the same rules as above. If
649+
you only want to define simple exposures you don't have to supply
650+
a block and can instead simply supply a list of comma-separated
651+
symbols.
652+
607653
### Using Entities
608654

609-
Once an entity is defined, it can be used within endpoints, by calling #present. The #present
655+
Once an entity is defined, it can be used within endpoints, by calling `present`. The `present`
610656
method accepts two arguments, the object to be presented and the options associated with it. The
611-
options hash must always include :with, which defines the entity to expose.
657+
options hash must always include `:with`, which defines the entity to expose.
612658

613659
If the entity includes documentation it can be included in an endpoint's description.
614660

@@ -629,32 +675,58 @@ module API
629675
end
630676
```
631677

678+
### Entity Organization
679+
680+
In addition to separately organizing entities, it may be useful to
681+
put them as namespaced classes underneath the model they represent.
682+
For example:
683+
684+
```ruby
685+
class User
686+
def entity
687+
Entity.new(self)
688+
end
689+
690+
class Entity < Grape::Entity
691+
expose :name, :email
692+
end
693+
end
694+
```
695+
696+
If you organize your entities this way, Grape will automatically
697+
detect the `Entity` class and use it to present your models. In
698+
this example, if you added `present User.new` to your endpoint,
699+
Grape would automatically detect that there is a `User::Entity`
700+
class and use that as the representative entity. This can still
701+
be overridden by using the `:with` option or an explicit
702+
`represents` call.
703+
632704
### Caveats
633705

634706
Entities with duplicate exposure names and conditions will silently overwrite one another.
635-
In the following example, when object#check equals "foo", only afield will be exposed.
636-
However, when object#check equals "bar" both bfield and foo will be exposed.
707+
In the following example, when `object.check` equals "foo", only `field_a` will be exposed.
708+
However, when `object.check` equals "bar" both `field_b` and `foo` will be exposed.
637709

638710
```ruby
639711
module API
640712
module Entities
641713
class User < Grape::Entity
642-
expose :afield, :foo, :if => lambda{|object,options| object.check=="foo"}
643-
expose :bfield, :foo, :if => lambda{|object,options| object.check=="bar"}
714+
expose :field_a, :foo, :if => lambda { |object, options| object.check == "foo" }
715+
expose :field_b, :foo, :if => lambda { |object, options| object.check == "bar" }
644716
end
645717
end
646718
end
647719
```
648720

649-
This can be problematic, when you have mixed collections. Using #respond_to? is safer.
721+
This can be problematic, when you have mixed collections. Using `respond_to?` is safer.
650722

651723
```ruby
652724
module API
653725
module Entities
654726
class User < Grape::Entity
655-
expose :afield, :if => lambda{|object,options| object.check=="foo"}
656-
expose :bfield, :if => lambda{|object,options| object.check=="bar"}
657-
expose :foo, :if => lambda{object,options| object.respond_to?(:foo)}
727+
expose :field_a, :if => lambda { |object, options| object.check == "foo" }
728+
expose :field_b, :if => lambda { |object, options| object.check == "bar" }
729+
expose :foo, :if => lambda { |object, options| object.respond_to?(:foo) }
658730
end
659731
end
660732
end
@@ -793,19 +865,21 @@ RSpec.configure do |config|
793865
end
794866
```
795867

796-
## Note on Patches/Pull Requests
868+
## Contributing to Grape
869+
870+
Grape is work of dozens of contributors. You're encouraged to submit pull requests, propose
871+
features and discuss issues.
797872

798873
* Fork the project
799874
* Write tests for your new feature or a test that reproduces a bug
800875
* Implement your feature or make a bug fix
801876
* Do not mess with Rakefile, version or history
802-
* Commit, push and make a pull request. Bonus points for topical branches.
877+
* Commit, push and make a pull request. Bonus points for topic branches.
803878

804879
## License
805880

806881
MIT License. See LICENSE for details.
807882

808883
## Copyright
809884

810-
Copyright (c) 2010-2012 Michael Bleigh and Intridea, Inc.
811-
885+
Copyright (c) 2010-2012 Michael Bleigh, and Intridea, Inc.

grape.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ Gem::Specification.new do |s|
1717
s.add_runtime_dependency 'rack'
1818
s.add_runtime_dependency 'rack-mount'
1919
# s.add_runtime_dependency 'rack-jsonp'
20-
s.add_runtime_dependency 'multi_json'
20+
s.add_runtime_dependency 'multi_json', '>= 1.3.2'
2121
s.add_runtime_dependency 'multi_xml'
2222
s.add_runtime_dependency 'hashie'
2323
s.add_runtime_dependency 'virtus'

0 commit comments

Comments
 (0)