Instiki 0.16.5

Update to Rails 2.3.2 (the stable Rails 2.3 release).
Add audio/speex support
Update CHANGELOG
Bump version number
This commit is contained in:
Jacques Distler 2009-03-16 09:55:30 -05:00
parent 801d307405
commit e2ccdfd812
264 changed files with 4850 additions and 1906 deletions

View file

@ -31,7 +31,12 @@ rescue LoadError
end
end
require 'action_controller/vendor/rack-1.0/rack'
begin
gem 'rack', '~> 1.0.0'
require 'rack'
rescue Gem::LoadError
require 'action_controller/vendor/rack-1.0/rack'
end
module ActionController
# TODO: Review explicit to see if they will automatically be handled by

View file

@ -907,13 +907,15 @@ module ActionController #:nodoc:
extra_options[:template] = options
end
options = extra_options
elsif !options.is_a?(Hash)
extra_options[:partial] = options
options = extra_options
end
layout = pick_layout(options)
response.layout = layout.path_without_format_and_extension if layout
logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger && layout
layout = layout.path_without_format_and_extension if layout
if content_type = options[:content_type]
response.content_type = content_type.to_s
@ -1206,10 +1208,12 @@ module ActionController #:nodoc:
cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip }
cache_control << "max-age=#{seconds}"
cache_control.delete("no-cache")
if options[:public]
cache_control.delete("private")
cache_control.delete("no-cache")
cache_control << "public"
else
cache_control << "private"
end
# This allows for additional headers to be passed through like 'max-stale' => 5.hours

View file

@ -129,24 +129,23 @@ module ActionController #:nodoc:
attr_reader :path, :extension
class << self
def path_for(controller, options, infer_extension=true)
def path_for(controller, options, infer_extension = true)
new(controller, options, infer_extension).path
end
end
# When true, infer_extension will look up the cache path extension from the request's path & format.
# This is desirable when reading and writing the cache, but not when expiring the cache - expire_action should expire the same files regardless of the request format.
def initialize(controller, options = {}, infer_extension=true)
if infer_extension and options.is_a? Hash
request_extension = extract_extension(controller.request)
options = options.reverse_merge(:format => request_extension)
# This is desirable when reading and writing the cache, but not when expiring the cache -
# expire_action should expire the same files regardless of the request format.
def initialize(controller, options = {}, infer_extension = true)
if infer_extension
extract_extension(controller.request)
options = options.reverse_merge(:format => @extension) if options.is_a?(Hash)
end
path = controller.url_for(options).split('://').last
normalize!(path)
if infer_extension
@extension = request_extension
add_extension!(path, @extension)
end
add_extension!(path, @extension)
@path = URI.unescape(path)
end
@ -162,13 +161,7 @@ module ActionController #:nodoc:
def extract_extension(request)
# Don't want just what comes after the last '.' to accommodate multi part extensions
# such as tar.gz.
extension = request.path[/^[^.]+\.(.+)$/, 1]
# If there's no extension in the path, check request.format
if extension.nil?
extension = request.cache_format
end
extension
@extension = request.path[/^[^.]+\.(.+)$/, 1] || request.cache_format
end
end
end

View file

