Motivation
I’ve been using Monk with the armonk skeleton for the last couple of weeks to build a little app that monitors Craig’s List searches. Bebop aside, I’ve found it to be a really refreshing experience, but I knew when I started building the app that the vanilla routing DSL wasn’t going to meet my needs (it wasn’t built too!). I also knew I would end up missing the path helpers, targeted filters, resource nesting, and printable routing information from Rails. So! after mocking up a simple DSL and consulting some friends I built and tested this little Sinatra routing extension and dubbed it Bebop in honor of Thelonius Monk.
Examples
Once registered Bebop provides the resource class method on Sinatra::Base as the starting point for building your routes.
require 'sinatra' require 'bebop' class MyApp < Sinatra::Base register Bebop resource :foos do |foos| # GET /foos/hello foos.get '/hello' do 'hello world' end # GET '/foos/goodbye' foos.get '/goodbye' do 'goodbye' end end end
Routing Helpers
As illustrated, Bebop can be used for nothing more than prefixing the routes generated by the normal dsl methods with a resource name (‘/foos’ in this case). It also provides route helper methods that build RESTful routes for the actions index, new, create, show, edit, update, and destroy.
class MyApp < Sinatra::Base register Bebop resource :foos do |foos| # Get /foos foos.index {} # GET /foos/:foo_id foos.show {} # GET /foos/new foos.new {} # POST /foos foos.create {} # PUT /foos/:foo_id foos.update {} # GET /foos/:foo_id/edit foos.edit {} # DELETE /foos/:foo_id foos.destroy {} end end
And if you want to nest your resources bebop handles prepending the routes correctly:
class MyApp < Sinatra::Base register Bebop resource :foos do |foos| foos.resource :bars do |bars| # Get /foos/:foos_id/bars foos.index {} # GET /foos/:foo_id/bars/:bar_id foos.show {} end end end
Extras
Bebop also extends some of the basic Sinatra functionality like before and after filters and provides new functionality in the form of path helper methods. Filters can target specific routes based on identifier, all routes, or nested resource routes. The only stipulation is that the filters must be defined _before_ the filtered route in the body of the resource block.
class MyApp < Sinatra::Base register Bebop resource :foos do |foos| # To run a filter before all routes # foos.before :all do @all = 'all' end # To have your filter run before a specific route, the route must be one of the seven helper # routes (see example/routes.rb) or specify the :identifier parameter # foos.before :new do @new = 'new' end foos.new do "#{@all} #{@new}" # => 'all new' end # You can target the vanila methods by providing the :identifier hash option # # GET /foos/baz foos.get '/baz', :identifier => :new do @new # => 'new' end # You can also specify a before filter for nested routes by using the child resource name # foos.before :bars do @bars = 'some bars' end foos.resource :bars do |bars| bars.get '/some_bars' do @bars # => 'some bars' end end foos.get '/bak' do @bars # => nil end # Finally you can specify many different methods in your filters by passing many identifiers # foos.before :bak, :baz do @bak_baz = "bak 'n' baz" end foos.get('/something', :identifier => :bak) { @bak_baz } foos.get('/anything', :identifier => :baz) { @bak_baz } end end
Path helpers follow a simple naming convention that starts with the original parent resource, all the child resources ordered by nesting, and finishing with the action identifier.
class MyApp < Sinatra::Base register Bebop resource :foos do |foos| foos.resource :bars do |bars| # GET /foos/:foo_id/bars/:bar_id/edit bars.edit do "foo: #{params[:foo_id]} bar: #{params[:bar_id]}" end bars.get '/redirect' do # Redirects to /foos/1/bars/2/edit redirect foos_bars_edit_path(1, 2) end end end end
Code
Most of the above examples are borrowed from the examples directory in the gem if you are interested. The repo can be found at github, and you can install the gem right now as long as gemcutter.org is one of your sources. As suggested by Damian Janowski I’ll be adding some template helpers and some other goodies soon.
As always feedback and suggestions are welcome!

