Upgrade to Rails 2.0.2

Upgraded to Rails 2.0.2, except that we maintain

   vendor/rails/actionpack/lib/action_controller/routing.rb

from Rail 1.2.6 (at least for now), so that Routes don't change. We still
get to enjoy Rails's many new features.

Also fixed a bug in Chunk-handling: disable WikiWord processing in tags (for real this time).
This commit is contained in:
Jacques Distler 2007-12-21 01:48:59 -06:00
parent 0f6889e09f
commit 6873fc8026
1083 changed files with 52810 additions and 41058 deletions

View file

@ -1,36 +0,0 @@
require 'cgi'
require 'cgi/session'
require 'cgi/session/pstore'
require 'action_controller/cgi_ext/cgi_methods'
# Wrapper around the CGIMethods that have been secluded to allow testing without
# an instantiated CGI object
class CGI #:nodoc:
class << self
alias :escapeHTML_fail_on_nil :escapeHTML
def escapeHTML(string)
escapeHTML_fail_on_nil(string) unless string.nil?
end
end
# Returns a parameter hash including values from both the request (POST/GET)
# and the query string with the latter taking precedence.
def parameters
request_parameters.update(query_parameters)
end
def query_parameters
CGIMethods.parse_query_parameters(query_string)
end
def request_parameters
CGIMethods.parse_request_parameters(params, env_table)
end
def session(parameters = nil)
parameters = {} if parameters.nil?
parameters['database_manager'] = CGI::Session::PStore
CGI::Session.new(self, parameters)
end
end

View file

@ -1,211 +0,0 @@
require 'cgi'
require 'action_controller/vendor/xml_node'
require 'strscan'
# Static methods for parsing the query and request parameters that can be used in
# a CGI extension class or testing in isolation.
class CGIMethods #:nodoc:
class << self
# DEPRECATED: Use parse_form_encoded_parameters
def parse_query_parameters(query_string)
pairs = query_string.split('&').collect do |chunk|
next if chunk.empty?
key, value = chunk.split('=', 2)
next if key.empty?
value = (value.nil? || value.empty?) ? nil : CGI.unescape(value)
[ CGI.unescape(key), value ]
end.compact
FormEncodedPairParser.new(pairs).result
end
# DEPRECATED: Use parse_form_encoded_parameters
def parse_request_parameters(params)
parser = FormEncodedPairParser.new
params = params.dup
until params.empty?
for key, value in params
if key.blank?
params.delete key
elsif !key.include?('[')
# much faster to test for the most common case first (GET)
# and avoid the call to build_deep_hash
parser.result[key] = get_typed_value(value[0])
params.delete key
elsif value.is_a?(Array)
parser.parse(key, get_typed_value(value.shift))
params.delete key if value.empty?
else
raise TypeError, "Expected array, found #{value.inspect}"
end
end
end
parser.result
end
def parse_formatted_request_parameters(mime_type, raw_post_data)
case strategy = ActionController::Base.param_parsers[mime_type]
when Proc
strategy.call(raw_post_data)
when :xml_simple
raw_post_data.blank? ? {} : Hash.from_xml(raw_post_data)
when :yaml
YAML.load(raw_post_data)
when :xml_node
node = XmlNode.from_xml(raw_post_data)
{ node.node_name => node }
end
rescue Exception => e # YAML, XML or Ruby code block errors
{ "exception" => "#{e.message} (#{e.class})", "backtrace" => e.backtrace,
"raw_post_data" => raw_post_data, "format" => mime_type }
end
private
def get_typed_value(value)
case value
when String
value
when NilClass
''
when Array
value.map { |v| get_typed_value(v) }
else
# Uploaded file provides content type and filename.
if value.respond_to?(:content_type) &&
!value.content_type.blank? &&
!value.original_filename.blank?
unless value.respond_to?(:full_original_filename)
class << value
alias_method :full_original_filename, :original_filename
# Take the basename of the upload's original filename.
# This handles the full Windows paths given by Internet Explorer
# (and perhaps other broken user agents) without affecting
# those which give the lone filename.
# The Windows regexp is adapted from Perl's File::Basename.
def original_filename
if md = /^(?:.*[:\\\/])?(.*)/m.match(full_original_filename)
md.captures.first
else
File.basename full_original_filename
end
end
end
end
# Return the same value after overriding original_filename.
value
# Multipart values may have content type, but no filename.
elsif value.respond_to?(:read)
result = value.read
value.rewind
result
# Unknown value, neither string nor multipart.
else
raise "Unknown form value: #{value.inspect}"
end
end
end
end
class FormEncodedPairParser < StringScanner #:nodoc:
attr_reader :top, :parent, :result
def initialize(pairs = [])
super('')
@result = {}
pairs.each { |key, value| parse(key, value) }
end
KEY_REGEXP = %r{([^\[\]=&]+)}
BRACKETED_KEY_REGEXP = %r{\[([^\[\]=&]+)\]}
# Parse the query string
def parse(key, value)
self.string = key
@top, @parent = result, nil
# First scan the bare key
key = scan(KEY_REGEXP) or return
key = post_key_check(key)
# Then scan as many nestings as present
until eos?
r = scan(BRACKETED_KEY_REGEXP) or return
key = self[1]
key = post_key_check(key)
end
bind(key, value)
end
private
# After we see a key, we must look ahead to determine our next action. Cases:
#
# [] follows the key. Then the value must be an array.
# = follows the key. (A value comes next)
# & or the end of string follows the key. Then the key is a flag.
# otherwise, a hash follows the key.
def post_key_check(key)
if scan(/\[\]/) # a[b][] indicates that b is an array
container(key, Array)
nil
elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
container(key, Hash)
nil
else # End of key? We do nothing.
key
end
end
# Add a container to the stack.
#
def container(key, klass)
type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
value = bind(key, klass.new)
type_conflict! klass, value unless value.is_a?(klass)
push(value)
end
# Push a value onto the 'stack', which is actually only the top 2 items.
def push(value)
@parent, @top = @top, value
end
# Bind a key (which may be nil for items in an array) to the provided value.
def bind(key, value)
if top.is_a? Array
if key
if top[-1].is_a?(Hash) && ! top[-1].key?(key)
top[-1][key] = value
else
top << {key => value}.with_indifferent_access
push top.last
end
else
top << value
end
elsif top.is_a? Hash
key = CGI.unescape(key)
parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
return top[key] ||= value
else
raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
end
return value
end
def type_conflict!(klass, value)
raise TypeError,
"Conflicting types for parameter containers. " +
"Expected an instance of #{klass}, but found an instance of #{value.class}. " +
"This can be caused by passing Array and Hash based paramters qs[]=value&qs[key]=value. "
end
end
end

