diff --git a/README.es.rdoc b/README.es.rdoc
index d699cf6b..ba275a25 100644
--- a/README.es.rdoc
+++ b/README.es.rdoc
@@ -18,6 +18,9 @@ Instalá la gem y ejecutá la aplicación con:
Podés verla en: http://localhost:4567
+Es recomendable además ejecutar gem install thin, 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.
@@ -126,7 +129,7 @@ Podés definir tus propias condiciones fácilmente:
"Lo siento, perdiste."
end
-=== Valores de retorno
+=== 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
@@ -151,6 +154,47 @@ De esa manera podemos, por ejemplo, implementar fácilmente un streaming:
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
+ @caputras = Match.new([])
+ end
+
+ def match(str)
+ @caputras 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
@@ -372,7 +416,7 @@ plantillas:
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 `:layout_engine`:
+layout distinto al de la plantilla pasando la opción :layout_engine:
get '/' do
markdown :index, :layout_engine => :erb
@@ -432,7 +476,7 @@ plantillas:
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 `:layout_engine`:
+layout distinto al de la plantilla pasando la opción :layout_engine:
get '/' do
textile :index, :layout_engine => :erb
@@ -478,7 +522,7 @@ plantillas:
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 `:layout_engine`:
+al de la plantilla pasando la opción :layout_engine:
get '/' do
rdoc :index, :layout_engine => :erb
@@ -676,10 +720,10 @@ para aprender más de Tilt.
== Filtros
-Los filtros before son evaluados antes de cada petición dentro del mismo
-contexto que las rutas serán evaluadas y pueden modificar la petición y la
-respuesta. Las variables de instancia asignadas en los filtros son accesibles
-por las rutas y las plantillas:
+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!'
@@ -691,16 +735,16 @@ por las rutas y las plantillas:
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 rutas son accesibles por los
-filtros after:
+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
+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.
@@ -716,7 +760,7 @@ patrón:
session[:ultimo_slug] = slug
end
-Al igual que las rutas, los filtros también aceptan condiciones:
+Al igual que las rutas, los filtros también pueden aceptar condiciones:
before :agent => /Songbird/ do
# ...
@@ -741,6 +785,37 @@ pueden ser utilizados dentro de los manejadores de rutas y las plantillas:
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 enable :sessions 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 enable :sessions:
+
+ use Rack::Session::Pool, :expire_after => 2592000
+
+ get '/' do
+ "valor = " << session[:valor].inspect
+ end
+
+ get '/:valor' do
+ session[:valor] = params[:valor]
+ end
+
=== Interrupción
Para detener inmediatamente una petición dentro de un filtro o una ruta usá:
@@ -780,12 +855,36 @@ la petición usando pass:
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.
-=== Asignando el cuerpo y el código de estado de una respuesta
+=== 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 "bar" a un
+helper, y llamarlo desde /foo y /bar. 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á call! en lugar de call.
+
+En la especificación de Rack podés encontrar más información sobre
+call.
+
+=== 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
+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
@@ -796,35 +895,78 @@ mismo método para acceder al cuerpo de la respuesta:
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](#Valores%20de%20retorno)).
+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:
+De manera similar, también podés asignar el código de estado y encabezados:
get '/foo' do
status 418
- halt "I'm a teapot"
+ 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 send_file 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`:
+Podés redireccionar al navegador con el método +redirect+:
get '/foo' do
- redirect '/bar'
+ redirect to('/bar')
end
Cualquier parámetro adicional se utiliza de la misma manera que los argumentos
-pasados a `halt`:
+pasados a +halt+:
- redirect '/bar', 303
- redirect '/bar', 'te confundiste de lugar, compañero'
+ redirect to('/bar'), 303
+ redirect 'http://google.com', 'te confundiste de lugar, compañero'
-Para pasar argumetnos con una redirección, podés agregarlo a la cadena de
+También podés redireccionar fácilmente de vuelta hacia la página desde donde
+vino el usuario con +redirect back+:
+
+ get '/foo' do
+ "hacer algo"
+ 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 '/bar?suma=42'
+ redirect to('/bar?suma=42')
O usar una sesión:
@@ -832,18 +974,111 @@ O usar una sesión:
get '/foo' do
session[:secreto] = 'foo'
- redirect '/bar'
+ 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,
+Cache-Control 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 send_file:
+
+ 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), :attachment e
+ :inline
+
+[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
-`request`:
+request:
# app corriendo en http://ejemplo.com/ejemplo
get '/foo' do
@@ -860,14 +1095,15 @@ petición (filtros, rutas y manejadores de errores) a través del método
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.referer # la referencia del cliente o '/'
+ 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
+ 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
@@ -888,12 +1124,85 @@ El objeto request.body es una instancia de IO o StringIO:
"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 find_template 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 find_template 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
@@ -909,10 +1218,116 @@ Ejecutar cuando el entorno es :production o :test:
...
end
+Podés acceder a estas opciones utilizando el método settings:
+
+ 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 content_type 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 "utf-8").
+
+[dump_errors] mostrar errores en el log.
+
+[environment] entorno actual, por defecto toma el valor de
+ ENV['RACK_ENV'], o "development" 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 _method 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 request.script_name en las
+ redirecciones cuando no se proporciona un path absoluto.
+ De esta manera, cuando está habilitada,
+ redirect '/foo' se comporta de la misma manera
+ que redirect to('/foo'). 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,
+y los filtros +before+, lo que significa que podés usar, por ejemplo,
haml, erb, halt, etc.
=== No encontrado (Not Found)
@@ -969,18 +1384,6 @@ O un rango:
Sinatra instala manejadores not_found y error especiales
cuando se ejecuta dentro del entorno de desarrollo "development".
-== Tipos Mime
-
-Cuando usás send_file 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+:
-
- content_type :foo
-
== Rack Middleware
Sinatra corre sobre Rack[http://rack.rubyforge.org/], una interfaz minimalista
@@ -1080,7 +1483,7 @@ 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 +sinatra/base+ en lugar de +sinatra+; de otra
+* Tu archivo debe requerir sinatra/base 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
@@ -1091,6 +1494,34 @@ 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
@@ -1195,11 +1626,11 @@ disponibles.
Cada aplicación Sinatra es una subclase de Sinatra::Base. Si estás usando el
DSL de top-level (require 'sinatra'), 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
+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:
+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!
@@ -1215,21 +1646,21 @@ 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`
+* 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 (configure { |c| ...})
-* Llamando a `settings` desde dentro del ámbito de la petición
+* 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`:
+á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!
@@ -1259,13 +1690,13 @@ El ámbito de delegación solo reenvía métodos al ámbito de clase. De cualqui
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
+tenés un +self+ diferente). Podés agregar delegaciones de método llamando a
Sinatra::Delegator.delegate :nombre_del_metodo.
Tenés la ligadura al ámbito de delegación dentro de:
* La ligadura del top-level, si hiciste require "sinatra"
-* Un objeto extendido con el mixin `Sinatra::Delegator`
+* Un objeto extendido con el mixin Sinatra::Delegator
Pegale una mirada al código: acá está el
{Sinatra::Delegator mixin}[http://github.com/sinatra/sinatra/blob/ceac46f0bc129a6e994a06100aa854f606fe5992/lib/sinatra/base.rb#L1128]
@@ -1286,6 +1717,59 @@ Las opciones son:
-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.1), 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
@@ -1326,7 +1810,7 @@ Ahora podés arrancar tu aplicación así:
=== Con Git
Cloná el repositorio localmente y ejecutá tu aplicación, asegurándote que el
-directorio sinatra/lib esté en el LOAD_PATH:
+directorio sinatra/lib esté en el $LOAD_PATH:
cd miapp
git clone git://github.com/sinatra/sinatra.git