diff --git a/README.es.rdoc b/README.es.rdoc index 7fc9d31d..3ac03910 100644 --- a/README.es.rdoc +++ b/README.es.rdoc @@ -551,6 +551,15 @@ Las opciones :callback y :variable se pueden utilizar para dec var resource = {"foo":"bar","baz":"qux"}; present(resource); +=== Plantillas WLang + +Dependencias:: {wlang}[https://github.com/blambeau/wlang/] +Extensiones de Archivo:: .wlang +Ejemplo:: wlang :index, :locals => { :clave => 'valor' } + +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 get '/' do @@ -1349,10 +1358,10 @@ Podés acceder a estas opciones utilizando el método settings: === Configurando la Protección de Ataques 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 -cuenta que como consecuencia de esto puede venir asociada una disminución del -rendimiento de tu aplicación. Si por este, o algún otro motivo, querés -desactivar está funcionalidad, podés hacerlo: +para defender a tu aplicación de los ataques más comunes. Si por algún motivo, +querés desactivar esta funcionalidad, podés hacerlo como se indica a +continuación (tené en cuenta que tu aplicación va a quedar expuesta a un +montón de vulnerabilidades bien conocidas): disable :protection diff --git a/README.fr.rdoc b/README.fr.rdoc index 12173b3d..fa71a4cf 100644 --- a/README.fr.rdoc +++ b/README.fr.rdoc @@ -1,5 +1,4 @@ -= Sinatra - += Sinatra Attention : Ce document correspond à la traduction de la version anglaise et il n'est peut être plus à jour. diff --git a/README.rdoc b/README.rdoc index cd636c61..0e16e241 100644 --- a/README.rdoc +++ b/README.rdoc @@ -251,7 +251,7 @@ Use the :static_cache_control setting (see below) to add == 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: get '/' do @@ -755,7 +755,7 @@ middleware of choice as you would any other middleware: end 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 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, {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 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, 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 -+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 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. -You can easily set the Cache-Control header with like this: +You can easily set the Cache-Control header like this: get '/' do cache_control :public @@ -1033,7 +1033,7 @@ If you are using the +expires+ helper to set the corresponding header, end 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 version in its cache: @@ -1092,7 +1092,7 @@ For sending files, you can use the send_file helper method: send_file 'foo.png' end -It also takes a couple of options: +It also takes options: send_file 'foo.png', :type => :jpg @@ -1193,8 +1193,8 @@ You can also pass it a file name: === Dealing with Date and Time -Sinatra offers a +time_for+ helper method, which, from the given value -generates a Time object. It is also able to convert +DateTime+, +Date+ and +Sinatra offers a +time_for+ helper method that generates a Time object +from the given value. It is also able to convert +DateTime+, +Date+ and similar classes: get '/' do @@ -1315,7 +1315,7 @@ You can access those options via settings: Sinatra is using {Rack::Protection}[https://github.com/rkh/rack-protection#readme] to defend 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): 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 has not been set up properly. Note that the +url+ helper will still produce absolute URLs, unless you pass in - +false+ as second parameter. + +false+ as the second parameter. 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 indicates priority. -[sessions] enable cookie based sessions support using +[sessions] enable cookie-based sessions support using Rack::Session::Cookie. See 'Using Sessions' section for more information. [show_exceptions] show a stack trace in the browser when an exception happens. Enabled by default when environment is set to "development", disabled otherwise. + Can also be set to :after_handler to trigger + app-specified error handling before showing a stack + trace in the browser. [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. Enabled per default in classic style, disabled for 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+: "development", "production" and "test". Environments can be set through the +RACK_ENV+ environment variable. The default value is -"development". In this mode, all templates are reloaded between -requests. Special not_found and error handlers are installed -for this environment so you will see a stack trace in your browser. -In "production" and "test" templates are cached by default. +"development". In the "development" environment all templates are reloaded between +requests, and special not_found and error handlers +display stack traces in your browser. +In the "production" and "test" environments, templates are cached by default. To run different environments use the -e option: ruby my_app.rb -e [ENVIRONMENT] You can use predefined methods: +development?+, +test?+ and +production?+ to -check which enviroment is currently set. +check the current environment setting. == Error Handling @@ -1608,7 +1611,7 @@ directories, logging, exception detail page, etc.). That's where end end -The methods available to Sinatra::Base subclasses are exactly as those +The methods available to Sinatra::Base subclasses are exactly the same as those available via the top-level DSL. Most top-level apps can be converted to Sinatra::Base components with two modifications: @@ -1625,13 +1628,13 @@ for details on available options and their behavior. === 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. -The main downsides of using classic style rather than modular style is that -you may 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 -modular and classic style. +The main disadvantage of using the classic style rather than the modular style is that +you will only have one Sinatra application per Ruby process. If you plan to use +more than one, switch to the modular style. There is no reason you cannot mix +the modular and the classic styles. If switching from one style to the other, you should be aware of slightly different default settings: @@ -1665,7 +1668,7 @@ Start with: ruby my_app.rb -Or with a config.ru, which allows using any Rack handler: +Or with a config.ru file, which allows using any Rack handler: # config.ru require './my_app' @@ -1693,15 +1696,15 @@ And a corresponding config.ru: === When to use a config.ru? -Good signs you probably want to use a config.ru: +A config.ru file is recommended if: * You want to deploy with a different Rack handler (Passenger, Unicorn, Heroku, ...). * You want to use more than one subclass of Sinatra::Base. -* 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. -There is no need to switch to a config.ru only because you -switched to modular style, and you don't have to use modular style for running +There is no need to switch to a config.ru simply because you +switched to the modular style, and you don't have to use the modular style for running with a config.ru. === Using Sinatra as Middleware @@ -1749,7 +1752,7 @@ assign them to a constant, you can do this with Sinatra.new: my_app = Sinatra.new { get('/') { "hi" } } 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 require 'sinatra/base' @@ -1791,7 +1794,7 @@ Every Sinatra application corresponds to a subclass of Sinatra::Base. If you are using the top-level DSL (require 'sinatra'), then this class is Sinatra::Application, otherwise it is the subclass you 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. 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: * Via the object passed to configure blocks (configure { |c| ... }) -* +settings+ from within request scope +* +settings+ from within the request scope === Request/Instance Scope 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 -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 scope via the +settings+ helper: @@ -1852,8 +1855,8 @@ You have the request scope binding inside: === Delegation Scope 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 -binding. Only methods explicitly marked for delegation are available and you +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 do not share variables/state with the class scope (read: you have a different +self+). You can explicitly add method delegations by calling Sinatra::Delegator.delegate :method_name. @@ -1889,14 +1892,14 @@ The following Ruby versions are officially supported: [ Ruby 1.8.7 ] 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 - 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 supporting it. Ruby 1.8.6 is no longer supported. If you want to run with 1.8.6, downgrade to Sinatra 1.2, which will receive bug fixes until Sinatra 1.4.0 is released. [ 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 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. @@ -1906,8 +1909,8 @@ The following Ruby versions are officially supported: from an earlier version will invalidate all sessions. [ Rubinius ] - Rubinius is officially supported (Rubinius >= 1.2.4), everything, including - all template languages, works. The upcoming 2.0 release is supported as + Rubinius is officially supported (Rubinius >= 1.2.4), everything works, including + all template languages. The upcoming 2.0 release is supported as well, including 1.9 mode. [ 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 implementation. -You will not be able to run Sinatra on Cardinal, SmallRuby, BlueRuby or any -Ruby version prior to 1.8.7 as of the time being. +Sinatra currently doesn't run on Cardinal, SmallRuby, BlueRuby or any +Ruby version prior to 1.8.7. == 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. 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 '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 fetched and added by Bundler. diff --git a/README.ru.rdoc b/README.ru.rdoc index 61450c0d..49082a52 100644 --- a/README.ru.rdoc +++ b/README.ru.rdoc @@ -56,7 +56,7 @@ Thin — это более производительный и функцион Маршруты сверяются с запросом в порядке очередности их записи в файле приложения. Первый же совпавший с запросом маршрут и будет вызван. -Шаблоны маршрутов могут включать в себя именные параметры доступные +Шаблоны маршрутов могут включать в себя именованные параметры доступные в xэше params: get '/hello/:name' do @@ -65,7 +65,7 @@ Thin — это более производительный и функцион "Hello #{params[:name]}!" end -Также можно использовать именные параметры в качестве переменных +Также можно использовать именованные параметры в качестве переменных блока: get '/hello/:name' do |n| @@ -117,7 +117,7 @@ Thin — это более производительный и функцион === Условия Маршруты могут включать различные условия совпадений, например, -клиентской приложение (user agent): +клиентское приложение (user agent): get '/foo', :agent => /Songbird (\d\.\d)[\d\/]*?/ do "You're using Songbird version #{params[:agent][0]}" @@ -544,6 +544,15 @@ Thin — это более производительный и функцион var resource = {"foo":"bar","baz":"qux"}; present(resource); +=== WLang шаблоны + +Зависимости:: {wlang}[https://github.com/blambeau/wlang/] +Расширения файлов:: .wlang +Пример:: wlang :index, :locals => { :key => 'value' } + +Так как в WLang шаблонах невозможно вызывать методы из Ruby напрямую (за исключением +yield+), то +вы почти всегда будете передавать в шаблон локальные переменные. + === Встроенные шаблоны get '/' do @@ -594,7 +603,7 @@ Thin — это более производительный и функцион загружены автоматически. Вызовите enable :inline_templates напрямую, если используете вложенные шаблоны в других файлах. -=== Именные шаблоны +=== Именованные шаблоны Шаблоны также могут быть определены при помощи template метода: @@ -1314,7 +1323,7 @@ Sinatra предлагает метод-помощник +time_for+, котор Sinatra использует {Rack::Protection}[https://github.com/rkh/rack-protection#readme] для защиты приложения от простых атак. Вы можете легко выключить эту -защиту (что даст прирост в производительности): +защиту (что сделает ваше приложение чрезвычайно уязвимым): disable :protection @@ -1411,6 +1420,9 @@ Sinatra использует [show_exceptions] показывать исключения/стек вызовов (stack trace) в браузере. По умолчанию включено только в окружении development. + Может быть установлено в :after_handler для запуска специфичной + для приложения обработки ошибок, прежде чем показывать трассировку + стека в браузере. [static] должна ли Sinatra осуществлять раздачу статических файлов. Отключите, когда используете какой-либо веб-сервер для этой цели. diff --git a/examples/chat.rb b/examples/chat.rb index e0575f3c..736165bd 100755 --- a/examples/chat.rb +++ b/examples/chat.rb @@ -25,11 +25,11 @@ __END__ @@ layout - - Super Simple Chat with Sinatra + + Super Simple Chat with Sinatra - - + + <%= yield %> @@ -58,4 +58,4 @@ __END__
-
\ No newline at end of file + diff --git a/lib/sinatra/base.rb b/lib/sinatra/base.rb index deab7993..33648b28 100644 --- a/lib/sinatra/base.rb +++ b/lib/sinatra/base.rb @@ -1,7 +1,7 @@ # external dependencies require 'rack' require 'tilt' -require "rack/protection" +require 'rack/protection' # stdlib dependencies require 'thread' @@ -1314,17 +1314,21 @@ module Sinatra def compile(path) keys = [] 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| if match == "*" keys << 'splat' "(.*?)" else keys << $2[1..-1] - "([^/?#]+)" + "([^#{ignore}/?#]+)" end end - [/^#{pattern}$/, keys] + [/\A#{pattern}\z/, keys] elsif path.respond_to?(:keys) && path.respond_to?(:match) [path, path.keys] elsif path.respond_to?(:names) && path.respond_to?(:match) @@ -1340,11 +1344,15 @@ module Sinatra def encoded(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 end + def escaped(char, enc = URI.escape(char)) + [Regexp.escape(enc), URI.escape(char, /./)] + end + public # Makes the methods defined in the block and in the Modules given # in `extensions` available to the handlers and templates diff --git a/sinatra.gemspec b/sinatra.gemspec index bd9aa9ad..eb2422f2 100644 --- a/sinatra.gemspec +++ b/sinatra.gemspec @@ -10,7 +10,7 @@ Gem::Specification.new 'sinatra', Sinatra::VERSION do |s| s.files = `git ls-files`.split("\n") - %w[.gitignore .travis.yml] s.test_files = s.files.select { |p| p =~ /^test\/.*_test.rb/ } 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-protection', '~> 1.2' diff --git a/test/compile_test.rb b/test/compile_test.rb new file mode 100644 index 00000000..2344d3aa --- /dev/null +++ b/test/compile_test.rb @@ -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 diff --git a/test/contest.rb b/test/contest.rb index ab09d6e9..32ab84e9 100644 --- a/test/contest.rb +++ b/test/contest.rb @@ -1,15 +1,15 @@ # Copyright (c) 2009 Damian Janowski and Michel Martens for Citrusbyte -# +# # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: -# +# # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. -# +# # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE diff --git a/test/helpers_test.rb b/test/helpers_test.rb index 53d6980b..09ba8d5c 100644 --- a/test/helpers_test.rb +++ b/test/helpers_test.rb @@ -586,21 +586,21 @@ class HelpersTest < Test::Unit::TestCase end end end - + it 'sets the Content-Type response header' do attachment_app('test.xml') get '/attachment' assert_equal 'application/xml;charset=utf-8', response['Content-Type'] assert_equal '', body - end - + end + it 'sets the Content-Type response header without extname' do attachment_app('test') get '/attachment' assert_equal 'text/html;charset=utf-8', response['Content-Type'] - assert_equal '', body + assert_equal '', body end - + it 'sets the Content-Type response header with extname' do mock_app do get('/attachment') do @@ -612,9 +612,9 @@ class HelpersTest < Test::Unit::TestCase get '/attachment' assert_equal 'application/atom+xml', response['Content-Type'] - assert_equal '', body + assert_equal '', body end - + end describe 'send_file' do diff --git a/test/liquid_test.rb b/test/liquid_test.rb index 9d341ca1..4988dbd8 100644 --- a/test/liquid_test.rb +++ b/test/liquid_test.rb @@ -44,7 +44,7 @@ class LiquidTest < Test::Unit::TestCase mock_app { get('/') { liquid :no_such_template } } assert_raise(Errno::ENOENT) { get('/') } end - + it "allows passing locals" do liquid_app { liquid '{{ value }}', :locals => { :value => 'foo' } diff --git a/test/mapped_error_test.rb b/test/mapped_error_test.rb index c02082be..e07ebde8 100644 --- a/test/mapped_error_test.rb +++ b/test/mapped_error_test.rb @@ -211,7 +211,7 @@ class MappedErrorTest < Test::Unit::TestCase assert_equal 500, status end - it "allows a stack of exception_handlers" do + it "allows a stack of exception_handlers" do mock_app do set :raise_errors, false error(FirstError) { 'First!' } @@ -223,7 +223,7 @@ class MappedErrorTest < Test::Unit::TestCase assert_equal 'Second!', body 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 set :raise_errors, false error(500, FirstError) { 'First!' } diff --git a/test/markaby_test.rb b/test/markaby_test.rb index 546fb3c5..946f6846 100644 --- a/test/markaby_test.rb +++ b/test/markaby_test.rb @@ -55,7 +55,7 @@ class MarkabyTest < Test::Unit::TestCase assert_equal "

THIS. IS. SPARTA

", body 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" } } assert ok? assert_equal "