View file

@ -1,10 +1,11 @@
CGI.module_eval { remove_const "Cookie" }
# TODO: document how this differs from stdlib CGI::Cookie
class CGI #:nodoc:
# This is a cookie class that fixes the performance problems with the default one that ships with 1.8.1 and below.
# It replaces the inheritance on SimpleDelegator with DelegateClass(Array) following the suggestion from Matz on
# http://groups.google.com/groups?th=e3a4e68ba042f842&seekm=c3sioe%241qvm%241%40news.cybercity.dk#link14
class Cookie < DelegateClass(Array)
attr_accessor :name, :value, :path, :domain, :expires
attr_reader :secure, :http_only
# Create a new CGI::Cookie object.
#
# The contents of the cookie can be specified as a +name+ and one
@ -19,9 +20,11 @@ class CGI #:nodoc:
# domain:: the domain for which this cookie applies.
# expires:: the time at which this cookie expires, as a +Time+ object.
# secure:: whether this cookie is a secure cookie or not (default to
# false). Secure cookies are only transmitted to HTTPS
# false). Secure cookies are only transmitted to HTTPS
# servers.
#
# http_only:: whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP
# More details: http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx
# Defaults to false.
# These keywords correspond to attributes of the cookie object.
def initialize(name = '', *value)
if name.kind_of?(String)
@ -30,6 +33,7 @@ class CGI #:nodoc:
@domain = nil
@expires = nil
@secure = false
@http_only = false
@path = nil
else
@name = name['name']
@ -37,12 +41,11 @@ class CGI #:nodoc:
@domain = name['domain']
@expires = name['expires']
@secure = name['secure'] || false
@http_only = name['http_only'] || false
@path = name['path']
end
unless @name
raise ArgumentError, "`name' required"
end
raise ArgumentError, "`name' required" unless @name
# simple support for IE
unless @path
@ -53,48 +56,26 @@ class CGI #:nodoc:
super(@value)
end
def __setobj__(obj)
@_dc_obj = obj
# Set whether the Cookie is a secure cookie or not.
def secure=(val)
@secure = val == true
end
attr_accessor("name", "value", "path", "domain", "expires")
attr_reader("secure")
# Set whether the Cookie is a secure cookie or not.
#
# +val+ must be a boolean.
def secure=(val)
@secure = val if val == true or val == false
@secure
# Set whether the Cookie is an HTTP only cookie or not.
def http_only=(val)
@http_only = val == true
end
# Convert the Cookie to its string representation.
def to_s
buf = ""
buf = ''
buf << @name << '='
if @value.kind_of?(String)
buf << CGI::escape(@value)
else
buf << @value.collect{|v| CGI::escape(v) }.join("&")
end
if @domain
buf << '; domain=' << @domain
end
if @path
buf << '; path=' << @path
end
if @expires
buf << '; expires=' << CGI::rfc1123_date(@expires)
end
if @secure == true
buf << '; secure'
end
buf << (@value.kind_of?(String) ? CGI::escape(@value) : @value.collect{|v| CGI::escape(v) }.join("&"))
buf << '; domain=' << @domain if @domain
buf << '; path=' << @path if @path
buf << '; expires=' << CGI::rfc1123_date(@expires) if @expires
buf << '; secure' if @secure
buf << '; HttpOnly' if @http_only
buf
end
@ -108,7 +89,7 @@ class CGI #:nodoc:
cookies = Hash.new([])
if raw_cookie
raw_cookie.split(/; ?/).each do |pairs|
raw_cookie.split(/[;,]\s?/).each do |pairs|
name, values = pairs.split('=',2)
next unless name and values
name = CGI::unescape(name)

