From ca9ac7b5aeb3d5adc1a4c7fb86e21b0dc688e55b Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Fri, 11 Apr 2008 16:29:36 -0700 Subject: [PATCH 01/28] Sinatra 0.2.0 released --- CHANGELOG | 1 + Manifest | 45 +++++++++++++++++++++++++++++++++++++++++++++ Rakefile | 12 ++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 CHANGELOG create mode 100644 Manifest diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 00000000..1987a59a --- /dev/null +++ b/CHANGELOG @@ -0,0 +1 @@ +v0.2.0 Released! diff --git a/Manifest b/Manifest new file mode 100644 index 00000000..f09f9445 --- /dev/null +++ b/Manifest @@ -0,0 +1,45 @@ +CHANGELOG +images/404.png +images/500.png +index.html +lib/sinatra/test/methods.rb +lib/sinatra/test/spec.rb +lib/sinatra/test/unit.rb +lib/sinatra.rb +README.rdoc +test/app_test.rb +test/application_test.rb +test/builder_test.rb +test/custom_error_test.rb +test/diddy_test.rb +test/erb_test.rb +test/event_context_test.rb +test/events_test.rb +test/haml_test.rb +test/helper.rb +test/mapped_error_test.rb +test/public/foo.xml +test/rest_test.rb +test/sass_test.rb +test/sessions_test.rb +test/streaming_test.rb +test/sym_params_test.rb +test/template_test.rb +test/use_in_file_templates_test.rb +test/views/foo.builder +test/views/foo.erb +test/views/foo.haml +test/views/foo.sass +test/views/foo_layout.erb +test/views/foo_layout.haml +test/views/layout_test/foo.builder +test/views/layout_test/foo.erb +test/views/layout_test/foo.haml +test/views/layout_test/foo.sass +test/views/layout_test/layout.builder +test/views/layout_test/layout.erb +test/views/layout_test/layout.haml +test/views/layout_test/layout.sass +test/views/no_layout/no_layout.builder +test/views/no_layout/no_layout.haml +Manifest diff --git a/Rakefile b/Rakefile index 6bf8b6c5..93225fa3 100644 --- a/Rakefile +++ b/Rakefile @@ -1,6 +1,7 @@ require 'rubygems' require 'rake/testtask' require 'rake/rdoctask' +require 'echoe' task :default => :test @@ -15,3 +16,14 @@ Rake::TestTask.new do |t| ENV['SINATRA_ENV'] = 'test' t.pattern = File.dirname(__FILE__) + "/test/*_test.rb" end + +Echoe.new("sinatra") do |p| + p.author = "Blake Mizerany" + p.summary = "Classy web-development dressed in a DSL" + p.url = "http://www.sinatrarb.com" + p.docs_host = "sinatrarb.com:/var/www/blakemizerany.com/public/docs/" + p.dependencies = ["mongrel >=1.0.1", "rack >= 0.3.0"] + p.install_message = "*** Be sure to checkout the site for helpful tips! sinatrarb.com ***" + p.include_rakefile = true +end + From d343d27d877d8b3112a61fac3e61546952f449ea Mon Sep 17 00:00:00 2001 From: Ryan Tomayko Date: Sun, 13 Apr 2008 05:17:39 -0400 Subject: [PATCH 02/28] document and further spec out ResponseHelpers --- lib/sinatra.rb | 31 ++++++++++++++++++++++++++++--- test/app_test.rb | 30 ++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 5 deletions(-) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index ea36fd7f..4a4cf583 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -376,15 +376,40 @@ module Sinatra header('Cache-Control' => 'private') if headers['Cache-Control'] == 'no-cache' end end - + + + # Helper methods for building various aspects of the HTTP response. module ResponseHelpers + # Immediately halt response execution by redirecting to the resource + # specified. The +path+ argument may be an absolute URL or a path + # relative to the site root. Additional arguments are passed to the + # halt. + # + # With no integer status code, a '302 Temporary Redirect' response is + # sent. To send a permanent redirect, pass an explicit status code of + # 301: + # + # redirect '/somewhere/else', 301 + # + # NOTE: No attempt is made to rewrite the path based on application + # context. The 'Location' response header is set verbatim to the value + # provided. def redirect(path, *args) status(302) - headers 'Location' => path + header 'Location' => path throw :halt, *args end - + + # Access or modify response headers. With no argument, return the + # underlying headers Hash. With a Hash argument, add or overwrite + # existing response headers with the values provided: + # + # headers 'Content-Type' => "text/html; charset=utf-8", + # 'Last-Modified' => Time.now.httpdate, + # 'X-UA-Compatible' => 'IE=edge' + # + # This method also available in singular form (#header). def headers(header = nil) @response.headers.merge!(header) if header @response.headers diff --git a/test/app_test.rb b/test/app_test.rb index c272a34c..8a109538 100644 --- a/test/app_test.rb +++ b/test/app_test.rb @@ -26,7 +26,22 @@ context "Sinatra" do should.be.ok body.should.equal 'Hello Blake' end - + + specify "gives access to underlying response header Hash" do + get '/' do + header['X-Test'] = 'Is this thing on?' + headers 'X-Test2' => 'Foo', 'X-Test3' => 'Bar' + '' + end + + get_it '/' + should.be.ok + headers.should.include 'X-Test' + headers['X-Test'].should.equal 'Is this thing on?' + headers.should.include 'X-Test3' + headers['X-Test3'].should.equal 'Bar' + end + specify "follows redirects" do get '/' do redirect '/blake' @@ -55,7 +70,18 @@ context "Sinatra" do headers['Location'].should.equal 'foo' body.should.equal 'blah' end - + + specify "redirects permanently with 301 status code" do + get "/" do + redirect 'foo', 301 + end + get_it '/' + should.be.redirection + headers['Location'].should.equal 'foo' + status.should.equal 301 + body.should.be.empty + end + specify "body sets content and ends event" do Sinatra::EventContext.any_instance.expects(:foo).never From ccc19b0436add4fedea1c009b80a35c573a8a558 Mon Sep 17 00:00:00 2001 From: Ryan Tomayko Date: Sun, 13 Apr 2008 05:32:23 -0400 Subject: [PATCH 03/28] content_type response helper with mime type lookup and parameter support. ResponseHelpers#content_type takes a media type and parameters and sets the Content-Type response header accordingly. --- README.rdoc | 4 ++-- lib/sinatra.rb | 28 ++++++++++++++++++++++++++-- test/app_test.rb | 22 ++++++++++++++++++++++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/README.rdoc b/README.rdoc index 4e27acd1..b79e4a7f 100644 --- a/README.rdoc +++ b/README.rdoc @@ -100,7 +100,7 @@ This will render ./views/index.haml === Sass get '/stylesheet.css' do - header 'Content-Type' => 'text/css; charset=utf-8' + content_type 'text/css', :charset => 'utf-8' sass :stylesheet end @@ -174,7 +174,7 @@ This works like Haml except you use erb instead of haml === Sass -This works like Haml except you use sass instead of haml. It's also a good idea to add header 'Content-Type' => 'text/css; charset=utf-8' before your call to sass so Sinatra returns the proper content type header with the file. +This works like Haml except you use sass instead of haml. It's also a good idea to add content_type 'text/css', :charset => 'utf-8' before your call to sass so Sinatra returns the proper content type header with the file. === Builder diff --git a/lib/sinatra.rb b/lib/sinatra.rb index 4a4cf583..e43b18b9 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -405,7 +405,7 @@ module Sinatra # underlying headers Hash. With a Hash argument, add or overwrite # existing response headers with the values provided: # - # headers 'Content-Type' => "text/html; charset=utf-8", + # headers 'Content-Type' => "text/html;charset=utf-8", # 'Last-Modified' => Time.now.httpdate, # 'X-UA-Compatible' => 'IE=edge' # @@ -416,8 +416,32 @@ module Sinatra end alias :header :headers + # Set the content type of the response body (HTTP 'Content-Type' header). + # + # The +type+ argument may be an internet media type (e.g., 'text/html', + # 'application/xml+atom', 'image/png') or a Symbol key into the + # Rack::File::MIME_TYPES table. + # + # Media type parameters, such as "charset", may also be specified using the + # optional hash argument: + # + # get '/foo.html' do + # content_type 'text/html', :charset => 'utf-8' + # "