Markaby Layout!

Hello World

", body @@ -65,7 +65,7 @@ class MarkabyTest < Test::Unit::TestCase mock_app { get('/') { markaby :no_such_template } } assert_raise(Errno::ENOENT) { get('/') } end - + it "allows passing locals" do markaby_app { markaby 'text value', :locals => { :value => 'foo' } diff --git a/test/rabl_test.rb b/test/rabl_test.rb index 9b75ebf6..98d087d7 100644 --- a/test/rabl_test.rb +++ b/test/rabl_test.rb @@ -34,7 +34,7 @@ class RablTest < Test::Unit::TestCase assert ok? assert_equal '{"openstruct":{"bar":"baz"}}', body end - + it "renders with file layouts" do rabl_app { @foo = OpenStruct.new(:bar => 'baz') @@ -43,14 +43,14 @@ class RablTest < Test::Unit::TestCase assert ok? assert_equal '{"qux":{"openstruct":{"bar":"baz"}}}', body end - + it "raises error if template not found" do mock_app { get('/') { rabl :no_such_template } } assert_raise(Errno::ENOENT) { get('/') } end - + it "passes rabl options to the rabl engine" do mock_app do get('/') do @@ -65,7 +65,7 @@ class RablTest < Test::Unit::TestCase assert ok? assert_body 'baz' end - + it "passes default rabl options to the rabl engine" do mock_app do set :rabl, :format => 'xml' @@ -81,7 +81,7 @@ class RablTest < Test::Unit::TestCase assert ok? assert_body 'baz' end - + end rescue LoadError diff --git a/test/radius_test.rb b/test/radius_test.rb index 2c098e6e..90f8712e 100644 --- a/test/radius_test.rb +++ b/test/radius_test.rb @@ -44,7 +44,7 @@ class RadiusTest < Test::Unit::TestCase mock_app { get('/') { radius :no_such_template } } assert_raise(Errno::ENOENT) { get('/') } end - + it "allows passing locals" do radius_app { radius '', :locals => { :value => 'foo' } diff --git a/test/route_added_hook_test.rb b/test/route_added_hook_test.rb index b2a22e88..d6ef3410 100644 --- a/test/route_added_hook_test.rb +++ b/test/route_added_hook_test.rb @@ -47,13 +47,13 @@ class RouteAddedHookTest < Test::Unit::TestCase assert_equal [["GET", "/"], ["HEAD", "/"]], RouteAddedTest.routes end - + it "should pass route blocks as an argument" do mock_app(Class.new(Sinatra::Base)) do register RouteAddedTest get('/') {} end - assert_kind_of Proc, RouteAddedTest.procs.first + assert_kind_of Proc, RouteAddedTest.procs.first end end diff --git a/test/scss_test.rb b/test/scss_test.rb index e5d6857f..aa43930f 100644 --- a/test/scss_test.rb +++ b/test/scss_test.rb @@ -75,9 +75,9 @@ class ScssTest < Test::Unit::TestCase mock_app do set :scss, {:style => :compact} # default scss style is :nested get('/') { - scss("#scss {\n background-color: white;\n color: black;\n}") + scss("#scss {\n background-color: white;\n color: black;\n}") } - end + end get '/' assert ok? assert_equal "#scss { background-color: white; color: black; }\n", body diff --git a/test/sinatra_test.rb b/test/sinatra_test.rb index 459a37c4..9a76c974 100644 --- a/test/sinatra_test.rb +++ b/test/sinatra_test.rb @@ -5,7 +5,7 @@ class SinatraTest < Test::Unit::TestCase app = Sinatra.new { get('/') { 'Hello World' } } assert_same Sinatra::Base, app.superclass end - + it "responds to #template_cache" do assert_kind_of Tilt::Cache, Sinatra::Base.new!.template_cache end diff --git a/test/slim_test.rb b/test/slim_test.rb index e13e357c..59057047 100644 --- a/test/slim_test.rb +++ b/test/slim_test.rb @@ -17,13 +17,13 @@ class SlimTest < Test::Unit::TestCase assert ok? assert_equal "