@ -68,8 +68,11 @@ module ActionController
#
# Simple Digest example:
#
# require 'digest/md5'
# class PostsController < ApplicationController
# USERS = {"dhh" => "secret"}
# REALM = "SuperSecret"
# USERS = {"dhh" => "secret", #plain text password
# "dap" => Digest:MD5::hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password
#
# before_filter :authenticate, :except => [:index]
#
@ -83,14 +86,18 @@ module ActionController
#
# private
# def authenticate
# authenticate_or_request_with_http_digest(realm) do |username|
# authenticate_or_request_with_http_digest(REALM) do |username|
# USERS[username]
# end
# end
# end
#
# NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password so the framework can appropriately
# hash it to check the user's credentials. Returning +nil+ will cause authentication to fail.
# NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately
# hash to check the user's credentials. Returning +nil+ will cause authentication to fail.
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
# authenticate as the user at this +realm+, but would not have the user's password to try using at
# other sites.
#
# On shared hosts, Apache sometimes doesn't pass authentication headers to
# FCGI instances. If your environment matches this description and you cannot
@ -177,26 +184,37 @@ module ActionController
end
# Raises error unless the request credentials response value matches the expected value.
# First try the password as a ha1 digest password. If this fails, then try it as a plain
# text password.
def validate_digest_response(request, realm, &password_procedure)
credentials = decode_credentials_header(request)
valid_nonce = validate_nonce(request, credentials[:nonce])
if valid_nonce && realm == credentials[:realm] && opaque(request.session.session_id) == credentials[:opaque]
if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque]
password = password_procedure.call(credentials[:username])
expected = expected_response(request.env['REQUEST_METHOD'], credentials[:uri], credentials, password)
expected == credentials[:response]
[true, false].any? do |password_is_ha1|
expected = expected_response(request.env['REQUEST_METHOD'], request.env['REQUEST_URI'], credentials, password, password_is_ha1)
expected == credentials[:response]
end
end
end
# Returns the expected response for a request of +http_method+ to +uri+ with the decoded +credentials+ and the expected +password+
def expected_response(http_method, uri, credentials, password)
ha1 = ::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
# Optional parameter +password_is_ha1+ is set to +true+ by default, since best practice is to store ha1 digest instead
# of a plain-text password.
def expected_response(http_method, uri, credentials, password, password_is_ha1=true)
ha1 = password_is_ha1 ? password : ha1(credentials, password)
ha2 = ::Digest::MD5.hexdigest([http_method.to_s.upcase, uri].join(':'))
::Digest::MD5.hexdigest([ha1, credentials[:nonce], credentials[:nc], credentials[:cnonce], credentials[:qop], ha2].join(':'))
end
def encode_credentials(http_method, credentials, password)
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password)
def ha1(credentials, password)
::Digest::MD5.hexdigest([credentials[:username], credentials[:realm], password].join(':'))
end
def encode_credentials(http_method, credentials, password, password_is_ha1)
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
"Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ')
end
@ -213,8 +231,7 @@ module ActionController
end
def authentication_header(controller, realm)
session_id = controller.request.session.session_id
controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce(session_id)}", opaque="#{opaque(session_id)}")
controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}")
end
def authentication_request(controller, realm, message = nil)
@ -252,23 +269,36 @@ module ActionController
# POST or PUT requests and a time-stamp for GET requests. For more details on the issues involved see Section 4
# of this document.
#
# The nonce is opaque to the client.
def nonce(session_id, time = Time.now)
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
# key from the Rails session secret generated upon creation of project. Ensures
# the time cannot be modifed by client.
def nonce(time = Time.now)
t = time.to_i
hashed = [t, session_id]
hashed = [t, secret_key]
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
Base64.encode64("#{t}:#{digest}").gsub("\n", '')
end
def validate_nonce(request, value)
# Might want a shorter timeout depending on whether the request
# is a PUT or POST, and if client is browser or web service.
# Can be much shorter if the Stale directive is implemented. This would
# allow a user to use new nonce without prompting user again for their
# username and password.
def validate_nonce(request, value, seconds_to_timeout=5*60)
t = Base64.decode64(value).split(":").first.to_i
nonce(request.session.session_id, t) == value && (t - Time.now.to_i).abs <= 10 * 60
nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
end
# Opaque based on digest of session_id
def opaque(session_id)
Base64.encode64(::Digest::MD5::hexdigest(session_id)).gsub("\n", '')
# Opaque based on random generation - but changing each request?
def opaque()
::Digest::MD5.hexdigest(secret_key)
end
# Set in /initializers/session_store.rb, and loaded even if sessions are not in use.
def secret_key
ActionController::Base.session_options[:secret]
end
end
end
end

View file

@ -5,7 +5,7 @@ require 'active_support/test_case'
module ActionController
module Integration #:nodoc:
# An integration Session instance represents a set of requests and responses
# performed sequentially by some virtual user. Becase you can instantiate
# performed sequentially by some virtual user. Because you can instantiate
# multiple sessions and run them side-by-side, you can also mimic (to some
# limited extent) multiple simultaneous users interacting with your system.
#

View file

