Merge branch 'master' of github.com:sinatra/sinatra

This commit is contained in:
Konstantin Haase 2012-07-30 20:08:47 +02:00
commit 4d40a5e26c
21 changed files with 272 additions and 102 deletions

View File

@ -551,6 +551,15 @@ Las opciones <tt>:callback</tt> y <tt>:variable</tt> se pueden utilizar para dec
var resource = {"foo":"bar","baz":"qux"}; present(resource); var resource = {"foo":"bar","baz":"qux"}; present(resource);
=== Plantillas WLang
Dependencias:: {wlang}[https://github.com/blambeau/wlang/]
Extensiones de Archivo:: <tt>.wlang</tt>
Ejemplo:: <tt>wlang :index, :locals => { :clave => 'valor' }</tt>
Como no vas a poder llamar a métodos de Ruby (excepto por +yield+) desde una
plantilla WLang, casi siempre vas a querer pasarle locales.
=== Plantillas Embebidas === Plantillas Embebidas
get '/' do get '/' do
@ -1349,10 +1358,10 @@ Podés acceder a estas opciones utilizando el método <tt>settings</tt>:
=== Configurando la Protección de Ataques === Configurando la Protección de Ataques
Sinatra usa {Rack::Protection}[https://github.com/rkh/rack-protection#readme] Sinatra usa {Rack::Protection}[https://github.com/rkh/rack-protection#readme]
para defender a tu aplicación de los ataques más comunes. Tenés que tener en para defender a tu aplicación de los ataques más comunes. Si por algún motivo,
cuenta que como consecuencia de esto puede venir asociada una disminución del querés desactivar esta funcionalidad, podés hacerlo como se indica a
rendimiento de tu aplicación. Si por este, o algún otro motivo, querés continuación (tené en cuenta que tu aplicación va a quedar expuesta a un
desactivar está funcionalidad, podés hacerlo: montón de vulnerabilidades bien conocidas):
disable :protection disable :protection

View File

@ -1,5 +1,4 @@
= Sinatra = Sinatra
<i>Attention : Ce document correspond à la traduction de la version anglaise et <i>Attention : Ce document correspond à la traduction de la version anglaise et
il n'est peut être plus à jour.</i> il n'est peut être plus à jour.</i>

View File

@ -251,7 +251,7 @@ Use the <tt>:static_cache_control</tt> setting (see below) to add
== Views / Templates == Views / Templates
Each template language is exposed as via its own rendering method. These Each template language is exposed via its own rendering method. These
methods simply return a string: methods simply return a string:
get '/' do get '/' do
@ -755,7 +755,7 @@ middleware of choice as you would any other middleware:
end end
To improve security, the session data in the cookie is signed with a session To improve security, the session data in the cookie is signed with a session
secret. A random secret is generate for you by Sinatra. However, since this secret. A random secret is generated for you by Sinatra. However, since this
secret will change with every start of your application, you might want to secret will change with every start of your application, you might want to
set the secret yourself, so all your application instances share it: set the secret yourself, so all your application instances share it:
@ -882,15 +882,15 @@ creating your own wrapper:
This allows you to implement streaming APIs, This allows you to implement streaming APIs,
{Server Sent Events}[http://dev.w3.org/html5/eventsource/] and can be used as {Server Sent Events}[http://dev.w3.org/html5/eventsource/] and can be used as
basis for {WebSockets}[http://en.wikipedia.org/wiki/WebSocket]. It can also be the basis for {WebSockets}[http://en.wikipedia.org/wiki/WebSocket]. It can also be
used to increase throughput if some but not all content depends on a slow used to increase throughput if some but not all content depends on a slow
resource. resource.
Note that the streaming behavior, especially the number of concurrent request, Note that the streaming behavior, especially the number of concurrent requests,
highly depends on the web server used to serve the application. Some servers, highly depends on the web server used to serve the application. Some servers,
like WEBRick, might not even support streaming at all. If the server does not like WEBRick, might not even support streaming at all. If the server does not
support streaming, the body will be sent all at once after the block passed to support streaming, the body will be sent all at once after the block passed to
+stream+ finished executing. Streaming does not work at all with Shotgun. +stream+ finishes executing. Streaming does not work at all with Shotgun.
If the optional parameter is set to +keep_open+, it will not call +close+ on If the optional parameter is set to +keep_open+, it will not call +close+ on
the stream object, allowing you to close it at any later point in the the stream object, allowing you to close it at any later point in the
@ -1012,7 +1012,7 @@ Or use a session:
Setting your headers correctly is the foundation for proper HTTP caching. Setting your headers correctly is the foundation for proper HTTP caching.
You can easily set the Cache-Control header with like this: You can easily set the Cache-Control header like this:
get '/' do get '/' do
cache_control :public cache_control :public
@ -1033,7 +1033,7 @@ If you are using the +expires+ helper to set the corresponding header,
end end
To properly use caches, you should consider using +etag+ or +last_modified+. To properly use caches, you should consider using +etag+ or +last_modified+.
It is recommended to call those helpers *before* doing heavy lifting, as they It is recommended to call those helpers *before* doing any heavy lifting, as they
will immediately flush a response if the client already has the current will immediately flush a response if the client already has the current
version in its cache: version in its cache:
@ -1092,7 +1092,7 @@ For sending files, you can use the <tt>send_file</tt> helper method:
send_file 'foo.png' send_file 'foo.png'
end end
It also takes a couple of options: It also takes options:
send_file 'foo.png', :type => :jpg send_file 'foo.png', :type => :jpg
@ -1193,8 +1193,8 @@ You can also pass it a file name:
=== Dealing with Date and Time === Dealing with Date and Time
Sinatra offers a +time_for+ helper method, which, from the given value Sinatra offers a +time_for+ helper method that generates a Time object
generates a Time object. It is also able to convert +DateTime+, +Date+ and from the given value. It is also able to convert +DateTime+, +Date+ and
similar classes: similar classes:
get '/' do get '/' do
@ -1315,7 +1315,7 @@ You can access those options via <tt>settings</tt>:
Sinatra is using Sinatra is using
{Rack::Protection}[https://github.com/rkh/rack-protection#readme] to defend {Rack::Protection}[https://github.com/rkh/rack-protection#readme] to defend
you application against common, opportunistic attacks. You can easily disable you application against common, opportunistic attacks. You can easily disable
this behavior (which will open your application to tons of common this behavior (which will open up your application to tons of common
vulnerabilities): vulnerabilities):
disable :protection disable :protection
@ -1337,7 +1337,7 @@ You can also hand in an array in order to disable a list of protections:
Enable if your app is running behind a reverse proxy that Enable if your app is running behind a reverse proxy that
has not been set up properly. Note that the +url+ helper has not been set up properly. Note that the +url+ helper
will still produce absolute URLs, unless you pass in will still produce absolute URLs, unless you pass in
+false+ as second parameter. +false+ as the second parameter.
Disabled per default. Disabled per default.
@ -1410,16 +1410,19 @@ You can also hand in an array in order to disable a list of protections:
defaults to ['thin', 'mongrel', 'webrick'], order defaults to ['thin', 'mongrel', 'webrick'], order
indicates priority. indicates priority.
[sessions] enable cookie based sessions support using [sessions] enable cookie-based sessions support using
<tt>Rack::Session::Cookie</tt>. See 'Using Sessions' <tt>Rack::Session::Cookie</tt>. See 'Using Sessions'
section for more information. section for more information.
[show_exceptions] show a stack trace in the browser when an exception [show_exceptions] show a stack trace in the browser when an exception
happens. Enabled by default when <tt>environment</tt> happens. Enabled by default when <tt>environment</tt>
is set to <tt>"development"</tt>, disabled otherwise. is set to <tt>"development"</tt>, disabled otherwise.
Can also be set to <tt>:after_handler</tt> to trigger
app-specified error handling before showing a stack
trace in the browser.
[static] Whether Sinatra should handle serving static files. [static] Whether Sinatra should handle serving static files.
Disable when using a Server able to do this on its own. Disable when using a server able to do this on its own.
Disabling will boost performance. Disabling will boost performance.
Enabled per default in classic style, disabled for Enabled per default in classic style, disabled for
modular apps. modular apps.
@ -1441,17 +1444,17 @@ You can also hand in an array in order to disable a list of protections:
There are three predefined +environments+: <tt>"development"</tt>, There are three predefined +environments+: <tt>"development"</tt>,
<tt>"production"</tt> and <tt>"test"</tt>. Environments can be set <tt>"production"</tt> and <tt>"test"</tt>. Environments can be set
through the +RACK_ENV+ environment variable. The default value is through the +RACK_ENV+ environment variable. The default value is
<tt>"development"</tt>. In this mode, all templates are reloaded between <tt>"development"</tt>. In the <tt>"development"</tt> environment all templates are reloaded between
requests. Special <tt>not_found</tt> and <tt>error</tt> handlers are installed requests, and special <tt>not_found</tt> and <tt>error</tt> handlers
for this environment so you will see a stack trace in your browser. display stack traces in your browser.
In <tt>"production"</tt> and <tt>"test"</tt> templates are cached by default. In the <tt>"production"</tt> and <tt>"test"</tt> environments, templates are cached by default.
To run different environments use the <tt>-e</tt> option: To run different environments use the <tt>-e</tt> option:
ruby my_app.rb -e [ENVIRONMENT] ruby my_app.rb -e [ENVIRONMENT]
You can use predefined methods: +development?+, +test?+ and +production?+ to You can use predefined methods: +development?+, +test?+ and +production?+ to
check which enviroment is currently set. check the current environment setting.
== Error Handling == Error Handling
@ -1608,7 +1611,7 @@ directories, logging, exception detail page, etc.). That's where
end end
end end
The methods available to <tt>Sinatra::Base</tt> subclasses are exactly as those The methods available to <tt>Sinatra::Base</tt> subclasses are exactly the same as those
available via the top-level DSL. Most top-level apps can be converted to available via the top-level DSL. Most top-level apps can be converted to
<tt>Sinatra::Base</tt> components with two modifications: <tt>Sinatra::Base</tt> components with two modifications:
@ -1625,13 +1628,13 @@ for details on available options and their behavior.
=== Modular vs. Classic Style === Modular vs. Classic Style
Contrary to common belief, there is nothing wrong with classic style. If it Contrary to common belief, there is nothing wrong with the classic style. If it
suits your application, you do not have to switch to a modular application. suits your application, you do not have to switch to a modular application.
The main downsides of using classic style rather than modular style is that The main disadvantage of using the classic style rather than the modular style is that
you may only have one Sinatra application per Ruby process. If you plan to use you will only have one Sinatra application per Ruby process. If you plan to use
more than one, switch to modular style. There is no reason you cannot mix more than one, switch to the modular style. There is no reason you cannot mix
modular and classic style. the modular and the classic styles.
If switching from one style to the other, you should be aware of slightly If switching from one style to the other, you should be aware of slightly
different default settings: different default settings:
@ -1665,7 +1668,7 @@ Start with:
ruby my_app.rb ruby my_app.rb
Or with a <tt>config.ru</tt>, which allows using any Rack handler: Or with a <tt>config.ru</tt> file, which allows using any Rack handler:
# config.ru # config.ru
require './my_app' require './my_app'
@ -1693,15 +1696,15 @@ And a corresponding <tt>config.ru</tt>:
=== When to use a config.ru? === When to use a config.ru?
Good signs you probably want to use a <tt>config.ru</tt>: A <tt>config.ru</tt> file is recommended if:
* You want to deploy with a different Rack handler (Passenger, Unicorn, * You want to deploy with a different Rack handler (Passenger, Unicorn,
Heroku, ...). Heroku, ...).
* You want to use more than one subclass of <tt>Sinatra::Base</tt>. * You want to use more than one subclass of <tt>Sinatra::Base</tt>.
* You want to use Sinatra only for middleware, but not as endpoint. * You want to use Sinatra only for middleware, and not as an endpoint.
<b>There is no need to switch to a <tt>config.ru</tt> only because you <b>There is no need to switch to a <tt>config.ru</tt> simply because you
switched to modular style, and you don't have to use modular style for running switched to the modular style, and you don't have to use the modular style for running
with a <tt>config.ru</tt>.</b> with a <tt>config.ru</tt>.</b>
=== Using Sinatra as Middleware === Using Sinatra as Middleware
@ -1749,7 +1752,7 @@ assign them to a constant, you can do this with <tt>Sinatra.new</tt>:
my_app = Sinatra.new { get('/') { "hi" } } my_app = Sinatra.new { get('/') { "hi" } }
my_app.run! my_app.run!
It takes the application to inherit from as optional argument: It takes the application to inherit from as an optional argument:
# config.ru # config.ru
require 'sinatra/base' require 'sinatra/base'
@ -1791,7 +1794,7 @@ Every Sinatra application corresponds to a subclass of <tt>Sinatra::Base</tt>.
If you are using the top-level DSL (<tt>require 'sinatra'</tt>), then this If you are using the top-level DSL (<tt>require 'sinatra'</tt>), then this
class is <tt>Sinatra::Application</tt>, otherwise it is the subclass you class is <tt>Sinatra::Application</tt>, otherwise it is the subclass you
created explicitly. At class level you have methods like +get+ or +before+, but created explicitly. At class level you have methods like +get+ or +before+, but
you cannot access the +request+ object or the +session+, as there only is a you cannot access the +request+ or +session+ objects, as there is only a
single application class for all requests. single application class for all requests.
Options created via +set+ are methods at class level: Options created via +set+ are methods at class level:
@ -1817,13 +1820,13 @@ You have the application scope binding inside:
You can reach the scope object (the class) like this: You can reach the scope object (the class) like this:
* Via the object passed to configure blocks (<tt>configure { |c| ... }</tt>) * Via the object passed to configure blocks (<tt>configure { |c| ... }</tt>)
* +settings+ from within request scope * +settings+ from within the request scope
=== Request/Instance Scope === Request/Instance Scope
For every incoming request, a new instance of your application class is For every incoming request, a new instance of your application class is
created and all handler blocks run in that scope. From within this scope you created and all handler blocks run in that scope. From within this scope you
can access the +request+ and +session+ object or call rendering methods like can access the +request+ and +session+ objects or call rendering methods like
+erb+ or +haml+. You can access the application scope from within the request +erb+ or +haml+. You can access the application scope from within the request
scope via the +settings+ helper: scope via the +settings+ helper:
@ -1852,8 +1855,8 @@ You have the request scope binding inside:
=== Delegation Scope === Delegation Scope
The delegation scope just forwards methods to the class scope. However, it The delegation scope just forwards methods to the class scope. However, it
does not behave 100% like the class scope, as you do not have the class does not behave exactly like the class scope, as you do not have the class
binding. Only methods explicitly marked for delegation are available and you binding. Only methods explicitly marked for delegation are available, and you
do not share variables/state with the class scope (read: you have a different do not share variables/state with the class scope (read: you have a different
+self+). You can explicitly add method delegations by calling +self+). You can explicitly add method delegations by calling
<tt>Sinatra::Delegator.delegate :method_name</tt>. <tt>Sinatra::Delegator.delegate :method_name</tt>.
@ -1889,14 +1892,14 @@ The following Ruby versions are officially supported:
[ Ruby 1.8.7 ] [ Ruby 1.8.7 ]
1.8.7 is fully supported, however, if nothing is keeping you from it, we 1.8.7 is fully supported, however, if nothing is keeping you from it, we
recommend upgrading to 1.9.2 or switching to JRuby or Rubinius. Support for recommend upgrading to 1.9.2 or switching to JRuby or Rubinius. Support for
1.8.7 will not be dropped before Sinatra 2.0 and Ruby 2.0 except maybe for 1.8.7 will not be dropped before Sinatra 2.0 and Ruby 2.0 except maybe in
the unlikely event of 1.8.8 being released. Even then, we might continue the unlikely event of 1.8.8 being released. Even then, we might continue
supporting it. <b>Ruby 1.8.6 is no longer supported.</b> If you want to run supporting it. <b>Ruby 1.8.6 is no longer supported.</b> If you want to run
with 1.8.6, downgrade to Sinatra 1.2, which will receive bug fixes until with 1.8.6, downgrade to Sinatra 1.2, which will receive bug fixes until
Sinatra 1.4.0 is released. Sinatra 1.4.0 is released.
[ Ruby 1.9.2 ] [ Ruby 1.9.2 ]
1.9.2 is fully supported and recommended. Do not use 1.9.2p0, it is known to 1.9.2 is fully supported and recommended. Do not use 1.9.2p0, as it is known to
cause segmentation faults when running Sinatra. Support will continue at least cause segmentation faults when running Sinatra. Support will continue at least
until the release of Ruby 1.9.4/2.0 and support for the latest 1.9 release until the release of Ruby 1.9.4/2.0 and support for the latest 1.9 release
will continue as long as it is still supported by the Ruby core team. will continue as long as it is still supported by the Ruby core team.
@ -1906,8 +1909,8 @@ The following Ruby versions are officially supported:
from an earlier version will invalidate all sessions. from an earlier version will invalidate all sessions.
[ Rubinius ] [ Rubinius ]
Rubinius is officially supported (Rubinius >= 1.2.4), everything, including Rubinius is officially supported (Rubinius >= 1.2.4), everything works, including
all template languages, works. The upcoming 2.0 release is supported as all template languages. The upcoming 2.0 release is supported as
well, including 1.9 mode. well, including 1.9 mode.
[ JRuby ] [ JRuby ]
@ -1938,12 +1941,12 @@ both 1.9.4p0 and 2.0.0p0 to be supported.
Sinatra should work on any operating system supported by the chosen Ruby Sinatra should work on any operating system supported by the chosen Ruby
implementation. implementation.
You will not be able to run Sinatra on Cardinal, SmallRuby, BlueRuby or any Sinatra currently doesn't run on Cardinal, SmallRuby, BlueRuby or any
Ruby version prior to 1.8.7 as of the time being. Ruby version prior to 1.8.7.
== The Bleeding Edge == The Bleeding Edge
If you would like to use Sinatra's latest bleeding code, feel free to run your If you would like to use Sinatra's latest bleeding-edge code, feel free to run your
application against the master branch, it should be rather stable. application against the master branch, it should be rather stable.
We also push out prerelease gems from time to time, so you can do a We also push out prerelease gems from time to time, so you can do a
@ -1970,7 +1973,7 @@ Then, in your project directory, create a +Gemfile+:
gem 'haml' # for instance, if you use haml gem 'haml' # for instance, if you use haml
gem 'activerecord', '~> 3.0' # maybe you also need ActiveRecord 3.x gem 'activerecord', '~> 3.0' # maybe you also need ActiveRecord 3.x
Note that you will have to list all your applications dependencies in there. Note that you will have to list all your application's dependencies in the +Gemfile+.
Sinatra's direct dependencies (Rack and Tilt) will, however, be automatically Sinatra's direct dependencies (Rack and Tilt) will, however, be automatically
fetched and added by Bundler. fetched and added by Bundler.

View File

@ -56,7 +56,7 @@ Thin — это более производительный и функцион
Маршруты сверяются с запросом в порядке очередности их записи в файле приложения. Маршруты сверяются с запросом в порядке очередности их записи в файле приложения.
Первый же совпавший с запросом маршрут и будет вызван. Первый же совпавший с запросом маршрут и будет вызван.
Шаблоны маршрутов могут включать в себя именные параметры доступные Шаблоны маршрутов могут включать в себя именованные параметры доступные
в xэше <tt>params</tt>: в xэше <tt>params</tt>:
get '/hello/:name' do get '/hello/:name' do
@ -65,7 +65,7 @@ Thin — это более производительный и функцион
"Hello #{params[:name]}!" "Hello #{params[:name]}!"
end end
Также можно использовать именные параметры в качестве переменных Также можно использовать именованные параметры в качестве переменных
блока: блока:
get '/hello/:name' do |n| get '/hello/:name' do |n|
@ -117,7 +117,7 @@ Thin — это более производительный и функцион
=== Условия === Условия
Маршруты могут включать различные условия совпадений, например, Маршруты могут включать различные условия совпадений, например,
клиентской приложение (user agent): клиентское приложение (user agent):
get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do
"You're using Songbird version #{params[:agent][0]}" "You're using Songbird version #{params[:agent][0]}"
@ -544,6 +544,15 @@ Thin — это более производительный и функцион
var resource = {"foo":"bar","baz":"qux"}; present(resource); var resource = {"foo":"bar","baz":"qux"}; present(resource);
=== WLang шаблоны
Зависимости:: {wlang}[https://github.com/blambeau/wlang/]
Расширения файлов:: <tt>.wlang</tt>
Пример:: <tt>wlang :index, :locals => { :key => 'value' }</tt>
Так как в WLang шаблонах невозможно вызывать методы из Ruby напрямую (за исключением +yield+), то
вы почти всегда будете передавать в шаблон локальные переменные.
=== Встроенные шаблоны === Встроенные шаблоны
get '/' do get '/' do
@ -594,7 +603,7 @@ Thin — это более производительный и функцион
загружены автоматически. Вызовите <tt>enable :inline_templates</tt> напрямую, если загружены автоматически. Вызовите <tt>enable :inline_templates</tt> напрямую, если
используете вложенные шаблоны в других файлах. используете вложенные шаблоны в других файлах.
=== Именные шаблоны === Именованные шаблоны
Шаблоны также могут быть определены при помощи <tt>template</tt> метода: Шаблоны также могут быть определены при помощи <tt>template</tt> метода:
@ -1314,7 +1323,7 @@ Sinatra предлагает метод-помощник +time_for+, котор
Sinatra использует Sinatra использует
{Rack::Protection}[https://github.com/rkh/rack-protection#readme] для {Rack::Protection}[https://github.com/rkh/rack-protection#readme] для
защиты приложения от простых атак. Вы можете легко выключить эту защиты приложения от простых атак. Вы можете легко выключить эту
защиту (что даст прирост в производительности): защиту (что сделает ваше приложение чрезвычайно уязвимым):
disable :protection disable :protection
@ -1411,6 +1420,9 @@ Sinatra использует
[show_exceptions] показывать исключения/стек вызовов (stack trace) в браузере. [show_exceptions] показывать исключения/стек вызовов (stack trace) в браузере.
По умолчанию включено только в окружении <tt>development</tt>. По умолчанию включено только в окружении <tt>development</tt>.
Может быть установлено в <tt>:after_handler</tt> для запуска специфичной
для приложения обработки ошибок, прежде чем показывать трассировку
стека в браузере.
[static] должна ли Sinatra осуществлять раздачу статических файлов. [static] должна ли Sinatra осуществлять раздачу статических файлов.
Отключите, когда используете какой-либо веб-сервер для этой цели. Отключите, когда используете какой-либо веб-сервер для этой цели.

View File

@ -25,11 +25,11 @@ __END__
@@ layout @@ layout
<html> <html>
<head> <head>
<title>Super Simple Chat with Sinatra</title> <title>Super Simple Chat with Sinatra</title>
<meta charset="utf-8" /> <meta charset="utf-8" />
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
</head> </head>
<body><%= yield %></body> <body><%= yield %></body>
</html> </html>
@ -58,4 +58,4 @@ __END__
<form> <form>
<input id='msg' placeholder='type message here...' /> <input id='msg' placeholder='type message here...' />
</form> </form>

View File

@ -1,7 +1,7 @@
# external dependencies # external dependencies
require 'rack' require 'rack'
require 'tilt' require 'tilt'
require "rack/protection" require 'rack/protection'
# stdlib dependencies # stdlib dependencies
require 'thread' require 'thread'
@ -1314,17 +1314,21 @@ module Sinatra
def compile(path) def compile(path)
keys = [] keys = []
if path.respond_to? :to_str if path.respond_to? :to_str
pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) { |c| encoded(c) } ignore = ""
pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c|
ignore << escaped(c).join if c.match(/[\.@]/)
encoded(c)
end
pattern.gsub!(/((:\w+)|\*)/) do |match| pattern.gsub!(/((:\w+)|\*)/) do |match|
if match == "*" if match == "*"
keys << 'splat' keys << 'splat'
"(.*?)" "(.*?)"
else else
keys << $2[1..-1] keys << $2[1..-1]
"([^/?#]+)" "([^#{ignore}/?#]+)"
end end
end end
[/^#{pattern}$/, keys] [/\A#{pattern}\z/, keys]
elsif path.respond_to?(:keys) && path.respond_to?(:match) elsif path.respond_to?(:keys) && path.respond_to?(:match)
[path, path.keys] [path, path.keys]
elsif path.respond_to?(:names) && path.respond_to?(:match) elsif path.respond_to?(:names) && path.respond_to?(:match)
@ -1340,11 +1344,15 @@ module Sinatra
def encoded(char) def encoded(char)
enc = URI.escape(char) enc = URI.escape(char)
enc = "(?:#{Regexp.escape enc}|#{URI.escape char, /./})" if enc == char enc = "(?:#{escaped(char, enc).join('|')})" if enc == char
enc = "(?:#{enc}|#{encoded('+')})" if char == " " enc = "(?:#{enc}|#{encoded('+')})" if char == " "
enc enc
end end
def escaped(char, enc = URI.escape(char))
[Regexp.escape(enc), URI.escape(char, /./)]
end
public public
# Makes the methods defined in the block and in the Modules given # Makes the methods defined in the block and in the Modules given
# in `extensions` available to the handlers and templates # in `extensions` available to the handlers and templates

View File

@ -10,7 +10,7 @@ Gem::Specification.new 'sinatra', Sinatra::VERSION do |s|
s.files = `git ls-files`.split("\n") - %w[.gitignore .travis.yml] s.files = `git ls-files`.split("\n") - %w[.gitignore .travis.yml]
s.test_files = s.files.select { |p| p =~ /^test\/.*_test.rb/ } s.test_files = s.files.select { |p| p =~ /^test\/.*_test.rb/ }
s.extra_rdoc_files = s.files.select { |p| p =~ /^README/ } << 'LICENSE' s.extra_rdoc_files = s.files.select { |p| p =~ /^README/ } << 'LICENSE'
s.rdoc_options = %w[--line-numbers --inline-source --title Sinatra --main README.rdoc] s.rdoc_options = %w[--line-numbers --inline-source --title Sinatra --main README.rdoc --encoding=UTF-8]
s.add_dependency 'rack', '~> 1.3', '>= 1.3.6' s.add_dependency 'rack', '~> 1.3', '>= 1.3.6'
s.add_dependency 'rack-protection', '~> 1.2' s.add_dependency 'rack-protection', '~> 1.2'

139
test/compile_test.rb Normal file
View File

@ -0,0 +1,139 @@
# I like coding: UTF-8
require File.expand_path('../helper', __FILE__)
class CompileTest < Test::Unit::TestCase
def self.converts pattern, expected_regexp
it "generates #{expected_regexp.source} from #{pattern}" do
compiled, _ = compiled pattern
assert_equal expected_regexp, compiled
end
end
def self.parses pattern, example, expected_params
it "parses #{example} with #{pattern} into params #{expected_params}" do
compiled, keys = compiled pattern
match = compiled.match(example)
fail %Q{"#{example}" does not parse on pattern "#{pattern}".} unless match
# Aggregate e.g. multiple splat values into one array.
#
params = keys.zip(match.captures).reduce({}) do |hash, mapping|
key, value = mapping
hash[key] = if existing = hash[key]
existing.respond_to?(:to_ary) ? existing << value : [existing, value]
else
value
end
hash
end
assert_equal(expected_params, params)
end
end
def self.fails pattern, example
it "does not parse #{example} with #{pattern}" do
compiled, _ = compiled pattern
match = compiled.match(example)
fail %Q{"#{pattern}" does parse "#{example}" but it should fail} if match
end
end
def compiled pattern
app ||= mock_app {}
compiled, keys = app.send(:compile, pattern)
[compiled, keys]
end
converts "/", %r{\A/\z}
parses "/", "/", {}
converts "/foo", %r{\A/foo\z}
parses "/foo", "/foo", {}
converts "/:foo", %r{\A/([^/?#]+)\z}
parses "/:foo", "/foo", "foo" => "foo"
parses "/:foo", "/foo.bar", "foo" => "foo.bar"
parses "/:foo", "/foo%2Fbar", "foo" => "foo%2Fbar"
fails "/:foo", "/foo?"
fails "/:foo", "/foo/bar"
fails "/:foo", "/"
fails "/:foo", "/foo/"
converts "/föö", %r{\A/f%C3%B6%C3%B6\z}
parses "/föö", "/f%C3%B6%C3%B6", {}
converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}
parses "/:foo/:bar", "/foo/bar", "foo" => "foo", "bar" => "bar"
converts "/hello/:person", %r{\A/hello/([^/?#]+)\z}
parses "/hello/:person", "/hello/Frank", "person" => "Frank"
converts "/?:foo?/?:bar?", %r{\A/?([^/?#]+)?/?([^/?#]+)?\z}
parses "/?:foo?/?:bar?", "/hello/world", "foo" => "hello", "bar" => "world"
parses "/?:foo?/?:bar?", "/hello", "foo" => "hello", "bar" => nil
parses "/?:foo?/?:bar?", "/", "foo" => nil, "bar" => nil
parses "/?:foo?/?:bar?", "", "foo" => nil, "bar" => nil
converts "/*", %r{\A/(.*?)\z}
parses "/*", "/", "splat" => ""
parses "/*", "/foo", "splat" => "foo"
parses "/*", "/foo/bar", "splat" => "foo/bar"
converts "/:foo/*", %r{\A/([^/?#]+)/(.*?)\z}
parses "/:foo/*", "/foo/bar/baz", "foo" => "foo", "splat" => "bar/baz"
converts "/:foo/:bar", %r{\A/([^/?#]+)/([^/?#]+)\z}
parses "/:foo/:bar", "/user@example.com/name", "foo" => "user@example.com", "bar" => "name"
converts "/test$/", %r{\A/test(?:\$|%24)/\z}
parses "/test$/", "/test$/", {}
converts "/te+st/", %r{\A/te(?:\+|%2B)st/\z}
parses "/te+st/", "/te+st/", {}
fails "/te+st/", "/test/"
fails "/te+st/", "/teeest/"
converts "/test(bar)/", %r{\A/test(?:\(|%28)bar(?:\)|%29)/\z}
parses "/test(bar)/", "/test(bar)/", {}
converts "/path with spaces", %r{\A/path(?:%20|(?:\+|%2B))with(?:%20|(?:\+|%2B))spaces\z}
parses "/path with spaces", "/path%20with%20spaces", {}
parses "/path with spaces", "/path%2Bwith%2Bspaces", {}
parses "/path with spaces", "/path+with+spaces", {}
converts "/foo&bar", %r{\A/foo(?:&|%26)bar\z}
parses "/foo&bar", "/foo&bar", {}
converts "/:foo/*", %r{\A/([^/?#]+)/(.*?)\z}
parses "/:foo/*", "/hello%20world/how%20are%20you", "foo" => "hello%20world", "splat" => "how%20are%20you"
converts "/*/foo/*/*", %r{\A/(.*?)/foo/(.*?)/(.*?)\z}
parses "/*/foo/*/*", "/bar/foo/bling/baz/boom", "splat" => ["bar", "bling", "baz/boom"]
fails "/*/foo/*/*", "/bar/foo/baz"
converts "/test.bar", %r{\A/test(?:\.|%2E)bar\z}
parses "/test.bar", "/test.bar", {}
fails "/test.bar", "/test0bar"
converts "/:file.:ext", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)([^\.%2E/?#]+)\z}
parses "/:file.:ext", "/pony.jpg", "file" => "pony", "ext" => "jpg"
parses "/:file.:ext", "/pony%2Ejpg", "file" => "pony", "ext" => "jpg"
fails "/:file.:ext", "/.jpg"
converts "/:name.?:format?", %r{\A/([^\.%2E/?#]+)(?:\.|%2E)?([^\.%2E/?#]+)?\z}
parses "/:name.?:format?", "/foo", "name" => "foo", "format" => nil
parses "/:name.?:format?", "/foo.bar", "name" => "foo", "format" => "bar"
parses "/:name.?:format?", "/foo%2Ebar", "name" => "foo", "format" => "bar"
fails "/:name.?:format?", "/.bar"
converts "/:user@?:host?", %r{\A/([^@%40/?#]+)(?:@|%40)?([^@%40/?#]+)?\z}
parses "/:user@?:host?", "/foo@bar", "user" => "foo", "host" => "bar"
parses "/:user@?:host?", "/foo.foo@bar", "user" => "foo.foo", "host" => "bar"
parses "/:user@?:host?", "/foo@bar.bar", "user" => "foo", "host" => "bar.bar"
# From https://gist.github.com/2154980#gistcomment-169469.
#
# converts "/:name(.:format)?", %r{\A/([^\.%2E/?#]+)(?:\(|%28)(?:\.|%2E)([^\.%2E/?#]+)(?:\)|%29)?\z}
# parses "/:name(.:format)?", "/foo", "name" => "foo", "format" => nil
# parses "/:name(.:format)?", "/foo.bar", "name" => "foo", "format" => "bar"
fails "/:name(.:format)?", "/foo."
end

View File

@ -1,15 +1,15 @@
# Copyright (c) 2009 Damian Janowski and Michel Martens for Citrusbyte # Copyright (c) 2009 Damian Janowski and Michel Martens for Citrusbyte
# #
# Permission is hereby granted, free of charge, to any person obtaining a copy # Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal # of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights # in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is # copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions: # furnished to do so, subject to the following conditions:
# #
# The above copyright notice and this permission notice shall be included in # The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software. # all copies or substantial portions of the Software.
# #
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE

View File

@ -586,21 +586,21 @@ class HelpersTest < Test::Unit::TestCase
end end
end end
end end
it 'sets the Content-Type response header' do it 'sets the Content-Type response header' do
attachment_app('test.xml') attachment_app('test.xml')
get '/attachment' get '/attachment'
assert_equal 'application/xml;charset=utf-8', response['Content-Type'] assert_equal 'application/xml;charset=utf-8', response['Content-Type']
assert_equal '<sinatra></sinatra>', body assert_equal '<sinatra></sinatra>', body
end end
it 'sets the Content-Type response header without extname' do it 'sets the Content-Type response header without extname' do
attachment_app('test') attachment_app('test')
get '/attachment' get '/attachment'
assert_equal 'text/html;charset=utf-8', response['Content-Type'] assert_equal 'text/html;charset=utf-8', response['Content-Type']
assert_equal '<sinatra></sinatra>', body assert_equal '<sinatra></sinatra>', body
end end
it 'sets the Content-Type response header with extname' do it 'sets the Content-Type response header with extname' do
mock_app do mock_app do
get('/attachment') do get('/attachment') do
@ -612,9 +612,9 @@ class HelpersTest < Test::Unit::TestCase
get '/attachment' get '/attachment'
assert_equal 'application/atom+xml', response['Content-Type'] assert_equal 'application/atom+xml', response['Content-Type']
assert_equal '<sinatra></sinatra>', body assert_equal '<sinatra></sinatra>', body
end end
end end
describe 'send_file' do describe 'send_file' do

View File

@ -44,7 +44,7 @@ class LiquidTest < Test::Unit::TestCase
mock_app { get('/') { liquid :no_such_template } } mock_app { get('/') { liquid :no_such_template } }
assert_raise(Errno::ENOENT) { get('/') } assert_raise(Errno::ENOENT) { get('/') }
end end
it "allows passing locals" do it "allows passing locals" do
liquid_app { liquid_app {
liquid '{{ value }}', :locals => { :value => 'foo' } liquid '{{ value }}', :locals => { :value => 'foo' }

View File

@ -211,7 +211,7 @@ class MappedErrorTest < Test::Unit::TestCase
assert_equal 500, status assert_equal 500, status
end end
it "allows a stack of exception_handlers" do it "allows a stack of exception_handlers" do
mock_app do mock_app do
set :raise_errors, false set :raise_errors, false
error(FirstError) { 'First!' } error(FirstError) { 'First!' }
@ -223,7 +223,7 @@ class MappedErrorTest < Test::Unit::TestCase
assert_equal 'Second!', body assert_equal 'Second!', body
end end
it "allows an exception handler to pass control to the next exception handler" do it "allows an exception handler to pass control to the next exception handler" do
mock_app do mock_app do
set :raise_errors, false set :raise_errors, false
error(500, FirstError) { 'First!' } error(500, FirstError) { 'First!' }

View File

@ -55,7 +55,7 @@ class MarkabyTest < Test::Unit::TestCase
assert_equal "<h1>THIS. IS. <em>SPARTA</em></h1>", body assert_equal "<h1>THIS. IS. <em>SPARTA</em></h1>", body
end end
it 'renders inline markaby blocks with file layouts' do it 'renders inline markaby blocks with file layouts' do
markaby_app { markaby(:layout => :layout2) { text "Hello World" } } markaby_app { markaby(:layout => :layout2) { text "Hello World" } }
assert ok? assert ok?
assert_equal "<h1>Markaby Layout!</h1><p>Hello World</p>", body assert_equal "<h1>Markaby Layout!</h1><p>Hello World</p>", body
@ -65,7 +65,7 @@ class MarkabyTest < Test::Unit::TestCase
mock_app { get('/') { markaby :no_such_template } } mock_app { get('/') { markaby :no_such_template } }
assert_raise(Errno::ENOENT) { get('/') } assert_raise(Errno::ENOENT) { get('/') }
end end
it "allows passing locals" do it "allows passing locals" do
markaby_app { markaby_app {
markaby 'text value', :locals => { :value => 'foo' } markaby 'text value', :locals => { :value => 'foo' }

View File

@ -34,7 +34,7 @@ class RablTest < Test::Unit::TestCase
assert ok? assert ok?
assert_equal '{"openstruct":{"bar":"baz"}}', body assert_equal '{"openstruct":{"bar":"baz"}}', body
end end
it "renders with file layouts" do it "renders with file layouts" do
rabl_app { rabl_app {
@foo = OpenStruct.new(:bar => 'baz') @foo = OpenStruct.new(:bar => 'baz')
@ -43,14 +43,14 @@ class RablTest < Test::Unit::TestCase
assert ok? assert ok?
assert_equal '{"qux":{"openstruct":{"bar":"baz"}}}', body assert_equal '{"qux":{"openstruct":{"bar":"baz"}}}', body
end end
it "raises error if template not found" do it "raises error if template not found" do
mock_app { mock_app {
get('/') { rabl :no_such_template } get('/') { rabl :no_such_template }
} }
assert_raise(Errno::ENOENT) { get('/') } assert_raise(Errno::ENOENT) { get('/') }
end end
it "passes rabl options to the rabl engine" do it "passes rabl options to the rabl engine" do
mock_app do mock_app do
get('/') do get('/') do
@ -65,7 +65,7 @@ class RablTest < Test::Unit::TestCase
assert ok? assert ok?
assert_body '<?xml version="1.0" encoding="UTF-8"?><openstruct><bar>baz</bar></openstruct>' assert_body '<?xml version="1.0" encoding="UTF-8"?><openstruct><bar>baz</bar></openstruct>'
end end
it "passes default rabl options to the rabl engine" do it "passes default rabl options to the rabl engine" do
mock_app do mock_app do
set :rabl, :format => 'xml' set :rabl, :format => 'xml'
@ -81,7 +81,7 @@ class RablTest < Test::Unit::TestCase
assert ok? assert ok?
assert_body '<?xml version="1.0" encoding="UTF-8"?><openstruct><bar>baz</bar></openstruct>' assert_body '<?xml version="1.0" encoding="UTF-8"?><openstruct><bar>baz</bar></openstruct>'
end end
end end
rescue LoadError rescue LoadError

View File

@ -44,7 +44,7 @@ class RadiusTest < Test::Unit::TestCase
mock_app { get('/') { radius :no_such_template } } mock_app { get('/') { radius :no_such_template } }
assert_raise(Errno::ENOENT) { get('/') } assert_raise(Errno::ENOENT) { get('/') }
end end
it "allows passing locals" do it "allows passing locals" do
radius_app { radius_app {
radius '<r:value />', :locals => { :value => 'foo' } radius '<r:value />', :locals => { :value => 'foo' }

View File

@ -47,13 +47,13 @@ class RouteAddedHookTest < Test::Unit::TestCase
assert_equal [["GET", "/"], ["HEAD", "/"]], assert_equal [["GET", "/"], ["HEAD", "/"]],
RouteAddedTest.routes RouteAddedTest.routes
end end
it "should pass route blocks as an argument" do it "should pass route blocks as an argument" do
mock_app(Class.new(Sinatra::Base)) do mock_app(Class.new(Sinatra::Base)) do
register RouteAddedTest register RouteAddedTest
get('/') {} get('/') {}
end end
assert_kind_of Proc, RouteAddedTest.procs.first assert_kind_of Proc, RouteAddedTest.procs.first
end end
end end

View File

@ -75,9 +75,9 @@ class ScssTest < Test::Unit::TestCase
mock_app do mock_app do
set :scss, {:style => :compact} # default scss style is :nested set :scss, {:style => :compact} # default scss style is :nested
get('/') { get('/') {
scss("#scss {\n background-color: white;\n color: black;\n}") scss("#scss {\n background-color: white;\n color: black;\n}")
} }
end end
get '/' get '/'
assert ok? assert ok?
assert_equal "#scss { background-color: white; color: black; }\n", body assert_equal "#scss { background-color: white; color: black; }\n", body

View File

@ -5,7 +5,7 @@ class SinatraTest < Test::Unit::TestCase
app = Sinatra.new { get('/') { 'Hello World' } } app = Sinatra.new { get('/') { 'Hello World' } }
assert_same Sinatra::Base, app.superclass assert_same Sinatra::Base, app.superclass
end end
it "responds to #template_cache" do it "responds to #template_cache" do
assert_kind_of Tilt::Cache, Sinatra::Base.new!.template_cache assert_kind_of Tilt::Cache, Sinatra::Base.new!.template_cache
end end

View File

@ -17,13 +17,13 @@ class SlimTest < Test::Unit::TestCase
assert ok? assert ok?
assert_equal "<h1>Hiya</h1>", body assert_equal "<h1>Hiya</h1>", body
end end
it 'renders .slim files in views path' do it 'renders .slim files in views path' do
slim_app { slim :hello } slim_app { slim :hello }
assert ok? assert ok?
assert_equal "<h1>Hello From Slim</h1>", body assert_equal "<h1>Hello From Slim</h1>", body
end end
it "renders with inline layouts" do it "renders with inline layouts" do
mock_app do mock_app do
layout { %(h1\n | THIS. IS. \n == yield.upcase ) } layout { %(h1\n | THIS. IS. \n == yield.upcase ) }
@ -33,27 +33,27 @@ class SlimTest < Test::Unit::TestCase
assert ok? assert ok?
assert_equal "<h1>THIS. IS. <EM>SPARTA</EM></h1>", body assert_equal "<h1>THIS. IS. <EM>SPARTA</EM></h1>", body
end end
it "renders with file layouts" do it "renders with file layouts" do
slim_app { slim('| Hello World', :layout => :layout2) } slim_app { slim('| Hello World', :layout => :layout2) }
assert ok? assert ok?
assert_equal "<h1>Slim Layout!</h1><p>Hello World</p>", body assert_equal "<h1>Slim Layout!</h1><p>Hello World</p>", body
end end
it "raises error if template not found" do it "raises error if template not found" do
mock_app { get('/') { slim(:no_such_template) } } mock_app { get('/') { slim(:no_such_template) } }
assert_raise(Errno::ENOENT) { get('/') } assert_raise(Errno::ENOENT) { get('/') }
end end
HTML4_DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" HTML4_DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
it "passes slim options to the slim engine" do it "passes slim options to the slim engine" do
mock_app { get('/') { slim("x foo='bar'", :attr_wrapper => "'") }} mock_app { get('/') { slim("x foo='bar'", :attr_wrapper => "'") }}
get '/' get '/'
assert ok? assert ok?
assert_body "<x foo='bar'></x>" assert_body "<x foo='bar'></x>"
end end
it "passes default slim options to the slim engine" do it "passes default slim options to the slim engine" do
mock_app do mock_app do
set :slim, :attr_wrapper => "'" set :slim, :attr_wrapper => "'"
@ -63,7 +63,7 @@ class SlimTest < Test::Unit::TestCase
assert ok? assert ok?
assert_body "<x foo='bar'></x>" assert_body "<x foo='bar'></x>"
end end
it "merges the default slim options with the overrides and passes them to the slim engine" do it "merges the default slim options with the overrides and passes them to the slim engine" do
mock_app do mock_app do
set :slim, :attr_wrapper => "'" set :slim, :attr_wrapper => "'"

View File

@ -288,7 +288,7 @@ __END__
@@ foo @@ foo
this is foo this is foo
@@ bar @@ bar
There's a space after 'bar'! There's a space after 'bar'!
@@ foo bar @@ foo bar

View File

@ -50,7 +50,7 @@ class YajlTest < Test::Unit::TestCase
it 'decorates the json with a callback' do it 'decorates the json with a callback' do
yajl_app do yajl_app do
yajl( yajl(
'json = { :foo => "bar" }', 'json = { :foo => "bar" }',
{ :callback => 'baz' } { :callback => 'baz' }
) )
end end