View file

@ -1,30 +0,0 @@
# CGI::Session::PStore.initialize requires 'digest/md5' on every call.
# This makes sense when spawning processes per request, but is
# unnecessarily expensive when serving requests from a long-lived
# process.
require 'cgi/session'
require 'cgi/session/pstore'
require 'digest/md5'
class CGI::Session::PStore #:nodoc:
def initialize(session, option={})
dir = option['tmpdir'] || Dir::tmpdir
prefix = option['prefix'] || ''
id = session.session_id
md5 = Digest::MD5.hexdigest(id)[0,16]
path = dir+"/"+prefix+md5
path.untaint
if File::exist?(path)
@hash = nil
else
unless session.new_session
raise CGI::Session::NoSession, "uninitialized session"
end
@hash = {}
end
@p = ::PStore.new(path)
@p.transaction do |p|
File.chmod(0600, p.path)
end
end
end

View file

@ -0,0 +1,22 @@
require 'cgi'
class CGI #:nodoc:
module QueryExtension
# Remove the old initialize_query method before redefining it.
remove_method :initialize_query
# Neuter CGI parameter parsing.
def initialize_query
# Fix some strange request environments.
env_table['REQUEST_METHOD'] ||= 'GET'
# POST assumes missing Content-Type is application/x-www-form-urlencoded.
if env_table['CONTENT_TYPE'].blank? && env_table['REQUEST_METHOD'] == 'POST'
env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded'
end
@cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
@params = {}
end
end
end

View file

@ -1,95 +0,0 @@
class CGI #:nodoc:
module QueryExtension
# Initialize the data from the query.
#
# Handles multipart forms (in particular, forms that involve file uploads).
# Reads query parameters in the @params field, and cookies into @cookies.
def initialize_query
@cookies = CGI::Cookie::parse(env_table['HTTP_COOKIE'] || env_table['COOKIE'])
# Fix some strange request environments.
if method = env_table['REQUEST_METHOD']
method = method.to_s.downcase.intern
else
method = :get
end
# POST assumes missing Content-Type is application/x-www-form-urlencoded.
content_type = env_table['CONTENT_TYPE']
if content_type.blank? && method == :post
content_type = 'application/x-www-form-urlencoded'
end
# Force content length to zero if missing.
content_length = env_table['CONTENT_LENGTH'].to_i
# Set multipart to false by default.
@multipart = false
# POST and PUT may have params in entity body. If content type is
# missing for POST, assume urlencoded. If content type is missing
# for PUT, don't assume anything and don't parse the parameters:
# it's likely binary data.
#
# The other HTTP methods have their params in the query string.
if method == :post || method == :put
if boundary = extract_multipart_form_boundary(content_type)
@multipart = true
@params = read_multipart(boundary, content_length)
elsif content_type.blank? || content_type !~ %r{application/x-www-form-urlencoded}i
read_params(method, content_length)
@params = {}
end
end
@params ||= CGI.parse(read_params(method, content_length))
end
private
unless defined?(MULTIPART_FORM_BOUNDARY_RE)
MULTIPART_FORM_BOUNDARY_RE = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n #"
end
def extract_multipart_form_boundary(content_type)
MULTIPART_FORM_BOUNDARY_RE.match(content_type).to_a.pop
end
if defined? MOD_RUBY
def read_query
Apache::request.args || ''
end
else
def read_query
# fixes CGI querystring parsing for lighttpd
env_qs = env_table['QUERY_STRING']
if env_qs.blank? && !(uri = env_table['REQUEST_URI']).blank?
uri.split('?', 2)[1] || ''
else
env_qs || ''
end
end
end
def read_body(content_length)
stdinput.binmode if stdinput.respond_to?(:binmode)
content = stdinput.read(content_length) || ''
# Fix for Safari Ajax postings that always append \000
content.chop! if content[-1] == 0
content.gsub!(/&_=$/, '')
env_table['RAW_POST_DATA'] = content.freeze
end
def read_params(method, content_length)
case method
when :get
read_query
when :post, :put
read_body(content_length)
when :cmd
read_from_cmdline
else # :head, :delete, :options, :trace, :connect
read_query
end
end
end # module QueryExtension
end

