API building tools on top of ActionController. Also, an awesome name.
API building tools on top of ActionController. Also, an awesome name.
Please Note: Work on RocketPants 2.0 is currently underway on the 2.0-rewrite branch. Please check there before requesting features.
First thing's first, you're probably asking yourself - "Why the ridiculous name?". It's simple, really - RocketPants is memorable, and sounds completely bad ass. - everything a library needs.
At its core, RocketPants is a set of tools (built around existing toolsets such as ActionPack) to make it easier to build well-designed APIs in Ruby and more importantly, along side Rails. You can think of it like Grape, a fantastic library which RocketPants was originally inspired by but with deeper Rails and ActionPack integration.
Why use RocketPants over alternatives like Grape or normal Rails? The reasons we built it come down to a couple of simple things:
expose object_or_presenter in your controller and RocketPants will do it's best to send as much information back to the user.RocketPants::Client class which builds upon APISmith to make it easier to build clients e.g. automatically converting paginated responses back.Link: headers (it's even partly built-in for paginated data - see below), and request metadata (e.g. Object count, etc.) can easily be embedded in the headers of the response, making useful HEAD requests.ActiveRecord::RecordNotFound, ActiveRecord::RecordNotSaved and ActiveRecord::RecordInvalid for you, even including validation messages where possible.:serializer as expected (or :each_serializer) and we'll automatically take care of invoking it for you.Learn better by reading code? There is also have an example app mixing models and api clients over at Sutto/transperth-api that is built using RocketPants.
Say, for example, you have a basic Food model:
class Food 5.minutes
def index
expose Food.paginate(:page => params[:page])
end
def show
expose Food.find(params[:id])
end
endAnd in the router we'd just use the normal REST-like routes in Rails:
api :version => 1 do
resources :foods, :only => [:index, :show]
endAnd then, using this example, hitting GET http://localhost:3000/1/foods would result in:
{
"response": [{
"id": 1,
"name": "Delicious Food"
}, {
"id": 2,
"name": "More Delicious Food"
}],
"count": 2,
"pagination": {
"previous": null,
"next": null,
"current": 1,
"per_page": 10,
"count": 2,
"pages": 1
}
}with the Cache-Control header set whilst hitting GET http://localhost:3000/1/foods/1 would return:
{
"response": {
"id": 1,
"name": "Delicious Food"
}
}with the Etag header set.
If you want to enable JSONP support, it's as simple as calling jsonp in your class method:
class MyController :jsonp
endFinally, to disable it in a subclass, simple call jsonp in the child and pass :enable => false as an option.
When RocketPants.header_metadata or config.rocket_pants.header_metadata are set to true, RocketPants can automatically
expose metadata via X-Api- headers. Likewise, for paginated responses, if you implement page_url(page_number) in your controller
with header metadata enabled, RocketPants will automatically add HTTP Link Headers for the next, prev, first and last to your
response.
Likewise, you can manually add link headers using the link(rel, href, attributes = {}) method like so:
def index
# Not an actual rel, just an example...
link :profile, user_profile_path(current_user)
expose current_user
endFor batch adding links, you can use the links method:
def index
# Probably not the best example...
links :next => random_wallpaper_path, :prev => random_wallpaper_path
expose Wallpaper.random
endUsing the example above, we could then use the following to write a client:
class FoodsClient Food
end
def food(id)
get "foods/#{id}", :transformer => Food
end
endRocketPants builds upon the mixin-based approach to ActionController-based rails applications that Rails 3 made possible. Instead of including everything like Rails does in ActionController::Base, RocketPants only includes the bare minimum to make apis. In the near future, it may be modified to work with ActionController::Base for the purposes of better compatibility with other gems.
Out of the box, we use the following ActionController components:
ActionController::HideActions - Lets you hide methods from actions.ActionController::UrlFor - url_for helpers / tweaks by Rails to make integration with routes work better.ActionController::Redirecting - Allows you to use redirect_to.ActionController::ConditionalGet - Adds support for Rails caching controls, e.g. fresh_when and expires_in.ActionController::RackDelegation - Lets you reset the session and set the response body.ActionController::RecordIdentifier - Gives dom_class and dom_id methods, used for polymorphic routing.ActionController::HttpAuthentication Mixins - Gives Token, Digest and Basic authentication.AbstractController::Callbacks - Adds support for callbacks / filters.ActionController::Rescue - Lets you use rescue_from.And added our own:
RocketPants::UrlFor - Automatically includes the current version when generating URLs from the controller.RocketPants::Respondable - The core of RocketPants, the code that handles converting objects to the different container types.RocketPants::Versioning - Allows versioning requirements on the controller to ensure it is only callable with a specific api version.RocketPants::Instrumentation - Adds Instrumentation notifications making it easy to use and hook into with Rails.RocketPants::Caching - Implements time-based caching for index actions and etag-based efficient validation for singular resources.RocketPants::ErrorHandling - Short hand to create errors as well as simplifications to catch and render a standardised error representation.RocketPants::Rescuable - Allows you to hook in to rescuing exceptions and to make it easy to post notifications to tools such as Airbrake.RocketPants::StrongParameters - Adds support for strong parameters.To use RocketPants, instead of inheriting from ActionController::Base, just inherit from RocketPants::Base.
Likewise, in Rails applications RocketPants also adds RocketPants::CacheMiddleware before the controller endpoints to implement "Efficient Validation".
Installing RocketPants is a simple matter of adding:
gem 'rocket_pants', '~> 1.0'To your Gemfile and running bundle install. Next, instead of inheriting from ActionController::Base, simply inherit from RocketPants::Base. For example, if you're working with an API-only application, instead of having this at the top of application_controller.rb:
class ApplicationController 1 do
get 'x', :to => 'test#item' end
Which will route `GET /1/x` to `TestController#item`.
Likewise, you can specify a route for multiple versions by:
```ruby
api :versions => 1..3 do
get 'x', :to => 'test#item'
endUsing users an an example, for a namespaced / modularised version controller the file system location would be
app/controllers/api/v1/users_controller.rb - Rails uses it's own inflection to look for Api::V1::UsersController in that file.
In here, you'd write your control roughly like:
class Api::V1::UsersController 100Will look up the translation rocket_pants.errors.throttled in your I18N files, and call them with :max_per_hour as an argument.
Finally, You can use this to also pass custom values to include in the response, e.g:
error! :throttled, :metadata => {:code => 123}Will return something similar to:
{
"error": "throttled",
"error_description": "The example error message goes here",
"code": 123
}* Note that :invalid_resource (RocketPants::InvalidResource), although registered as a default RocketPants error, does not behave like other default registered errors. When using it, you must include an ActiveModel errors object, e.g.:
error!(:invalid_resource, post.errors)
# or
render_error RocketPants::InvalidResource.new(post.errors)
…
json
{
"error": "invalid_resource",
"error_description": "The current resource was deemed invalid.",
"messages": {
"name": ["can't be blank"],
"child_number":["can't be blank", "is not a number"],
"latin_name": ["is too short (minimum is 5 characters)", "is invalid"]
}
}We currently don't support mongoid / other ORMs in RocketPants, but you can map errors directly like so:
ApiController current cache key. RocketPants will then generate the etag when caching is enabled in the controller for singular-responses, generating an etag that can be quickly validated.
For example, you'd add the following to your model:
```ruby
class User 5.minutes
def index
expose User.all
end
def show
expose User.find(params[:id])
end
endWhen the user hits the index endpoint, i
No open issues yet, or sync has not completed.