= Sinatra
<i>Atención: Este documento es una traducción de la versión en inglés y puede estar desactualizado.</i>

Sinatra es un DSL para crear aplicaciones web rápidamente en Ruby con un mínimo
esfuerzo:

  # miapp.rb
  require 'sinatra'

  get '/' do
    'Hola mundo!'
  end

Instalá la gem y ejecutá la aplicación con:

  gem install sinatra
  ruby -rubygems miapp.rb

Podés verla en: http://localhost:4567

Es recomendable además ejecutar <tt>gem install thin</tt>, ya que Sinatra lo va
a utilizar cuando esté disponible.

== Rutas

En Sinatra, una ruta está compuesta por un método HTTP y un patrón de una URL.
Cada ruta se asocia con un bloque:

  get '/' do
    .. mostrar algo ..
  end

  post '/' do
    .. crear algo ..
  end

  put '/' do
    .. actualizar algo ..
  end

  delete '/' do
    .. aniquilar algo ..
  end

  options '/' do
    .. informar algo ..
  end


Las rutas son comparadas en el orden en el que son definidas. La primer ruta
que coincide con la petición es invocada.

Los patrones de las rutas pueden incluir parámetros nombrados, accesibles a
través de el hash <tt>params</tt>:

  get '/hola/:nombre' do
    # coincide con "GET /hola/foo" y "GET /hola/bar"
    # params[:nombre] es 'foo' o 'bar'
    "Hola #{params[:nombre]}!"
  end

También podés acceder a los parámetros nombrados usando parámetros de bloque:

  get '/hola/:nombre' do |n|
    "Hola #{n}!"
  end

Los patrones de ruta también pueden incluir parámetros splat (o wildcard),
accesibles a través del arreglo <tt>params[:splat]</tt>:

  get '/decir/*/al/*' do
    # coincide con /decir/hola/al/mundo
    params[:splat] # => ["hola", "mundo"]
  end

  get '/descargar/*.*' do
    # coincide con /descargar/path/al/archivo.xml
    params[:splat] # => ["path/al/archivo", "xml"]
  end

Rutas con Expresiones Regulares:

  get %r{/hola/([\w]+)} do
    "Hola, #{params[:captures].first}!"
  end

O con un parámetro de bloque:

  get %r{/hola/([\w]+)} do |c|
    "Hola, #{c}!"
  end

=== Condiciones

Las rutas pueden incluir una variedad de condiciones de selección, como por
ejemplo el user agent:

  get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
    "Estás usando la versión de Songbird #{params[:agent][0]}"
  end

  get '/foo' do
    # Coincide con browsers que no sean songbird
  end

Otras condiciones disponibles son +host_name+ y +provides+:

  get '/', :host_name => /^admin\./ do
    "Área de Administración, Acceso denegado!"
  end

  get '/', :provides => 'html' do
    haml :index
  end

  get '/', :provides => ['rss', 'atom', 'xml'] do
    builder :feed
  end

Podés definir tus propias condiciones fácilmente:

  set(:probabilidad) { |valor| condition { rand <= valor } }

  get '/gana_un_auto', :probabilidad => 0.1 do
    "Ganaste!"
  end

  get '/gana_un_auto' do
    "Lo siento, perdiste."
  end

=== Valores de Retorno

El valor de retorno de un bloque de ruta determina al menos el cuerpo de la
respuesta que se le pasa al cliente HTTP o al siguiente middleware en la pila
de Rack. Lo más común es que sea un string, como en los ejemplos anteriores.
Sin embargo, otros valor también son aceptados.

Podés devolver cualquier objeto que sea una respuesta Rack válida, un objeto
que represente el cuerpo de una respuesta Rack o un código de estado HTTP:

