title | layout | id |
---|---|---|
Sinatra: Writing Extensions |
default |
extensions |
Sinatra includes an API for extension authors to help ensure that consistent behavior is provided for application developers.
Some knowledge of Sinatra's internal design is required to write good extensions. This section provides a high level overview of the classes and idioms at the core of Sinatra's design.
Sinatra has two distinct modes of use:
-
The "Classic" style, where applications are defined on main / the top-level. Most of the examples and documentation target this usage. Classic applications are often single-file, standalone apps that are run directly from the command line or with a minimal rackup file.
-
The "Modular" style, where
Sinatra::Base
is subclassed explicitly and the application is defined within the subclass's scope. These applications are often bundled as libraries and used as components within a larger Rack-based system.
Most extensions are relevant to both styles. However, care must be taken to ensure that extensions do the right thing under each style. The extension API is provided to help extension authors with this task.
The Sinatra::Base
class provides the context for all evaluation in a
Sinatra application. Applications are defined within the class scope of a
Sinatra::Base
class. The "DSL" (e.g., get
, post
, before
,
configure
, set
, etc.) is simply a set of class methods defined on
Sinatra::Base
. Extending the DSL is achieved by adding class methods to
Sinatra::Base
.
Requests are evaluated within a new Sinatra::Base instance -- routes, before
filters, views, helpers, and error pages all share this same context. The
default set of request-level helper methods (e.g, erb
, haml
, halt
,
content_type
, etc.) are simple instance methods defined on Sinatra::Base
or within modules that are included in Sinatra::Base
. Providing new
functionality at the request level is achieved by adding instance methods to
Sinatra::Base
.
The Sinatra::Default
class provides the default execution context for
classic style applications. It is a simple subclass of Sinatra::Base
that provides default option values and other behavior tailored for
top-level apps. Extensions are free to modify this class directly when they
are loaded.
When a classic style application is run, a new Sinatra::Default
subclass
is created at Sinatra::Application
and the public class methods are
exported to the top-level.
-
Never modify
Sinatra::Base
directly. You should not include or extend, change option values, or modify its behavior elsewise. Modular style applications will include your extension in their subclass explicitly. -
Do modify
Sinatra::Default
when your extension is loaded. If the extension was required, it's safe to assume that the functionality it provides should be made available to classic style applications without any additional action on the part of the user. -
Never
require 'sinatra'
in your extension. You should only ever need torequire 'sinatra/base'
. (The reason for this is thatrequire 'sinatra'
is what triggers the classic style and extensions should never trigger the classic style.) -
Use the APIs described below where possible. You should not need to include or extend modules directly or define methods directly on a core Sinatra class. The specialized methods below handle all of that for you.
The most common type of extension is one that adds methods for use in routes, views, and helper methods.
For example, suppose you wanted to write an extension that added
an h
method that escaped reserved HTML characters (such as
those found in other popular Ruby web frameworks).
require 'sinatra/base'
module Sinatra
module HTMLEscapeExtension
def h(text)
Rake::Utils.escape_html(text)
end
end
helpers HTMLEscapeExtension
end
The call to Sinatra.helpers
includes the module in
Sinatra::Default
, making all methods defined in the module
available to classic style applications. Using this extension in
classic style apps is a simple as requiring the extension and
using the new method:
require 'sinatra'
require 'sinatra/htmlescape'
get "/hello" do
h "1 < 2" # => "1 < 2"
end
Sinatra::Base
subclasses must include the module explicitly:
require 'sinatra/base'
require 'sinatra/htmlescape'
class HelloApp < Sinatra::Base
helpers Sinatra::HTMLEscapeExtension
get "/hello" do
h "1 < 2"
end
end
Extensions can also extend Sinatra's class level DSL.
require 'sinatra/base'
module Sinatra
module DiggBlocker
def block_digg
before {
halt 403, "Go Away!" if request.referer == 'digg.com'
}
end
end
register DiggBlocker
end
The Sinatra.register
method adds all public methods in the module
specified as class methods on Sinatra::Default
. It also handles exporting
public methods to the top-level when classic style apps are executed.
A classic style application might use this extension as follows:
require 'sinatra'
require 'sinatra/diggblocker'
block_digg
get '/' do
"Hello World"
end
Modular style applications must register the extension explicitly in their
Sinatra::Base
subclasses:
require 'sinatra/base'
require 'sinatra/diggblocker'
class Hello < Sinatra::Base
register Sinatra::DiggBlocker
block_digg
get '/' do
"Hello World"
end
end
Sinatra extensions should be built as separate libraries
packaged as gems or as single files that can included within an
application's lib
directory. The ideal process for using an
extensions is installing a gem and requiring a single file.
The following is an example file layout for a typical extension packaged as a gem:
sinatra-fu
|-- README
|-- LICENSE
|-- Rakefile
|-- lib
| `-- sinatra
| `-- fu.rb
|-- test
| `-- spec_sinatra_fu.rb
`-- sinatra-fu.gemspec