View file

@ -0,0 +1,73 @@
require 'digest/md5'
require 'cgi/session'
require 'cgi/session/pstore'
class CGI #:nodoc:
# * Expose the CGI instance to session stores.
# * Don't require 'digest/md5' whenever a new session id is generated.
class Session #:nodoc:
begin
require 'securerandom'
# Generate a 32-character unique id using SecureRandom.
# This is used to generate session ids but may be reused elsewhere.
def self.generate_unique_id(constant = nil)
SecureRandom.hex(16)
end
rescue LoadError
# Generate an 32-character unique id based on a hash of the current time,
# a random number, the process id, and a constant string. This is used
# to generate session ids but may be reused elsewhere.
def self.generate_unique_id(constant = 'foobar')
md5 = Digest::MD5.new
now = Time.now
md5 << now.to_s
md5 << String(now.usec)
md5 << String(rand(0))
md5 << String($$)
md5 << constant
md5.hexdigest
end
end
# Make the CGI instance available to session stores.
attr_reader :cgi
attr_reader :dbman
alias_method :initialize_without_cgi_reader, :initialize
def initialize(cgi, options = {})
@cgi = cgi
initialize_without_cgi_reader(cgi, options)
end
private
# Create a new session id.
def create_new_id
@new_session = true
self.class.generate_unique_id
end
# * Don't require 'digest/md5' whenever a new session is started.
class PStore #:nodoc:
def initialize(session, option={})
dir = option['tmpdir'] || Dir::tmpdir
prefix = option['prefix'] || ''
id = session.session_id
md5 = Digest::MD5.hexdigest(id)[0,16]
path = dir+"/"+prefix+md5
path.untaint
if File::exist?(path)
@hash = nil
else
unless session.new_session
raise CGI::Session::NoSession, "uninitialized session"
end
@hash = {}
end
@p = ::PStore.new(path)
@p.transaction do |p|
File.chmod(0600, p.path)
end
end
end
end
end

View file

@ -1,30 +0,0 @@
# CGI::Session#create_new_id requires 'digest/md5' on every call. This makes
# sense when spawning processes per request, but is unnecessarily expensive
# when serving requests from a long-lived process.
#
# http://railsexpress.de/blog/articles/2005/11/22/speeding-up-the-creation-of-new-sessions
require 'cgi/session'
require 'digest/md5'
class CGI
class Session #:nodoc:
private
# Create a new session id.
#
# The session id is an MD5 hash based upon the time,
# a random number, and a constant string. This routine
# is used internally for automatically generated
# session ids.
def create_new_id
md5 = Digest::MD5::new
now = Time::now
md5.update(now.to_s)
md5.update(String(now.usec))
md5.update(String(rand(0)))
md5.update(String($$))
md5.update('foobar')
@new_session = true
md5.hexdigest
end
end
end

View file

@ -0,0 +1,23 @@
require 'cgi'
module ActionController
module CgiExt
# Publicize the CGI's internal input stream so we can lazy-read
# request.body. Make it writable so we don't have to play $stdin games.
module Stdinput
def self.included(base)
base.class_eval do
remove_method :stdinput
attr_accessor :stdinput
end
base.alias_method_chain :initialize, :stdinput
end
def initialize_with_stdinput(type = nil, stdinput = $stdin)
@stdinput = stdinput
initialize_without_stdinput(type || 'query')
end
end
end
end