@ -198,7 +198,7 @@ module ActionController #:nodoc:
# is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method
# object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
# weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
def active_layout(passed_layout = nil)
def active_layout(passed_layout = nil, options = {})
layout = passed_layout || default_layout
return layout if layout.respond_to?(:render)
@ -208,7 +208,7 @@ module ActionController #:nodoc:
else layout
end
find_layout(active_layout, default_template_format) if active_layout
find_layout(active_layout, default_template_format, options[:html_fallback]) if active_layout
end
private
@ -220,8 +220,8 @@ module ActionController #:nodoc:
nil
end
def find_layout(layout, format) #:nodoc:
view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", format, false)
def find_layout(layout, format, html_fallback=false) #:nodoc:
view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", format, html_fallback)
rescue ActionView::MissingTemplate
raise if Mime::Type.lookup_by_extension(format.to_s).html?
end
@ -234,7 +234,7 @@ module ActionController #:nodoc:
when NilClass, TrueClass
active_layout if action_has_layout? && candidate_for_layout?(:template => default_template_name)
else
active_layout(layout)
active_layout(layout, :html_fallback => true)
end
else
active_layout if action_has_layout? && candidate_for_layout?(options)

View file

@ -163,7 +163,8 @@ module ActionController
if parent.is_a?(Symbol) || parent.is_a?(String)
string << "#{parent}_"
else
string << "#{RecordIdentifier.__send__("singular_class_name", parent)}_"
string << "#{RecordIdentifier.__send__("plural_class_name", parent)}".singularize
string << "_"
end
end
end
@ -171,7 +172,9 @@ module ActionController
if record.is_a?(Symbol) || record.is_a?(String)
route << "#{record}_"
else
route << "#{RecordIdentifier.__send__("#{inflection}_class_name", record)}_"
route << "#{RecordIdentifier.__send__("plural_class_name", record)}"
route = route.singularize if inflection == :singular
route << "_"
end
action_prefix(options) + namespace + route + routing_type(options).to_s

View file

@ -442,6 +442,7 @@ EOM
end
def reset_session
@env['rack.session.options'].delete(:id)
@env['rack.session'] = {}
end

View file

@ -91,7 +91,7 @@ module ActionController
end
def shallow_path_prefix
@shallow_path_prefix ||= "#{path_prefix unless @options[:shallow]}"
@shallow_path_prefix ||= @options[:shallow] ? @options[:namespace].try(:sub, /\/$/, '') : path_prefix
end
def member_path
@ -103,7 +103,7 @@ module ActionController
end
def shallow_name_prefix
@shallow_name_prefix ||= "#{name_prefix unless @options[:shallow]}"
@shallow_name_prefix ||= @options[:shallow] ? @options[:namespace].try(:gsub, /\//, '_') : name_prefix
end
def nesting_name_prefix
@ -630,7 +630,7 @@ module ActionController
action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
action_path ||= Base.resources_path_names[action] || action
map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m)
map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m, { :force_id => true })
end
end
end
@ -641,9 +641,9 @@ module ActionController
map_resource_routes(map, resource, :destroy, resource.member_path, route_path)
end
def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil)
def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil, resource_options = {} )
if resource.has_action?(action)
action_options = action_options_for(action, resource, method)
action_options = action_options_for(action, resource, method, resource_options)
formatted_route_path = "#{route_path}.:format"
if route_name && @set.named_routes[route_name.to_sym].nil?
@ -660,9 +660,10 @@ module ActionController
end
end
def action_options_for(action, resource, method = nil)
def action_options_for(action, resource, method = nil, resource_options = {})
default_options = { :action => action.to_s }
require_id = !resource.kind_of?(SingletonResource)
force_id = resource_options[:force_id] && !resource.kind_of?(SingletonResource)
case default_options[:action]
when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
@ -670,7 +671,7 @@ module ActionController
when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id))
when "update"; default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id))
when "destroy"; default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id))
else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements)
else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(force_id))
end
end
end

View file

@ -267,7 +267,7 @@ module ActionController
module Routing
SEPARATORS = %w( / . ? )
HTTP_METHODS = [:get, :head, :post, :put, :delete]
HTTP_METHODS = [:get, :head, :post, :put, :delete, :options]
ALLOWED_REQUIREMENTS_FOR_OPTIMISATION = [:controller, :action].to_set