* Un arreglo con tres elementos: <tt>[estado (Fixnum), cabeceras (Hash), cuerpo de la respuesta (responde a #each)]</tt>
* Un arreglo con dos elementos: <tt>[estado (Fixnum), cuerpo de la respuesta (responde a #each)]</tt>
* Un objeto que responde a <tt>#each</tt> y que le pasa únicamente strings al bloque dado
* Un Fixnum representando el código de estado

De esa manera podemos, por ejemplo, implementar fácilmente un streaming:

    class Stream
      def each
        100.times { |i| yield "#{i}\n" }
      end
    end

    get('/') { Stream.new }

=== Comparadores de Rutas Personalizados

Como se mostró anteriormente, Sinatra permite utilizar Strings y expresiones
regulares para definir las rutas.  Sin embargo, la cosa no termina ahí.  Podés
definir tus propios comparadores muy fácilmente:

  class PattronCualquieraMenos
    Match = Struct.new(:captures)

    def initialize(excepto)
      @excepto  = excepto
      @capturas = Match.new([])
    end

    def match(str)
      @capturas unless @excepto === str
    end
  end

  def cualquiera_menos(patron)
    PatronCualquieraMenos.new(patron)
  end

  get cualquiera_menos("/index") do
    # ...
  end

Tené en cuenta que el ejemplo anterior es un poco rebuscado.  Un resultado
similar puede conseguirse más sencillamente:

  get // do
    pass if request.path_info == "/index"
    # ...
  end

O, usando un lookahead negativo:

  get %r{^(?!/index$)} do
    # ...
  end

== Archivos Estáticos

Los archivos estáticos son servidos desde el directorio público
<tt>./public</tt>. Podés especificar una ubicación diferente ajustando la
opción <tt>:public</tt>:

  set :public, File.dirname(__FILE__) + '/estaticos'

Notá que el nombre del directorio público no está incluido en la URL. Por
ejemplo, el archivo <tt>./public/css/style.css</tt> se accede a través de
<tt>http://ejemplo.com/css/style.css</tt>.

== Vistas / Plantillas

Se asume que las plantillas están ubicadas directamente bajo el directorio
<tt>./views</tt>. Para usar un directorio de vistas diferente:

  set :views, File.dirname(__FILE__) + '/plantillas'

Es importante acordarse que siempre tenés que referenciar a las plantillas con
símbolos, incluso cuando se encuentran en un subdirectorio (en este caso tenés
que usar <tt>:'subdir/plantilla'</tt>). Tenés que usar un símbolo porque los
métodos de renderización van a renderizar directamente cualquier string que se
les pase como argumento.

=== Plantillas Haml

La gem/librería <tt>haml</tt> es necesaria para para renderizar plantillas HAML:

  # Vas a necesitar requerir haml en tu app
  require 'haml'

  get '/' do
    haml :index
  end

Renderiza <tt>./views/index.haml</tt>.

Las {opciones de Haml}[http://haml-lang.com/docs/yardoc/file.HAML_REFERENCE.html#options]
pueden ser ajustadas globalmente a través de las configuraciones de Sinatra,
ver {Opciones y Configuraciones}[http://www.sinatrarb.com/configuration.html],
y reemplazadas individualmente.

  set :haml, :format => :html5 # el formato por defecto de Haml es :xhtml

  get '/' do
    haml :index, :format => :html4 # reemplazado
  end

=== Plantillas Erb

  # Vas a necesitar requerir erb en tu app
  require 'erb'

  get '/' do
    erb :index
  end

Renderiza <tt>./views/index.erb</tt>

=== Plantillas Erubis

La gem/librería <tt>erubis</tt> es necesaria para renderizar plantillas Erubis:

  # Vas a necesitar requerir erubis en tu app
  require 'erubis'

  get '/' do
    erubis :index
  end

Renderiza <tt>./views/index.erubis</tt>.

También es posible reemplazar Erb con Erubis:

  require 'erubis'
  Tilt.register :erb, Tilt[:erubis]

  get '/' do
    erb :index
  end

Renderiza <tt>./views/index.erb</tt> con Erubis.

=== Plantillas Builder

La gem/librería <tt>builder</tt> es necesaria para renderizar plantillas
builder:

  # Vas a necesitar requerir builder en tu app
  require 'builder'

  get '/' do
    builder :index
  end

Renderiza <tt>./views/index.builder</tt>.

=== Plantillas Nokogiri

La gem/librería <tt>nokogiri</tt> es necesaria para renderizar plantillas
nokogiri:

  # Vas a necesitar requerir nokogiri en tu app
  require 'nokogiri'

  get '/' do
    nokogiri :index
  end

Renderiza <tt>./views/index.nokogiri</tt>.

=== Plantillas Sass

La gem/librería <tt>haml</tt> o <tt>sass</tt> es necesaria para renderizar
plantillas Sass:

  # Vas a necesitar requerir haml o sass en tu app
  require 'sass'

  get '/stylesheet.css' do
    sass :stylesheet
  end

Renderiza <tt>./views/stylesheet.sass</tt>.

Las {opciones de Sass}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
pueden ser ajustadas globalmente a través de las configuraciones de Sinatra,
ver {Opciones y Configuraciones}[http://www.sinatrarb.com/configuration.html],
y reemplazadas individualmente.

  set :sass, :style => :compact # el estilo por defecto de Sass es :nested

  get '/stylesheet.css' do
    sass :stylesheet, :style => :expanded # reemplazado
  end

=== Plantillas Scss

La gem/librería <tt>haml</tt> o <tt>sass</tt>es necesaria para renderizar
plantillas Scss:

  # Vas a necesitar requerir haml o sass en tu app
  require 'sass'

  get '/stylesheet.css' do
    scss :stylesheet
  end

Renderiza <tt>./views/stylesheet.scss</tt>.

Las {opciones de Scss}[http://sass-lang.com/docs/yardoc/file.SASS_REFERENCE.html#options]
pueden ser ajustadas globalmente a través de las configuraciones de Sinatra,
ver {Opciones y Configuraciones}[http://www.sinatrarb.com/configuration.html],
y reemplazadas individualmente.

  set :scss, :style => :compact # el estilo por defecto de Sass es :nested

  get '/stylesheet.css' do
    scss :stylesheet, :style => :expanded # reemplazado
  end

=== Plantillas Less

La gem/librería <tt>less</tt> es necesaria para renderizar plantillas Less:

  # Vas a necesitar requerir less en tu app
  require 'less'

  get '/stylesheet.css' do
    less :stylesheet
  end

Renderiza <tt>./views/stylesheet.less</tt>.

=== Plantillas Liquid

La gem/librería <tt>liquid</tt> es necesaria para renderizar plantillas Liquid:

  # Vas a necesitar requerir liquid en tu app
  require 'liquid'

  get '/' do
    liquid :index
  end

Renderiza <tt>./views/index.liquid</tt>.

Como no vas a poder llamar a métodos de Ruby (excepto a +yield+) desde una
plantilla Liquid, casi siempre vas a querer pasarle locales:

  liquid :index, :locals => { :clave => 'valor' }

=== Plantillas Markdown

La gem/librería <tt>rdiscount</tt> es necesaria para renderizar plantillas
Markdown:

  # Vas a necesitar requerir rdiscount en tu app
  require "rdiscount"

  get '/' do
    markdown :index
  end

Renderiza <tt>./views/index.markdown</tt> (+md+ y +mkd+ también son extensiones
de archivo válidas).

No es posible llamar métodos desde markdown, ni pasarle locales. Por lo tanto,
generalmente vas a usarlo en combinación con otro motor de renderizado:

  erb :resumen, :locals => { :texto => markdown(:introduccion) }

Tené en cuenta que también podés llamar al método +markdown+ desde otras
plantillas:

  %h1 Hola Desde Haml!
  %p= markdown(:saludos)

Como no podés utilizar Ruby desde Markdown, no podés usar layouts escritos en
Markdown. De todos modos, es posible usar un motor de renderizado para el
layout distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>:

  get '/' do
    markdown :index, :layout_engine => :erb
  end

Renderiza <tt>./views/index.md</tt> con el layout <tt>./views/layout.erb</tt>.

Recordá que podés asignar las opciones de renderizado globalmente:

  set :markdown, :layout_engine => :haml, :layout => :post

  get '/' do
    markdown :index
  end

Renderiza <tt>./views/index.md</tt> (o cualquier otra plantilla Markdown) con
el layout <tt>./views/post.haml</tt>.

También es posible parsear Markdown con BlueCloth en lugar de RDiscount:

  require 'bluecloth'

  Tilt.register 'markdown', BlueClothTemplate
  Tilt.register 'mkd',      BlueClothTemplate
  Tilt.register 'md',       BlueClothTemplate

  get '/' do
    markdown :index
  end

Renderiza <tt>./views/index.md</tt> con BlueCloth.

=== Plantillas Textile

La gem/librería <tt>RedCloth</tt> es necesaria para renderizar plantillas
Textile:

  # Vas a necesitar requerir redcloth en tu app
  require "redcloth"

  get '/' do
    textile :index
  end

Renderiza <tt>./views/index.textile</tt>.

No es posible llamar métodos desde textile, ni pasarle locales. Por lo tanto,
generalmente vas a usarlo en combinación con otro motor de renderizado:

  erb :resumen, :locals => { :texto => textile(:introduccion) }

Tené en cuenta que también podés llamar al método +textile+ desde otras
plantillas:

  %h1 Hola Desde Haml!
  %p= textile(:saludos)

Como no podés utilizar Ruby desde Textile, no podés usar layouts escritos en
Textile. De todos modos, es posible usar un motor de renderizado para el
layout distinto al de la plantilla pasando la opción <tt>:layout_engine</tt>:

  get '/' do
    textile :index, :layout_engine => :erb
  end

Renderiza <tt>./views/index.textile</tt> con el layout
<tt>./views/layout.erb</tt>.

Recordá que podés asignar las opciones de renderizado globalmente:

  set :textile, :layout_engine => :haml, :layout => :post

  get '/' do
    textile :index
  end

Renderiza <tt>./views/index.textile</tt> (o cualquier otra plantilla Textile)
con el layout <tt>./views/post.haml</tt>.

=== Plantillas RDoc

La gem/librería <tt>rdoc</tt> es necesaria para renderizar plantillas RDoc:

  # Vas a necesitar requerir rdoc/markup/to_html en tu app
  require "rdoc/markup/to_html"

  get '/' do
    rdoc :index
  end

Renderiza <tt>./views/index.rdoc</tt>.

No es posible llamar métodos desde rdoc, ni pasarle locales. Por lo tanto,
generalmente vas a usarlo en combinación con otro motor de renderizado:

  erb :resumen, :locals => { :texto => rdoc(:introduccion) }

Tené en cuenta que también podés llamar al método +rdoc+ desde otras
plantillas:

  %h1 Hola Desde Haml!
  %p= rdoc(:saludos)

Como no podés utilizar Ruby desde RDoc, no podés usar layouts escritos en RDoc.
De todos modos, es posible usar un motor de renderizado para el layout distinto
al de la plantilla pasando la opción <tt>:layout_engine</tt>:

  get '/' do
    rdoc :index, :layout_engine => :erb
  end

Renderiza <tt>./views/index.rdoc</tt> con el layout <tt>./views/layout.erb</tt>.

Recordá que podés asignar las opciones de renderizado globalmente:

  set :rdoc, :layout_engine => :haml, :layout => :post

  get '/' do
    rdoc :index
  end

Renderiza <tt>./views/index.rdoc</tt> (o cualquier otra plantilla RDoc) con el
layout <tt>./views/post.haml</tt>.

=== Plantillas Radius

La gem/librería <tt>radius</tt> es necesaria para renderizar plantillas Radius:

  # Vas a necesitar requerir radius en tu app
  require 'radius'

  get '/' do
    radius :index
  end

Renderiza <tt>./views/index.radius</tt>.

Como no vas a poder llamar a métodos de Ruby (excepto a +yield+) desde una
plantilla Radius, casi siempre vas a querer pasarle locales:

  radius :index, :locals => { :clave => 'valor' }

=== Plantillas Markaby

La gem/librería <tt>markaby</tt> es necesaria para renderizar plantillas
Markaby:

  # Vas a necesitar requerir markaby en tu app
  require 'markaby'

  get '/' do
    markaby :index
  end

Renderiza <tt>./views/index.mab</tt>.

También podés usar Markaby inline:

  get '/' do
    markaby { h1 "Bienvenido!" }
  end


=== Plantillas Slim

La gem/librería <tt>slim</tt> es necesaria para renderizar plantillas Slim:

  # Vas a necesitar requerir slim en tu app
  require 'slim'

  get '/' do
    slim :index
  end

Renderiza <tt>./views/index.slim</tt>.

=== Plantillas CoffeeScript

La gem/librería <tt>coffee-script</tt> y al menos <b>una</b> de las siguientes
opciones para ejecutar JavaScript:

* +node+ (de Node.js) en tu path
* utilizar OSX
* la gem/librería +therubyracer+

son necesarios para renderizar plantillas CoffeeScript:

  # Vas a necesitar requerir coffee-script en tu app
  require 'coffee-script'

  get '/application.js' do
    coffee :application
  end

Renderiza <tt>./views/application.coffee</tt>.

=== Plantillas Embebidas

  get '/' do
    haml '%div.titulo Hola Mundo'
  end

Renderiza el template embebido en el string.

=== Accediendo a Variables en Plantillas

Las plantillas son evaluadas dentro del mismo contexto que los manejadores de
ruta. Las variables de instancia asignadas en los manejadores de ruta son
accesibles directamente por las plantillas:

  get '/:id' do
    @foo = Foo.find(params[:id])
    haml '%h1= @foo.nombre'
  end

O es posible especificar un Hash de variables locales explícitamente:

  get '/:id' do
    foo = Foo.find(params[:id])
    haml '%h1= foo.nombre', :locals => { :foo => foo }
  end

Esto es usado típicamente cuando se renderizan plantillas como parciales desde
adentro de otras plantillas.

=== Plantillas Inline

Las plantillas pueden ser definidas al final del archivo fuente:

  require 'rubygems'
  require 'sinatra'

  get '/' do
    haml :index
  end

  __END__

  @@ layout
  %html
    = yield

  @@ index
  %div.titulo Hola mundo!!!!!

NOTA: únicamente las plantillas inline definidas en el archivo fuente que
requiere sinatra son cargadas automáticamente. Llamá <tt>enable
:inline_templates</tt> explícitamente si tenés plantillas inline en otros
archivos fuente.

=== Plantillas Nombradas

Las plantillas también pueden ser definidas usando el método top-level
<tt>template</tt>:

  template :layout do
    "%html\n  =yield\n"
  end

  template :index do
    '%div.titulo Hola Mundo!'
  end

  get '/' do
    haml :index
  end

Si existe una plantilla con el nombre "layout", va a ser usada cada vez que
una plantilla es renderizada. Podés desactivar los layouts individualmente
pasando <tt>:layout => false</tt> o globalmente con
<tt>set :haml, :layout => false</tt>:

  get '/' do
    haml :index, :layout => !request.xhr?
  end

=== Asociando Extensiones de Archivo

Para asociar una extensión de archivo con un motor de renderizado, usá
<tt>Tilt.register</tt>. Por ejemplo, si querés usar la extensión +tt+ para
las plantillas Textile, podés hacer lo siguiente:

  Tilt.register :tt, Tilt[:textile]

=== Agregando Tu Propio Motor de Renderizado

Primero, registrá tu motor con Tilt, y después, creá tu método de renderizado:

  Tilt.register :mipg, MiMotorParaPlantillaGenial

  helpers do
    def mypg(*args) render(:mypg, *args) end
  end

  get '/' do
    mypg :index
  end

Renderiza <tt>./views/index.mypg</tt>. Mirá https://github.com/rtomayko/tilt
para aprender más de Tilt.

== Filtros

Los filtros +before+ son evaluados antes de cada petición dentro del mismo
contexto que las rutas. Pueden modificar la petición y la respuesta.  Las
variables de instancia asignadas en los filtros son accesibles por las rutas y
las plantillas:

  before do
    @nota = 'Hey!'
    request.path_info = '/foo/bar/baz'
  end

  get '/foo/*' do
    @nota #=> 'Hey!'
    params[:splat] #=> 'bar/baz'
  end

Los filtros +after+ son evaluados después de cada petición dentro del mismo
contexto y también pueden modificar la petición y la respuesta.  Las variables
de instancia asignadas en los filtros +before+ y en las rutas son accesibles por
los filtros +after+:

  after do
    puts response.status
  end

Nota: A menos que usés el método +body+ en lugar de simplemente devolver un
string desde una ruta, el cuerpo de la respuesta no va a estar disponible en
un filtro after, debido a que todavía no se ha generado.

Los filtros aceptan un patrón opcional, que cuando está presente causa que los
mismos sean evaluados únicamente si el path de la petición coincide con ese
patrón:

  before '/protegido/*' do
    autenticar!
  end

  after '/crear/:slug' do |slug|
    session[:ultimo_slug] = slug
  end

Al igual que las rutas, los filtros también pueden aceptar condiciones:

  before :agent => /Songbird/ do
    # ...
  end

  after '/blog/*', :host_name => 'ejemplo.com' do
    # ...
  end

== Ayudantes

Usá el método top-level <tt>helpers</tt> para definir métodos ayudantes que
pueden ser utilizados dentro de los manejadores de rutas y las plantillas:

  helpers do
    def bar(nombre)
      "#{nombre}bar"
    end
  end

  get '/:nombre' do
    bar(params[:nombre])
  end

=== Usando Sesiones

Una sesión es usada para mantener el estado a través de distintas peticiones.
Cuando están activadas, tenés un hash de sesión para cada sesión de usuario:

  enable :sessions

  get '/' do
    "valor = " << session[:valor].inspect
  end

  get '/:valor' do
    session[:valor] = params[:valor]
  end

Tené en cuenta que <tt>enable :sessions</tt> guarda todos los datos en una
cookie, lo que no es siempre deseable (guardar muchos datos va a incrementar
tu tráfico, por citar un ejemplo).  Podés usar cualquier middleware Rack para
manejar sesiones, de la misma manera que usarías cualquier otro middleware,
pero con la salvedad de que *no* tenés que llamar a <tt>enable :sessions</tt>:

  use Rack::Session::Pool, :expire_after => 2592000

  get '/' do
    "valor = " << session[:valor].inspect
  end

  get '/:valor' do
    session[:valor] = params[:valor]
  end

Para incrementar la seguridad, los datos de la sesión almacenados en
la cookie son firmados con un secreto de sesión.  Este secreto, es
generado aleatoriamente por Sinatra.  De cualquier manera, hay que
tener en cuenta que cada vez que inicies la aplicación se va a generar
uno nuevo.  Así, si querés que todas las instancias de tu aplicación
compartan un único secreto, tenés que definirlo vos:

  set :session_secret, 'super secreto'

=== Interrupción

Para detener inmediatamente una petición dentro de un filtro o una ruta usá:

  halt

También podés especificar el estado:

  halt 410

O el cuerpo:

  halt 'esto va a ser el cuerpo'

O los dos:

  halt 401, 'salí de acá!'

Con cabeceras:

  halt 402, { 'Content-Type' => 'text/plain' }, 'venganza'

Obviamente, es posible utilizar +halt+ con una plantilla:

  halt erb(:error)

=== Paso

Una ruta puede pasarle el procesamiento a la siguiente ruta que coincida con
la petición usando <tt>pass</tt>:

  get '/adivina/:quien' do
    pass unless params[:quien] == 'Franco'
    'Adivinaste!'
  end

  get '/adivina/*' do
    'Erraste!'
  end

Se sale inmediatamente del bloque de la ruta y se le pasa el control a la
siguiente ruta que coincida. Si no coincide ninguna ruta, se devuelve un 404.

=== Ejecutando Otra Ruta

Cuando querés obtener el resultado de la llamada a una ruta, +pass+ no te va a
servir.  Para lograr esto, podés usar +call+:

  get '/foo' do
    status, headers, body = call request.env.merge("PATH_INFO" => '/bar')
    [status, body.upcase]
  end

  get '/bar' do
    "bar"
  end

Notá que en el ejemplo anterior, es conveniente mover <tt>"bar"</tt> a un
helper, y llamarlo desde <tt>/foo</tt> y <tt>/bar</tt>.  Así, vas a simplificar
las pruebas y a mejorar el rendimiento.

Si querés que la petición se envíe a la misma instancia de la aplicación en
lugar de a otra, usá <tt>call!</tt> en lugar de <tt>call</tt>.

En la especificación de Rack podés encontrar más información sobre
<tt>call</tt>.

=== Asignando el Código de Estado, los Encabezados y el Cuerpo de una Respuesta

Es posible, y se recomienda, asignar el código de estado y el cuerpo de una
respuesta con el valor de retorno de una ruta.  De cualquier manera, en varios
escenarios, puede que sea conveniente asignar el cuerpo en un punto arbitrario
del flujo de ejecución con el método +body+.  A partir de ahí, podés usar ese
mismo método para acceder al cuerpo de la respuesta:

  get '/foo' do
    body "bar"
  end

  after do
    puts body
  end

También es posible pasarle un bloque a +body+, que será ejecutado por el Rack
handler (podés usar esto para implementar streaming, mirá "Valores de retorno").

De manera similar, también podés asignar el código de estado y encabezados:

  get '/foo' do
    status 418
    headers \
      "Allow"   => "BREW, POST, GET, PROPFIND, WHEN"
      "Refresh" => "Refresh: 20; http://www.ietf.org/rfc/rfc2324.txt"
    body "I'm a tea pot!"
  end

También, al igual que +body+, tanto +status+ como +headers+ pueden utilizarse
para obtener sus valores cuando no se les pasa argumentos.

=== Tipos Mime

Cuando usás <tt>send_file</tt> o archivos estáticos tal vez tengas tipos mime
que Sinatra no entiende. Usá +mime_type+ para registrarlos a través de la
extensión de archivo:

  mime_type :foo, 'text/foo'

También lo podés usar con el ayudante +content_type+:

  get '/' do
    content_type :foo
    "foo foo foo"
  end

=== Generando URLs

Para generar URLs deberías usar el método +url+.  Por ejemplo, en Haml:

  %a{:href => url('/foo')} foo

Tiene en cuenta proxies inversos y encaminadores de Rack, si están presentes.

Este método también puede invocarse mediante su alias +to+  (mirá un ejemplo
a continuación).

=== Redirección del Navegador

Podés redireccionar al navegador con el método +redirect+:

  get '/foo' do
    redirect to('/bar')
  end

Cualquier parámetro adicional se utiliza de la misma manera que los argumentos
pasados a +halt+:

  redirect to('/bar'), 303
  redirect 'http://google.com', 'te confundiste de lugar, compañero'

También podés redireccionar fácilmente de vuelta hacia la página desde donde
vino el usuario con +redirect back+:

  get '/foo' do
    "<a href='/bar'>hacer algo</a>"
  end

  get '/bar' do
    hacer_algo
    redirect back
  end

Para pasar argumentos con una redirección, podés agregarlos a la cadena de
búsqueda:

  redirect to('/bar?suma=42')

O usar una sesión:

  enable :session

  get '/foo' do
    session[:secreto] = 'foo'
    redirect to('/bar')
  end

  get '/bar' do
    session[:secreto]
  end

=== Cache Control

Asignar tus encabezados correctamente es el cimiento para realizar un cacheo
HTTP correcto.

Podés asignar el encabezado Cache-Control fácilmente:

  get '/' do
    cache_control :public
    "cachealo!"
  end

Pro tip: configurar el cacheo en un filtro +before+:

  before do
    cache_control :public, :must_revalidate, :max_age => 60
  end

Si estás usando el helper +expires+ para definir el encabezado correspondiente,
<tt>Cache-Control</tt> se va a definir automáticamente:

  before do
    expires 500, :public, :must_revalidate
  end

Para usar cachés adecuadamente, deberías considerar usar +etag+ y
+last_modified+.  Es recomendable que llames a estos helpers *antes* de hacer
cualquier trabajo pesado, ya que van a enviar la respuesta inmediatamente si
el cliente ya tiene la versión actual en su caché:

  get '/articulo/:id' do
    @articulo = Articulo.find params[:id]
    last_modified @articulo.updated_at
    etag @articulo.sha1
    erb :articulo
  end

También es posible usar una
{weak ETag}[http://en.wikipedia.org/wiki/HTTP_ETag#Strong_and_weak_validation]:

  etag @articulo.sha1, :weak

Estos helpers no van a cachear nada por vos, sino que van a facilitar la
información necesaria para poder hacerlo.  Si estás buscando soluciones rápidas
de cacheo, mirá {rack-cache}[http://rtomayko.github.com/rack-cache/]:

  require "rack/cache"
  require "sinatra"

  use Rack::Cache

  get '/' do
    cache_control :public, :max_age => 36000
    sleep 5
    "hola"
  end

=== Enviando Archivos

Para enviar archivos, podés usar el método <tt>send_file</tt>:

  get '/' do
    send_file 'foo.png'
  end

Además acepta un par de opciones:

  send_file 'foo.png', :type => :jpg

Estas opciones son:

[filename]
  nombre del archivo respondido, por defecto es el nombre real del archivo.

[last_modified]
  valor para el encabezado Last-Modified, por defecto toma el mtime del archivo.

[type]
  el content type que se va a utilizar, si no está presente se intenta adivinar
  a partir de la extensión del archivo.

[disposition]
  se utiliza para el encabezado Content-Disposition, y puede tomar alguno de los
  siguientes valores: +nil+ (por defecto), <tt>:attachment</tt> e
  <tt>:inline</tt>

[length]
  encabezado Content-Length, por defecto toma el tamaño del archivo.

Si el Rack handler lo soporta, se intentará no transmitir directamente desde el
proceso de Ruby.  Si usás este método, Sinatra se va a encargar automáticamente
peticiones de rango.

=== Accediendo al objeto de la petición

El objeto de la petición entrante puede ser accedido desde el nivel de la
petición (filtros, rutas y manejadores de errores) a través del método
<tt>request</tt>:

  # app corriendo en http://ejemplo.com/ejemplo
  get '/foo' do
    request.body              # cuerpo de la petición enviado por el cliente (ver más abajo)
    request.scheme            # "http"
    request.script_name       # "/ejemplo"
    request.path_info         # "/foo"
    request.port              # 80
    request.request_method    # "GET"
    request.query_string      # ""
    request.content_length    # longitud de request.body
    request.media_type        # tipo de medio de request.body
    request.host              # "ejemplo.com"
    request.get?              # verdadero (hay métodos análogos para los otros verbos)
    request.form_data?        # falso
    request["UNA_CABECERA"]   # valor de la cabecera UNA_CABECERA
    request.referrer          # la referencia del cliente o '/'
    request.user_agent        # user agent (usado por la condición :agent)
    request.cookies           # hash de las cookies del browser
    request.xhr?              # es una petición ajax?
    request.url               # "http://ejemplo.com/ejemplo/foo"
    request.path              # "/ejemplo/foo"
    request.ip                # dirección IP del cliente
    request.secure?           # falso (sería verdadero sobre ssl)
    request.forwarded?        # verdadero (si se está corriendo atrás de un proxy inverso)
    requuest.env              # hash de entorno directamente entregado por Rack
  end

Algunas opciones, como <tt>script_name</tt> o <tt>path_info</tt> pueden
también ser escritas:

  before { request.path_info = "/" }

  get "/" do
    "todas las peticiones llegan acá"
  end

El objeto <tt>request.body</tt> es una instancia de IO o StringIO:

  post "/api" do
    request.body.rewind  # en caso de que alguien ya lo haya leído
    datos = JSON.parse request.body.read
    "Hola #{datos['nombre']}!"
  end

=== Archivos Adjuntos

Podés usar el método helper +attachment+ para indicarle al navegador que
almacene la respuesta en el disco en lugar de mostrarla en pantalla:

  get '/' do
    attachment
    "guardalo!"
  end

También podés pasarle un nombre de archivo:

  get '/' do
    attachment "info.txt"
    "guardalo!"
  end

=== Buscando los Archivos de las Plantillas

El helper <tt>find_template</tt> se utiliza para encontrar los archivos de las
plantillas que se van a renderizar:

  find_template settings.views, 'foo', Tilt[:haml] do |archivo|
    puts "podría ser #{archivo}"
  end

Si bien esto no es muy útil, lo interesante es que podés sobreescribir este
método, y así enganchar tu propio mecanismo de búsqueda.  Por ejemplo, para
poder utilizar más de un directorio de vistas:

  set :views, ['vistas', 'plantillas']

  helpers do
    def find_template(views, name, engine, &block)
      Array(views).each { |v| super(v, name, engine, &block) }
    end
  end

Otro ejemplo consiste en usar directorios diferentes para los distintos motores
de renderizado:

  set :views, :sass => 'vistas/sass', :haml => 'plantillas', :defecto => 'vistas'

  helpers do
    def find_template(views, name, engine, &block)
      _, folder = views.detect { |k,v| engine == Tilt[k] }
      folder ||= views[:defecto]
      super(folder, name, engine, &block)
    end
  end

¡Es muy fácil convertir estos ejemplos en una extensión y compartirla!.

Notá que <tt>find_template</tt> no verifica si un archivo existe realmente, sino
que llama al bloque que recibe para cada path posible.  Esto no representa un
problema de rendimiento debido a que +render+ va a usar +break+ ni bien
encuentre un archivo que exista.  Además, las ubicaciones de las plantillas (y
su contenido) se cachean cuando no estás en el modo de desarrollo.  Es bueno
tener en cuenta lo anteiror si escribís un método medio loco.

== Configuración

Ejecutar una vez, en el inicio, en cualquier entorno:

  configure do
    # asignando una opción
    set :opcion, 'valor'

    # asignando varias opciones
    set :a => 1, :b => 2

    # atajo para `set :opcion, true`
    enable :opcion

    # atajo para `set :opcion, false`
    disable :opcion

    # también podés tener configuraciones dinámicas usando bloques
    set(:css_dir) { File.join(views, 'css') }
  end

Ejecutar únicamente cuando el entorno (la variable de entorno RACK_ENV) es
<tt>:production</tt>:

  configure :production do
    ...
  end

Ejecutar cuando el entorno es <tt>:production</tt> o <tt>:test</tt>:

  configure :production, :test do
    ...
  end

Podés acceder a estas opciones utilizando el método <tt>settings</tt>:

  configure do
    set :foo, 'bar'
  end

  get '/' do
    settings.foo? # => true
    settings.foo  # => 'bar'
    ...
  end

=== Configuraciones Disponibles

[absolute_redirects]  si está deshabilitada, Sinatra va a permitir redirecciones
                      relativas, sin embargo, como consecuencia de esto, va a
                      dejar de cumplir con el RFC 2616 (HTTP 1.1), que solamente
                      permite redirecciones absolutas.

                      Activalo si tu apliación está corriendo atrás de un proxy
                      inverso que no se ha configurado adecuadamente.  Notá que
                      el helper +url+ va a seguir produciendo URLs absolutas, a
                      menos que le pasés +false+ como segundo parámetro.

                      Deshabilitada por defecto.

[add_charsets]        tipos mime a los que el helper <tt>content_type</tt> les
                      añade automáticamente el charset.

                      En general, no deberías asignar directamente esta opción,
                      sino añadirle los charsets que quieras:

                        settings.add_charsets << "application/foobar"

[app_file]            archivo principal de la aplicación, se utiliza para
                      detectar la raíz del proyecto, el directorio de las vistas
                      y el público así como las plantillas inline.

[bind]                dirección IP que utilizará el servidor integrado (por
                      defecto: 0.0.0.0).

[default_encoding]    encoding utilizado cuando el mismo se desconoce (por
                      defecto <tt>"utf-8"</tt>).

[dump_errors]         mostrar errores en el log.

[environment]         entorno actual, por defecto toma el valor de
                      <tt>ENV['RACK_ENV']</tt>, o <tt>"development"</tt> si no
                      está disponible.

[logging]             define si se utiliza el logger.

[lock]                coloca un lock alrededor de cada petición, procesando
                      solamente una por proceso.

                      Habilitá esta opción si tu aplicación no es thread-safe.
                      Se encuentra deshabilitada por defecto.

[method_override]     utiliza el parámetro <tt>_method</tt> para permtir
                      formularios put/delete en navegadores que no los soportan.

[port]                puerto en el que escuchará el servidor integrado.

[prefixed_redirects]  define si inserta <tt>request.script_name</tt> en las
                      redirecciones cuando no se proporciona un path absoluto.
                      De esta manera, cuando está habilitada,
                      <tt>redirect '/foo'</tt> se comporta de la misma manera
                      que <tt>redirect to('/foo')</tt>.  Se encuentra
                      deshabilitada por defecto.

[public]              directorio desde donde se sirven los archivos públicos.

[reload_templates]    define si se recargan las plantillas entre peticiones.

                      Se encuentra activado en el entorno de desarrollo y en
                      Ruby 1.8.6 (para compoensar un bug en Ruby que provoca una
                      pérdida de memoria).

[root]                directorio raíz del proyecto.

[raise_errors]        elevar excepciones (detiene la aplicación).

[run]                 cuando está habilitada, Sinatra se va a encargar de
                      iniciar el servidor web, no la habilités cuando estés
                      usando rackup o algún otro medio.

[running]             indica si el servidor integrado está ejecutandose, ¡no
                      cambiés esta configuración!.

[server]              servidor, o lista de servidores, para usar como servidor
                      integrado.  Por defecto: ['thin', 'mongrel', 'webrick'],
                      el orden establece la prioridad.

[sessions]            habilita sesiones basadas en cookies.

[show_exceptions]     muestra un stack trace en el navegador.

[static]              define si Sinatra debe encargarse de servir archivos
                      estáticos.

                      Deshabilitala cuando usés un servidor capaz de hacerlo
                      por sí solo, porque mejorará el rendimiento.  Se encuentra
                      habilitada por defecto.

[views]               directorio de las vistas.

== Manejo de Errores

Los manejadores de errores se ejecutan dentro del mismo contexto que las rutas
y los filtros +before+, lo que significa que podés usar, por ejemplo,
<tt>haml</tt>, <tt>erb</tt>, <tt>halt</tt>, etc.

=== No encontrado <em>(Not Found)</em>

Cuando se eleva una excepción <tt>Sinatra::NotFound</tt>, o el código de
estado de la respuesta es 404, el manejador <tt>not_found</tt> es invocado:

  not_found do
    'No existo'
  end

=== Error

El manejador +error+ es invocado cada vez que una excepción es elevada
desde un bloque de ruta o un filtro. El objeto de la excepción se puede
obtener de la variable Rack <tt>sinatra.error</tt>:

  error do
    'Disculpá, ocurrió un error horrible - ' + env['sinatra.error'].name
  end

Errores personalizados:

  error MiErrorPersonalizado do
    'Lo que pasó fue...' request.env['sinatra.error'].message
  end

Entonces, si pasa esto:

  get '/' do
    raise MiErrorPersonalizado, 'algo malo'
  end

Obtenés esto:

  Lo que pasó fue... algo malo

También, podés instalar un manejador de errores para un código de estado:

  error 403 do
    'Acceso prohibido'
  end

  get '/secreto' do
    403
  end

O un rango:

  error 400..510 do
    'Boom'
  end

Sinatra instala manejadores <tt>not_found</tt> y <tt>error</ttt> especiales
cuando se ejecuta dentro del entorno de desarrollo "development".

== Rack Middleware

Sinatra corre sobre Rack[http://rack.rubyforge.org/], una interfaz minimalista
que es un estándar para frameworks webs escritos en Ruby. Una de las
capacidades más interesantes de Rack para los desarrolladores de aplicaciones
es el soporte de "middleware" -- componentes que se ubican entre el servidor y
tu aplicación, supervisando y/o manipulando la petición/respuesta HTTP para
proporcionar varios tipos de funcionalidades comunes.

Sinatra hace muy sencillo construir tuberías de Rack middleware a través del
método top-level +use+:

  require 'sinatra'
  require 'mi_middleware_personalizado'

  use Rack::Lint
  use MiMiddlewarePersonalizado

  get '/hola' do
    'Hola Mundo'
  end

Las semánticas de +use+ son idénticas a las definidas para el DSL
Rack::Builder[http://rack.rubyforge.org/doc/classes/Rack/Builder.html] (más
frecuentemente usado desde archivos rackup). Por ejemplo, el método +use+
acepta argumentos múltiples/variables así como bloques:

  use Rack::Auth::Basic do |nombre_de_usuario, password|
    nombre_de_usuario == 'admin' && password == 'secreto'
  end

Rack es distribuido con una variedad de middleware estándar para logging,
debugging, enrutamiento URL, autenticación, y manejo de sesiones. Sinatra
usa muchos de estos componentes automáticamente de acuerdo a su configuración
para que típicamente no tengas que usarlas (con +use+) explícitamente.

== Pruebas

Las pruebas para las aplicaciones Sinatra pueden ser escritas utilizando
cualquier framework o librería de pruebas basada en Rack. Se recomienda usar
{Rack::Test}[http://gitrdoc.com/brynary/rack-test]:

  require 'mi_app_sinatra'
  require 'test/unit'
  require 'rack/test'

  class MiAppTest < Test::Unit::TestCase
    include Rack::Test::Methods

    def app
      Sinatra::Application
    end

    def test_mi_defecto
      get '/'
      assert_equal 'Hola Mundo!', last_response.body
    end

    def test_con_parametros
      get '/saludar', :name => 'Franco'
      assert_equal 'Hola Frank!', last_response.body
    end

    def test_con_entorno_rack
      get '/', {}, 'HTTP_USER_AGENT' => 'Songbird'
      assert_equal "Estás usando Songbird!", last_response.body
    end
  end

NOTA: El módulo Sinatra::Test y la clase Sinatra::TestHarness están
deprecados a partir de la versión 0.9.2.

== Sinatra::Base - Middleware, Librerías, y Aplicaciones Modulares

Definir tu aplicación en el top-level funciona bien para micro-aplicaciones
pero trae inconvenientes considerables a la hora de construir componentes
reutilizables como Rack middleware, Rails metal, simple librerías con un
componente de servidor, o incluso extensiones de Sinatra. El DSL de top-level
contamina el espacio de nombres de Object y asume una configuración apropiada
para micro-aplicaciones (por ejemplo, un único archivo de aplicación, los
directorios ./public y ./views, logging, página con detalles de excepción,
etc.). Ahí es donde Sinatra::Base entra en el juego:

  require 'sinatra/base'

  class MiApp < Sinatra::Base
    set :sessions, true
    set :foo, 'bar'

    get '/' do
      'Hola Mundo!'
    end
  end

Las subclases de Sinatra::Base tienen disponibles exactamente los mismos
métodos que los provistos por el DSL de top-level. La mayoría de las
aplicaciones top-level se pueden convertir en componentes Sinatra::Base con
dos modificaciones:

* Tu archivo debe requerir <tt>sinatra/base</tt> en lugar de +sinatra+; de otra
  manera, todos los métodos del DSL de sinatra son importados dentro del
  espacio de nombres principal.
* Poné las rutas, manejadores de errores, filtros y opciones de tu aplicación
  en una subclase de Sinatra::Base.

<tt>Sinatra::Base</tt> es una pizarra en blanco. La mayoría de las opciones están
desactivadas por defecto, incluyendo el servidor incorporado. Mirá
{Opciones y Configuraciones}[http://sinatra.github.com/configuration.html]
para detalles sobre las opciones disponibles y su comportamiento.

=== Estilo Modular vs. Clásico

Contrariamente a la creencia popular, no hay nada de malo con el estilo clásico.
Si se ajusta a tu aplicación, no es necesario que la cambies a una modular.

Existen tan solo dos desventajas en comparación con el estilo modular:

* Solamente podés tener una aplicación Sinatra por proceso Ruby - si tenés
  planificado usar más, cambiá al estilo modular.

* El estilo clásico contamina Object con métodos delegadores - si tenés
  planificado empaquetar tu aplicación en una librería/gem, cambiá al estilo
  modular.

No hay ninguna razón por la cuál no puedas mezclar los estilos modular y
clásico.

Cuando cambiés de un estilo al otro, tené en cuenta las sutiles diferencias
entre sus configuraciones:

  Configuración       Clásica                      Modular

  app_file            archivo que carga sinatra    nil
  run                 $0 == app_file               false
  logging             true                         false
  method_override     true                         false
  inline_templates    true                         false

=== Sirviendo una Aplicación Modular

Las dos opciones más comunes para iniciar una aplicación modular son, iniciarla
activamente con <tt>run!</tt>:

  # mi_app.rb
  require 'sinatra/base'

  class MiApp < Sinatra::Base
    # ... código de la app  ...

    # iniciar el servidor si el archivo fue ejecutado directamente
    run! if app_file == $0
  end

Iniciar con:

  ruby mi_app.rb

O, con un archivo <tt>config.ru</tt>, que permite usar cualquier handler Rack:

  # config.ru
  require 'mi_app'
  run MiApp

Después ejecutar:

  rackup -p 4567

=== Usando una Aplicación Clásica con un Archivo config.ru

Escribí el archivo de tu aplicación:

  # app.rb
  require 'sinatra'

  get '/' do
    'Hola mundo!'
  end

Y el <tt>config.ru</tt> correspondiente:

  require 'app'
  run Sinatra::Application

=== ¿Cuándo Usar config.ru?

Indicadores de que probablemente querés usar <tt>config.ru</tt>:

* Querés realizar el deploy con un hanlder Rack distinto (Passenger, Unicorn,
  Heroku, ...).
* Querés usar más de una subclase de <tt>Sinatra::Base</tt>.
* Querés usar Sinatra únicamente para middleware, pero no como un endpoint.

<b>No hay necesidad de utilizar un archivo <tt>config.ru</tt> exclusivamente
porque tenés una aplicación modular, y no necesitás una aplicación modular para
iniciarla con <tt>config.ru</tt>.</b>

=== Utilizando Sinatra como Middleware

Sinatra no solo es capaz de usar otro Rack middleware, sino que a su vez,
cualquier aplicación Sinatra puede ser agregada delante de un endpoint Rack
como middleware. Este endpoint puede ser otra aplicación Sinatra, o cualquier
aplicación basada en Rack (Rails/Ramaze/Camping/...):

  require 'sinatra/base'

  class PantallaDeLogin < Sinatra::Base
    enable :sessions

    get('/login') { haml :login }

    post('/login') do
      if params[:nombre] = 'admin' and params[:password] = 'admin'
        session['nombre_de_usuario'] = params[:nombre]
      else
        redirect '/login'
      end
    end
  end

  class MiApp < Sinatra::Base
    # el middleware se ejecutará antes que los filtros
    use PantallaDeLogin

    before do
      unless session['nombre_de_usuario']
        halt "Acceso denegado, por favor <a href='/login'>iniciá sesión</a>."
      end
    end

    get('/') { "Hola #{session['nombre_de_usuario']}." }
  end

== Ámbitos y Ligaduras

El ámbito en el que te encontrás determina que métodos y variables están
disponibles.

=== Ámbito de Aplicación/Clase

Cada aplicación Sinatra es una subclase de Sinatra::Base. Si estás usando el
DSL de top-level (<tt>require 'sinatra'</tt>), entonces esta clase es
Sinatra::Application, de otra manera es la subclase que creaste explícitamente.
Al nivel de la clase tenés métodos como +get+ o +before+, pero no podés acceder
a los objetos +request+ o +session+, ya que hay una única clase de la
aplicación para todas las peticiones.

Las opciones creadas utilizando +set+ son métodos al nivel de la clase:

    class MiApp < Sinatra::Base
      # Ey, estoy en el ámbito de la aplicación!
      set :foo, 42
      foo # => 42

      get '/foo' do
        # Hey, ya no estoy en el ámbito de la aplicación!
      end
    end

Tenés la ligadura al ámbito de la aplicación dentro de:

* El cuerpo de la clase de tu aplicación
* Métodos definidos por extensiones
* El bloque pasado a +helpers+
* Procs/bloques usados como el valor para +set+

Este ámbito puede alcanzarse de las siguientes maneras:

* A través del objeto pasado a los bloques de configuración (<tt>configure { |c| ...}</tt>)
* Llamando a +settings+ desde dentro del ámbito de la petición

=== Ámbito de Petición/Instancia

Para cada petición entrante, una nueva instancia de la clase de tu aplicación
es creada y todos los bloques de rutas son ejecutados en ese ámbito. Desde este
ámbito podés acceder a los objetos +request+ y +session+ o llamar a los métodos
de renderización como +erb+ o +haml+. Podés acceder al ámbito de la aplicación
desde el ámbito de la petición utilizando +settings+:

  class MiApp < Sinatra::Base
    # Ey, estoy en el ámbito de la aplicación!
    get '/definir_ruta/:nombre' do
      # Ámbito de petición para '/definir_ruta/:nombre'
      @valor = 42

      settings.get("/#{params[:nombre]}") do
        # Ámbito de petición para "/#{params[:nombre]}"
        @valor # => nil (no es la misma petición)
      end

      "Ruta definida!"
    end
  end

Tenés la ligadura al ámbito de la petición dentro de:

* bloques pasados a get/head/post/put/delete/options
* filtros before/after
* métodos ayudantes
* plantillas/vistas

=== Ámbito de Delegación

El ámbito de delegación solo reenvía métodos al ámbito de clase. De cualquier
manera, no se comporta 100% como el ámbito de clase porque no tenés la ligadura
de la clase: únicamente métodos marcados explícitamente para delegación están
disponibles y no compartís variables/estado con el ámbito de clase (léase:
tenés un +self+ diferente). Podés agregar delegaciones de método llamando a
<tt>Sinatra::Delegator.delegate :nombre_del_metodo</tt>.

Tenés la ligadura al ámbito de delegación dentro de:

* La ligadura del top-level, si hiciste <tt>require "sinatra"</tt>
* Un objeto extendido con el mixin <tt>Sinatra::Delegator</tt>

Pegale una mirada al código: acá está el
{Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128]
que es {incluido en el espacio de nombres principal}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/main.rb#L28].

== Línea de Comandos

Las aplicaciones Sinatra pueden ser ejecutadas directamente:

  ruby miapp.rb [-h] [-x] [-e ENTORNO] [-p PUERTO] [-o HOST] [-s MANEJADOR]

Las opciones son:

  -h # ayuda
  -p # asigna el puerto (4567 es usado por defecto)
  -o # asigna el host (0.0.0.0 es usado por defecto)
  -e # asigna el entorno (development es usado por defecto)
  -s # especifica el servidor/manejador rack (thin es usado por defecto)
  -x # activa el mutex lock (está desactivado por defecto)

== Requerimientos

Se recomienda instalar Sinatra en Ruby 1.8.7, 1.9.2, JRuby o Rubinius.

Las siguientes versiones de Ruby son soportadas oficialmente:

[ Ruby 1.8.6 ]
  No se recomienda utilizar Sinatra en 1.8.6. Sin embargo, esta versión será
  soportada oficialmente hasta que se libere Sinatra 1.3.0.  RDoc y CoffeeScript
  no son soportadas por esta versión de Ruby. 1.8.6 contiene una falla
  importante de pérdida de memoria en su implementación de Hash, que afecta a
  las versiones de Sinatra anteriores a 1.1.1.  La versión actual evita
  explícitamente esta falla a expensas de una disminución del rendimiento.  Por
  último, Rack >= 1.2 dejó de soportar 1.8.6, por lo que vas a tener que usar
  alguna versión 1.1.x.

[ Ruby 1.8.7 ]
  1.8.7 es soportado completamente.  Sin embargo, si no hay nada que te lo
  prohíba, te recomendamos que usés 1.9.2 o cambies a JRuby o Rubinius.

[ Ruby 1.9.2 ]
  1.9.2 es soportado y recomendado.  Tené en cuenta que Radius y Markaby no
  son compatibles con 1.9 actualmente.  Además, no usés 1.9.2p0, porque produce
  fallos de segmentación cuando se utiliza Sinatra.

[ Rubinius ]
  Rubinius es soportado oficialmente (Rubinius >= 1.2.2), con la excepción de
  las plantillas Textile.

[ JRuby ]
  JRuby es soportado oficialmente (JRuby >= 1.5.6).  No se conocen problemas con
  librerías de plantillas de terceras partes.  Sin embargo, si elegís usar
  JRuby, deberías examinar sus Rack handlers porque el servidor web Thin no es
  soportado actualmente.

Siempre le prestamos atención a las nuevas versiones de Ruby.

Las siguientes implementaciones de Ruby no se encuentran soportadas
oficialmente.  De cualquier manera, pueden ejecutar Sinatra:

* Versiones anteriores de JRuby y Rubinius
* MacRuby
* Maglev
* IronRuby
* Ruby 1.9.0 y 1.9.1

No estar soportada oficialmente, significa que si las cosas solamente se rompen
ahí y no en una plataforma soportada, asumimos que no es nuestro problema sino
el suyo.

Sinatra debería funcionar en cualquier sistema operativo soportado por la
implementación de Ruby elegida.

== A la Vanguardia

Si querés usar el código de Sinatra más reciente, sentite libre de ejecutar
tu aplicación sobre la rama master, en general es bastante estable.

También liberamos prereleases de vez en cuando, así, podés hacer

  gem install sinatra --pre

Para obtener algunas de las últimas características.

=== Con Bundler

Esta es la manera recomendada para ejecutar tu aplicación sobre la última
versión de Sinatra usando {Bundler}[http://gembundler.com/].

Primero, instalá bundler si no lo hiciste todavía:

  gem install bundler

Después, en el directorio de tu proyecto, creá un archivo +Gemfile+:

  source :rubygems
  gem 'sinatra', :git => "git://github.com/sinatra/sinatra.git"

  # otras dependencias
  gem 'haml'                    # por ejemplo, si usás haml
  gem 'activerecord', '~> 3.0'  # quizás también necesités ActiveRecord 3.x

Tené en cuenta que tenés que listar todas las dependencias directas de tu
aplicación. No es necesario listar las dependencias de Sinatra (Rack y Tilt)
porque Bundler las agrega directamente.

Ahora podés arrancar tu aplicación así:

  bundle exec ruby miapp.rb

=== Con Git

Cloná el repositorio localmente y ejecutá tu aplicación, asegurándote que el
directorio <tt>sinatra/lib</tt> esté en el <tt>$LOAD_PATH</tt>:

  cd miapp
  git clone git://github.com/sinatra/sinatra.git
  ruby -Isinatra/lib miapp.rb

Para actualizar el código fuente de Sinatra en el futuro:

  cd miapp/sinatra
  git pull

=== Instalación Global

Podés construir la gem vos mismo:

  git clone git://github.com/sinatra/sinatra.git
  cd sinatra
  rake sinatra.gemspec
  rake install

Si instalás tus gems como root, el último paso debería ser

  sudo rake install

== Versionado

Sinatra utiliza el {Versionado Semántico}[http://semver.org/],
siguiendo las especificaciones SemVer y SemVerTag.

== Lecturas Recomendadas

* {Sito web del proyecto}[http://www.sinatrarb.com/] - Documentación
  adicional, noticias, y enlaces a otros recursos.
* {Contribuyendo}[http://www.sinatrarb.com/contributing] - ¿Encontraste un
  error?. ¿Necesitás ayuda?. ¿Tenés un parche?.
* {Seguimiento de problemas}[http://github.com/sinatra/sinatra/issues]
* {Twitter}[http://twitter.com/sinatra]
* {Lista de Correo}[http://groups.google.com/group/sinatrarb/topics]
* {IRC: #sinatra}[irc://chat.freenode.net/#sinatra] en http://freenode.net
