Merge commit 'source/master'
This commit is contained in:
commit
5e56613a32
|
@ -0,0 +1,3 @@
|
|||
[submodule "vendor/rack"]
|
||||
path = vendor/rack
|
||||
url = git://github.com/chneukirchen/rack-mirror.git
|
|
@ -0,0 +1,2 @@
|
|||
v0.2.1 File upload fix and minor tweaks
|
||||
v0.2.0 Released!
|
|
@ -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.
|
|
@ -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
|
48
README.rdoc
48
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!
|
||||
|
@ -82,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/
|
||||
|
@ -100,7 +106,7 @@ This will render <tt>./views/index.haml</tt>
|
|||
|
||||
=== Sass
|
||||
get '/stylesheet.css' do
|
||||
header 'Content-Type' => 'text/css; charset=utf-8'
|
||||
content_type 'text/css', :charset => 'utf-8'
|
||||
sass :stylesheet
|
||||
end
|
||||
|
||||
|
@ -144,12 +150,12 @@ This one is cool:
|
|||
|
||||
__END__
|
||||
|
||||
## layout
|
||||
@@ layout
|
||||
X
|
||||
= yield
|
||||
X
|
||||
|
||||
## index
|
||||
@@ index
|
||||
%div.title Hello world!!!!!
|
||||
|
||||
Try it!
|
||||
|
@ -174,7 +180,7 @@ This works like Haml except you use <tt>erb</tt> instead of <tt>haml</tt>
|
|||
|
||||
=== Sass
|
||||
|
||||
This works like Haml except you use <tt>sass</tt> instead of <tt>haml</tt>. It's also a good idea to add <tt>header 'Content-Type' => 'text/css; charset=utf-8'</tt> before your call to <tt>sass</tt> so Sinatra returns the proper content type header with the file.
|
||||
This works like Haml except you use <tt>sass</tt> instead of <tt>haml</tt>. It's also a good idea to add <tt>content_type 'text/css', :charset => 'utf-8'</tt> before your call to <tt>sass</tt> so Sinatra returns the proper content type header with the file.
|
||||
|
||||
=== Builder
|
||||
|
||||
|
@ -301,7 +307,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:
|
||||
|
@ -345,6 +351,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', '<myxml></myxml>', 'HTTP_ACCEPT' => 'application/xml'
|
||||
|
||||
also works with:
|
||||
|
||||
get_it, post_it, put_it, delete_it, head_it
|
||||
|
||||
=== Test/Unit
|
||||
|
||||
require 'my_sinatra_app'
|
||||
|
@ -401,7 +421,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 +438,3 @@ at the top of your sinatra.rb file
|
|||
get '/about' do
|
||||
"I'm running on Version " + Sinatra::Version.combined
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
21
Rakefile
21
Rakefile
|
@ -1,17 +1,14 @@
|
|||
require 'rubygems'
|
||||
require 'rake/testtask'
|
||||
require 'rake/rdoctask'
|
||||
require 'echoe'
|
||||
|
||||
task :default => :test
|
||||
|
||||
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
|
||||
|
||||
Rake::TestTask.new do |t|
|
||||
ENV['SINATRA_ENV'] = 'test'
|
||||
t.pattern = File.dirname(__FILE__) + "/test/*_test.rb"
|
||||
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"]
|
||||
p.install_message = "*** Be sure to checkout the site for helpful tips! sinatrarb.com ***"
|
||||
p.include_rakefile = true
|
||||
end
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
||||
<html>
|
||||
<head>
|
||||
<title>Your Page Title</title>
|
||||
<meta http-equiv="REFRESH" content="0;url=http://sinatrarb.com"></HEAD>
|
||||
<BODY>
|
||||
This site has <a href="http:://sinatrarb.com">moved</a>.
|
||||
</BODY>
|
||||
</HTML>
|
208
lib/sinatra.rb
208
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'
|
||||
require "uri"
|
||||
|
||||
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|
|
||||
|
@ -48,7 +52,7 @@ module Rack #:nodoc:
|
|||
end
|
||||
|
||||
def user_agent
|
||||
env['HTTP_USER_AGENT']
|
||||
@env['HTTP_USER_AGENT']
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -78,7 +82,7 @@ module Sinatra
|
|||
module Version
|
||||
MAJOR = '0'
|
||||
MINOR = '2'
|
||||
REVISION = '0'
|
||||
REVISION = '1'
|
||||
def self.combined
|
||||
[MAJOR, MINOR, REVISION].join('.')
|
||||
end
|
||||
|
@ -120,12 +124,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)"
|
||||
|
@ -140,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
|
||||
|
||||
|
@ -149,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
|
||||
|
||||
|
@ -170,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
|
||||
|
||||
|
@ -193,14 +227,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
|
||||
|
@ -376,23 +410,130 @@ 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
|
||||
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'
|
||||
# "<h1>Hello World</h1>"
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
def render(renderer, template, options={})
|
||||
|
@ -473,7 +614,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
|
||||
|
@ -678,7 +820,7 @@ module Sinatra
|
|||
end
|
||||
|
||||
def session
|
||||
@request.env['rack.session'] || {}
|
||||
request.env['rack.session'] ||= {}
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -697,15 +839,16 @@ 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,
|
||||
:logging => true
|
||||
}
|
||||
end
|
||||
|
||||
|
@ -722,7 +865,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
|
||||
|
||||
|
@ -993,7 +1137,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
|
||||
|
@ -1040,14 +1184,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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
@ -46,7 +43,7 @@ module Sinatra
|
|||
end
|
||||
|
||||
def method_missing(name, *args)
|
||||
@response.send(name, *args)
|
||||
@response.send(name, *args) rescue super
|
||||
end
|
||||
|
||||
end
|
||||
|
|
132
test/app_test.rb
132
test/app_test.rb
|
@ -26,7 +26,62 @@ context "Sinatra" do
|
|||
should.be.ok
|
||||
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?'
|
||||
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 +110,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
|
||||
|
@ -87,6 +153,70 @@ context "Sinatra" do
|
|||
|
||||
end
|
||||
|
||||
specify "should easily set response Content-Type" do
|
||||
get '/foo.html' do
|
||||
content_type 'text/html', :charset => 'utf-8'
|
||||
"<h1>Hello, World</h1>"
|
||||
end
|
||||
|
||||
get_it '/foo.html'
|
||||
should.be.ok
|
||||
headers['Content-Type'].should.equal 'text/html;charset=utf-8'
|
||||
body.should.equal '<h1>Hello, World</h1>'
|
||||
|
||||
get '/foo_test.xml' do
|
||||
content_type :xml
|
||||
"<feed></feed>"
|
||||
end
|
||||
|
||||
get_it '/foo_test.xml'
|
||||
should.be.ok
|
||||
headers['Content-Type'].should.equal 'application/xml'
|
||||
body.should.equal '<feed></feed>'
|
||||
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', :env => { 'HTTP_IF_MODIFIED_SINCE' => modified_at.httpdate }
|
||||
status.should.equal 304
|
||||
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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 "<foo></foo>\n"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "SendData" do
|
||||
|
|
|
@ -38,10 +38,10 @@ end
|
|||
|
||||
__END__
|
||||
|
||||
## foo
|
||||
@@ foo
|
||||
this is foo
|
||||
|
||||
## layout
|
||||
@@ layout
|
||||
X
|
||||
= yield
|
||||
X
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 67a7507fb07c9d49bf4be4fc6c4fc4578f1673ac
|
Loading…
Reference in New Issue