Hiya

", body end - + it 'renders .slim files in views path' do slim_app { slim :hello } assert ok? assert_equal "

Hello From Slim

", body end - + it "renders with inline layouts" do mock_app do layout { %(h1\n | THIS. IS. \n == yield.upcase ) } @@ -33,27 +33,27 @@ class SlimTest < Test::Unit::TestCase assert ok? assert_equal "

THIS. IS. SPARTA

", body end - + it "renders with file layouts" do slim_app { slim('| Hello World', :layout => :layout2) } assert ok? assert_equal "

Slim Layout!

Hello World

", body end - + it "raises error if template not found" do mock_app { get('/') { slim(:no_such_template) } } assert_raise(Errno::ENOENT) { get('/') } end - + HTML4_DOCTYPE = "" - + it "passes slim options to the slim engine" do mock_app { get('/') { slim("x foo='bar'", :attr_wrapper => "'") }} get '/' assert ok? assert_body "" end - + it "passes default slim options to the slim engine" do mock_app do set :slim, :attr_wrapper => "'" @@ -63,7 +63,7 @@ class SlimTest < Test::Unit::TestCase assert ok? assert_body "" end - + it "merges the default slim options with the overrides and passes them to the slim engine" do mock_app do set :slim, :attr_wrapper => "'" diff --git a/test/templates_test.rb b/test/templates_test.rb index 5874862d..e426d326 100644 --- a/test/templates_test.rb +++ b/test/templates_test.rb @@ -288,7 +288,7 @@ __END__ @@ foo this is foo -@@ bar +@@ bar There's a space after 'bar'! @@ foo bar diff --git a/test/yajl_test.rb b/test/yajl_test.rb index e10f764a..69a7d145 100644 --- a/test/yajl_test.rb +++ b/test/yajl_test.rb @@ -50,7 +50,7 @@ class YajlTest < Test::Unit::TestCase it 'decorates the json with a callback' do yajl_app do yajl( - 'json = { :foo => "bar" }', + 'json = { :foo => "bar" }', { :callback => 'baz' } ) end