View file

@ -159,7 +159,8 @@ module ActionController
path = "/#{path}" unless path[0] == ?/
path = "#{path}/" unless path[-1] == ?/
path = "/#{options[:path_prefix].to_s.gsub(/^\//,'')}#{path}" if options[:path_prefix]
prefix = options[:path_prefix].to_s.gsub(/^\//,'')
path = "/#{prefix}#{path}" unless prefix.blank?
segments = segments_for_route_path(path)
defaults, requirements, conditions = divide_route_options(segments, options)

View file

@ -98,7 +98,6 @@ module ActionController
if Array === item
i += 1
start = (i == 1)
final = (i == list.size)
tag, sub = item
if tag == :dynamic
body += padding + "#{start ? 'if' : 'elsif'} true\n"

View file

@ -556,7 +556,7 @@ module HTML
end
# Attribute value.
next if statement.sub!(/^\[\s*([[:alpha:]][\w\-]*)\s*((?:[~|^$*])?=)?\s*('[^']*'|"[^*]"|[^\]]*)\s*\]/) do |match|
next if statement.sub!(/^\[\s*([[:alpha:]][\w\-:]*)\s*((?:[~|^$*])?=)?\s*('[^']*'|"[^*]"|[^\]]*)\s*\]/) do |match|
name, equality, value = $1, $2, $3
if value == "?"
value = values.shift

View file

@ -23,14 +23,16 @@ module Rack
# Return the Rack release as a dotted string.
def self.release
"0.4"
"1.0 bundled"
end
autoload :Builder, "rack/builder"
autoload :Cascade, "rack/cascade"
autoload :Chunked, "rack/chunked"
autoload :CommonLogger, "rack/commonlogger"
autoload :ConditionalGet, "rack/conditionalget"
autoload :ContentLength, "rack/content_length"
autoload :ContentType, "rack/content_type"
autoload :File, "rack/file"
autoload :Deflater, "rack/deflater"
autoload :Directory, "rack/directory"

View file

@ -8,8 +8,8 @@ module Rack
attr_accessor :realm
def initialize(app, &authenticator)
@app, @authenticator = app, authenticator
def initialize(app, realm=nil, &authenticator)
@app, @realm, @authenticator = app, realm, authenticator
end

View file

@ -21,7 +21,7 @@ module Rack
attr_writer :passwords_hashed
def initialize(app)
def initialize(*args)
super
@passwords_hashed = nil
end

View file

@ -8,7 +8,7 @@ module Rack
class Request < Auth::AbstractRequest
def method
@env['REQUEST_METHOD']
@env['rack.methodoverride.original_method'] || @env['REQUEST_METHOD']
end
def digest?

View file

@ -34,11 +34,7 @@ module Rack
end
def use(middleware, *args, &block)
@ins << if block_given?
lambda { |app| middleware.new(app, *args, &block) }
else
lambda { |app| middleware.new(app, *args) }
end
@ins << lambda { |app| middleware.new(app, *args, &block) }
end
def run(app)

View file

@ -0,0 +1,49 @@
require 'rack/utils'
module Rack
# Middleware that applies chunked transfer encoding to response bodies
# when the response does not include a Content-Length header.
class Chunked
include Rack::Utils
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
headers = HeaderHash.new(headers)
if env['HTTP_VERSION'] == 'HTTP/1.0' ||
STATUS_WITH_NO_ENTITY_BODY.include?(status) ||
headers['Content-Length'] ||
headers['Transfer-Encoding']
[status, headers.to_hash, body]
else
dup.chunk(status, headers, body)
end
end
def chunk(status, headers, body)
@body = body
headers.delete('Content-Length')
headers['Transfer-Encoding'] = 'chunked'
[status, headers.to_hash, self]
end
def each
term = "\r\n"
@body.each do |chunk|
size = bytesize(chunk)
next if size == 0
yield [size.to_s(16), term, chunk, term].join
end
yield ["0", term, "", term].join
end
def close
@body.close if @body.respond_to?(:close)
end
end
end

View file

@ -3,21 +3,23 @@ require 'rack/utils'
module Rack
# Sets the Content-Length header on responses with fixed-length bodies.
class ContentLength
include Rack::Utils
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call(env)
headers = Utils::HeaderHash.new(headers)
headers = HeaderHash.new(headers)
if !Utils::STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
if !STATUS_WITH_NO_ENTITY_BODY.include?(status) &&
!headers['Content-Length'] &&
!headers['Transfer-Encoding'] &&
(body.respond_to?(:to_ary) || body.respond_to?(:to_str))
body = [body] if body.respond_to?(:to_str) # rack 0.4 compat
length = body.to_ary.inject(0) { |len, part| len + part.length }
length = body.to_ary.inject(0) { |len, part| len + bytesize(part) }
headers['Content-Length'] = length.to_s
end

View file

@ -0,0 +1,23 @@
require 'rack/utils'
module Rack
# Sets the Content-Type header on responses which don't have one.
#
# Builder Usage:
# use Rack::ContentType, "text/plain"
#
# When no content type argument is provided, "text/html" is assumed.
class ContentType
def initialize(app, content_type = "text/html")
@app, @content_type = app, content_type
end
def call(env)
status, headers, body = @app.call(env)
headers = Utils::HeaderHash.new(headers)
headers['Content-Type'] ||= @content_type
[status, headers.to_hash, body]
end
end
end

View file

@ -36,12 +36,12 @@ module Rack
mtime = headers.key?("Last-Modified") ?
Time.httpdate(headers["Last-Modified"]) : Time.now
body = self.class.gzip(body, mtime)
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
size = Rack::Utils.bytesize(body)
headers = headers.merge("Content-Encoding" => "gzip", "Content-Length" => size.to_s)
[status, headers, [body]]
when "deflate"
body = self.class.deflate(body)
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
size = Rack::Utils.bytesize(body)
headers = headers.merge("Content-Encoding" => "deflate", "Content-Length" => size.to_s)
[status, headers, [body]]
when "identity"

View file

@ -70,7 +70,7 @@ table { width:100%%; }
return unless @path_info.include? ".."
body = "Forbidden\n"
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
size = Rack::Utils.bytesize(body)
return [403, {"Content-Type" => "text/plain","Content-Length" => size.to_s}, [body]]
end
@ -89,6 +89,8 @@ table { width:100%%; }
type = stat.directory? ? 'directory' : Mime.mime_type(ext)
size = stat.directory? ? '-' : filesize_format(size)
mtime = stat.mtime.httpdate
url << '/' if stat.directory?
basename << '/' if stat.directory?
@files << [ url, basename, size, type, mtime ]
end
@ -120,7 +122,7 @@ table { width:100%%; }
def entity_not_found
body = "Entity not found: #{@path_info}\n"
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
size = Rack::Utils.bytesize(body)
return [404, {"Content-Type" => "text/plain", "Content-Length" => size.to_s}, [body]]
end

View file

@ -60,7 +60,7 @@ module Rack
body = self
else
body = [F.read(@path)]
size = body.first.size
size = Utils.bytesize(body.first)
end
[200, {

View file

@ -1,3 +1,5 @@
require 'rack/content_length'
module Rack
module Handler
class CGI
@ -6,6 +8,8 @@ module Rack
end
def self.serve(app)
app = ContentLength.new(app)
env = ENV.to_hash
env.delete "HTTP_CONTENT_LENGTH"

View file

@ -1,5 +1,6 @@
require 'fcgi'
require 'socket'
require 'rack/content_length'
module Rack
module Handler
@ -29,6 +30,8 @@ module Rack
end
def self.serve(request, app)
app = Rack::ContentLength.new(app)
env = request.env
env.delete "HTTP_CONTENT_LENGTH"

View file

@ -1,5 +1,6 @@
require 'lsapi'
#require 'cgi'
require 'rack/content_length'
module Rack
module Handler
class LSWS
@ -9,11 +10,13 @@ module Rack
end
end
def self.serve(app)
app = Rack::ContentLength.new(app)
env = ENV.to_hash
env.delete "HTTP_CONTENT_LENGTH"
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
env.update({"rack.version" => [0,1],
"rack.input" => $stdin,
"rack.input" => StringIO.new($stdin.read.to_s),
"rack.errors" => $stderr,
"rack.multithread" => false,
"rack.multiprocess" => true,

View file

@ -1,5 +1,7 @@
require 'mongrel'
require 'stringio'
require 'rack/content_length'
require 'rack/chunked'
module Rack
module Handler
@ -33,7 +35,7 @@ module Rack
end
def initialize(app)
@app = app
@app = Rack::Chunked.new(Rack::ContentLength.new(app))
end
def process(request, response)

View file

@ -1,5 +1,7 @@
require 'scgi'
require 'stringio'
require 'rack/content_length'
require 'rack/chunked'
module Rack
module Handler
@ -14,7 +16,7 @@ module Rack
end
def initialize(settings = {})
@app = settings[:app]
@app = Rack::Chunked.new(Rack::ContentLength.new(settings[:app]))
@log = Object.new
def @log.info(*args); end
def @log.error(*args); end

View file

@ -1,9 +1,12 @@
require "thin"
require "rack/content_length"
require "rack/chunked"
module Rack
module Handler
class Thin
def self.run(app, options={})
app = Rack::Chunked.new(Rack::ContentLength.new(app))
server = ::Thin::Server.new(options[:Host] || '0.0.0.0',
options[:Port] || 8080,
app)

View file

@ -1,5 +1,6 @@
require 'webrick'
require 'stringio'
require 'rack/content_length'
module Rack
module Handler
@ -14,7 +15,7 @@ module Rack
def initialize(server, app)
super server
@app = app
@app = Rack::ContentLength.new(app)
end
def service(req, res)
@ -35,7 +36,12 @@ module Rack
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
env["QUERY_STRING"] ||= ""
env["REQUEST_PATH"] ||= "/"
env.delete "PATH_INFO" if env["PATH_INFO"] == ""
if env["PATH_INFO"] == ""
env.delete "PATH_INFO"
else
path, n = req.request_uri.path, env["SCRIPT_NAME"].length
env["PATH_INFO"] = path[n, path.length-n]
end
status, headers, body = @app.call(env)
begin

View file

@ -88,7 +88,9 @@ module Rack
## within the application. This may be an
## empty string, if the request URL targets
## the application root and does not have a
## trailing slash.
## trailing slash. This information should be
## decoded by the server if it comes from a
## URL.
## <tt>QUERY_STRING</tt>:: The portion of the request URL that
## follows the <tt>?</tt>, if any. May be
@ -372,59 +374,43 @@ module Rack
## === The Content-Length
def check_content_length(status, headers, env)
chunked_response = false
headers.each { |key, value|
if key.downcase == 'transfer-encoding'
chunked_response = value.downcase != 'identity'
end
}
headers.each { |key, value|
if key.downcase == 'content-length'
## There must be a <tt>Content-Length</tt>, except when the
## +Status+ is 1xx, 204 or 304, in which case there must be none
## given.
## There must not be a <tt>Content-Length</tt> header when the
## +Status+ is 1xx, 204 or 304.
assert("Content-Length header found in #{status} response, not allowed") {
not Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}
assert('Content-Length header should not be used if body is chunked') {
not chunked_response
}
bytes = 0
string_body = true
@body.each { |part|
unless part.kind_of?(String)
string_body = false
break
end
if @body.respond_to?(:to_ary)
@body.each { |part|
unless part.kind_of?(String)
string_body = false
break
end
bytes += (part.respond_to?(:bytesize) ? part.bytesize : part.size)
}
if env["REQUEST_METHOD"] == "HEAD"
assert("Response body was given for HEAD request, but should be empty") {
bytes == 0
bytes += Rack::Utils.bytesize(part)
}
else
if string_body
assert("Content-Length header was #{value}, but should be #{bytes}") {
value == bytes.to_s
if env["REQUEST_METHOD"] == "HEAD"
assert("Response body was given for HEAD request, but should be empty") {
bytes == 0
}
else
if string_body
assert("Content-Length header was #{value}, but should be #{bytes}") {
value == bytes.to_s
}
end
end
end
return
end
}
if [ String, Array ].include?(@body.class) && !chunked_response
assert('No Content-Length header found') {
Rack::Utils::STATUS_WITH_NO_ENTITY_BODY.include? status.to_i
}
end
end
## === The Body

View file

@ -16,6 +16,8 @@ module Rack
# Your application's +call+ should end returning Response#finish.
class Response
attr_accessor :length
def initialize(body=[], status=200, header={}, &block)
@status = status
@header = Utils::HeaderHash.new({"Content-Type" => "text/html"}.

View file

@ -27,7 +27,7 @@ module Rack
message = Rack::Utils::HTTP_STATUS_CODES[status.to_i] || status.to_s
detail = env["rack.showstatus.detail"] || message
body = @template.result(binding)
size = body.respond_to?(:bytesize) ? body.bytesize : body.size
size = Rack::Utils.bytesize(body)
[status, headers.merge("Content-Type" => "text/html", "Content-Length" => size.to_s), [body]]
else
[status, headers, body]

View file

@ -12,7 +12,11 @@ module Rack
# first, since they are most specific.
class URLMap
def initialize(map)
def initialize(map = {})
remap(map)
end
def remap(map)
@mapping = map.map { |location, app|
if location =~ %r{\Ahttps?://(.*?)(/.*)}
host, location = $1, $2

View file

@ -144,6 +144,19 @@ module Rack
end
module_function :select_best_encoding
# Return the bytesize of String; uses String#length under Ruby 1.8 and
# String#bytesize under 1.9.
if ''.respond_to?(:bytesize)
def bytesize(string)
string.bytesize
end
else
def bytesize(string)
string.size
end
end
module_function :bytesize
# Context allows the use of a compatible middleware at different points
# in a request handling stack. A compatible middleware must define
# #context which should take the arguments env and app. The first of which
@ -359,7 +372,7 @@ module Rack
data = body
end
Utils.normalize_params(params, name, data)
Utils.normalize_params(params, name, data) unless data.nil?
break if buf.empty? || content_length == -1
}

View file

@ -2,7 +2,7 @@ module ActionPack #:nodoc:
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 1
TINY = 2
STRING = [MAJOR, MINOR, TINY].join('.')
end

View file

@ -221,10 +221,12 @@ module ActionView #:nodoc:
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
@assigns = assigns_for_first_render
@assigns_added = nil
@_render_stack = []
@controller = controller
@helpers = ProxyModule.new(self)
self.view_paths = view_paths
@_first_render = nil
@_current_render = nil
end
attr_reader :view_paths
@ -286,7 +288,19 @@ module ActionView #:nodoc:
# Access the current template being rendered.
# Returns a ActionView::Template object.
def template
@_render_stack.last
@_current_render
end
def template=(template) #:nodoc:
@_first_render ||= template
@_current_render = template
end
def with_template(current_template)
last_template, self.template = template, current_template
yield
ensure
self.template = last_template
end
private

View file

@ -121,7 +121,7 @@ module ActionView
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
(errors = obj.errors.on(method))
content_tag("div",
"#{options[:prepend_text]}#{errors.is_a?(Array) ? errors.first : errors}#{options[:append_text]}",
"#{options[:prepend_text]}#{ERB::Util.html_escape(errors.is_a?(Array) ? errors.first : errors)}#{options[:append_text]}",
:class => options[:css_class]
)
else
@ -198,7 +198,7 @@ module ActionView
locale.t :header, :count => count, :model => object_name
end
message = options.include?(:message) ? options[:message] : locale.t(:body)
error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, msg) } }.join
error_messages = objects.sum {|object| object.errors.full_messages.map {|msg| content_tag(:li, ERB::Util.html_escape(msg)) } }.join
contents = ''
contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?

View file

@ -876,8 +876,8 @@ module ActionView
input_name_from_type(type).gsub(/([\[\(])|(\]\[)/, '_').gsub(/[\]\)]/, '')
end
# Given an ordering of datetime components, create the selection html
# and join them with their appropriate seperators
# Given an ordering of datetime components, create the selection HTML
# and join them with their appropriate separators.
def build_selects_from_types(order)
select = ''
order.reverse.each do |type|

View file

@ -360,8 +360,8 @@ module ActionView
end
if confirm = options.delete("confirm")
options["onclick"] ||= ''
options["onclick"] << "return #{confirm_javascript_function(confirm)};"
options["onclick"] ||= 'return true;'
options["onclick"] = "if (!#{confirm_javascript_function(confirm)}) return false; #{options['onclick']}"
end
tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys)

View file

@ -15,6 +15,7 @@ module ActionView
# * <tt>:country_code</tt> - Sets the country code for the phone number.
#
# ==== Examples
# number_to_phone(5551234) # => 555-1234
# number_to_phone(1235551234) # => 123-555-1234
# number_to_phone(1235551234, :area_code => true) # => (123) 555-1234
# number_to_phone(1235551234, :delimiter => " ") # => 123 555 1234
@ -37,7 +38,8 @@ module ActionView
str << if area_code
number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
else
number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
number.gsub!(/([0-9]{0,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
number.starts_with?('-') ? number.slice!(1..-1) : number
end
str << " x #{extension}" unless extension.blank?
str
@ -138,7 +140,7 @@ module ActionView
# number_with_delimiter(12345678) # => 12,345,678
# number_with_delimiter(12345678.05) # => 12,345,678.05
# number_with_delimiter(12345678, :delimiter => ".") # => 12.345.678
# number_with_delimiter(12345678, :seperator => ",") # => 12,345,678
# number_with_delimiter(12345678, :separator => ",") # => 12,345,678
# number_with_delimiter(98765432.98, :delimiter => " ", :separator => ",")
# # => 98 765 432,98
#

View file

@ -107,7 +107,7 @@ module ActionView
# on the page in an Ajax response.
module PrototypeHelper
unless const_defined? :CALLBACKS
CALLBACKS = Set.new([ :uninitialized, :loading, :loaded,
CALLBACKS = Set.new([ :create, :uninitialized, :loading, :loaded,
:interactive, :complete, :failure, :success ] +
(100..599).to_a)
AJAX_OPTIONS = Set.new([ :before, :after, :condition, :url,

View file

@ -536,8 +536,9 @@ module ActionView
text.gsub(AUTO_LINK_RE) do
href = $&
punctuation = ''
# detect already linked URLs
if $` =~ /<a\s[^>]*href="$/
left, right = $`, $'
# detect already linked URLs and URLs in the middle of a tag
if left =~ /<[^>]+$/ && right =~ /^[^>]*>/
# do not change string; URL is alreay linked
href
else

View file

@ -61,7 +61,7 @@ module ActionView #:nodoc:
end
end
return Template.new(original_template_path, original_template_path =~ /\A\// ? "" : ".") if File.file?(original_template_path)
return Template.new(original_template_path, original_template_path.to_s =~ /\A\// ? "" : ".") if File.file?(original_template_path)
raise MissingTemplate.new(self, original_template_path, format)
end

View file

@ -27,23 +27,19 @@ module ActionView
def render(view, local_assigns = {})
compile(local_assigns)
stack = view.instance_variable_get(:@_render_stack)
stack.push(self)
view.with_template self do
view.send(:_evaluate_assigns_and_ivars)
view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
view.send(:_evaluate_assigns_and_ivars)
view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
result = view.send(method_name(local_assigns), local_assigns) do |*names|
ivar = :@_proc_for_layout
if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
view.capture(*names, &proc)
elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
view.instance_variable_get(ivar)
view.send(method_name(local_assigns), local_assigns) do |*names|
ivar = :@_proc_for_layout
if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
view.capture(*names, &proc)
elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
view.instance_variable_get(ivar)
end
end
end
stack.pop
result
end
def method_name(local_assigns)

View file

@ -218,7 +218,7 @@ module ActionView #:nodoc:
# Returns file split into an array
# [base_path, name, locale, format, extension]
def split(file)
if m = file.match(/^(.*\/)?([^\.]+)\.(.*)$/)
if m = file.to_s.match(/^(.*\/)?([^\.]+)\.(.*)$/)
base_path = m[1]
name = m[2]
extensions = m[3]