New Version

Sync with Latest Instiki Trunk.
Migrate to Rails 1.2.5.
Bump version number.
This commit is contained in:
Jacques Distler 2007-10-15 12:16:54 -05:00
parent de125367b0
commit 207fb1f7f2
120 changed files with 2592 additions and 662 deletions

View file

@ -1,7 +1,7 @@
module ActionController
module Assertions
module DomAssertions
# test 2 html strings to be equivalent, i.e. identical up to reordering of attributes
# Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
def assert_dom_equal(expected, actual, message="")
clean_backtrace do
expected_dom = HTML::Document.new(expected).root
@ -11,7 +11,7 @@ module ActionController
end
end
# negated form of +assert_dom_equivalent+
# The negated form of +assert_dom_equivalent+.
def assert_dom_not_equal(expected, actual, message="")
clean_backtrace do
expected_dom = HTML::Document.new(expected).root

View file

@ -1,7 +1,7 @@
module ActionController
module Assertions
module ModelAssertions
# ensures that the passed record is valid by active record standards. returns the error messages if not
# Ensures that the passed record is valid by ActiveRecord standards and returns any error messages if it is not.
def assert_valid(record)
clean_backtrace do
assert record.valid?, record.errors.full_messages.join("\n")

View file

@ -69,6 +69,7 @@ module ActionController
end
if value.respond_to?(:[]) && value['controller']
value['controller'] = value['controller'].to_s
if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/')
new_controller_path = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path)
value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path)
@ -120,6 +121,7 @@ module ActionController
end
private
# Recognizes the route for a given path.
def recognized_request_for(path, request_method = nil)
path = "/#{path}" unless path.first == '/'
@ -132,6 +134,7 @@ module ActionController
request
end
# Proxy to to_param if the object will respond to it.
def parameterize(value)
value.respond_to?(:to_param) ? value.to_param : value
end

View file

@ -82,6 +82,7 @@ module ActionController
end
private
# Recognizes the route for a given path.
def recognized_request_for(path, request_method = nil)
path = "/#{path}" unless path.first == '/'

View file