Hello World

" + # end + # + def content_type(type, params={}) + type = Rack::File::MIME_TYPES[type.to_s] if type.kind_of?(Symbol) + fail "Invalid or undefined media_type: #{type}" if type.nil? + if params.any? + params = params.collect { |kv| "%s=%s" % kv }.join(', ') + type = [ type, params ].join(";") + end + response.header['Content-Type'] = type + end + end - + module RenderingHelpers def render(renderer, template, options={}) diff --git a/test/app_test.rb b/test/app_test.rb index 8a109538..618e1ed4 100644 --- a/test/app_test.rb +++ b/test/app_test.rb @@ -113,6 +113,28 @@ context "Sinatra" do end + specify "should easily set response Content-Type" do + get '/foo.html' do + content_type 'text/html', :charset => 'utf-8' + "

Hello, World

" + end + + get_it '/foo.html' + should.be.ok + headers['Content-Type'].should.equal 'text/html;charset=utf-8' + body.should.equal '

Hello, World

' + + get '/foo.xml' do + content_type :xml + "" + end + + get_it '/foo.xml' + should.be.ok + headers['Content-Type'].should.equal 'application/xml' + body.should.equal '' + end + specify "delegates HEAD requests to GET handlers" do get '/invisible' do "I am invisible to the world" From 302a03cc3767b7e1639ce4a5249929a383d1afd5 Mon Sep 17 00:00:00 2001 From: Ryan Tomayko Date: Mon, 31 Mar 2008 09:10:40 -0400 Subject: [PATCH 04/28] last_modified response helper / conditional GET support ResponseHelpers#last_modified sets the Last-Modified response header and (potentially) halts execution if an If-Modified-Since request header is present and matches the time given. --- lib/sinatra.rb | 19 +++++++++++++++++++ test/app_test.rb | 17 +++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index e43b18b9..9e966550 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -440,6 +440,25 @@ module Sinatra response.header['Content-Type'] = type end + # Set the last modified time of the resource (HTTP 'Last-Modified' header) + # and halt if conditional GET matches. The +time+ argument is a Time, + # DateTime, or other object that responds to +to_time+. + # + # When the current request includes an 'If-Modified-Since' header that + # matches the time specified, execution is immediately halted with a + # '304 Not Modified' response. + # + # Calling this method before perfoming heavy processing (e.g., lengthy + # database queries, template rendering, complex logic) can dramatically + # increase overall throughput with caching clients. + def last_modified(time) + time = time.to_time if time.respond_to?(:to_time) + time = time.httpdate if time.respond_to?(:httpdate) + response.header['Last-Modified'] = time + throw :halt, 304 if time == request.env['HTTP_IF_MODIFIED_SINCE'] + time + end + end module RenderingHelpers diff --git a/test/app_test.rb b/test/app_test.rb index 618e1ed4..be89945d 100644 --- a/test/app_test.rb +++ b/test/app_test.rb @@ -135,6 +135,23 @@ context "Sinatra" do body.should.equal '' end + specify "supports conditional GETs with last_modified" do + modified_at = Time.now + get '/maybe' do + last_modified modified_at + 'response body, maybe' + end + + get_it '/maybe' + should.be.ok + body.should.equal 'response body, maybe' + + get_it '/maybe', {}, + 'HTTP_IF_MODIFIED_SINCE' => modified_at.httpdate + status.should.equal 304 + body.should.equal '' + end + specify "delegates HEAD requests to GET handlers" do get '/invisible' do "I am invisible to the world" From ce4cf34e7731b754b1d6b3eade7110a4352324f1 Mon Sep 17 00:00:00 2001 From: Ryan Tomayko Date: Sun, 13 Apr 2008 05:07:15 -0400 Subject: [PATCH 05/28] entity_tag response helper / conditional GET support ResponseHelpers#entity_tag sets the ETag response header and (potentially) halts execution if an If-None-Match request header is present matches the value given. More information on HTTP's ETag header and conditional requests available in RFC 2616: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19 --- lib/sinatra.rb | 39 +++++++++++++++++++++++++++++++++++++++ test/app_test.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index 9e966550..92ab17e8 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -459,6 +459,45 @@ module Sinatra time end + # Set the response entity tag (HTTP 'ETag' header) and halt if conditional + # GET matches. The +value+ argument is an identifier that uniquely + # identifies the current version of the resource. The +strength+ argument + # indicates whether the etag should be used as a :strong (default) or :weak + # cache validator. + # + # When the current request includes an 'If-None-Match' header with a + # matching etag, execution is immediately halted. If the request method is + # GET or HEAD, a '304 Not Modified' response is sent. For all other request + # methods, a '412 Precondition Failed' response is sent. + # + # Calling this method before perfoming heavy processing (e.g., lengthy + # database queries, template rendering, complex logic) can dramatically + # increase overall throughput with caching clients. + # + # === See Also + # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.19[RFC2616: ETag], + # ResponseHelpers#last_modified + def entity_tag(value, strength=:strong) + value = + case strength + when :strong then '"%s"' % value + when :weak then 'W/"%s"' % value + else raise TypeError, "strength must be one of :strong or :weak" + end + response.header['ETag'] = value + + # Check for If-None-Match request header and halt if match is found. + etags = (request.env['HTTP_IF_NONE_MATCH'] || '').split(/\s*,\s*/) + if etags.include?(value) || etags.include?('*') + # GET/HEAD requests: send Not Modified response + throw :halt, 304 if request.get? || request.head? + # Other requests: send Precondition Failed response + throw :halt, 412 + end + end + + alias :etag :entity_tag + end module RenderingHelpers diff --git a/test/app_test.rb b/test/app_test.rb index be89945d..90c7abc2 100644 --- a/test/app_test.rb +++ b/test/app_test.rb @@ -152,6 +152,32 @@ context "Sinatra" do body.should.equal '' end + specify "supports conditional GETs with entity_tag" do + get '/strong' do + entity_tag 'FOO' + 'foo response' + end + + get_it '/strong' + should.be.ok + body.should.equal 'foo response' + + get_it '/strong', {}, + 'HTTP_IF_NONE_MATCH' => '"BAR"' + should.be.ok + body.should.equal 'foo response' + + get_it '/strong', {}, + 'HTTP_IF_NONE_MATCH' => '"FOO"' + status.should.equal 304 + body.should.equal '' + + get_it '/strong', {}, + 'HTTP_IF_NONE_MATCH' => '"BAR", *' + status.should.equal 304 + body.should.equal '' + end + specify "delegates HEAD requests to GET handlers" do get '/invisible' do "I am invisible to the world" From 415b75bb57352bd1c62d6797ff7540e18763695b Mon Sep 17 00:00:00 2001 From: Ryan Tomayko Date: Sun, 13 Apr 2008 23:11:07 -0400 Subject: [PATCH 06/28] Fix unit tests running twice Echoe creates a test task that works exactly like the one explicitly specified in the Rakefile. Remove the explicit test task. --- Rakefile | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/Rakefile b/Rakefile index 93225fa3..ad4ad15d 100644 --- a/Rakefile +++ b/Rakefile @@ -1,10 +1,13 @@ require 'rubygems' -require 'rake/testtask' require 'rake/rdoctask' require 'echoe' task :default => :test +task :test do + ENV['SINATRA_ENV'] = 'test' +end + Rake::RDocTask.new do |rd| rd.main = "README.rdoc" rd.rdoc_files += ["README.rdoc"] @@ -12,11 +15,6 @@ Rake::RDocTask.new do |rd| rd.rdoc_dir = 'doc' end -Rake::TestTask.new do |t| - ENV['SINATRA_ENV'] = 'test' - t.pattern = File.dirname(__FILE__) + "/test/*_test.rb" -end - Echoe.new("sinatra") do |p| p.author = "Blake Mizerany" p.summary = "Classy web-development dressed in a DSL" @@ -26,4 +24,3 @@ Echoe.new("sinatra") do |p| p.install_message = "*** Be sure to checkout the site for helpful tips! sinatrarb.com ***" p.include_rakefile = true end - From 39cee5c8da2caf27359e8bc8cb8cd5fef3d426d1 Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Thu, 10 Apr 2008 15:38:39 -0700 Subject: [PATCH 07/28] added MIT for release --- LICENSE | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..86cc7dcb --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +Copyright (c) 2007 Blake Mizerany + +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 AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file From f747bfe403bcc964cd32157c9563aad93a73f325 Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Mon, 14 Apr 2008 00:25:06 -0700 Subject: [PATCH 08/28] pick a server, any server! --- .gitignore | 1 + lib/sinatra.rb | 27 ++++++++++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 3b42519e..88754620 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ doc pkg *.log .DS_Store +rack* \ No newline at end of file diff --git a/lib/sinatra.rb b/lib/sinatra.rb index ea36fd7f..f037ab51 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -120,12 +120,32 @@ module Sinatra app end + def server + @server ||= case options.server + when "mongrel" + Rack::Handler::Mongrel + when "webrick" + Rack::Handler::WEBrick + when "cgi" + Rack::Handler::CGI + when "fastcgi" + Rack::Handler::FastCGI + else + if defined?(Rack::Handler::Thin) + Rack::Handler::Thin + else + options.server ||= "mongrel" + eval("Rack::Handler::#{options.server.capitalize}") + end + end + end + def run begin - puts "== Sinatra has taken the stage on port #{port} for #{env}" + puts "== Sinatra has taken the stage on port #{port} for #{env} with backup by #{server.name}" require 'pp' - Rack::Handler::Mongrel.run(build_application, :Port => port) do |server| + server.run(build_application, :Port => port) do |server| trap(:INT) do server.stop puts "\n== Sinatra has ended his set (crowd applauds)" @@ -722,7 +742,8 @@ module Sinatra OptionParser.new do |op| op.on('-p port') { |port| default_options[:port] = port } op.on('-e env') { |env| default_options[:env] = env } - op.on('-x') { |env| default_options[:mutex] = true } + op.on('-x') { default_options[:mutex] = true } + op.on('-s server') { |server| default_options[:server] = server } end.parse!(ARGV.dup.select { |o| o !~ /--name/ }) end From e7e0e5581533d6e8a3ded8049d509664402221db Mon Sep 17 00:00:00 2001 From: Ryan Tomayko Date: Mon, 14 Apr 2008 16:31:52 -0400 Subject: [PATCH 09/28] Minor docfixes in README.rdoc --- README.rdoc | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/README.rdoc b/README.rdoc index b79e4a7f..9d585162 100644 --- a/README.rdoc +++ b/README.rdoc @@ -301,7 +301,7 @@ Sinatra will pass you the error via the 'sinatra.error' in request.env Custom error mapping: error MyCustomError do - 'So what happened was...' + request.env['sinatra.env'].message + 'So what happened was...' + request.env['sinatra.error'].message end then if this happens: @@ -401,7 +401,7 @@ Options are: -h # help -p # set the port (default is 4567) -e # set the environment (default is development) - -x # turn on the mutext lock (default is off) + -x # turn on the mutex lock (default is off) = Contribute @@ -418,13 +418,3 @@ at the top of your sinatra.rb file get '/about' do "I'm running on Version " + Sinatra::Version.combined end - - - - - - - - - - From a3bbfead68d4afdc4311b7e191ea74e4d2f17d6d Mon Sep 17 00:00:00 2001 From: Ryan Tomayko Date: Tue, 15 Apr 2008 03:12:39 -0400 Subject: [PATCH 10/28] Rakefile: completely remove test and doc tasks It turns out Echoe creates doc and test tasks that do exactly what we want. --- Rakefile | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Rakefile b/Rakefile index ad4ad15d..93215d5a 100644 --- a/Rakefile +++ b/Rakefile @@ -1,20 +1,8 @@ require 'rubygems' -require 'rake/rdoctask' require 'echoe' task :default => :test -task :test do - ENV['SINATRA_ENV'] = 'test' -end - -Rake::RDocTask.new do |rd| - rd.main = "README.rdoc" - rd.rdoc_files += ["README.rdoc"] - rd.rdoc_files += Dir.glob("lib/**/*.rb") - rd.rdoc_dir = 'doc' -end - Echoe.new("sinatra") do |p| p.author = "Blake Mizerany" p.summary = "Classy web-development dressed in a DSL" From f9e6b88504aa30d4aecb80f7ac8e300afeffd6da Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Tue, 15 Apr 2008 12:54:04 -0700 Subject: [PATCH 11/28] refactored content_type to use to_params --- lib/sinatra.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index b02fd17d..7c0826ad 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -454,7 +454,7 @@ module Sinatra type = Rack::File::MIME_TYPES[type.to_s] if type.kind_of?(Symbol) fail "Invalid or undefined media_type: #{type}" if type.nil? if params.any? - params = params.collect { |kv| "%s=%s" % kv }.join(', ') + params = params.to_params type = [ type, params ].join(";") end response.header['Content-Type'] = type From 540aa5faed8d9918f2056092a4b6466c6dffcc74 Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Tue, 15 Apr 2008 16:56:08 -0700 Subject: [PATCH 12/28] Revert "refactored content_type to use to_params" This reverts commit f9e6b88504aa30d4aecb80f7ac8e300afeffd6da. --- lib/sinatra.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index 7c0826ad..b02fd17d 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -454,7 +454,7 @@ module Sinatra type = Rack::File::MIME_TYPES[type.to_s] if type.kind_of?(Symbol) fail "Invalid or undefined media_type: #{type}" if type.nil? if params.any? - params = params.to_params + params = params.collect { |kv| "%s=%s" % kv }.join(', ') type = [ type, params ].join(";") end response.header['Content-Type'] = type From 983358b3c487a77966f40fa1bc337ae5f0a74b65 Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Tue, 15 Apr 2008 15:10:50 -0700 Subject: [PATCH 13/28] Freezing Rack into package to take care of upload issue --- .gitmodules | 3 +++ Rakefile | 2 +- lib/sinatra.rb | 10 +++++++--- vendor/rack | 1 + 4 files changed, 12 insertions(+), 4 deletions(-) create mode 100644 .gitmodules create mode 160000 vendor/rack diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..396824f0 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "vendor/rack"] + path = vendor/rack + url = git://github.com/chneukirchen/rack-mirror.git diff --git a/Rakefile b/Rakefile index 93215d5a..8a3c582f 100644 --- a/Rakefile +++ b/Rakefile @@ -8,7 +8,7 @@ Echoe.new("sinatra") do |p| p.summary = "Classy web-development dressed in a DSL" p.url = "http://www.sinatrarb.com" p.docs_host = "sinatrarb.com:/var/www/blakemizerany.com/public/docs/" - p.dependencies = ["mongrel >=1.0.1", "rack >= 0.3.0"] + p.dependencies = ["mongrel >=1.0.1"] p.install_message = "*** Be sure to checkout the site for helpful tips! sinatrarb.com ***" p.include_rakefile = true end diff --git a/lib/sinatra.rb b/lib/sinatra.rb index b02fd17d..d59d6939 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -1,6 +1,13 @@ +Dir[File.dirname(__FILE__) + "/../vendor/*"].each do |l| + $:.unshift "#{File.expand_path(l)}/lib" +end + +require 'rack' + require 'rubygems' require 'uri' require 'time' +require 'ostruct' if ENV['SWIFT'] require 'swiftcore/swiftiplied_mongrel' @@ -10,9 +17,6 @@ elsif ENV['EVENT'] puts "Using Evented Mongrel" end -require 'rack' -require 'ostruct' - class Class def dslify_writer(*syms) syms.each do |sym| diff --git a/vendor/rack b/vendor/rack new file mode 160000 index 00000000..67a7507f --- /dev/null +++ b/vendor/rack @@ -0,0 +1 @@ +Subproject commit 67a7507fb07c9d49bf4be4fc6c4fc4578f1673ac From d7e9f3002a49f26c72fae382484b0677dd18292d Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Tue, 15 Apr 2008 17:50:37 -0700 Subject: [PATCH 14/28] Sinatra 0.2.1 Release --- CHANGELOG | 1 + lib/sinatra.rb | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index 1987a59a..0275d525 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1 +1,2 @@ +v0.2.1 File upload fix and minor tweaks v0.2.0 Released! diff --git a/lib/sinatra.rb b/lib/sinatra.rb index d59d6939..2d5e760b 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -82,7 +82,7 @@ module Sinatra module Version MAJOR = '0' MINOR = '2' - REVISION = '0' + REVISION = '1' def self.combined [MAJOR, MINOR, REVISION].join('.') end From 28b12858d67563dafca1f328e7251fa3c6f63ad0 Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Tue, 15 Apr 2008 19:10:48 -0700 Subject: [PATCH 15/28] better docs on testing --- README.rdoc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.rdoc b/README.rdoc index 9d585162..3911dfc3 100644 --- a/README.rdoc +++ b/README.rdoc @@ -345,6 +345,20 @@ When using send_file or static files you may have mime types Sinatra doesn't und = Testing +=== Methods + + get_it path, params + get_it path, params.merge(:env => { 'HTTP_HOST' => 'www.sinatrarb.com' }) or + get_it path, params.merge(:env => { :host => 'www.sinatrarb.com' }) + +RESTful: + + post_it '/foo', '', 'HTTP_ACCEPT' => 'application/xml' + +also works with: + + get_it, post_it, put_it, delete_it, head_it + === Test/Unit require 'my_sinatra_app' From 0da75abb023bf5d68de645a809a4a09c9274b10c Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Tue, 15 Apr 2008 19:11:10 -0700 Subject: [PATCH 16/28] cascade NoMethodError to super for better response to user --- lib/sinatra/test/methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/sinatra/test/methods.rb b/lib/sinatra/test/methods.rb index b2081b50..1e6fea48 100644 --- a/lib/sinatra/test/methods.rb +++ b/lib/sinatra/test/methods.rb @@ -46,7 +46,7 @@ module Sinatra end def method_missing(name, *args) - @response.send(name, *args) + @response.send(name, *args) rescue super end end From abfa696f344742b36bc8f373d48f6483daa4d1bd Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Tue, 15 Apr 2008 19:14:31 -0700 Subject: [PATCH 17/28] fixed broken test using static files intermittently --- test/app_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/app_test.rb b/test/app_test.rb index 90c7abc2..784c4479 100644 --- a/test/app_test.rb +++ b/test/app_test.rb @@ -124,12 +124,12 @@ context "Sinatra" do headers['Content-Type'].should.equal 'text/html;charset=utf-8' body.should.equal '