@ -561,6 +561,8 @@ module ActionController
# RJS encodes double quotes and line breaks.
unescaped= rjs_string.gsub('\"', '"')
unescaped.gsub!('\n', "\n")
unescaped.gsub!('\076', '>')
unescaped.gsub!('\074', '<')
# RJS encodes non-ascii characters.
unescaped.gsub!(RJS_PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
unescaped

View file

@ -7,7 +7,6 @@ require 'action_controller/url_rewriter'
require 'action_controller/status_codes'
require 'drb'
require 'set'
require 'md5'
module ActionController #:nodoc:
class ActionControllerError < StandardError #:nodoc:
@ -293,6 +292,10 @@ module ActionController #:nodoc:
# Turn on +ignore_missing_templates+ if you want to unit test actions without making the associated templates.
cattr_accessor :ignore_missing_templates
# Controls the resource action separator
@@resource_action_separator = "/"
cattr_accessor :resource_action_separator
# Holds the request object that's primarily used to get environment variables through access like
# <tt>request.env["REQUEST_URI"]</tt>.
attr_internal :request
@ -394,7 +397,8 @@ module ActionController #:nodoc:
elsif value.is_a?(Hash)
filtered_parameters[key] = filter_parameters(value)
elsif block_given?
key, value = key.dup, value.dup
key = key.dup
value = value.dup if value
yield key, value
filtered_parameters[key] = value
else
@ -539,6 +543,7 @@ module ActionController #:nodoc:
self.class.controller_path
end
# Test whether the session is enabled for this request.
def session_enabled?
request.session_options && request.session_options[:disabled] != false
end
@ -600,12 +605,6 @@ module ActionController #:nodoc:
# _Deprecation_ _notice_: This used to have the signatures
# <tt>render_partial(partial_path = default_template_name, object = nil, local_assigns = {})</tt> and
# <tt>render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = {})</tt>.
# == Automatic etagging
#
# Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the
# response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified
# and the response body will be set to an empty string.
#
#
# === Rendering a template
#
@ -829,8 +828,6 @@ module ActionController #:nodoc:
else
response.body = text
end
response.body
end
def render_javascript(javascript, status = nil, append_response = true) #:nodoc:

View file

@ -1,5 +1,6 @@
require 'fileutils'
require 'uri'
require 'set'
module ActionController #:nodoc:
# Caching is a cheap way of speeding up slow applications by keeping the result of calculations, renderings, and database calls
@ -163,13 +164,24 @@ module ActionController #:nodoc:
module Actions
def self.included(base) #:nodoc:
base.extend(ClassMethods)
base.send(:attr_accessor, :rendered_action_cache)
base.class_eval do
attr_accessor :rendered_action_cache, :action_cache_path
alias_method_chain :protected_instance_variables, :action_caching
end
end
module ClassMethods #:nodoc:
def protected_instance_variables_with_action_caching
protected_instance_variables_without_action_caching + %w(@action_cache_path)
end
module ClassMethods
# Declares that +actions+ should be cached.
# See ActionController::Caching::Actions for details.
def caches_action(*actions)
return unless perform_caching
around_filter(ActionCacheFilter.new(*actions))
action_cache_filter = ActionCacheFilter.new(*actions)
before_filter action_cache_filter
after_filter action_cache_filter
end
end
@ -185,70 +197,59 @@ module ActionController #:nodoc:
end
class ActionCacheFilter #:nodoc:
def initialize(*actions, &block)
@actions = actions
def initialize(*actions)
@actions = Set.new actions
end
def before(controller)
return unless @actions.include?(controller.action_name.intern)
action_cache_path = ActionCachePath.new(controller)
if cache = controller.read_fragment(action_cache_path.path)
return unless @actions.include?(controller.action_name.to_sym)
cache_path = ActionCachePath.new(controller, {})
if cache = controller.read_fragment(cache_path.path)
controller.rendered_action_cache = true
set_content_type!(action_cache_path)
set_content_type!(controller, cache_path.extension)
controller.send(:render_text, cache)
false
else
controller.action_cache_path = cache_path
end
end
def after(controller)
return if !@actions.include?(controller.action_name.intern) || controller.rendered_action_cache
controller.write_fragment(ActionCachePath.path_for(controller), controller.response.body)
return if !@actions.include?(controller.action_name.to_sym) || controller.rendered_action_cache
controller.write_fragment(controller.action_cache_path.path, controller.response.body)
end
private
def set_content_type!(action_cache_path)
if extention = action_cache_path.extension
content_type = Mime::EXTENSION_LOOKUP[extention]
action_cache_path.controller.response.content_type = content_type.to_s
end
def set_content_type!(controller, extension)
controller.response.content_type = Mime::EXTENSION_LOOKUP[extension].to_s if extension
end
end
class ActionCachePath
attr_reader :controller, :options
attr_reader :path, :extension
class << self
def path_for(*args, &block)
new(*args).path
def path_for(controller, options)
new(controller, options).path
end
end
def initialize(controller, options = {})
@controller = controller
@options = options
end
def path
return @path if @path
@path = controller.url_for(options).split('://').last
normalize!
add_extension!
URI.unescape(@path)
end
def extension
@extension ||= extract_extension(controller.request.path)
@extension = extract_extension(controller.request.path)
path = controller.url_for(options).split('://').last
normalize!(path)
add_extension!(path, @extension)
@path = URI.unescape(path)
end
private
def normalize!
@path << 'index' if @path.last == '/'
def normalize!(path)
path << 'index' if path[-1] == ?/
end
def add_extension!
@path << ".#{extension}" if extension
def add_extension!(path, extension)
path << ".#{extension}" if extension
end
def extract_extension(file_path)
@ -472,7 +473,6 @@ module ActionController #:nodoc:
end
def write(name, value, options = nil) #:nodoc:
File.umask(0006)
ensure_cache_path(File.dirname(real_file_path(name)))
File.open(real_file_path(name), "wb+") { |f| f.write(value) }
rescue => e

View file

@ -0,0 +1,30 @@
# 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

@ -65,7 +65,7 @@ class CGI #:nodoc:
if env_qs.blank? && !(uri = env_table['REQUEST_URI']).blank?
uri.split('?', 2)[1] || ''
else
env_qs
env_qs || ''
end
end
end

View file

@ -2,6 +2,7 @@ require 'action_controller/cgi_ext/cgi_ext'
require 'action_controller/cgi_ext/cookie_performance_fix'
require 'action_controller/cgi_ext/raw_post_data_fix'
require 'action_controller/cgi_ext/session_performance_fix'
require 'action_controller/cgi_ext/pstore_performance_fix'
module ActionController #:nodoc:
class Base
@ -12,8 +13,8 @@ module ActionController #:nodoc:
# (default). Additionally, there is CGI::Session::DRbStore and CGI::Session::ActiveRecordStore. Read more about these in
# lib/action_controller/session.
# * <tt>:session_key</tt> - the parameter name used for the session id. Defaults to '_session_id'.
# * <tt>:session_id</tt> - the session id to use. If not provided, then it is retrieved from the +session_key+ parameter
# of the request, or automatically generated for a new session.
# * <tt>:session_id</tt> - the session id to use. If not provided, then it is retrieved from the +session_key+ cookie, or
# automatically generated for a new session.
# * <tt>:new_session</tt> - if true, force creation of a new session. If not set, a new session is only created if none currently
# exists. If false, a new session is never created, and if none currently exists and the +session_id+ option is not set,
# an ArgumentError is raised.
@ -23,6 +24,8 @@ module ActionController #:nodoc:
# server.
# * <tt>:session_secure</tt> - if +true+, this session will only work over HTTPS.
# * <tt>:session_path</tt> - the path for which this session applies. Defaults to the directory of the CGI script.
# * <tt>:cookie_only</tt> - if +true+ (the default), session IDs will only be accepted from cookies and not from
# the query string or POST parameters. This protects against session fixation attacks.
def self.process_cgi(cgi = CGI.new, session_options = {})
new.process_cgi(cgi, session_options)
end
@ -33,18 +36,21 @@ module ActionController #:nodoc:
end
class CgiRequest < AbstractRequest #:nodoc:
attr_accessor :cgi, :session_options
attr_accessor :cgi, :session_options, :cookie_only
class SessionFixationAttempt < StandardError; end #:nodoc:
DEFAULT_SESSION_OPTIONS = {
:database_manager => CGI::Session::PStore,
:prefix => "ruby_sess.",
:session_path => "/"
:session_path => "/",
:cookie_only => true
} unless const_defined?(:DEFAULT_SESSION_OPTIONS)
def initialize(cgi, session_options = {})
@cgi = cgi
@session_options = session_options
@env = @cgi.send(:env_table)
@cookie_only = session_options.delete :cookie_only
super()
end
@ -108,6 +114,9 @@ module ActionController #:nodoc:
@session = Hash.new
else
stale_session_check! do
if @cookie_only && request_parameters[session_options_with_string_keys['session_key']]
raise SessionFixationAttempt
end
case value = session_options_with_string_keys['new_session']
when true
@session = new_session

View file

@ -62,9 +62,11 @@ module ActionController #:nodoc:
end
# Removes the cookie on the client machine by setting the value to an empty string
# and setting its expiration date into the past
def delete(name)
set_cookie("name" => name.to_s, "value" => "", "expires" => Time.at(0))
# and setting its expiration date into the past. Like []=, you can pass in an options
# hash to delete cookies with extra data such as a +path+.
def delete(name, options = {})
options.stringify_keys!
set_cookie(options.merge("name" => name.to_s, "value" => "", "expires" => Time.at(0)))
end
private

View file

@ -214,9 +214,10 @@ module ActionController #:nodoc:
# == Filter Chain Halting
#
# <tt>before_filter</tt> and <tt>around_filter</tt> may halt the request
# before controller action is run. This is useful, for example, to deny
# before a controller action is run. This is useful, for example, to deny
# access to unauthenticated users or to redirect from http to https.
# Simply return false from the filter or call render or redirect.
# After filters will not be executed if the filter chain is halted.
#
# Around filters halt the request unless the action block is called.
# Given these filters
@ -238,12 +239,12 @@ module ActionController #:nodoc:
# . . /
# . #around (code after yield)
# . /
# #after (actual filter code is run)
# #after (actual filter code is run, unless the around filter does not yield)
#
# If #around returns before yielding, only #after will be run. The #before
# filter and controller action will not be run. If #before returns false,
# the second half of #around and all of #after will still run but the
# action will not.
# If #around returns before yielding, #after will still not be run. The #before
# filter and controller action will not be run. If #before returns false,
# the second half of #around and will still run but #after and the
# action will not. If #around does not yield, #after will not be run.
module ClassMethods
# The passed <tt>filters</tt> will be appended to the filter_chain and
# will execute before the action on this controller is performed.
@ -263,13 +264,13 @@ module ActionController #:nodoc:
# The passed <tt>filters</tt> will be appended to the array of filters
# that run _after_ actions on this controller are performed.
def append_after_filter(*filters, &block)
prepend_filter_to_chain(filters, :after, &block)
append_filter_to_chain(filters, :after, &block)
end
# The passed <tt>filters</tt> will be prepended to the array of filters
# that run _after_ actions on this controller are performed.
def prepend_after_filter(*filters, &block)
append_filter_to_chain(filters, :after, &block)
prepend_filter_to_chain(filters, :after, &block)
end
# Shorthand for append_after_filter since it's the most common.
@ -362,12 +363,12 @@ module ActionController #:nodoc:
# Returns a mapping between filters and the actions that may run them.
def included_actions #:nodoc:
read_inheritable_attribute("included_actions") || {}
@included_actions ||= read_inheritable_attribute("included_actions") || {}
end
# Returns a mapping between filters and actions that may not run them.
def excluded_actions #:nodoc:
read_inheritable_attribute("excluded_actions") || {}
@excluded_actions ||= read_inheritable_attribute("excluded_actions") || {}
end
# Find a filter in the filter_chain where the filter method matches the _filter_ param
@ -381,10 +382,11 @@ module ActionController #:nodoc:
# Returns true if the filter is excluded from the given action
def filter_excluded_from_action?(filter,action) #:nodoc:
if (ia = included_actions[filter]) && !ia.empty?
case
when ia = included_actions[filter]
!ia.include?(action)
else
(excluded_actions[filter] || []).include?(action)
when ea = excluded_actions[filter]
ea.include?(action)
end
end
@ -397,20 +399,28 @@ module ActionController #:nodoc:
@filter = filter
end
def type
:around
end
def before?
false
type == :before
end
def after?
false
type == :after
end
def around?
true
type == :around
end
def run(controller)
raise ActionControllerError, 'No filter type: Nothing to do here.'
end
def call(controller, &block)
raise(ActionControllerError, 'No filter type: Nothing to do here.')
run(controller)
end
end
@ -420,35 +430,38 @@ module ActionController #:nodoc:
def filter
@filter.filter
end
def around?
false
end
end
class BeforeFilterProxy < FilterProxy #:nodoc:
def before?
true
def type
:before
end
def call(controller, &block)
if false == @filter.call(controller) # must only stop if equal to false. only filters returning false are halted.
controller.halt_filter_chain(@filter, :returned_false)
else
yield
def run(controller)
# only filters returning false are halted.
if false == @filter.call(controller)
controller.send :halt_filter_chain, @filter, :returned_false
end
end
def call(controller)
yield unless run(controller)
end
end
class AfterFilterProxy < FilterProxy #:nodoc:
def after?
true
def type
:after
end
def call(controller, &block)
yield
def run(controller)
@filter.call(controller)
end
def call(controller)
yield
run(controller)
end
end
class SymbolFilter < Filter #:nodoc:
@ -485,29 +498,72 @@ module ActionController #:nodoc:
end
end
class ClassBeforeFilter < Filter #:nodoc:
def call(controller, &block)
@filter.before(controller)
end
end
class ClassAfterFilter < Filter #:nodoc:
def call(controller, &block)
@filter.after(controller)
end
end
protected
def append_filter_to_chain(filters, position = :around, &block)
write_inheritable_array('filter_chain', create_filters(filters, position, &block) )
def append_filter_to_chain(filters, filter_type = :around, &block)
pos = find_filter_append_position(filters, filter_type)
update_filter_chain(filters, filter_type, pos, &block)
end
def prepend_filter_to_chain(filters, position = :around, &block)
write_inheritable_attribute('filter_chain', create_filters(filters, position, &block) + filter_chain)
def prepend_filter_to_chain(filters, filter_type = :around, &block)
pos = find_filter_prepend_position(filters, filter_type)
update_filter_chain(filters, filter_type, pos, &block)
end
def create_filters(filters, position, &block) #:nodoc:
def update_filter_chain(filters, filter_type, pos, &block)
new_filters = create_filters(filters, filter_type, &block)
new_chain = filter_chain.insert(pos, new_filters).flatten
write_inheritable_attribute('filter_chain', new_chain)
end
def find_filter_append_position(filters, filter_type)
# appending an after filter puts it at the end of the call chain
# before and around filters go before the first after filter in the chain
unless filter_type == :after
filter_chain.each_with_index do |f,i|
return i if f.after?
end
end
return -1
end
def find_filter_prepend_position(filters, filter_type)
# prepending a before or around filter puts it at the front of the call chain
# after filters go before the first after filter in the chain
if filter_type == :after
filter_chain.each_with_index do |f,i|
return i if f.after?
end
return -1
end
return 0
end
def create_filters(filters, filter_type, &block) #:nodoc:
filters, conditions = extract_conditions(filters, &block)
filters.map! { |filter| find_or_create_filter(filter,position) }
filters.map! { |filter| find_or_create_filter(filter, filter_type) }
update_conditions(filters, conditions)
filters
end
def find_or_create_filter(filter,position)
if found_filter = find_filter(filter) { |f| f.send("#{position}?") }
def find_or_create_filter(filter, filter_type)
if found_filter = find_filter(filter) { |f| f.type == filter_type }
found_filter
else
f = class_for_filter(filter).new(filter)
f = class_for_filter(filter, filter_type).new(filter)
# apply proxy to filter if necessary
case position
case filter_type
when :before
BeforeFilterProxy.new(f)
when :after
@ -520,7 +576,7 @@ module ActionController #:nodoc:
# The determination of the filter type was once done at run time.
# This method is here to extract as much logic from the filter run time as possible
def class_for_filter(filter) #:nodoc:
def class_for_filter(filter, filter_type) #:nodoc:
case
when filter.is_a?(Symbol)
SymbolFilter
@ -534,8 +590,12 @@ module ActionController #:nodoc:
end
when filter.respond_to?(:filter)
ClassFilter
when filter.respond_to?(:before) && filter_type == :before
ClassBeforeFilter
when filter.respond_to?(:after) && filter_type == :after
ClassAfterFilter
else
raise(ActionControllerError, 'A filters must be a Symbol, Proc, Method, or object responding to filter.')
raise(ActionControllerError, 'A filter must be a Symbol, Proc, Method, or object responding to filter, after or before.')
end
end
@ -550,8 +610,8 @@ module ActionController #:nodoc:
return if conditions.empty?
if conditions[:only]
write_inheritable_hash('included_actions', condition_hash(filters, conditions[:only]))
else
write_inheritable_hash('excluded_actions', condition_hash(filters, conditions[:except])) if conditions[:except]
elsif conditions[:except]
write_inheritable_hash('excluded_actions', condition_hash(filters, conditions[:except]))
end
end
@ -576,9 +636,9 @@ module ActionController #:nodoc:
def remove_actions_from_included_actions!(filters,*actions)
actions = actions.flatten.map(&:to_s)
updated_hash = filters.inject(included_actions) do |hash,filter|
updated_hash = filters.inject(read_inheritable_attribute('included_actions')||{}) do |hash,filter|
ia = (hash[filter] || []) - actions
ia.blank? ? hash.delete(filter) : hash[filter] = ia
ia.empty? ? hash.delete(filter) : hash[filter] = ia
hash
end
write_inheritable_attribute('included_actions', updated_hash)
@ -595,7 +655,9 @@ module ActionController #:nodoc:
def proxy_before_and_after_filter(filter) #:nodoc:
return filter unless filter_responds_to_before_and_after(filter)
Proc.new do |controller, action|
unless filter.before(controller) == false
if filter.before(controller) == false
controller.send :halt_filter_chain, filter, :returned_false
else
begin
action.call
ensure
@ -615,53 +677,90 @@ module ActionController #:nodoc:
end
end
def perform_action_with_filters
call_filter(self.class.filter_chain, 0)
end
protected
def process_with_filters(request, response, method = :perform_action, *arguments) #:nodoc:
@before_filter_chain_aborted = false
process_without_filters(request, response, method, *arguments)
end
def filter_chain
self.class.filter_chain
end
def call_filter(chain, index)
return (performed? || perform_action_without_filters) if index >= chain.size
filter = chain[index]
return call_filter(chain, index.next) if self.class.filter_excluded_from_action?(filter,action_name)
halted = false
filter.call(self) do
halted = call_filter(chain, index.next)
end
halt_filter_chain(filter.filter, :no_yield) if halted == false unless @before_filter_chain_aborted
halted
end
def halt_filter_chain(filter, reason)
if logger
case reason
when :no_yield
logger.info "Filter chain halted as [#{filter.inspect}] did not yield."
when :returned_false
logger.info "Filter chain halted as [#{filter.inspect}] returned false."
end
end
@before_filter_chain_aborted = true
return false
def perform_action_with_filters
call_filters(self.class.filter_chain, 0, 0)
end
private
def process_cleanup_with_filters
if @before_filter_chain_aborted
close_session
def call_filters(chain, index, nesting)
index = run_before_filters(chain, index, nesting)
aborted = @before_filter_chain_aborted
perform_action_without_filters unless performed? || aborted
return index if nesting != 0 || aborted
run_after_filters(chain, index)
end
def skip_excluded_filters(chain, index)
while (filter = chain[index]) && self.class.filter_excluded_from_action?(filter, action_name)
index = index.next
end
[filter, index]
end
def run_before_filters(chain, index, nesting)
while chain[index]
filter, index = skip_excluded_filters(chain, index)
break unless filter # end of call chain reached
case filter.type
when :before
filter.run(self) # invoke before filter
index = index.next
break if @before_filter_chain_aborted
when :around
yielded = false
filter.call(self) do
yielded = true
# all remaining before and around filters will be run in this call
index = call_filters(chain, index.next, nesting.next)
end
halt_filter_chain(filter, :did_not_yield) unless yielded
break
else
process_cleanup_without_filters
break # no before or around filters left
end
end
index
end
def run_after_filters(chain, index)
seen_after_filter = false
while chain[index]
filter, index = skip_excluded_filters(chain, index)
break unless filter # end of call chain reached
case filter.type
when :after
seen_after_filter = true
filter.run(self) # invoke after filter
else
# implementation error or someone has mucked with the filter chain
raise ActionControllerError, "filter #{filter.inspect} was in the wrong place!" if seen_after_filter
end
index = index.next
end
index.next
end
def halt_filter_chain(filter, reason)
@before_filter_chain_aborted = true
logger.info "Filter chain halted as [#{filter.inspect}] #{reason}." if logger
false
end
def process_cleanup_with_filters
if @before_filter_chain_aborted
close_session
else
process_cleanup_without_filters
end
end
end
end
end

View file

@ -67,7 +67,7 @@ module ActionController
@https = false
@cookies = {}
@controller = @request = @response = nil
self.host = "www.example.com"
self.remote_addr = "127.0.0.1"
self.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5"
@ -89,7 +89,7 @@ module ActionController
# session.https!
# session.https!(false)
def https!(flag=true)
@https = flag
@https = flag
end
# Return +true+ if the session is mimicing a secure HTTPS request.
@ -143,10 +143,10 @@ module ActionController
# Performs a GET request with the given parameters. The parameters may
# be +nil+, a Hash, or a string that is appropriately encoded
# (application/x-www-form-urlencoded or multipart/form-data). The headers
# should be a hash. The keys will automatically be upcased, with the
# should be a hash. The keys will automatically be upcased, with the
# prefix 'HTTP_' added if needed.
#
# You can also perform POST, PUT, DELETE, and HEAD requests with #post,
# You can also perform POST, PUT, DELETE, and HEAD requests with #post,
# #put, #delete, and #head.
def get(path, parameters=nil, headers=nil)
process :get, path, parameters, headers
@ -161,31 +161,41 @@ module ActionController
def put(path, parameters=nil, headers=nil)
process :put, path, parameters, headers
end
# Performs a DELETE request with the given parameters. See get() for more details.
def delete(path, parameters=nil, headers=nil)
process :delete, path, parameters, headers
end
# Performs a HEAD request with the given parameters. See get() for more details.
def head(path, parameters=nil, headers=nil)
process :head, path, parameters, headers
end
# Performs an XMLHttpRequest request with the given parameters, mimicing
# the request environment created by the Prototype library. The parameters
# may be +nil+, a Hash, or a string that is appropriately encoded
# (application/x-www-form-urlencoded or multipart/form-data). The headers
# should be a hash. The keys will automatically be upcased, with the
# prefix 'HTTP_' added if needed.
def xml_http_request(path, parameters=nil, headers=nil)
headers = (headers || {}).merge(
"X-Requested-With" => "XMLHttpRequest",
"Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
)
# Performs an XMLHttpRequest request with the given parameters, mirroring
# a request from the Prototype library.
#
# The request_method is :get, :post, :put, :delete or :head; the
# parameters are +nil+, a hash, or a url-encoded or multipart string;
# the headers are a hash. Keys are automatically upcased and prefixed
# with 'HTTP_' if not already.
#
# This method used to omit the request_method parameter, assuming it
# was :post. This was deprecated in Rails 1.2.4. Always pass the request
# method as the first argument.
def xml_http_request(request_method, path, parameters = nil, headers = nil)
unless request_method.is_a?(Symbol)
ActiveSupport::Deprecation.warn 'xml_http_request now takes the request_method (:get, :post, etc.) as the first argument. It used to assume :post, so add the :post argument to your existing method calls to silence this warning.'
request_method, path, parameters, headers = :post, request_method, path, parameters
end
post(path, parameters, headers)
headers ||= {}
headers['X-Requested-With'] = 'XMLHttpRequest'
headers['Accept'] = 'text/javascript, text/html, application/xml, text/xml, */*'
process(request_method, path, parameters, headers)
end
alias xhr :xml_http_request
# Returns the URL for the given options, according to the rules specified
# in the application's routes.
@ -292,7 +302,7 @@ module ActionController
@status = @status.to_i
end
# Encode the cookies hash in a format suitable for passing to a
# Encode the cookies hash in a format suitable for passing to a
# request.
def encode_cookies
cookies.inject("") do |string, (name, value)|
@ -450,7 +460,7 @@ module ActionController
# without any test methods.
def run(*args) #:nodoc:
return if @method_name == "default_test"
super
super
end
# Because of how use_instantiated_fixtures and use_transactional_fixtures
@ -490,7 +500,7 @@ module ActionController
@integration_session = open_session
end
%w(get post cookies assigns xml_http_request).each do |method|
%w(get post put head delete cookies assigns xml_http_request).each do |method|
define_method(method) do |*args|
reset! unless @integration_session
# reset the html_document variable, but only for new get/post calls

View file

@ -24,7 +24,7 @@ module ActionController
define_method("set_#{object}_#{attribute}") do
@item = object.to_s.camelize.constantize.find(params[:id])
@item.update_attribute(attribute, params[:value])
render :text => @item.send(attribute)
render :text => @item.send(attribute).to_s
end
end
end

View file

@ -1,7 +1,9 @@
module ActionController
# === Action Pack pagination for Active Record collections
#
# DEPRECATION WARNING: Pagination will be separated into its own plugin with Rails 2.0.
# DEPRECATION WARNING: Pagination will be moved to a plugin in Rails 2.0.
# Install the classic_pagination plugin for forward compatibility:
# script/plugin install svn://errtheblog.com/svn/plugins/classic_pagination
#
# The Pagination module aids in the process of paging large collections of
# Active Record objects. It offers macro-style automatic fetching of your
@ -130,6 +132,8 @@ module ActionController
paginator_and_collection_for(collection_id, options)
end
deprecate :paginate => 'Pagination is moving to a plugin in Rails 2.0: script/plugin install svn://errtheblog.com/svn/plugins/classic_pagination'
# These methods become class methods on any controller
module ClassMethods
# Creates a +before_filter+ which automatically paginates an Active
@ -148,6 +152,8 @@ module ActionController
OPTIONS[self][collection_id] = options
end
end
deprecate :paginate => 'Pagination is moving to a plugin in Rails 2.0: script/plugin install svn://errtheblog.com/svn/plugins/classic_pagination'
end
def create_paginators_and_retrieve_collections #:nodoc:

View file

@ -48,10 +48,6 @@ module ActionController
# REQUEST_METHOD header directly. Thus, for head, both get? and head? will return true.
def head?
@env['REQUEST_METHOD'].downcase.to_sym == :head
end
def headers
@env
end
# Determine whether the body of a HTTP call is URL-encoded (default)

View file

@ -2,48 +2,68 @@ module ActionController
module Resources
class Resource #:nodoc:
attr_reader :collection_methods, :member_methods, :new_methods
attr_reader :path_prefix, :name_prefix
attr_reader :path_prefix, :new_name_prefix
attr_reader :plural, :singular
attr_reader :options
def initialize(entities, options)
@plural = entities
@singular = options[:singular] || plural.to_s.singularize
@options = options
arrange_actions
add_default_actions
set_prefixes
end
def controller
@controller ||= (options[:controller] || plural).to_s
end
def path
@path ||= "#{path_prefix}/#{plural}"
end
def new_path
@new_path ||= "#{path}/new"
end
def member_path
@member_path ||= "#{path}/:id"
end
def nesting_path_prefix
@nesting_path_prefix ||= "#{path}/:#{singular}_id"
end
def deprecate_name_prefix?
@name_prefix.blank? && !@new_name_prefix.blank?
end
def name_prefix
deprecate_name_prefix? ? @new_name_prefix : @name_prefix
end
def old_name_prefix
@name_prefix
end
def nesting_name_prefix
"#{new_name_prefix}#{singular}_"
end
def action_separator
@action_separator ||= Base.resource_action_separator
end
protected
def arrange_actions
@collection_methods = arrange_actions_by_methods(options.delete(:collection))
@member_methods = arrange_actions_by_methods(options.delete(:member))
@new_methods = arrange_actions_by_methods(options.delete(:new))
end
def add_default_actions
add_default_action(member_methods, :get, :edit)
add_default_action(new_methods, :get, :new)
@ -52,6 +72,7 @@ module ActionController
def set_prefixes
@path_prefix = options.delete(:path_prefix)
@name_prefix = options.delete(:name_prefix)
@new_name_prefix = options.delete(:new_name_prefix)
end
def arrange_actions_by_methods(actions)
@ -60,7 +81,7 @@ module ActionController
flipped_hash
end
end
def add_default_action(collection, method, action)
(collection[method] ||= []).unshift(action)
end
@ -178,11 +199,11 @@ module ActionController
#
# The comment resources work the same, but must now include a value for :article_id.
#
# comments_url(@article)
# comment_url(@article, @comment)
# article_comments_url(@article)
# article_comment_url(@article, @comment)
#
# comments_url(:article_id => @article)
# comment_url(:article_id => @article, :id => @comment)
# article_comments_url(:article_id => @article)
# article_comment_url(:article_id => @article, :id => @comment)
#
# * <tt>:name_prefix</tt> -- define a prefix for all generated routes, usually ending in an underscore.
# Use this if you have named routes that may clash.
@ -192,7 +213,7 @@ module ActionController
#
# * <tt>:collection</tt> -- add named routes for other actions that operate on the collection.
# Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>
# or <tt>:any</tt> if the method does not matter. These routes map to a URL like /messages;rss, with a route of rss_messages_url.
# or <tt>:any</tt> if the method does not matter. These routes map to a URL like /messages/rss, with a route of rss_messages_url.
# * <tt>:member</tt> -- same as :collection, but for actions that operate on a specific member.
# * <tt>:new</tt> -- same as :collection, but for actions that operate on the new resource action.
#
@ -204,19 +225,19 @@ module ActionController
# # --> GET /thread/7/messages/1
#
# map.resources :messages, :collection => { :rss => :get }
# # --> GET /messages;rss (maps to the #rss action)
# # --> GET /messages/rss (maps to the #rss action)
# # also adds a named route called "rss_messages"
#
# map.resources :messages, :member => { :mark => :post }
# # --> POST /messages/1;mark (maps to the #mark action)
# # --> POST /messages/1/mark (maps to the #mark action)
# # also adds a named route called "mark_message"
#
# map.resources :messages, :new => { :preview => :post }
# # --> POST /messages/new;preview (maps to the #preview action)
# # --> POST /messages/new/preview (maps to the #preview action)
# # also adds a named route called "preview_new_message"
#
# map.resources :messages, :new => { :new => :any, :preview => :post }
# # --> POST /messages/new;preview (maps to the #preview action)
# # --> POST /messages/new/preview (maps to the #preview action)
# # also adds a named route called "preview_new_message"
# # --> /messages/new can be invoked via any request method
#
@ -235,9 +256,10 @@ module ActionController
# /account profile.
#
# See map.resources for general conventions. These are the main differences:
# - a singular name is given to map.resource. The default controller name is taken from the singular name.
# - To specify a custom plural name, use the :plural option. There is no :singular option
# - No default index, new, or create routes are created for the singleton resource controller.
# - A singular name is given to map.resource. The default controller name is taken from the singular name.
# - There is no <tt>:collection</tt> option as there is only the singleton resource.
# - There is no <tt>:singular</tt> option as the singular name is passed to map.resource.
# - No default index route is created for the singleton resource controller.
# - When nesting singleton resources, only the singular name is used as the path prefix (example: 'account/messages/1')
#
# Example:
@ -300,7 +322,7 @@ module ActionController
map_member_actions(map, resource)
if block_given?
with_options(:path_prefix => resource.nesting_path_prefix, &block)
with_options(:path_prefix => resource.nesting_path_prefix, :new_name_prefix => resource.nesting_name_prefix, &block)
end
end
end
@ -315,7 +337,7 @@ module ActionController
map_member_actions(map, resource)
if block_given?
with_options(:path_prefix => resource.nesting_path_prefix, &block)
with_options(:path_prefix => resource.nesting_path_prefix, :new_name_prefix => resource.nesting_name_prefix, &block)
end
end
end
@ -324,8 +346,21 @@ module ActionController
resource.collection_methods.each do |method, actions|
actions.each do |action|
action_options = action_options_for(action, resource, method)
map.named_route("#{resource.name_prefix}#{action}_#{resource.plural}", "#{resource.path};#{action}", action_options)
map.named_route("formatted_#{resource.name_prefix}#{action}_#{resource.plural}", "#{resource.path}.:format;#{action}", action_options)
unless resource.old_name_prefix.blank?
map.deprecated_named_route("#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.old_name_prefix}#{action}_#{resource.plural}")
map.deprecated_named_route("formatted_#{action}_#{resource.name_prefix}#{resource.plural}", "formatted_#{resource.old_name_prefix}#{action}_#{resource.plural}")
end
if resource.deprecate_name_prefix?
map.deprecated_named_route("#{action}_#{resource.name_prefix}#{resource.plural}", "#{action}_#{resource.plural}")
map.deprecated_named_route("formatted_#{action}_#{resource.name_prefix}#{resource.plural}", "formatted_#{action}_#{resource.plural}")
end
map.named_route("#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options)
map.connect("#{resource.path};#{action}", action_options)
map.connect("#{resource.path}.:format;#{action}", action_options)
map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}.:format", action_options)
end
end
end
@ -335,6 +370,11 @@ module ActionController
map.named_route("#{resource.name_prefix}#{resource.plural}", resource.path, index_action_options)
map.named_route("formatted_#{resource.name_prefix}#{resource.plural}", "#{resource.path}.:format", index_action_options)
if resource.deprecate_name_prefix?
map.deprecated_named_route("#{resource.name_prefix}#{resource.plural}", "#{resource.plural}")
map.deprecated_named_route("formatted_#{resource.name_prefix}#{resource.plural}", "formatted_#{resource.plural}")
end
create_action_options = action_options_for("create", resource)
map.connect(resource.path, create_action_options)
map.connect("#{resource.path}.:format", create_action_options)
@ -351,11 +391,37 @@ module ActionController
actions.each do |action|
action_options = action_options_for(action, resource, method)
if action == :new
map.named_route("#{resource.name_prefix}new_#{resource.singular}", resource.new_path, action_options)
map.named_route("formatted_#{resource.name_prefix}new_#{resource.singular}", "#{resource.new_path}.:format", action_options)
unless resource.old_name_prefix.blank?
map.deprecated_named_route("new_#{resource.name_prefix}#{resource.singular}", "#{resource.old_name_prefix}new_#{resource.singular}")
map.deprecated_named_route("formatted_new_#{resource.name_prefix}#{resource.singular}", "formatted_#{resource.old_name_prefix}new_#{resource.singular}")
end
if resource.deprecate_name_prefix?
map.deprecated_named_route("new_#{resource.name_prefix}#{resource.singular}", "new_#{resource.singular}")
map.deprecated_named_route("formatted_new_#{resource.name_prefix}#{resource.singular}", "formatted_new_#{resource.singular}")
end
map.named_route("new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options)
map.named_route("formatted_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}.:format", action_options)
else
map.named_route("#{resource.name_prefix}#{action}_new_#{resource.singular}", "#{resource.new_path};#{action}", action_options)
map.named_route("formatted_#{resource.name_prefix}#{action}_new_#{resource.singular}", "#{resource.new_path}.:format;#{action}", action_options)
unless resource.old_name_prefix.blank?
map.deprecated_named_route("#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.old_name_prefix}#{action}_new_#{resource.singular}")
map.deprecated_named_route("formatted_#{action}_new_#{resource.name_prefix}#{resource.singular}", "formatted_#{resource.old_name_prefix}#{action}_new_#{resource.singular}")
end
if resource.deprecate_name_prefix?
map.deprecated_named_route("#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{action}_new_#{resource.singular}")
map.deprecated_named_route("formatted_#{action}_new_#{resource.name_prefix}#{resource.singular}", "formatted_#{action}_new_#{resource.singular}")
end
map.named_route("#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options)
map.connect("#{resource.new_path};#{action}", action_options)
map.connect("#{resource.new_path}.:format;#{action}", action_options)
map.named_route("formatted_#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}.:format", action_options)
end
end
end
@ -365,8 +431,22 @@ module ActionController
resource.member_methods.each do |method, actions|
actions.each do |action|
action_options = action_options_for(action, resource, method)
map.named_route("#{resource.name_prefix}#{action}_#{resource.singular}", "#{resource.member_path};#{action}", action_options)
map.named_route("formatted_#{resource.name_prefix}#{action}_#{resource.singular}", "#{resource.member_path}.:format;#{action}",action_options)
unless resource.old_name_prefix.blank?
map.deprecated_named_route("#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.old_name_prefix}#{action}_#{resource.singular}")
map.deprecated_named_route("formatted_#{action}_#{resource.name_prefix}#{resource.singular}", "formatted_#{resource.old_name_prefix}#{action}_#{resource.singular}")
end
if resource.deprecate_name_prefix?
map.deprecated_named_route("#{action}_#{resource.name_prefix}#{resource.singular}", "#{action}_#{resource.singular}")
map.deprecated_named_route("formatted_#{action}_#{resource.name_prefix}#{resource.singular}", "formatted_#{action}_#{resource.singular}")
end
map.named_route("#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action}", action_options)
map.connect("#{resource.member_path};#{action}", action_options)
map.connect("#{resource.member_path}.:format;#{action}", action_options)
map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action}.:format", action_options)
end
end
@ -374,6 +454,11 @@ module ActionController
map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options)
map.named_route("formatted_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", show_action_options)
if resource.deprecate_name_prefix?
map.deprecated_named_route("#{resource.name_prefix}#{resource.singular}", "#{resource.singular}")
map.deprecated_named_route("formatted_#{resource.name_prefix}#{resource.singular}", "formatted_#{resource.singular}")
end
update_action_options = action_options_for("update", resource)
map.connect(resource.member_path, update_action_options)
map.connect("#{resource.member_path}.:format", update_action_options)

View file

@ -451,26 +451,17 @@ module ActionController
# is given (as an array), only the keys indicated will be used to build
# the query string. The query string will correctly build array parameter
# values.
def build_query_string(hash, only_keys=nil)
def build_query_string(hash, only_keys = nil)
elements = []
only_keys ||= hash.keys
only_keys.each do |key|
value = hash[key] or next
key = CGI.escape key.to_s
if value.class == Array
key << '[]'
else
value = [ value ]
end
value.each { |val| elements << "#{key}=#{CGI.escape(val.to_param.to_s)}" }
end
query_string = "?#{elements.join("&")}" unless elements.empty?
query_string || ""
(only_keys || hash.keys).each do |key|
if value = hash[key]
elements << value.to_query(key)
end
end
elements.empty? ? '' : "?#{elements.sort * '&'}"
end
# Write the real recognition implementation and then resend the message.
def recognize(path, environment={})
write_recognition
@ -668,7 +659,7 @@ module ActionController
end
def extract_value
"#{local_name} = hash[:#{key}] #{"|| #{default.inspect}" if default}"
"#{local_name} = hash[:#{key}] && hash[:#{key}].to_param #{"|| #{default.inspect}" if default}"
end
def value_check
if default # Then we know it won't be nil
@ -989,6 +980,10 @@ module ActionController
def named_route(name, path, options = {})
@set.add_named_route(name, path, options)
end
def deprecated_named_route(name, deprecated_name, options = {})
@set.add_deprecated_named_route(name, deprecated_name)
end
# Added deprecation notice for anyone who already added a named route called "root".
# It'll be used as a shortcut for map.connect '' in Rails 2.0.
@ -1019,7 +1014,7 @@ module ActionController
def clear!
@routes = {}
@helpers = []
@module ||= Module.new
@module.instance_methods.each do |selector|
@module.send :remove_method, selector
@ -1055,6 +1050,38 @@ module ActionController
def install(destinations = [ActionController::Base, ActionView::Base])
Array(destinations).each { |dest| dest.send :include, @module }
end
def define_deprecated_named_route_methods(name, deprecated_name)
[:url, :path].each do |kind|
@module.send :module_eval, <<-end_eval # We use module_eval to avoid leaks
def #{url_helper_name(deprecated_name, kind)}(*args)
ActiveSupport::Deprecation.warn(
'The named route "#{url_helper_name(deprecated_name, kind)}" uses a format that has been deprecated. ' +
'You should use "#{url_helper_name(name, kind)}" instead.', caller
)
send :#{url_helper_name(name, kind)}, *args
end
def #{hash_access_name(deprecated_name, kind)}(*args)
ActiveSupport::Deprecation.warn(
'The named route "#{hash_access_name(deprecated_name, kind)}" uses a format that has been deprecated. ' +
'You should use "#{hash_access_name(name, kind)}" instead.', caller
)
send :#{hash_access_name(name, kind)}, *args
end
end_eval
end
end
private
def url_helper_name(name, kind = :url)
@ -1177,6 +1204,10 @@ module ActionController
def add_named_route(name, path, options = {})
named_routes[name] = add_route(path, options)
end
def add_deprecated_named_route(name, deprecated_name)
named_routes.define_deprecated_named_route_methods(name, deprecated_name)
end
def options_as_params(options)
# If an explicit :controller was given, always make :action explicit
@ -1190,10 +1221,9 @@ module ActionController
#
# great fun, eh?
options_as_params = options[:controller] ? { :action => "index" } : {}
options.each do |k, value|
options_as_params[k] = value.to_param
end
options_as_params = options.clone
options_as_params[:action] ||= 'index' if options[:controller]
options_as_params[:action] = options_as_params[:action].to_s if options_as_params[:action]
options_as_params
end
@ -1224,6 +1254,9 @@ module ActionController
options = options_as_params(options)
expire_on = build_expiry(options, recall)
if options[:controller]
options[:controller] = options[:controller].to_s
end
# if the controller has changed, make sure it changes relative to the
# current controller module, if any. In other words, if we're currently
# on admin/get, and the new controller is 'set', the new controller

View file

@ -24,6 +24,7 @@ module ActionController #:nodoc:
attr_accessor :cookies, :session_options
attr_accessor :query_parameters, :request_parameters, :path, :session, :env
attr_accessor :host
attr_reader :request_uri_overridden
def initialize(query_parameters = nil, request_parameters = nil, session = nil)
@query_parameters = query_parameters || {}
@ -67,12 +68,14 @@ module ActionController #:nodoc:
# Used to check AbstractRequest's request_uri functionality.
# Disables the use of @path and @request_uri so superclass can handle those.
def set_REQUEST_URI(value)
@request_uri_overridden = true
@env["REQUEST_URI"] = value
@request_uri = nil
@path = nil
end
def request_uri=(uri)
@env["REQUEST_URI"] = uri
@request_uri = uri
@path = uri.split("?").first
end
@ -426,12 +429,12 @@ module ActionController #:nodoc:
end
def build_request_uri(action, parameters)
unless @request.env['REQUEST_URI']
unless @request.request_uri_overridden
options = @controller.send(:rewrite_options, parameters)
options.update(:only_path => true, :action => action)
url = ActionController::UrlRewriter.new(@request, parameters)
@request.set_REQUEST_URI(url.rewrite(options))
@request.request_uri = url.rewrite(options)
end
end

View file

@ -52,8 +52,9 @@ module ActionController
# Delete the unused options to prevent their appearance in the query string
[:protocol, :host, :port].each { |k| options.delete k }
end
anchor = "##{options.delete(:anchor)}" if options.key?(:anchor)
url << Routing::Routes.generate(options, {})
return url
return "#{url}#{anchor}"
end
end
@ -76,6 +77,7 @@ module ActionController
alias_method :to_s, :to_str
private
# Given a path and options, returns a rewritten URL string
def rewrite_url(path, options)
rewritten_url = ""
unless options[:only_path]
@ -91,6 +93,7 @@ module ActionController
rewritten_url
end
# Given a Hash of options, generates a route
def rewrite_path(options)
options = options.symbolize_keys
options.update(options[:params].symbolize_keys) if options[:params]

View file

@ -95,6 +95,7 @@ module ActionController #:nodoc:
response.headers.update(options[:add_headers]) if options[:add_headers]
unless performed?
render(options[:render]) if options[:render]
options[:redirect_to] = self.send(options[:redirect_to]) if options[:redirect_to].is_a? Symbol
redirect_to(options[:redirect_to]) if options[:redirect_to]
end
return false

View file

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

View file

@ -148,7 +148,7 @@ module ActionView #:nodoc:
#
# This refreshes the sidebar, removes a person element and highlights the user list.
#
# See the ActionView::Helpers::PrototypeHelper::JavaScriptGenerator documentation for more details.
# See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
class Base
include ERB::Util
@ -160,7 +160,7 @@ module ActionView #:nodoc:
attr_internal *ActionController::Base::DEPRECATED_INSTANCE_VARIABLES
# Specify trim mode for the ERB compiler. Defaults to '-'.
# See ERB documentation for suitable values.
# See ERb documentation for suitable values.
@@erb_trim_mode = '-'
cattr_accessor :erb_trim_mode
@ -191,17 +191,17 @@ module ActionView #:nodoc:
end
include CompiledTemplates
# maps inline templates to their method names
# Maps inline templates to their method names
@@method_names = {}
# map method names to their compile time
# Map method names to their compile time
@@compile_time = {}
# map method names to the names passed in local assigns so far
# Map method names to the names passed in local assigns so far
@@template_args = {}
# count the number of inline templates
# Count the number of inline templates
@@inline_template_count = 0
# maps template paths without extension to their file extension returned by pick_template_extension.
# if for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions
# used by pick_template_extension determines whether ext1 or ext2 will be stored
# Maps template paths without extension to their file extension returned by pick_template_extension.
# If for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions
# used by pick_template_extension determines whether ext1 or ext2 will be stored.
@@cached_template_extension = {}
class ObjectWrapper < Struct.new(:value) #:nodoc:
@ -305,7 +305,6 @@ module ActionView #:nodoc:
# Render the provided template with the given local assigns. If the template has not been rendered with the provided
# local assigns yet, or if the template has been updated on disk, then the template will be compiled to a method.
#
# Either, but not both, of template and file_path may be nil. If file_path is given, the template
# will only be read if it has to be compiled.
#
@ -371,10 +370,12 @@ module ActionView #:nodoc:
end
private
# Builds a string holding the full path of the template including extension
def full_template_path(template_path, extension)
"#{@base_path}/#{template_path}.#{extension}"
end
# Asserts the existence of a template.
def template_exists?(template_path, extension)
file_path = full_template_path(template_path, extension)
@@method_names.has_key?(file_path) || FileTest.exists?(file_path)
@ -389,6 +390,7 @@ module ActionView #:nodoc:
@@cache_template_extensions && @@cached_template_extension[template_path]
end
# Determines the template's file extension, such as rhtml, rxml, or rjs.
def find_template_extension_for(template_path)
if match = delegate_template_exists?(template_path)
match.first.to_sym
@ -405,6 +407,7 @@ module ActionView #:nodoc:
File.read(template_path)
end
# Evaluate the local assigns and pushes them to the view.
def evaluate_assigns
unless @assigns_added
assign_variables_from_controller
@ -416,6 +419,7 @@ module ActionView #:nodoc:
handler.new(self).render(template, local_assigns)
end
# Assigns instance variables from the controller to the view.
def assign_variables_from_controller
@assigns.each { |key, value| instance_variable_set("@#{key}", value) }
end
@ -427,10 +431,10 @@ module ActionView #:nodoc:
((args = @@template_args[render_symbol]) && local_assigns.all? { |k,_| args.has_key?(k) })
end
# Check whether compilation is necessary.
# Compile if the inline template or file has not been compiled yet.
# Or if local_assigns has a new key, which isn't supported by the compiled code yet.
# Or if the file has changed on disk and checking file mods hasn't been disabled.
# Method to check whether template compilation is necessary.
# The template will be compiled if the inline template or file has not been compiled yet,
# if local_assigns has a new key, which isn't supported by the compiled code yet,
# or if the file has changed on disk and checking file mods hasn't been disabled.
def compile_template?(template, file_name, local_assigns)
method_key = file_name || template
render_symbol = @@method_names[method_key]
@ -445,14 +449,15 @@ module ActionView #:nodoc:
end
end
# Create source code for given template
# Method to create the source code for a given template.
def create_template_source(extension, template, render_symbol, locals)
if template_requires_setup?(extension)
body = case extension.to_sym
when :rxml
"controller.response.content_type ||= 'application/xml'\n" +
"xml = Builder::XmlMarkup.new(:indent => 2)\n" +
template
"xml ||= Builder::XmlMarkup.new(:indent => 2)\n" +
template +
"\nxml.target!\n"
when :rjs
"controller.response.content_type ||= 'text/javascript'\n" +
"update_page do |page|\n#{template}\nend"
@ -473,11 +478,11 @@ module ActionView #:nodoc:
"def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend"
end
def template_requires_setup?(extension)
def template_requires_setup?(extension) #:nodoc:
templates_requiring_setup.include? extension.to_s
end
def templates_requiring_setup
def templates_requiring_setup #:nodoc:
%w(rxml rjs)
end
@ -501,6 +506,7 @@ module ActionView #:nodoc:
end
end
# Compile and evaluate the template's code
def compile_template(extension, template, file_name, local_assigns)
render_symbol = assign_method_name(extension, template, file_name)
render_source = create_template_source(extension, template, render_symbol, local_assigns.keys)

View file

@ -3,14 +3,14 @@ module ActionView
# CompiledTemplates modules hold methods that have been compiled.
# Templates are compiled into these methods so that they do not need to be
# re-read and re-parsed each request.
# read and parsed for each request.
#
# Each template may be compiled into one or more methods. Each method accepts a given
# set of parameters which is used to implement local assigns passing.
#
# To use a compiled template module, create a new instance and include it into the class
# in which you want the template to be rendered.
class CompiledTemplates < Module #:nodoc:
class CompiledTemplates < Module
attr_reader :method_names
def initialize

View file

@ -13,17 +13,18 @@ module ActionView
# is a great of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form.
# In that case, it's better to use the input method and the specialized form methods in link:classes/ActionView/Helpers/FormHelper.html
module ActiveRecordHelper
# Returns a default input tag for the type of object returned by the method. Example
# (title is a VARCHAR column and holds "Hello World"):
# Returns a default input tag for the type of object returned by the method. For example, let's say you have a model
# that has an attribute +title+ of type VARCHAR column, and this instance holds "Hello World":
# input("post", "title") =>
# <input id="post_title" name="post[title]" size="30" type="text" value="Hello World" />
def input(record_name, method, options = {})
InstanceTag.new(record_name, method, self).to_tag(options)
end
# Returns an entire form with input tags and everything for a specified Active Record object. Example
# (post is a new record that has a title using VARCHAR and a body using TEXT):
# form("post") =>
# Returns an entire form with all needed input tags for a specified Active Record object. For example, let's say you
# have a table model <tt>Post</tt> with attributes named <tt>title</tt> of type <tt>VARCHAR</tt> and <tt>body</tt> of type <tt>TEXT</tt>:
# form("post")
# That line would yield a form like the following:
# <form action='/post/create' method='post'>
# <p>
# <label for="post_title">Title</label><br />
@ -32,14 +33,13 @@ module ActionView
# <p>
# <label for="post_body">Body</label><br />
# <textarea cols="40" id="post_body" name="post[body]" rows="20">
# Back to the hill and over it again!
# </textarea>
# </p>
# <input type='submit' value='Create' />
# </form>
#
# It's possible to specialize the form builder by using a different action name and by supplying another
# block renderer. Example (entry is a new record that has a message attribute using VARCHAR):
# block renderer. For example, let's say you have a model <tt>Entry</tt> with an attribute <tt>message</tt> of type <tt>VARCHAR</tt>:
#
# form("entry", :action => "sign", :input_block =>
# Proc.new { |record, column| "#{column.human_name}: #{input(record, column.name)}<br />" }) =>
@ -74,16 +74,16 @@ module ActionView
content_tag('form', contents, :action => action, :method => 'post', :enctype => options[:multipart] ? 'multipart/form-data': nil)
end
# Returns a string containing the error message attached to the +method+ on the +object+, if one exists.
# This error message is wrapped in a DIV tag, which can be specialized to include both a +prepend_text+ and +append_text+
# to properly introduce the error and a +css_class+ to style it accordingly. Examples (post has an error message
# "can't be empty" on the title attribute):
# Returns a string containing the error message attached to the +method+ on the +object+ if one exists.
# This error message is wrapped in a <tt>DIV</tt> tag, which can be extended to include a +prepend_text+ and/or +append_text+
# (to properly explain the error), and a +css_class+ to style it accordingly. As an example, let's say you have a model
# +post+ that has an error message on the +title+ attribute:
#
# <%= error_message_on "post", "title" %> =>
# <div class="formError">can't be empty</div>
#
# <%= error_message_on "post", "title", "Title simply ", " (or it won't work)", "inputError" %> =>
# <div class="inputError">Title simply can't be empty (or it won't work)</div>
# <%= error_message_on "post", "title", "Title simply ", " (or it won't work).", "inputError" %> =>
# <div class="inputError">Title simply can't be empty (or it won't work).</div>
def error_message_on(object, method, prepend_text = "", append_text = "", css_class = "formError")
if (obj = instance_variable_get("@#{object}")) && (errors = obj.errors.on(method))
content_tag("div", "#{prepend_text}#{errors.is_a?(Array) ? errors.first : errors}#{append_text}", :class => css_class)
@ -92,11 +92,11 @@ module ActionView
end
end
# Returns a string with a div containing all of the error messages for the objects located as instance variables by the names
# Returns a string with a <tt>DIV</tt> containing all of the error messages for the objects located as instance variables by the names
# given. If more than one object is specified, the errors for the objects are displayed in the order that the object names are
# provided.
#
# This div can be tailored by the following options:
# This <tt>DIV</tt> can be tailored by the following options:
#
# * <tt>header_tag</tt> - Used for the header of the error div (default: h2)
# * <tt>id</tt> - The id of the error div (default: errorExplanation)
@ -105,12 +105,12 @@ module ActionView
# any text that you prefer. If <tt>object_name</tt> is not set, the name of
# the first object will be used.
#
# Specifying one object:
# To specify the display for one object, you simply provide its name as a parameter. For example, for the +User+ model:
#
# error_messages_for 'user'
#
# Specifying more than one object (and using the name 'user' in the
# header as the <tt>object_name</tt> instead of 'user_common'):
# To specify more than one object, you simply list them; optionally, you can add an extra +object_name+ parameter, which
# be the name in the header.
#
# error_messages_for 'user_common', 'user', :object_name => 'user'
#

View file

@ -3,6 +3,16 @@ module ActionView
# Provides a set of methods for making it easier to locate problems.
module DebugHelper
# Returns a <pre>-tag set with the +object+ dumped by YAML. Very readable way to inspect an object.
# my_hash = {'first' => 1, 'second' => 'two', 'third' => [1,2,3]}
# debug(my_hash)
# => <pre class='debug_dump'>---
# first: 1
# second: two
# third:
# - 1
# - 2
# - 3
# </pre>
def debug(object)
begin
Marshal::dump(object)

View file

@ -2,6 +2,9 @@ module ActionView
module Helpers
module PrototypeHelper
# Method to execute an element update using Prototype.
# DEPRECATION WARNING: This helper has been depercated; use RJS instead.
# See ActionView::Helpers::PrototypeHelper::JavaScriptGenerator::GeneratorMethods for more information.
def update_element_function(element_id, options = {}, &block)
content = escape_javascript(options[:content] || '')
content = escape_javascript(capture(&block)) if block

View file

@ -250,8 +250,10 @@ module ActionView
return function
end
# Observes the field with the DOM ID specified by +field_id+ and makes
# an Ajax call when its contents have changed.
# Observes the field with the DOM ID specified by +field_id+ and calls a
# callback when its contents have changed. The default callback is an
# Ajax call. By default the value of the observed field is sent as a
# parameter with the Ajax call.
#
# Required +options+ are either of:
# <tt>:url</tt>:: +url_for+-style options for the action to call
@ -268,14 +270,24 @@ module ActionView
# <tt>:update</tt>:: Specifies the DOM ID of the element whose
# innerHTML should be updated with the
# XMLHttpRequest response text.
# <tt>:with</tt>:: A JavaScript expression specifying the
# parameters for the XMLHttpRequest. This defaults
# to 'value', which in the evaluated context
# refers to the new field value. If you specify a
# string without a "=", it'll be extended to mean
# the form key that the value should be assigned to.
# So :with => "term" gives "'term'=value". If a "=" is
# present, no extension will happen.
# <tt>:with</tt>:: A JavaScript expression specifying the parameters
# for the XMLHttpRequest. The default is to send the
# key and value of the observed field. Any custom
# expressions should return a valid URL query string.
# The value of the field is stored in the JavaScript
# variable +value+.
#
# Examples
#
# :with => "'my_custom_key=' + value"
# :with => "'person[name]=' + prompt('New name')"
# :with => "Form.Element.serialize('other-field')"
#
# Finally
# :with => 'name'
# is shorthand for
# :with => "'name=' + value"
# This essentially just changes the key of the parameter.
# <tt>:on</tt>:: Specifies which event handler to observe. By default,
# it's set to "changed" for text fields and areas and
# "click" for radio buttons and checkboxes. With this,
@ -291,11 +303,15 @@ module ActionView
build_observer('Form.Element.EventObserver', field_id, options)
end
end
# Like +observe_field+, but operates on an entire form identified by the
# DOM ID +form_id+. +options+ are the same as +observe_field+, except
# the default value of the <tt>:with</tt> option evaluates to the
# serialized (request string) value of the form.
# Observes the form with the DOM ID specified by +form_id+ and calls a
# callback when its contents have changed. The default callback is an
# Ajax call. By default all fields of the observed field are sent as
# parameters with the Ajax call.
#
# The +options+ for +observe_form+ are the same as the options for
# +observe_field+. The JavaScript variable +value+ available to the
# <tt>:with</tt> option is set to the serialized form by default.
def observe_form(form_id, options = {})
if options[:frequency]
build_observer('Form.Observer', form_id, options)
@ -660,10 +676,10 @@ module ActionView
end
def build_observer(klass, name, options = {})
if options[:with] && !options[:with].include?("=")
if options[:with] && (options[:with] !~ /[=(.]/)
options[:with] = "'#{options[:with]}=' + value"
else
options[:with] ||= 'value' if options[:update]
options[:with] ||= 'value' unless options[:function]
end
callback = options[:function] || remote_function(options)