Hello, World

' - get '/foo.xml' do + get '/foo_test.xml' do content_type :xml "" end - get_it '/foo.xml' + get_it '/foo_test.xml' should.be.ok headers['Content-Type'].should.equal 'application/xml' body.should.equal '' From d7032a0c91ca962aee6f25dbf5d5c3228658a9f1 Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Tue, 15 Apr 2008 19:17:05 -0700 Subject: [PATCH 18/28] using new RESTful testing --- test/app_test.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/app_test.rb b/test/app_test.rb index 784c4479..507e6fa2 100644 --- a/test/app_test.rb +++ b/test/app_test.rb @@ -146,8 +146,7 @@ context "Sinatra" do should.be.ok body.should.equal 'response body, maybe' - get_it '/maybe', {}, - 'HTTP_IF_MODIFIED_SINCE' => modified_at.httpdate + get_it '/maybe', :env => { 'HTTP_IF_MODIFIED_SINCE' => modified_at.httpdate } status.should.equal 304 body.should.equal '' end From d2ff40a7de0aa18b31e555ef5bc176046907d408 Mon Sep 17 00:00:00 2001 From: Simon Rozet Date: Sat, 19 Apr 2008 14:19:36 +0200 Subject: [PATCH 19/28] remove index.html --- index.html | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 index.html diff --git a/index.html b/index.html deleted file mode 100644 index 40cdf8be..00000000 --- a/index.html +++ /dev/null @@ -1,9 +0,0 @@ - - - -Your Page Title - - -This site has moved. - - \ No newline at end of file From 23999054c6242357a5a2c982301b96ec36bf2a7b Mon Sep 17 00:00:00 2001 From: Simon Rozet Date: Sat, 19 Apr 2008 16:43:12 +0200 Subject: [PATCH 20/28] Add the possibility to configure options that are passed to Haml::Engine when evalutating HAML template. Options can be configured in two different ways: * Application-wide, using set_option :haml e.g.: set_option :haml, :format => :haml4, :escape_html => true * By passing options directly to the `haml` helper e.g.: haml '%strong Hello World', :options => {:format => :html4} Note that if you use both way, options will be merged. --- lib/sinatra.rb | 4 +++- test/haml_test.rb | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index 2d5e760b..4727feb5 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -604,7 +604,8 @@ module Sinatra private def render_haml(content, options = {}, &b) - ::Haml::Engine.new(content).render(options[:scope] || self, options[:locals] || {}, &b) + haml_options = (options[:options] || {}).merge(Sinatra.options.haml) + ::Haml::Engine.new(content, haml_options).render(options[:scope] || self, options[:locals] || {}, &b) end end @@ -837,6 +838,7 @@ module Sinatra :public => Dir.pwd + '/public', :sessions => false, :logging => true, + :haml => {} } end diff --git a/test/haml_test.rb b/test/haml_test.rb index eefa779f..44e278ad 100644 --- a/test/haml_test.rb +++ b/test/haml_test.rb @@ -178,4 +178,56 @@ context "Haml" do end + describe 'Options passed to the HAML interpreter' do + setup do + Sinatra.application = nil + end + + specify 'are empty be default' do + + get '/' do + haml 'foo' + end + + Haml::Engine.expects(:new).with('foo', {}).returns(stub(:render => 'foo')) + + get_it '/' + should.be.ok + + end + + specify 'can be configured by passing :options to haml' do + + get '/' do + haml 'foo', :options => {:format => :html4} + end + + Haml::Engine.expects(:new).with('foo', {:format => :html4}).returns(stub(:render => 'foo')) + + get_it '/' + should.be.ok + + end + + specify 'can be configured using set_option :haml' do + + configure do + set_option :haml, :format => :html4, + :escape_html => true + end + + get '/' do + haml 'foo' + end + + Haml::Engine.expects(:new).with('foo', {:format => :html4, + :escape_html => true}).returns(stub(:render => 'foo')) + + get_it '/' + should.be.ok + + end + + end + end From 5b3440e251a66abf6776a4d5259b579d828a3b4a Mon Sep 17 00:00:00 2001 From: Jack Danger Canty Date: Sat, 19 Apr 2008 11:14:43 -0700 Subject: [PATCH 21/28] Using a more accurate root directory Dir.pwd will not provide a valid directory name when the app is started from any other directory. This prevents monitoring apps (like god) from starting an application without extra configuration. --- lib/sinatra.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index 2d5e760b..64dc7fd5 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -828,13 +828,14 @@ module Sinatra attr_writer :options def self.default_options + root = File.expand_path(File.dirname($0)) @@default_options ||= { :run => true, :port => 4567, :env => :development, - :root => Dir.pwd, - :views => Dir.pwd + '/views', - :public => Dir.pwd + '/public', + :root => root, + :views => root + '/views', + :public => root + '/public', :sessions => false, :logging => true, } From 40acdeebb74a3af34a73b8deb4c7b4ae830d1681 Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Sat, 19 Apr 2008 13:56:40 -0700 Subject: [PATCH 22/28] don't ignore rack --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index 88754620..3b42519e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,3 @@ doc pkg *.log .DS_Store -rack* \ No newline at end of file From f80203adb6bc5756bdcf2388c937891c756db5a6 Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Sat, 19 Apr 2008 14:07:12 -0700 Subject: [PATCH 23/28] Let's keep default_options renderer agnostic --- lib/sinatra.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index 88569c48..a4a6f719 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -604,7 +604,7 @@ module Sinatra private def render_haml(content, options = {}, &b) - haml_options = (options[:options] || {}).merge(Sinatra.options.haml) + haml_options = (options[:options] || {}).merge(Sinatra.options.haml || {}) ::Haml::Engine.new(content, haml_options).render(options[:scope] || self, options[:locals] || {}, &b) end @@ -838,8 +838,7 @@ module Sinatra :views => root + '/views', :public => root + '/public', :sessions => false, - :logging => true, - :haml => {} + :logging => true } end From 9c875ffda0d9d12bd54f83606b30601b1b9b6bf7 Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Sun, 20 Apr 2008 18:03:41 -0700 Subject: [PATCH 24/28] Session testing made easy Just say: get '/foo' do session[:bar] # => 'baz' end get_it '/foo', :session => session(:bar => 'baz') --- lib/sinatra.rb | 4 ++-- lib/sinatra/test/methods.rb | 31 ++++++++++++++----------------- test/application_test.rb | 6 +++--- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index a4a6f719..72213a91 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -52,7 +52,7 @@ module Rack #:nodoc: end def user_agent - env['HTTP_USER_AGENT'] + @env['HTTP_USER_AGENT'] end private @@ -810,7 +810,7 @@ module Sinatra end def session - @request.env['rack.session'] || {} + request.env['rack.session'] ||= {} end private diff --git a/lib/sinatra/test/methods.rb b/lib/sinatra/test/methods.rb index 1e6fea48..6fec510b 100644 --- a/lib/sinatra/test/methods.rb +++ b/lib/sinatra/test/methods.rb @@ -1,13 +1,3 @@ -class Rack::MockRequest - class << self - alias :env_for_without_env :env_for - def env_for(uri = "", opts = {}) - env = { 'HTTP_USER_AGENT' => opts.delete(:agent) } - env_for_without_env(uri, opts).merge(env) - end - end -end - module Sinatra module Test @@ -16,12 +6,19 @@ module Sinatra def easy_env_map { - :accept => 'HTTP_ACCEPT', - :agent => 'HTTP_AGENT', - :host => 'HTTP_POST' + :accept => "HTTP_ACCEPT", + :agent => "HTTP_USER_AGENT", + :host => "HTTP_HOST", + :session => "HTTP_COOKIE", + :cookies => "HTTP_COOKIE" } end - + + def session(data, key = 'rack.session') + data = data.from_params if data.respond_to?(:from_params) + "#{Rack::Utils.escape(key)}=#{[Marshal.dump(data)].pack("m*")}" + end + def map_easys(params) easy_env_map.inject(params.dup) do |m, (from, to)| m[to] = m.delete(from) if m.has_key?(from); m @@ -30,14 +27,14 @@ module Sinatra %w(get head post put delete).each do |m| define_method("#{m}_it") do |path, *args| - request = Rack::MockRequest.new(Sinatra.build_application) env, input = if args.size == 2 [args.last, args.first] elsif args.size == 1 data = args.first - data.is_a?(Hash) ? [data.delete(:env), data.to_params] : [nil, data] + data.is_a?(Hash) ? [map_easys(data.delete(:env) || {}), data.to_params] : [nil, data] end - @response = request.request(m.upcase, path, {:input => input}.merge(env || {})) + @request = Rack::MockRequest.new(Sinatra.build_application) + @response = @request.request(m.upcase, path, {:input => input}.merge(env || {})) end end diff --git a/test/application_test.rb b/test/application_test.rb index 20594a9e..20a195eb 100644 --- a/test/application_test.rb +++ b/test/application_test.rb @@ -135,17 +135,17 @@ context "Events in an app" do get '/', :agent => /Windows/ do request.env['HTTP_USER_AGENT'] end - + get_it '/', :env => { :agent => 'Windows' } should.be.ok body.should.equal 'Windows' - get_it '/', :agent => 'Mac' + get_it '/', :env => { :agent => 'Mac' } should.not.be.ok end - specify "can filters by agent" do + specify "can use regex to get parts of user-agent" do get '/', :agent => /Windows (NT)/ do params[:agent].first From d16ee6533b01cda980a6311019d6f13efe109e6b Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Sat, 26 Apr 2008 15:09:40 -0700 Subject: [PATCH 25/28] fixed escaped paths not resolving static files [Matthew Walker] Signed-off-by: Blake Mizerany --- lib/sinatra.rb | 12 +++++++----- test/streaming_test.rb | 6 ++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index 72213a91..be0a05fd 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -5,9 +5,9 @@ end require 'rack' require 'rubygems' -require 'uri' require 'time' require 'ostruct' +require "uri" if ENV['SWIFT'] require 'swiftcore/swiftiplied_mongrel' @@ -217,14 +217,14 @@ module Sinatra def invoke(request) return unless File.file?( - Sinatra.application.options.public + request.path_info + Sinatra.application.options.public + request.path_info.http_unescape ) Result.new(block, {}, 200) end def block Proc.new do - send_file Sinatra.application.options.public + request.path_info, + send_file Sinatra.application.options.public + request.path_info.http_unescape, :disposition => nil end end @@ -1174,14 +1174,16 @@ class String # Converts +self+ to an escaped URI parameter value # 'Foo Bar'.to_param # => 'Foo%20Bar' def to_param - URI.escape(self) + Rack::Utils.escape(self) end + alias :http_escape :to_param # Converts +self+ from an escaped URI parameter value # 'Foo%20Bar'.from_param # => 'Foo Bar' def from_param - URI.unescape(self) + Rack::Utils.unescape(self) end + alias :http_unescape :from_param end diff --git a/test/streaming_test.rb b/test/streaming_test.rb index 8449b4e3..998bbc66 100644 --- a/test/streaming_test.rb +++ b/test/streaming_test.rb @@ -77,6 +77,12 @@ context "Static files (by default)" do headers['Content-Transfer-Encoding'].should.be.nil end + specify "should be served even if their path is url escaped" do + get_it('/fo%6f.xml') + should.be.ok + body.should.equal "\n" + end + end context "SendData" do From 1987e55049132d35377c16a41f5c66d9f469bb95 Mon Sep 17 00:00:00 2001 From: Blake Mizerany Date: Sat, 26 Apr 2008 20:03:12 -0700 Subject: [PATCH 26/28] New standard: use_in_file_templates uses @@ mytemplate for lookup i.e. no more: ## index is now: @@ index This is is to help confusion when using Haml and looks better in pastie ;) --- lib/sinatra.rb | 2 +- test/use_in_file_templates_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/sinatra.rb b/lib/sinatra.rb index be0a05fd..03e2ebb3 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -1127,7 +1127,7 @@ def use_in_file_templates! data = StringIO.new(templates) current_template = nil data.each do |line| - if line =~ /^##\s?(.*)/ + if line =~ /^@@\s?(.*)/ current_template = $1.to_sym Sinatra.application.templates[current_template] = '' elsif current_template diff --git a/test/use_in_file_templates_test.rb b/test/use_in_file_templates_test.rb index feaaece4..ca61a051 100644 --- a/test/use_in_file_templates_test.rb +++ b/test/use_in_file_templates_test.rb @@ -38,10 +38,10 @@ end __END__ -## foo +@@ foo this is foo -## layout +@@ layout X = yield X From 9c85e99c995c742671ba970defac59e3dfcb7bb5 Mon Sep 17 00:00:00 2001 From: Victor Hugo Borja Date: Thu, 24 Apr 2008 21:24:31 -0500 Subject: [PATCH 27/28] Specs, documentation and fixes for splat'n routes --- README.rdoc | 10 ++++++++-- lib/sinatra.rb | 24 +++++++++++++++++------- test/app_test.rb | 40 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 9 deletions(-) diff --git a/README.rdoc b/README.rdoc index 3911dfc3..0f3bbe7e 100644 --- a/README.rdoc +++ b/README.rdoc @@ -57,8 +57,14 @@ With params Splat'n - get '/message/*' do - # matches /message/1/2/3/4/5 + get '/say/*/to/*' do + # matches /say/hello/to/world + params["splat"] # => ["hello", "world"] + end + + get '/download/*.*' do + # matches /download/path/to/file.xml + params["splat"] # => ["path/to/file", "xml"] end Get an agent! diff --git a/lib/sinatra.rb b/lib/sinatra.rb index 03e2ebb3..1b4fdec6 100644 --- a/lib/sinatra.rb +++ b/lib/sinatra.rb @@ -164,7 +164,7 @@ module Sinatra class Event URI_CHAR = '[^/?:,&#\.]'.freeze unless defined?(URI_CHAR) - PARAM = /:(#{URI_CHAR}+)/.freeze unless defined?(PARAM) + PARAM = /(:(#{URI_CHAR}+)|\*)/.freeze unless defined?(PARAM) SPLAT = /(.*?)/ attr_reader :path, :block, :param_keys, :pattern, :options @@ -173,13 +173,18 @@ module Sinatra @block = b @param_keys = [] @options = options - regex = @path.to_s.gsub(PARAM) do - @param_keys << $1 - "(#{URI_CHAR}+)" + splats = 0 + regex = @path.to_s.gsub(PARAM) do |match| + if match == "*" + @param_keys << "_splat_#{splats}" + splats += 1 + SPLAT.to_s + else + @param_keys << $2 + "(#{URI_CHAR}+)" + end end - - regex.gsub!('*', SPLAT.to_s) - + @pattern = /^#{regex}$/ end @@ -194,6 +199,11 @@ module Sinatra end return unless pattern =~ request.path_info.squeeze('/') params.merge!(param_keys.zip($~.captures.map(&:from_param)).to_hash) + splats = params.select { |k, v| k =~ /^_splat_\d+$/ }.sort.map(&:last) + unless splats.empty? + params.delete_if { |k, v| k =~ /^_splat_\d+$/ } + params["splat"] = splats + end Result.new(block, params, 200) end diff --git a/test/app_test.rb b/test/app_test.rb index 507e6fa2..c7d2e51b 100644 --- a/test/app_test.rb +++ b/test/app_test.rb @@ -27,6 +27,46 @@ context "Sinatra" do body.should.equal 'Hello Blake' end + + specify "handles splats" do + get '/hi/*' do + params["splat"].kind_of?(Array).should.equal true + params["splat"].first + end + + get_it '/hi/Blake' + + should.be.ok + body.should.equal 'Blake' + end + + specify "handles multiple splats" do + get '/say/*/to/*' do + params["splat"].join(' ') + end + + get_it '/say/hello/to/world' + + should.be.ok + body.should.equal 'hello world' + end + + specify "allow empty splats" do + get '/say/*/to*/*' do + params["splat"].join(' ') + end + + get_it '/say/hello/to/world' + + should.be.ok + body.should.equal 'hello world' # second splat is empty + + get_it '/say/hello/tomy/world' + + should.be.ok + body.should.equal 'hello my world' + end + specify "gives access to underlying response header Hash" do get '/' do header['X-Test'] = 'Is this thing on?' From 3588b55bc17f1e8ab359241aa5a532dd25cc869b Mon Sep 17 00:00:00 2001 From: bmizerany Date: Wed, 7 May 2008 14:18:43 -0700 Subject: [PATCH 28/28] quick doc fix --- README.rdoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.rdoc b/README.rdoc index 0f3bbe7e..f1dc1013 100644 --- a/README.rdoc +++ b/README.rdoc @@ -88,7 +88,7 @@ If a file exists that maps to the REQUEST_PATH then it is served and the request = Views (if you need MVC) -All views are looked up in: +All file-based views are looked up in: root | - views/ @@ -150,12 +150,12 @@ This one is cool: __END__ - ## layout + @@ layout X = yield X - ## index + @@ index %div.title Hello world!!!!! Try it!