Merge commit 'mainstream/master'

Conflicts:
	actionpack/lib/action_controller.rb
	actionpack/lib/action_controller/base/base.rb
	actionpack/lib/action_view/template/path.rb
	activesupport/lib/active_support/json/encoders/hash.rb
This commit is contained in:
Pratik Naik 2009-07-03 13:01:39 +01:00
commit 2fe263bb32
484 changed files with 7252 additions and 6674 deletions

@ -4,7 +4,6 @@ require 'rake/testtask'
require 'rake/rdoctask'
require 'rake/packagetask'
require 'rake/gempackagetask'
require 'rake/contrib/sshpublisher'
require File.join(File.dirname(__FILE__), 'lib', 'action_mailer', 'version')
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
@ -61,7 +60,7 @@ spec = Gem::Specification.new do |s|
s.rubyforge_project = "actionmailer"
s.homepage = "http://www.rubyonrails.org"
s.add_dependency('actionpack', '= 2.3.2' + PKG_BUILD)
s.add_dependency('actionpack', '= 3.0.pre' + PKG_BUILD)
s.has_rdoc = true
s.requirements << 'none'
@ -82,12 +81,14 @@ end
desc "Publish the API documentation"
task :pgem => [:package] do
require 'rake/contrib/sshpublisher'
Rake::SshFilePublisher.new("gems.rubyonrails.org", "/u/sites/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
`ssh gems.rubyonrails.org '/u/sites/gems/gemupdate.sh'`
end
desc "Publish the API documentation"
task :pdoc => [:rdoc] do
require 'rake/contrib/sshpublisher'
Rake::SshDirPublisher.new("wrath.rubyonrails.org", "public_html/am", "doc").upload
end

@ -21,16 +21,9 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
begin
require 'action_controller'
rescue LoadError
actionpack_path = "#{File.dirname(__FILE__)}/../../actionpack/lib"
if File.directory?(actionpack_path)
$:.unshift actionpack_path
require 'action_controller'
end
end
actionpack_path = "#{File.dirname(__FILE__)}/../../actionpack/lib"
$:.unshift(actionpack_path) if File.directory?(actionpack_path)
require 'action_controller'
require 'action_view'
module ActionMailer

@ -1,3 +1,7 @@
require "active_support/core_ext/class"
# Use the old layouts until actionmailer gets refactored
require "action_controller/legacy/layout"
module ActionMailer #:nodoc:
# Action Mailer allows you to send email from your application using a mailer model and views.
#
@ -697,7 +701,7 @@ def create_mail
def perform_delivery_smtp(mail)
destinations = mail.destinations
mail.ready_to_send
sender = (mail['return-path'] && mail['return-path'].spec) || mail.from
sender = (mail['return-path'] && mail['return-path'].spec) || mail['from']
smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto)

@ -48,13 +48,14 @@ def helper(*args, &block)
file_name = arg.to_s.underscore + '_helper'
class_name = file_name.camelize
begin
require_dependency(file_name)
rescue LoadError => load_error
requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
raise LoadError.new(msg).copy_blame!(load_error)
end
require_dependency(file_name, "Missing helper file helpers/%s.rb")
# begin
# require_dependency(file_name)
# rescue LoadError => load_error
# requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
# msg = (requiree == file_name) ? "Missing helper file helpers/#{file_name}.rb" : "Can't load file: #{requiree}"
# raise LoadError.new(msg).copy_blame!(load_error)
# end
add_template_helper(class_name.constantize)
else
@ -97,7 +98,7 @@ def inherited_with_helper(child)
child.master_helper_module.__send__(:include, master_helper_module)
child.helper child.name.to_s.underscore
rescue MissingSourceFile => e
raise unless e.is_missing?("helpers/#{child.name.to_s.underscore}_helper")
raise unless e.is_missing?("#{child.name.to_s.underscore}_helper")
end
end
end

@ -1,8 +1,8 @@
module ActionMailer
module VERSION #:nodoc:
MAJOR = 2
MINOR = 3
TINY = 2
MAJOR = 3
MINOR = 0
TINY = "pre"
STRING = [MAJOR, MINOR, TINY].join('.')
end

@ -1,9 +1,6 @@
require 'rubygems'
require 'test/unit'
gem 'mocha', '>= 0.9.5'
require 'mocha'
$:.unshift "#{File.dirname(__FILE__)}/../lib"
$:.unshift "#{File.dirname(__FILE__)}/../../activesupport/lib"
$:.unshift "#{File.dirname(__FILE__)}/../../actionpack/lib"

@ -22,9 +22,11 @@ task :default => [ :test ]
# Run the unit tests
desc "Run all unit tests"
task :test => [:test_action_pack, :test_active_record_integration, :test_new_base, :test_new_base_on_old_tests]
task :test => [:test_action_pack, :test_active_record_integration, :test_new_base]
test_lib_dirs = [ENV["NEW"] ? "test/new_base" : "test", "test/lib"]
test_lib_dirs = ENV["NEW"] ? ["test/new_base"] : []
test_lib_dirs.push "test", "test/lib"
# test_lib_dirs = [ENV["NEW"] ? "test/new_base" : "test", "test/lib"]
Rake::TestTask.new(:test_action_pack) do |t|
t.libs.concat test_lib_dirs
@ -35,6 +37,7 @@ Rake::TestTask.new(:test_action_pack) do |t|
t.verbose = true
#t.warning = true
end
task :isolated_test do
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
Dir.glob("test/{controller,dispatch,template}/**/*_test.rb").all? do |file|
@ -112,7 +115,7 @@ spec = Gem::Specification.new do |s|
s.has_rdoc = true
s.requirements << 'none'
s.add_dependency('activesupport', '= 2.3.2' + PKG_BUILD)
s.add_dependency('activesupport', '= 3.0.pre' + PKG_BUILD)
s.require_path = 'lib'
s.autorequire = 'action_controller'

@ -1,84 +1,62 @@
#--
# Copyright (c) 2004-2009 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
$:.unshift activesupport_path if File.directory?(activesupport_path)
require 'active_support'
require File.join(File.dirname(__FILE__), "action_pack")
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initializer if they are really needed.
def self.load_all!
[Base, Request, Response, UrlRewriter, UrlWriter]
[ActionDispatch::Http::Headers]
end
autoload :Base, "action_controller/base/base"
autoload :ConditionalGet, "action_controller/base/conditional_get"
autoload :HideActions, "action_controller/base/hide_actions"
autoload :Http, "action_controller/base/http"
autoload :Layouts, "action_controller/base/layouts"
autoload :RackConvenience, "action_controller/base/rack_convenience"
autoload :Rails2Compatibility, "action_controller/base/compatibility"
autoload :Redirector, "action_controller/base/redirector"
autoload :Renderer, "action_controller/base/renderer"
autoload :RenderOptions, "action_controller/base/render_options"
autoload :Renderers, "action_controller/base/render_options"
autoload :Rescue, "action_controller/base/rescuable"
autoload :Testing, "action_controller/base/testing"
autoload :UrlFor, "action_controller/base/url_for"
autoload :Session, "action_controller/base/session"
autoload :Helpers, "action_controller/base/helpers"
autoload :Base, 'action_controller/base/base'
autoload :Benchmarking, 'action_controller/base/chained/benchmarking'
autoload :Caching, 'action_controller/caching'
autoload :Cookies, 'action_controller/base/cookies'
autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
autoload :Filters, 'action_controller/base/chained/filters'
autoload :Flash, 'action_controller/base/chained/flash'
autoload :Helpers, 'action_controller/base/helpers'
autoload :HttpAuthentication, 'action_controller/base/http_authentication'
autoload :Integration, 'action_controller/testing/integration'
autoload :IntegrationTest, 'action_controller/testing/integration'
autoload :Layout, 'action_controller/base/layout'
autoload :MimeResponds, 'action_controller/base/mime_responds'
# Ported modules
# require 'action_controller/routing'
autoload :Caching, 'action_controller/caching'
autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
autoload :Integration, 'action_controller/testing/integration'
autoload :MimeResponds, 'action_controller/base/mime_responds'
autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes'
autoload :RecordIdentifier, 'action_controller/record_identifier'
autoload :Redirector, 'action_controller/base/redirect'
autoload :Renderer, 'action_controller/base/render'
autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection'
autoload :Rescue, 'action_controller/base/rescue'
autoload :Resources, 'action_controller/routing/resources'
autoload :Responder, 'action_controller/base/responder'
autoload :Routing, 'action_controller/routing'
autoload :RecordIdentifier, 'action_controller/record_identifier'
autoload :Resources, 'action_controller/routing/resources'
autoload :SessionManagement, 'action_controller/base/session_management'
autoload :Streaming, 'action_controller/base/streaming'
autoload :TestCase, 'action_controller/testing/test_case'
autoload :TestProcess, 'action_controller/testing/process'
autoload :Translation, 'action_controller/translation'
autoload :UrlEncodedPairParser, 'action_controller/dispatch/url_encoded_pair_parser'
autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter'
autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter'
autoload :Verification, 'action_controller/base/verification'
autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging'
autoload :TestCase, 'action_controller/testing/test_case'
autoload :TestProcess, 'action_controller/testing/process'
autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter'
autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter'
module Assertions
autoload :DomAssertions, 'action_controller/testing/assertions/dom'
autoload :ModelAssertions, 'action_controller/testing/assertions/model'
autoload :ResponseAssertions, 'action_controller/testing/assertions/response'
autoload :RoutingAssertions, 'action_controller/testing/assertions/routing'
autoload :SelectorAssertions, 'action_controller/testing/assertions/selector'
autoload :TagAssertions, 'action_controller/testing/assertions/tag'
end
autoload :Verification, 'action_controller/base/verification'
autoload :Flash, 'action_controller/base/flash'
autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection'
autoload :Streaming, 'action_controller/base/streaming'
autoload :HttpAuthentication, 'action_controller/base/http_authentication'
autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging'
autoload :Translation, 'action_controller/translation'
autoload :Cookies, 'action_controller/base/cookies'
autoload :ActionControllerError, 'action_controller/base/exceptions'
autoload :SessionRestoreError, 'action_controller/base/exceptions'
autoload :RenderError, 'action_controller/base/exceptions'
autoload :RoutingError, 'action_controller/base/exceptions'
autoload :MethodNotAllowed, 'action_controller/base/exceptions'
autoload :NotImplemented, 'action_controller/base/exceptions'
autoload :UnknownController, 'action_controller/base/exceptions'
autoload :MissingFile, 'action_controller/base/exceptions'
autoload :RenderError, 'action_controller/base/exceptions'
autoload :SessionOverflowError, 'action_controller/base/exceptions'
autoload :UnknownHttpMethod, 'action_controller/base/exceptions'
require 'action_controller/routing'
end
autoload :HTML, 'action_controller/vendor/html-scanner'
autoload :AbstractController, 'action_controller/abstract'
require 'action_dispatch'
require 'action_view'

@ -2,13 +2,15 @@
require "active_support/core_ext/module/delegation"
module AbstractController
autoload :Base, "action_controller/abstract/base"
autoload :Benchmarker, "action_controller/abstract/benchmarker"
autoload :Callbacks, "action_controller/abstract/callbacks"
autoload :Helpers, "action_controller/abstract/helpers"
autoload :Layouts, "action_controller/abstract/layouts"
autoload :Logger, "action_controller/abstract/logger"
autoload :Renderer, "action_controller/abstract/renderer"
autoload :Base, "action_controller/abstract/base"
autoload :Benchmarker, "action_controller/abstract/benchmarker"
autoload :Callbacks, "action_controller/abstract/callbacks"
autoload :Helpers, "action_controller/abstract/helpers"
autoload :Layouts, "action_controller/abstract/layouts"
autoload :Logger, "action_controller/abstract/logger"
autoload :Renderer, "action_controller/abstract/renderer"
# === Exceptions
autoload :ActionNotFound, "action_controller/abstract/exceptions"
autoload :ActionNotFound, "action_controller/abstract/exceptions"
autoload :DoubleRenderError, "action_controller/abstract/exceptions"
autoload :Error, "action_controller/abstract/exceptions"
end

@ -1,53 +1,65 @@
require 'active_support/core_ext/module/attr_internal'
module AbstractController
class Error < StandardError; end
class DoubleRenderError < Error
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
def initialize(message = nil)
super(message || DEFAULT_MESSAGE)
end
end
class Base
attr_internal :response_body
attr_internal :response_obj
attr_internal :action_name
class << self
attr_reader :abstract
alias_method :abstract?, :abstract
# Define a controller as abstract. See internal_methods for more
# details.
def abstract!
@abstract = true
end
alias_method :abstract?, :abstract
def inherited(klass)
::AbstractController::Base.subclasses << klass.to_s
::AbstractController::Base.descendants << klass.to_s
super
end
def subclasses
@subclasses ||= []
# A list of all descendents of AbstractController::Base. This is
# useful for initializers which need to add behavior to all controllers.
def descendants
@descendants ||= []
end
# A list of all internal methods for a controller. This finds the first
# abstract superclass of a controller, and gets a list of all public
# instance methods on that abstract class. Public instance methods of
# a controller would normally be considered action methods, so we
# are removing those methods on classes declared as abstract
# (ActionController::Http and ActionController::Base are defined
# as abstract)
def internal_methods
controller = self
controller = controller.superclass until controller.abstract?
controller.public_instance_methods(true)
end
def process(action)
new.process(action.to_s)
end
# The list of hidden actions to an empty Array. Defaults to an
# empty Array. This can be modified by other modules or subclasses
# to specify particular actions as hidden.
#
# ==== Returns
# Array[String]:: An array of method names that should not be
# considered actions.
def hidden_actions
[]
end
# A list of method names that should be considered actions. This
# includes all public instance methods on a controller, less
# any internal methods (see #internal_methods), adding back in
# any methods that are internal, but still exist on the class
# itself. Finally, #hidden_actions are removed.
#
# ==== Returns
# Array[String]:: A list of all methods that should be considered
# actions.
def action_methods
@action_methods ||=
# All public instance methods of this class, including ancestors
@ -63,10 +75,14 @@ def action_methods
abstract!
def initialize
self.response_obj = {}
end
# Calls the action going through the entire action dispatch stack.
#
# The actual method that is called is determined by calling
# #method_for_action. If no method can handle the action, then an
# ActionNotFound error is raised.
#
# ==== Returns
# self
def process(action)
@_action_name = action_name = action.to_s
@ -79,33 +95,63 @@ def process(action)
end
private
def action_methods
self.class.action_methods
# Returns true if the name can be considered an action. This can
# be overridden in subclasses to modify the semantics of what
# can be considered an action.
#
# ==== Parameters
# name<String>:: The name of an action to be tested
#
# ==== Returns
# TrueClass, FalseClass
def action_method?(name)
self.class.action_methods.include?(name)
end
def action_method?(action)
action_methods.include?(action)
end
# It is possible for respond_to?(action_name) to be false and
# respond_to?(:action_missing) to be false if respond_to_action?
# is overridden in a subclass. For instance, ActionController::Base
# overrides it to include the case where a template matching the
# action_name is found.
# Call the action. Override this in a subclass to modify the
# behavior around processing an action. This, and not #process,
# is the intended way to override action dispatching.
def process_action(method_name)
send_action(method_name)
end
# Actually call the method associated with the action. Override
# this method if you wish to change how action methods are called,
# not to add additional behavior around it. For example, you would
# override #send_action if you want to inject arguments into the
# method.
alias send_action send
# If the action name was not found, but a method called "action_missing"
# was found, #method_for_action will return "_handle_action_missing".
# This method calls #action_missing with the current action name.
def _handle_action_missing
action_missing(@_action_name)
end
# Override this to change the conditions that will raise an
# ActionNotFound error. If you accept a difference case,
# you must handle it by also overriding process_action and
# handling the case.
# Takes an action name and returns the name of the method that will
# handle the action. In normal cases, this method returns the same
# name as it receives. By default, if #method_for_action receives
# a name that is not an action, it will look for an #action_missing
# method and return "_handle_action_missing" if one is found.
#
# Subclasses may override this method to add additional conditions
# that should be considered an action. For instance, an HTTP controller
# with a template matching the action name is considered to exist.
#
# If you override this method to handle additional cases, you may
# also provide a method (like _handle_method_missing) to handle
# the case.
#
# If none of these conditions are true, and method_for_action
# returns nil, an ActionNotFound exception will be raised.
#
# ==== Parameters
# action_name<String>:: An action name to find a method name for
#
# ==== Returns
# String:: The name of the method that handles the action
# nil:: No method name could be found. Raise ActionNotFound.
def method_for_action(action_name)
if action_method?(action_name) then action_name
elsif respond_to?(:action_missing, true) then "_handle_action_missing"

@ -5,6 +5,16 @@ module Benchmarker
include Logger
module ClassMethods
# Execute the passed in block, timing the duration of the block in ms.
#
# ==== Parameters
# title<#to_s>:: The title of block to benchmark
# log_level<Integer>:: A valid log level. Defaults to Logger::DEBUG
# use_silence<TrueClass, FalseClass>:: Whether or not to silence the
# logger for the duration of the block.
#
# ==== Returns
# Object:: The result of the block
def benchmark(title, log_level = ::Logger::DEBUG, use_silence = true)
if logger && logger.level >= log_level
result = nil

@ -2,12 +2,17 @@ module AbstractController
module Callbacks
extend ActiveSupport::Concern
# Uses ActiveSupport::NewCallbacks as the base functionality. For
# more details on the whole callback system, read the documentation
# for ActiveSupport::NewCallbacks.
include ActiveSupport::NewCallbacks
included do
define_callbacks :process_action, "response_body"
end
# Override AbstractController::Base's process_action to run the
# process_action callbacks around the normal behavior.
def process_action(method_name)
_run_process_action_callbacks(method_name) do
super
@ -15,6 +20,17 @@ def process_action(method_name)
end
module ClassMethods
# If :only or :accept are used, convert the options into the
# primitive form (:per_key) used by ActiveSupport::Callbacks.
# The basic idea is that :only => :index gets converted to
# :if => proc {|c| c.action_name == "index" }, but that the
# proc is only evaluated once per action for the lifetime of
# a Rails process.
#
# ==== Options
# :only<#to_s>:: The callback should be run only for this action
# :except<#to_s>:: The callback should be run for all actions
# except this action
def _normalize_callback_options(options)
if only = options[:only]
only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
@ -26,35 +42,69 @@ def _normalize_callback_options(options)
end
end
# Skip before, after, and around filters matching any of the names
#
# ==== Parameters
# *names<Object>:: A list of valid names that could be used for
# callbacks. Note that skipping uses Ruby equality, so it's
# impossible to skip a callback defined using an anonymous proc
# using #skip_filter
def skip_filter(*names, &blk)
skip_before_filter(*names)
skip_after_filter(*names)
skip_around_filter(*names)
end
# Take callback names and an optional callback proc, normalize them,
# then call the block with each callback. This allows us to abstract
# the normalization across several methods that use it.
#
# ==== Parameters
# callbacks<Array[*Object, Hash]>:: A list of callbacks, with an optional
# options hash as the last parameter.
# block<Proc>:: A proc that should be added to the callbacks.
#
# ==== Block Parameters
# name<Symbol>:: The callback to be added
# options<Hash>:: A list of options to be used when adding the callback
def _insert_callbacks(callbacks, block)
options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
_normalize_callback_options(options)
callbacks.push(block) if block
callbacks.each do |callback|
yield callback, options
end
end
# set up before_filter, prepend_before_filter, skip_before_filter, etc.
# for each of before, after, and around.
[:before, :after, :around].each do |filter|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
# Append a before, after or around filter. See _insert_callbacks
# for details on the allowed parameters.
def #{filter}_filter(*names, &blk)
options = names.last.is_a?(Hash) ? names.pop : {}
_normalize_callback_options(options)
names.push(blk) if block_given?
names.each do |name|
process_action_callback(:#{filter}, name, options)
_insert_callbacks(names, blk) do |name, options|
set_callback(:process_action, :#{filter}, name, options)
end
end
# Prepend a before, after or around filter. See _insert_callbacks
# for details on the allowed parameters.
def prepend_#{filter}_filter(*names, &blk)
options = names.last.is_a?(Hash) ? names.pop : {}
_normalize_callback_options(options)
names.push(blk) if block_given?
names.each do |name|
process_action_callback(:#{filter}, name, options.merge(:prepend => true))
_insert_callbacks(names, blk) do |name, options|
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true))
end
end
# Skip a before, after or around filter. See _insert_callbacks
# for details on the allowed parameters.
def skip_#{filter}_filter(*names, &blk)
options = names.last.is_a?(Hash) ? names.pop : {}
_normalize_callback_options(options)
names.push(blk) if block_given?
names.each do |name|
skip_process_action_callback(:#{filter}, name, options)
_insert_callbacks(names, blk) do |name, options|
skip_callback(:process_action, :#{filter}, name, options)
end
end
# *_filter is the same as append_*_filter
alias_method :append_#{filter}_filter, :#{filter}_filter
RUBY_EVAL
end

@ -1,3 +1,12 @@
module AbstractController
class Error < StandardError; end
class ActionNotFound < StandardError; end
class DoubleRenderError < Error
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
def initialize(message = nil)
super(message || DEFAULT_MESSAGE)
end
end
end

@ -5,33 +5,26 @@ module Helpers
include Renderer
included do
extlib_inheritable_accessor :master_helper_module
self.master_helper_module = Module.new
extlib_inheritable_accessor(:_helpers) { Module.new }
end
# Override AbstractController::Renderer's _action_view to include the
# helper module for this class into its helpers module.
def _action_view
@_action_view ||= begin
av = super
av.helpers.send(:include, master_helper_module)
av
end
@_action_view ||= super.tap { |av| av.helpers.include(_helpers) }
end
module ClassMethods
# When a class is inherited, wrap its helper module in a new module.
# This ensures that the parent class's module can be changed
# independently of the child class's.
def inherited(klass)
klass.master_helper_module = Module.new
klass.master_helper_module.__send__ :include, master_helper_module
helpers = _helpers
klass._helpers = Module.new { include helpers }
super
end
# Makes all the (instance) methods in the helper module available to templates rendered through this controller.
# See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
# available to the templates.
def add_template_helper(mod)
master_helper_module.module_eval { include mod }
end
# Declare a controller method as a helper. For example, the following
# makes the +current_user+ controller method available to the view:
# class ApplicationController < ActionController::Base
@ -48,9 +41,13 @@ def add_template_helper(mod)
#
# In a view:
# <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
#
# ==== Parameters
# meths<Array[#to_s]>:: The name of a method on the controller
# to be made available on the view.
def helper_method(*meths)
meths.flatten.each do |meth|
master_helper_module.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
_helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
def #{meth}(*args, &blk)
controller.send(%(#{meth}), *args, &blk)
end
@ -58,6 +55,14 @@ def #{meth}(*args, &blk)
end
end
# Make a number of helper modules part of this class' default
# helpers.
#
# ==== Parameters
# *args<Array[Module]>:: Modules to be included
# block<Block>:: Evalulate the block in the context
# of the helper module. Any methods defined in the block
# will be helpers.
def helper(*args, &block)
args.flatten.each do |arg|
case arg
@ -65,7 +70,18 @@ def helper(*args, &block)
add_template_helper(arg)
end
end
master_helper_module.module_eval(&block) if block_given?
_helpers.module_eval(&block) if block_given?
end
private
# Makes all the (instance) methods in the helper module available to templates
# rendered through this controller.
#
# ==== Parameters
# mod<Module>:: The module to include into the current helper module
# for the class
def add_template_helper(mod)
_helpers.module_eval { include mod }
end
end
end

@ -5,16 +5,31 @@ module Layouts
include Renderer
included do
extlib_inheritable_accessor :_layout_conditions
self._layout_conditions = {}
extlib_inheritable_accessor(:_layout_conditions) { Hash.new }
end
module ClassMethods
def layout(layout, conditions = {})
unless [String, Symbol, FalseClass, NilClass].include?(layout.class)
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
end
def inherited(klass)
super
klass._write_layout_method
end
# Specify the layout to use for this class.
#
# If the specified layout is a:
# String:: the String is the template name
# Symbol:: call the method specified by the symbol, which will return
# the template name
# false:: There is no layout
# true:: raise an ArgumentError
#
# ==== Parameters
# layout<String, Symbol, false)>:: The layout to use.
#
# ==== Options (conditions)
# :only<#to_s, Array[#to_s]>:: A list of actions to apply this layout to.
# :except<#to_s, Array[#to_s]>:: Apply this layout to all actions but this one
def layout(layout, conditions = {})
conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
self._layout_conditions = conditions
@ -22,6 +37,11 @@ def layout(layout, conditions = {})
_write_layout_method
end
# If no layout is supplied, look for a template named the return
# value of this method.
#
# ==== Returns
# String:: A template name
def _implied_layout_name
name.underscore
end
@ -29,23 +49,31 @@ def _implied_layout_name
# Takes the specified layout and creates a _layout method to be called
# by _default_layout
#
# If the specified layout is a:
# String:: return the string
# Symbol:: call the method specified by the symbol
# false:: return nil
# none:: If a layout is found in the view paths with the controller's
# name, return that string. Otherwise, use the superclass'
# layout (which might also be implied)
# If there is no explicit layout specified:
# If a layout is found in the view paths with the controller's
# name, return that string. Otherwise, use the superclass'
# layout (which might also be implied)
def _write_layout_method
case @_layout
when String
self.class_eval %{def _layout(details) #{@_layout.inspect} end}
when Symbol
self.class_eval %{def _layout(details) #{@_layout} end}
self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
def _layout(details)
#{@_layout}.tap do |layout|
unless layout.is_a?(String) || !layout
raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
"should have returned a String, false, or nil"
end
end
end
ruby_eval
when false
self.class_eval %{def _layout(details) end}
else
self.class_eval %{
when true
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
when nil
self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
def _layout(details)
if view_paths.find_by_parts?("#{_implied_layout_name}", details, "layouts")
"#{_implied_layout_name}"
@ -53,33 +81,55 @@ def _layout(details)
super
end
end
}
ruby_eval
end
self.class_eval { private :_layout }
end
end
private
# This will be overwritten
def _layout(details)
end
# This will be overwritten by _write_layout_method
def _layout(details) end
# :api: plugin
# ====
# Override this to mutate the inbound layout name
# Determine the layout for a given name and details.
#
# ==== Parameters
# name<String>:: The name of the template
# details<Hash{Symbol => Object}>:: A list of details to restrict
# the lookup to. By default, layout lookup is limited to the
# formats specified for the current request.
def _layout_for_name(name, details = {:formats => formats})
unless [String, FalseClass, NilClass].include?(name.class)
raise ArgumentError, "String, false, or nil expected; you passed #{name.inspect}"
end
name && view_paths.find_by_parts(name, details, _layout_prefix(name))
name && _find_by_parts(name, details)
end
# TODO: Decide if this is the best hook point for the feature
def _layout_prefix(name)
"layouts"
# Take in the name and details and find a Template.
#
# ==== Parameters
# name<String>:: The name of the template to retrieve
# details<Hash>:: A list of details to restrict the search by. This
# might include details like the format or locale of the template.
#
# ==== Returns
# Template:: A template object matching the name and details
def _find_by_parts(name, details)
# TODO: Make prefix actually part of details in ViewPath#find_by_parts
prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts"
view_paths.find_by_parts(name, details, prefix)
end
def _default_layout(require_layout = false, details = {:formats => formats})
# Returns the default layout for this controller and a given set of details.
# Optionally raises an exception if the layout could not be found.
#
# ==== Parameters
# details<Hash>:: A list of details to restrict the search by. This
# might include details like the format or locale of the template.
# require_layout<Boolean>:: If this is true, raise an ArgumentError
# with details about the fact that the exception could not be
# found (defaults to false)
#
# ==== Returns
# Template:: The template object for the default layout (or nil)
def _default_layout(details, require_layout = false)
if require_layout && _action_has_layout? && !_layout(details)
raise ArgumentError,
"There was no default layout for #{self.class} in #{view_paths.inspect}"
@ -93,6 +143,12 @@ def _default_layout(require_layout = false, details = {:formats => formats})
end
end
# Determines whether the current action has a layout by checking the
# action name against the :only and :except conditions set on the
# layout.
#
# ==== Returns
# Boolean:: True if the action has a layout, false otherwise.
def _action_has_layout?
conditions = _layout_conditions
if only = conditions[:only]

@ -5,6 +5,13 @@ module AbstractController
module Logger
extend ActiveSupport::Concern
# A class that allows you to defer expensive processing
# until the logger actually tries to log. Otherwise, you are
# forced to do the processing in advance, and send the
# entire processed String to the logger, which might
# just discard the String if the log level is too low.
#
# TODO: Require that Rails loggers accept a block.
class DelayedLog
def initialize(&blk)
@blk = blk
@ -20,8 +27,10 @@ def to_s
cattr_accessor :logger
end
def process(action)
ret = super
# Override process_action in the AbstractController::Base
# to log details about the method.
def process_action(action)
super
if logger
log = DelayedLog.new do
@ -32,10 +41,9 @@ def process(action)
logger.info(log)
end
ret
end
private
def request_origin
# this *needs* to be cached!
# otherwise you'd get different results if calling it more than once

@ -14,10 +14,32 @@ module Renderer
self._view_paths ||= ActionView::PathSet.new
end
# An instance of a view class. The default view class is ActionView::Base
#
# The view class must have the following methods:
# initialize[paths, assigns_for_first_render, controller]
# paths<Array[ViewPath]>:: A list of resolvers to look for templates in
# controller<AbstractController::Base> A controller
# _render_partial_from_controller[options]
# options<Hash>:: see _render_partial in ActionView::Base
# _render_template_from_controller[template, layout, options, partial]
# template<ActionView::Template>:: The template to render
# layout<ActionView::Template>:: The layout to render around the template
# options<Hash>:: See _render_template_with_layout in ActionView::Base
# partial<Boolean>:: Whether or not the template to render is a partial
# _partial:: If a partial, rather than a template, was rendered, return
# the partial.
# helpers:: A module containing the helpers to be used in the view. This
# module should respond_to include.
# controller:: The controller that initialized the ActionView
#
# Override this method in a to change the default behavior.
def _action_view
@_action_view ||= ActionView::Base.new(self.class.view_paths, {}, self)
end
# Mostly abstracts the fact that calling render twice is a DoubleRenderError.
# Delegates render_to_body and sticks the result in self.response_body.
def render(*args)
if response_body
raise AbstractController::DoubleRenderError, "OMG"
@ -27,9 +49,10 @@ def render(*args)
end
# Raw rendering of a template to a Rack-compatible body.
# ====
# @option _prefix<String> The template's path prefix
# @option _layout<String> The relative path to the layout template to use
#
# ==== Options
# _partial_object<Object>:: The object that is being rendered. If this
# exists, we are in the special case of rendering an object as a partial.
#
# :api: plugin
def render_to_body(options = {})
@ -42,21 +65,27 @@ def render_to_body(options = {})
end
end
# Raw rendering of a template to a string.
# ====
# @option _prefix<String> The template's path prefix
# @option _layout<String> The relative path to the layout template to use
# Raw rendering of a template to a string. Just convert the results of
# render_to_body into a String.
#
# :api: plugin
def render_to_string(options = {})
AbstractController::Renderer.body_to_s(render_to_body(options))
end
# Renders the template from an object.
#
# ==== Options
# _template<ActionView::Template>:: The template to render
# _layout<ActionView::Template>:: The layout to wrap the template in (optional)
# _partial<TrueClass, FalseClass>:: Whether or not the template to be rendered is a partial
def _render_template(options)
_action_view._render_template_from_controller(options[:_template], options[:_layout], options, options[:_partial])
end
def view_paths()
# The list of view paths for this controller. See ActionView::ViewPathSet for
# more details about writing custom view paths.
def view_paths
_view_paths
end
@ -73,6 +102,15 @@ def self.body_to_s(body)
end
private
# Take in a set of options and determine the template to render
#
# ==== Options
# _template<ActionView::Template>:: If this is provided, the search is over
# _template_name<#to_s>:: The name of the template to look up. Otherwise,
# use the current action name.
# _prefix<String>:: The prefix to look inside of. In a file system, this corresponds
# to a directory.
# _partial<TrueClass, FalseClass>:: Whether or not the file to look up is a partial
def _determine_template(options)
name = (options[:_template_name] || action_name).to_s
@ -82,18 +120,36 @@ def _determine_template(options)
end
module ClassMethods
# Append a path to the list of view paths for this controller.
#
# ==== Parameters
# path<String, ViewPath>:: If a String is provided, it gets converted into
# the default view path. You may also provide a custom view path
# (see ActionView::ViewPathSet for more information)
def append_view_path(path)
self.view_paths << path
end
# Prepend a path to the list of view paths for this controller.
#
# ==== Parameters
# path<String, ViewPath>:: If a String is provided, it gets converted into
# the default view path. You may also provide a custom view path
# (see ActionView::ViewPathSet for more information)
def prepend_view_path(path)
self.view_paths.unshift(path)
end
# A list of all of the default view paths for this controller.
def view_paths
self._view_paths
end
# Set the view paths.
#
# ==== Parameters
# paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
# otherwise, process the parameter into a ViewPathSet.
def view_paths=(paths)
self._view_paths = paths.is_a?(ActionView::PathSet) ?
paths : ActionView::Base.process_view_paths(paths)

File diff suppressed because it is too large Load Diff

@ -1,107 +0,0 @@
require 'active_support/core_ext/benchmark'
module ActionController #:nodoc:
# The benchmarking module times the performance of actions and reports to the logger. If the Active Record
# package has been included, a separate timing section for database calls will be added as well.
module Benchmarking #:nodoc:
def self.included(base)
base.extend(ClassMethods)
base.class_eval do
alias_method_chain :perform_action, :benchmark
alias_method_chain :render, :benchmark
end
end
module ClassMethods
# Log and benchmark the workings of a single block and silence whatever logging that may have happened inside it
# (unless <tt>use_silence</tt> is set to false).
#
# The benchmark is only recorded if the current level of the logger matches the <tt>log_level</tt>, which makes it
# easy to include benchmarking statements in production software that will remain inexpensive because the benchmark
# will only be conducted if the log level is low enough.
def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
if logger && logger.level >= log_level
result = nil
ms = Benchmark.ms { result = use_silence ? silence { yield } : yield }
logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)")
result
else
yield
end
end
# Silences the logger for the duration of the block.
def silence
old_logger_level, logger.level = logger.level, Logger::ERROR if logger
yield
ensure
logger.level = old_logger_level if logger
end
end
protected
def render_with_benchmark(options = nil, extra_options = {}, &block)
if logger
if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
db_runtime = ActiveRecord::Base.connection.reset_runtime
end
render_output = nil
@view_runtime = Benchmark.ms { render_output = render_without_benchmark(options, extra_options, &block) }
if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
@db_rt_before_render = db_runtime
@db_rt_after_render = ActiveRecord::Base.connection.reset_runtime
@view_runtime -= @db_rt_after_render
end
render_output
else
render_without_benchmark(options, extra_options, &block)
end
end
private
def perform_action_with_benchmark
if logger && logger.info?
ms = [Benchmark.ms { perform_action_without_benchmark }, 0.01].max
logging_view = defined?(@view_runtime)
logging_active_record = Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
log_message = 'Completed in %.0fms' % ms
if logging_view || logging_active_record
log_message << " ("
log_message << view_runtime if logging_view
if logging_active_record
log_message << ", " if logging_view
log_message << active_record_runtime + ")"
else
")"
end
end
log_message << " | #{response.status}"
log_message << " [#{complete_request_uri rescue "unknown"}]"
logger.info(log_message)
response.headers["X-Runtime"] = "%.0f" % ms
else
perform_action_without_benchmark
end
end
def view_runtime
"View: %.0f" % @view_runtime
end
def active_record_runtime
db_runtime = ActiveRecord::Base.connection.reset_runtime
db_runtime += @db_rt_before_render if @db_rt_before_render
db_runtime += @db_rt_after_render if @db_rt_after_render
"DB: %.0f" % db_runtime
end
end
end

@ -1,670 +0,0 @@
module ActionController #:nodoc:
module Filters #:nodoc:
extend ActiveSupport::Concern
class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
def append_filter_to_chain(filters, filter_type, &block)
pos = find_filter_append_position(filters, filter_type)
update_filter_chain(filters, filter_type, pos, &block)
end
def prepend_filter_to_chain(filters, filter_type, &block)
pos = find_filter_prepend_position(filters, filter_type)
update_filter_chain(filters, filter_type, pos, &block)
end
def create_filters(filters, filter_type, &block)
filters, conditions = extract_options(filters, &block)
filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) }
filters
end
def skip_filter_in_chain(*filters, &test)
filters, conditions = extract_options(filters)
filters.each do |filter|
if callback = find(filter) then delete(callback) end
end if conditions.empty?
update_filter_in_chain(filters, :skip => conditions, &test)
end
private
def update_filter_chain(filters, filter_type, pos, &block)
new_filters = create_filters(filters, filter_type, &block)
insert(pos, new_filters).flatten!
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
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
each_with_index do |f,i|
return i if f.after?
end
return -1
end
return 0
end
def find_or_create_filter(filter, filter_type, options = {})
update_filter_in_chain([filter], options)
if found_filter = find(filter) { |f| f.type == filter_type }
found_filter
else
filter_kind = case
when filter.respond_to?(:before) && filter_type == :before
:before
when filter.respond_to?(:after) && filter_type == :after
:after
else
:filter
end
case filter_type
when :before
BeforeFilter.new(filter_kind, filter, options)
when :after
AfterFilter.new(filter_kind, filter, options)
else
AroundFilter.new(filter_kind, filter, options)
end
end
end
def update_filter_in_chain(filters, options, &test)
filters.map! { |f| block_given? ? find(f, &test) : find(f) }
filters.compact!
map! do |filter|
if filters.include?(filter)
new_filter = filter.dup
new_filter.update_options!(options)
new_filter
else
filter
end
end
end
end
class Filter < ActiveSupport::Callbacks::Callback #:nodoc:
def initialize(kind, method, options = {})
super
update_options! options
end
# override these to return true in appropriate subclass
def before?
false
end
def after?
false
end
def around?
false
end
# Make sets of strings from :only/:except options
def update_options!(other)
if other
convert_only_and_except_options_to_sets_of_strings(other)
if other[:skip]
convert_only_and_except_options_to_sets_of_strings(other[:skip])
end
end
options.update(other)
end
private
def should_not_skip?(controller)
if options[:skip]
!included_in_action?(controller, options[:skip])
else
true
end
end
def included_in_action?(controller, options)
if options[:only]
options[:only].include?(controller.action_name)
elsif options[:except]
!options[:except].include?(controller.action_name)
else
true
end
end
def should_run_callback?(controller)
should_not_skip?(controller) && included_in_action?(controller, options) && super
end
def convert_only_and_except_options_to_sets_of_strings(opts)
[:only, :except].each do |key|
if values = opts[key]
opts[key] = Array(values).map {|val| val.to_s }.to_set
end
end
end
end
class AroundFilter < Filter #:nodoc:
def type
:around
end
def around?
true
end
def call(controller, &block)
if should_run_callback?(controller)
method = filter_responds_to_before_and_after? ? around_proc : self.method
# For around_filter do |controller, action|
if method.is_a?(Proc) && method.arity == 2
evaluate_method(method, controller, block)
else
evaluate_method(method, controller, &block)
end
else
block.call
end
end
private
def filter_responds_to_before_and_after?
method.respond_to?(:before) && method.respond_to?(:after)
end
def around_proc
Proc.new do |controller, action|
method.before(controller)
if controller.__send__(:performed?)
controller.__send__(:halt_filter_chain, method, :rendered_or_redirected)
else
begin
action.call
ensure
method.after(controller)
end
end
end
end
end
class BeforeFilter < Filter #:nodoc:
def type
:before
end
def before?
true
end
def call(controller, &block)
super
if controller.__send__(:performed?)
controller.__send__(:halt_filter_chain, method, :rendered_or_redirected)
end
end
end
class AfterFilter < Filter #:nodoc:
def type
:after
end
def after?
true
end
end
# Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do
# authentication, caching, or auditing before the intended action is performed. Or to do localization or output
# compression after the action has been performed. Filters have access to the request, response, and all the instance
# variables set by other filters in the chain or by the action (in the case of after filters).
#
# == Filter inheritance
#
# Controller inheritance hierarchies share filters downwards, but subclasses can also add or skip filters without
# affecting the superclass. For example:
#
# class BankController < ActionController::Base
# before_filter :audit
#
# private
# def audit
# # record the action and parameters in an audit log
# end
# end
#
# class VaultController < BankController
# before_filter :verify_credentials
#
# private
# def verify_credentials
# # make sure the user is allowed into the vault
# end
# end
#
# Now any actions performed on the BankController will have the audit method called before. On the VaultController,
# first the audit method is called, then the verify_credentials method. If the audit method renders or redirects, then
# verify_credentials and the intended action are never called.
#
# == Filter types
#
# A filter can take one of three forms: method reference (symbol), external class, or inline method (proc). The first
# is the most common and works by referencing a protected or private method somewhere in the inheritance hierarchy of
# the controller by use of a symbol. In the bank example above, both BankController and VaultController use this form.
#
# Using an external class makes for more easily reused generic filters, such as output compression. External filter classes
# are implemented by having a static +filter+ method on any class and then passing this class to the filter method. Example:
#
# class OutputCompressionFilter
# def self.filter(controller)
# controller.response.body = compress(controller.response.body)
# end
# end
#
# class NewspaperController < ActionController::Base
# after_filter OutputCompressionFilter
# end
#
# The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can
# manipulate them as it sees fit.
#
# The inline method (using a proc) can be used to quickly do something small that doesn't require a lot of explanation.
# Or just as a quick test. It works like this:
#
# class WeblogController < ActionController::Base
# before_filter { |controller| head(400) if controller.params["stop_action"] }
# end
#
# As you can see, the block expects to be passed the controller after it has assigned the request to the internal variables.
# This means that the block has access to both the request and response objects complete with convenience methods for params,
# session, template, and assigns. Note: The inline method doesn't strictly have to be a block; any object that responds to call
# and returns 1 or -1 on arity will do (such as a Proc or an Method object).
#
# Please note that around_filters function a little differently than the normal before and after filters with regard to filter
# types. Please see the section dedicated to around_filters below.
#
# == Filter chain ordering
#
# Using <tt>before_filter</tt> and <tt>after_filter</tt> appends the specified filters to the existing chain. That's usually
# just fine, but some times you care more about the order in which the filters are executed. When that's the case, you
# can use <tt>prepend_before_filter</tt> and <tt>prepend_after_filter</tt>. Filters added by these methods will be put at the
# beginning of their respective chain and executed before the rest. For example:
#
# class ShoppingController < ActionController::Base
# before_filter :verify_open_shop
#
# class CheckoutController < ShoppingController
# prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock
#
# The filter chain for the CheckoutController is now <tt>:ensure_items_in_cart, :ensure_items_in_stock,</tt>
# <tt>:verify_open_shop</tt>. So if either of the ensure filters renders or redirects, we'll never get around to see if the shop
# is open or not.
#
# You may pass multiple filter arguments of each type as well as a filter block.
# If a block is given, it is treated as the last argument.
#
# == Around filters
#
# Around filters wrap an action, executing code both before and after.
# They may be declared as method references, blocks, or objects responding
# to +filter+ or to both +before+ and +after+.
#
# To use a method as an +around_filter+, pass a symbol naming the Ruby method.
# Yield (or <tt>block.call</tt>) within the method to run the action.
#
# around_filter :catch_exceptions
#
# private
# def catch_exceptions
# yield
# rescue => exception
# logger.debug "Caught exception! #{exception}"
# raise
# end
#
# To use a block as an +around_filter+, pass a block taking as args both
# the controller and the action block. You can't call yield directly from
# an +around_filter+ block; explicitly call the action block instead:
#
# around_filter do |controller, action|
# logger.debug "before #{controller.action_name}"
# action.call
# logger.debug "after #{controller.action_name}"
# end
#
# To use a filter object with +around_filter+, pass an object responding
# to <tt>:filter</tt> or both <tt>:before</tt> and <tt>:after</tt>. With a
# filter method, yield to the block as above:
#
# around_filter BenchmarkingFilter
#
# class BenchmarkingFilter
# def self.filter(controller, &block)
# Benchmark.measure(&block)
# end
# end
#
# With +before+ and +after+ methods:
#
# around_filter Authorizer.new
#
# class Authorizer
# # This will run before the action. Redirecting aborts the action.
# def before(controller)
# unless user.authorized?
# redirect_to(login_url)
# end
# end
#
# # This will run after the action if and only if before did not render or redirect.
# def after(controller)
# end
# end
#
# If the filter has +before+ and +after+ methods, the +before+ method will be
# called before the action. If +before+ renders or redirects, the filter chain is
# halted and +after+ will not be run. See Filter Chain Halting below for
# an example.
#
# == Filter chain skipping
#
# Declaring a filter on a base class conveniently applies to its subclasses,
# but sometimes a subclass should skip some of its superclass' filters:
#
# class ApplicationController < ActionController::Base
# before_filter :authenticate
# around_filter :catch_exceptions
# end
#
# class WeblogController < ApplicationController
# # Will run the :authenticate and :catch_exceptions filters.
# end
#
# class SignupController < ApplicationController
# # Skip :authenticate, run :catch_exceptions.
# skip_before_filter :authenticate
# end
#
# class ProjectsController < ApplicationController
# # Skip :catch_exceptions, run :authenticate.
# skip_filter :catch_exceptions
# end
#
# class ClientsController < ApplicationController
# # Skip :catch_exceptions and :authenticate unless action is index.
# skip_filter :catch_exceptions, :authenticate, :except => :index
# end
#
# == Filter conditions
#
# Filters may be limited to specific actions by declaring the actions to
# include or exclude. Both options accept single actions
# (<tt>:only => :index</tt>) or arrays of actions
# (<tt>:except => [:foo, :bar]</tt>).
#
# class Journal < ActionController::Base
# # Require authentication for edit and delete.
# before_filter :authorize, :only => [:edit, :delete]
#
# # Passing options to a filter with a block.
# around_filter(:except => :index) do |controller, action_block|
# results = Profiler.run(&action_block)
# controller.response.sub! "</body>", "#{results}</body>"
# end
#
# private
# def authorize
# # Redirect to login unless authenticated.
# end
# end
#
# == Filter Chain Halting
#
# <tt>before_filter</tt> and <tt>around_filter</tt> may halt the request
# 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 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
# after_filter :after
# around_filter :around
# before_filter :before
#
# The filter chain will look like:
#
# ...
# . \
# . #around (code before yield)
# . . \
# . . #before (actual filter code is run)
# . . . \
# . . . execute controller action
# . . . /
# . . ...
# . . /
# . #around (code after yield)
# . /
# #after (actual filter code is run, unless the around filter does not yield)
#
# If +around+ returns before yielding, +after+ will still not be run. The +before+
# filter and controller action will not be run. If +before+ renders or redirects,
# the second half of +around+ and will still run but +after+ and the
# action will not. If +around+ fails to 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.
def append_before_filter(*filters, &block)
filter_chain.append_filter_to_chain(filters, :before, &block)
end
# The passed <tt>filters</tt> will be prepended to the filter_chain and
# will execute before the action on this controller is performed.
def prepend_before_filter(*filters, &block)
filter_chain.prepend_filter_to_chain(filters, :before, &block)
end
# Shorthand for append_before_filter since it's the most common.
alias :before_filter :append_before_filter
# 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)
filter_chain.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)
filter_chain.prepend_filter_to_chain(filters, :after, &block)
end
# Shorthand for append_after_filter since it's the most common.
alias :after_filter :append_after_filter
# If you <tt>append_around_filter A.new, B.new</tt>, the filter chain looks like
#
# B#before
# A#before
# # run the action
# A#after
# B#after
#
# With around filters which yield to the action block, +before+ and +after+
# are the code before and after the yield.
def append_around_filter(*filters, &block)
filter_chain.append_filter_to_chain(filters, :around, &block)
end
# If you <tt>prepend_around_filter A.new, B.new</tt>, the filter chain looks like:
#
# A#before
# B#before
# # run the action
# B#after
# A#after
#
# With around filters which yield to the action block, +before+ and +after+
# are the code before and after the yield.
def prepend_around_filter(*filters, &block)
filter_chain.prepend_filter_to_chain(filters, :around, &block)
end
# Shorthand for +append_around_filter+ since it's the most common.
alias :around_filter :append_around_filter
# Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference
# filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
# of many sub-controllers need a different hierarchy.
#
# You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
# just like when you apply the filters.
def skip_before_filter(*filters)
filter_chain.skip_filter_in_chain(*filters, &:before?)
end
# Removes the specified filters from the +after+ filter chain. Note that this only works for skipping method-reference
# filters, not procs. This is especially useful for managing the chain in inheritance hierarchies where only one out
# of many sub-controllers need a different hierarchy.
#
# You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
# just like when you apply the filters.
def skip_after_filter(*filters)
filter_chain.skip_filter_in_chain(*filters, &:after?)
end
# Removes the specified filters from the filter chain. This only works for method reference (symbol)
# filters, not procs. This method is different from skip_after_filter and skip_before_filter in that
# it will match any before, after or yielding around filter.
#
# You can control the actions to skip the filter for with the <tt>:only</tt> and <tt>:except</tt> options,
# just like when you apply the filters.
def skip_filter(*filters)
filter_chain.skip_filter_in_chain(*filters)
end
# Returns an array of Filter objects for this controller.
def filter_chain
read_inheritable_attribute('filter_chain') || write_inheritable_attribute('filter_chain', FilterChain.new)
end
# Returns all the before filters for this class and all its ancestors.
# This method returns the actual filter that was assigned in the controller to maintain existing functionality.
def before_filters #:nodoc:
filter_chain.select(&:before?).map(&:method)
end
# Returns all the after filters for this class and all its ancestors.
# This method returns the actual filter that was assigned in the controller to maintain existing functionality.
def after_filters #:nodoc:
filter_chain.select(&:after?).map(&:method)
end
end
module InstanceMethods # :nodoc:
def self.included(base)
base.class_eval do
alias_method_chain :perform_action, :filters
alias_method_chain :process, :filters
end
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 perform_action_with_filters
call_filters(self.class.filter_chain, 0, 0)
end
private
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 run_before_filters(chain, index, nesting)
while chain[index]
filter, index = chain[index], index
break unless filter # end of call chain reached
case filter
when BeforeFilter
filter.call(self) # invoke before filter
index = index.next
break if @before_filter_chain_aborted
when AroundFilter
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
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 = chain[index], index
break unless filter # end of call chain reached
case filter
when AfterFilter
seen_after_filter = true
filter.call(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
end
end
end
end

@ -114,8 +114,17 @@ def method_for_action(action_name)
super || (respond_to?(:method_missing) && "_handle_method_missing")
end
def _layout_prefix(name)
super unless name =~ /\blayouts/
def _find_by_parts(name, details)
details[:prefix] = nil if name =~ /\blayouts/
super
end
# Move this into a "don't run in production" module
def _default_layout(details, require_layout = false)
super
rescue ActionView::MissingTemplate
_find_by_parts(_layout({}), {})
nil
end
def performed?

@ -0,0 +1,58 @@
module ActionController
class ActionControllerError < StandardError #:nodoc:
end
class SessionRestoreError < ActionControllerError #:nodoc:
end
class RenderError < ActionControllerError #:nodoc:
end
class RoutingError < ActionControllerError #:nodoc:
attr_reader :failures
def initialize(message, failures=[])
super(message)
@failures = failures
end
end
class MethodNotAllowed < ActionControllerError #:nodoc:
attr_reader :allowed_methods
def initialize(*allowed_methods)
super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
@allowed_methods = allowed_methods
end
def allowed_methods_header
allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', '
end
def handle_response!(response)
response.headers['Allow'] ||= allowed_methods_header
end
end
class NotImplemented < MethodNotAllowed #:nodoc:
end
class UnknownController < ActionControllerError #:nodoc:
end
class MissingFile < ActionControllerError #:nodoc:
end
class RenderError < ActionControllerError #:nodoc:
end
class SessionOverflowError < ActionControllerError #:nodoc:
DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
def initialize(message = nil)
super(message || DEFAULT_MESSAGE)
end
end
class UnknownHttpMethod < ActionControllerError #:nodoc:
end
end

@ -2,10 +2,7 @@ module ActionController
module FilterParameterLogging
extend ActiveSupport::Concern
# TODO : Remove the defined? check when new base is the main base
if defined?(ActionController::Http)
include AbstractController::Logger
end
include AbstractController::Logger
included do
include InstanceMethodsForNewBase
@ -46,6 +43,10 @@ def filter_parameter_logging(*filter_words, &block)
filtered_parameters[key] = '[FILTERED]'
elsif value.is_a?(Hash)
filtered_parameters[key] = filter_parameters(value)
elsif value.is_a?(Array)
filtered_parameters[key] = value.collect do |item|
filter_parameters(item)
end
elsif block_given?
key = key.dup
value = value.dup if value

@ -28,20 +28,7 @@ module ActionController #:nodoc:
module Flash
extend ActiveSupport::Concern
# TODO : Remove the defined? check when new base is the main base
include Session if defined?(ActionController::Http)
included do
# TODO : Remove the defined? check when new base is the main base
if defined?(ActionController::Http)
include InstanceMethodsForNewBase
else
include InstanceMethodsForBase
alias_method_chain :perform_action, :flash
alias_method_chain :reset_session, :flash
end
end
include Session
class FlashNow #:nodoc:
def initialize(flash)
@ -148,49 +135,30 @@ def use(key = nil, used = true)
end
end
module InstanceMethodsForBase #:nodoc:
protected
def perform_action_with_flash
perform_action_without_flash
if defined? @_flash
@_flash.store(session)
remove_instance_variable(:@_flash)
end
end
def reset_session_with_flash
reset_session_without_flash
remove_instance_variable(:@_flash) if defined?(@_flash)
end
end
module InstanceMethodsForNewBase #:nodoc:
protected
def process_action(method_name)
super
if defined? @_flash
@_flash.store(session)
remove_instance_variable(:@_flash)
end
end
def reset_session
super
remove_instance_variable(:@_flash) if defined?(@_flash)
end
end
protected
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash #:doc:
if !defined?(@_flash)
@_flash = session["flash"] || FlashHash.new
@_flash.sweep
end
@_flash
protected
def process_action(method_name)
super
if defined? @_flash
@_flash.store(session)
remove_instance_variable(:@_flash)
end
end
def reset_session
super
remove_instance_variable(:@_flash) if defined?(@_flash)
end
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash #:doc:
if !defined?(@_flash)
@_flash = session["flash"] || FlashHash.new
@_flash.sweep
end
@_flash
end
end
end

@ -1,85 +1,80 @@
require 'active_support/core_ext/load_error'
require 'active_support/core_ext/name_error'
require 'active_support/dependencies'
# FIXME: helper { ... } is broken on Ruby 1.9
module ActionController #:nodoc:
module Helpers #:nodoc:
module ActionController
# The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+,
# +numbers+ and model objects, to name a few. These helpers are available to all templates
# by default.
#
# In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
# extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will
# include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
# include <tt>MyHelper</tt>.
#
# Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
# controller which inherits from it.
#
# ==== Examples
# The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if
# the Time object is blank:
#
# module FormattedTimeHelper
# def format_time(time, format=:long, blank_message="&nbsp;")
# time.blank? ? blank_message : time.to_s(format)
# end
# end
#
# FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
#
# class EventsController < ActionController::Base
# helper FormattedTimeHelper
# def index
# @events = Event.find(:all)
# end
# end
#
# Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
#
# <% @events.each do |event| -%>
# <p>
# <% format_time(event.time, :short, "N/A") %> | <%= event.name %>
# </p>
# <% end -%>
#
# Finally, assuming we have two event instances, one which has a time and one which does not,
# the output might look like this:
#
# 23 Aug 11:30 | Carolina Railhawks Soccer Match
# N/A | Carolina Railhaws Training Workshop
#
module Helpers
extend ActiveSupport::Concern
include AbstractController::Helpers
included do
# Initialize the base module to aggregate its helpers.
class_inheritable_accessor :master_helper_module
self.master_helper_module = Module.new
# Set the default directory for helpers
class_inheritable_accessor :helpers_dir
self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
class << self
alias_method_chain :inherited, :helper
extlib_inheritable_accessor(:helpers_dir) do
defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers"
end
end
# The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+,
# +numbers+ and Active Record objects, to name a few. These helpers are available to all templates
# by default.
#
# In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
# extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will
# include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
# include <tt>MyHelper</tt>.
#
# Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
# controller which inherits from it.
#
# ==== Examples
# The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if
# the Time object is blank:
#
# module FormattedTimeHelper
# def format_time(time, format=:long, blank_message="&nbsp;")
# time.blank? ? blank_message : time.to_s(format)
# end
# end
#
# FormattedTimeHelper can now be included in a controller, using the +helper+ class method:
#
# class EventsController < ActionController::Base
# helper FormattedTimeHelper
# def index
# @events = Event.find(:all)
# end
# end
#
# Then, in any view rendered by <tt>EventController</tt>, the <tt>format_time</tt> method can be called:
#
# <% @events.each do |event| -%>
# <p>
# <% format_time(event.time, :short, "N/A") %> | <%= event.name %>
# </p>
# <% end -%>
#
# Finally, assuming we have two event instances, one which has a time and one which does not,
# the output might look like this:
#
# 23 Aug 11:30 | Carolina Railhawks Soccer Match
# N/A | Carolina Railhaws Training Workshop
#
module ClassMethods
# Makes all the (instance) methods in the helper module available to templates rendered through this controller.
# See ActionView::Helpers (link:classes/ActionView/Helpers.html) for more about making your own helper modules
# available to the templates.
def add_template_helper(helper_module) #:nodoc:
master_helper_module.module_eval { include helper_module }
def inherited(klass)
klass.class_eval { default_helper_module! unless name.blank? }
super
end
# The +helper+ class method can take a series of helper module names, a block, or both.
#
# * <tt>*args</tt>: One or more modules, strings or symbols, or the special symbol <tt>:all</tt>.
# * <tt>&block</tt>: A block defining helper methods.
#
# ==== Parameters
# *args<Array[Module, Symbol, String, :all]>
# block<Block>:: A block defining helper methods
#
# ==== Examples
# When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
# and include the module in the template class. The second form illustrates how to include custom helpers
# When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
# and include the module in the template class. The second form illustrates how to include custom helpers
# when working with namespaced controllers, or other cases where the file containing the helper definition is not
# in one of Rails' standard load paths:
# helper :foo # => requires 'foo_helper' and includes FooHelper
@ -92,78 +87,23 @@ def add_template_helper(helper_module) #:nodoc:
# <tt>ActionController::Base.helpers_dir</tt> (defaults to <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT).
# helper :all
#
# Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
# Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
# to the template.
# # One line
# helper { def hello() "Hello, world!" end }
# # Multi-line
# helper do
# def foo(bar)
# "#{bar} is the very best"
# def foo(bar)
# "#{bar} is the very best"
# end
# end
#
#
# Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
# +symbols+, +strings+, +modules+ and blocks.
# helper(:three, BlindHelper) { def mice() 'mice' end }
#
def helper(*args, &block)
args.flatten.each do |arg|
case arg
when Module
add_template_helper(arg)
when :all
helper(all_application_helpers)
when String, Symbol
file_name = arg.to_s.underscore + '_helper'
class_name = file_name.camelize
begin
require_dependency(file_name)
rescue LoadError => load_error
requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
if requiree == file_name
msg = "Missing helper file helpers/#{file_name}.rb"
raise LoadError.new(msg).copy_blame!(load_error)
else
raise
end
end
add_template_helper(class_name.constantize)
else
raise ArgumentError, "helper expects String, Symbol, or Module argument (was: #{args.inspect})"
end
end
# Evaluate block in template class if given.
master_helper_module.module_eval(&block) if block_given?
end
# Declare a controller method as a helper. For example, the following
# makes the +current_user+ controller method available to the view:
# class ApplicationController < ActionController::Base
# helper_method :current_user, :logged_in?
#
# def current_user
# @current_user ||= User.find_by_id(session[:user])
# end
#
# def logged_in?
# current_user != nil
# end
# end
#
# In a view:
# <% if logged_in? -%>Welcome, <%= current_user.name %><% end -%>
def helper_method(*methods)
methods.flatten.each do |method|
master_helper_module.module_eval <<-end_eval
def #{method}(*args, &block) # def current_user(*args, &block)
controller.send(%(#{method}), *args, &block) # controller.send(%(current_user), *args, &block)
end # end
end_eval
end
super(*_modules_for_helpers(args), &block)
end
# Declares helper accessors for controller attributes. For example, the
@ -171,51 +111,68 @@ def #{method}(*args, &block) # def current_user(*args, &block
# controller and makes them available to the view:
# helper_attr :name
# attr_accessor :name
#
# ==== Parameters
# *attrs<Array[String, Symbol]>:: Names of attributes to be converted
# into helpers.
def helper_attr(*attrs)
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
end
# Provides a proxy to access helpers methods from outside the view.
def helpers
unless @helper_proxy
@helper_proxy = ActionView::Base.new
@helper_proxy.extend master_helper_module
else
@helper_proxy
@helper_proxy ||= ActionView::Base.new.extend(_helpers)
end
private
# Returns a list of modules, normalized from the acceptable kinds of
# helpers with the following behavior:
# String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
# and "foo_bar_helper.rb" is loaded using require_dependency.
# :all:: Loads all modules in the #helpers_dir
# Module:: No further processing
#
# After loading the appropriate files, the corresponding modules
# are returned.
#
# ==== Parameters
# args<Array[String, Symbol, Module, all]>:: A list of helpers
#
# ==== Returns
# Array[Module]:: A normalized list of modules for the list of
# helpers provided.
def _modules_for_helpers(args)
args.flatten.map! do |arg|
case arg
when :all
_modules_for_helpers all_application_helpers
when String, Symbol
file_name = "#{arg.to_s.underscore}_helper"
require_dependency(file_name, "Missing helper file helpers/%s.rb")
file_name.camelize.constantize
when Module
arg
else
raise ArgumentError, "helper must be a String, Symbol, or Module"
end
end
end
private
def default_helper_module!
unless name.blank?
module_name = name.sub(/Controller$|$/, 'Helper')
module_path = module_name.split('::').map { |m| m.underscore }.join('/')
require_dependency module_path
helper module_name.constantize
end
rescue MissingSourceFile => e
raise unless e.is_missing? module_path
rescue NameError => e
raise unless e.missing_name? module_name
end
def default_helper_module!
module_name = name.sub(/Controller$/, '')
module_path = module_name.underscore
helper module_path
rescue MissingSourceFile => e
raise e unless e.is_missing? "#{module_path}_helper"
rescue NameError => e
raise e unless e.missing_name? "#{module_name}Helper"
end
def inherited_with_helper(child)
inherited_without_helper(child)
begin
child.master_helper_module = Module.new
child.master_helper_module.__send__ :include, master_helper_module
child.__send__ :default_helper_module!
rescue MissingSourceFile => e
raise unless e.is_missing?("helpers/#{child.controller_path}_helper")
end
end
# Extract helper names from files in app/helpers/**/*.rb
def all_application_helpers
extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/
Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' }
end
# Extract helper names from files in app/helpers/**/*.rb
def all_application_helpers
extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/
Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' }
end
end
end
end

@ -0,0 +1,35 @@
module ActionController
# ActionController::HideActions adds the ability to prevent public methods on a controller
# to be called as actions.
module HideActions
extend ActiveSupport::Concern
included do
extlib_inheritable_accessor(:hidden_actions) { Set.new }
end
private
# Overrides AbstractController::Base#action_method? to return false if the
# action name is in the list of hidden actions.
def action_method?(action_name)
!hidden_actions.include?(action_name) && super
end
module ClassMethods
# Sets all of the actions passed in as hidden actions.
#
# ==== Parameters
# *args<#to_s>:: A list of actions
def hide_action(*args)
hidden_actions.merge(args.map! {|a| a.to_s })
end
# Overrides AbstractController::Base#action_methods to remove any methods
# that are listed as hidden methods.
def action_methods
@action_methods ||= Set.new(super.reject {|name| hidden_actions.include?(name)})
end
end
end
end

@ -2,48 +2,48 @@
require 'active_support/core_ext/module/delegation'
module ActionController
# ActionController::Http provides a way to get a valid Rack application from a controller.
#
# In AbstractController, dispatching is triggered directly by calling #process on a new controller.
# ActionController::Http provides an #action method that returns a valid Rack application for a
# given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router,
# can dispatch directly to the action returned by FooController.action(:index).
class Http < AbstractController::Base
abstract!
# :api: public
attr_internal :params, :env
# :api: public
# Returns the last part of the controller's name, underscored, without the ending
# "Controller". For instance, MyApp::MyPostsController would return "my_posts" for
# controller_name
#
# ==== Returns
# String
def self.controller_name
@controller_name ||= controller_path.split("/").last
end
# :api: public
# Delegates to the class' #controller_name
def controller_name
self.class.controller_name
end
# :api: public
# Returns the full controller name, underscored, without the ending Controller.
# For instance, MyApp::MyPostsController would return "my_app/my_posts" for
# controller_name.
#
# ==== Returns
# String
def self.controller_path
@controller_path ||= self.name.sub(/Controller$/, '').underscore
@controller_path ||= name && name.sub(/Controller$/, '').underscore
end
# :api: public
# Delegates to the class' #controller_path
def controller_path
self.class.controller_path
end
# :api: private
def self.action_names
action_methods
end
# :api: private
def action_names
action_methods
end
# :api: plugin
def self.call(env)
controller = new
controller.call(env).to_rack
end
# The details below can be overridden to support a specific
# Request and Response object. The default ActionController::Base
# implementation includes RackConvenience, which makes a request
@ -57,7 +57,7 @@ def initialize(*)
super
end
# Basic implements for content_type=, location=, and headers are
# Basic implementations for content_type=, location=, and headers are
# provided to reduce the dependency on the RackConvenience module
# in Renderer and Redirector.
@ -81,6 +81,15 @@ def to_rack
[status, headers, response_body]
end
# Return a rack endpoint for the given action. Memoize the endpoint, so
# multiple calls into MyController.action will return the same object
# for the same action.
#
# ==== Parameters
# action<#to_s>:: An action name
#
# ==== Returns
# Proc:: A rack application
def self.action(name)
@actions ||= {}
@actions[name.to_s] ||= proc do |env|

@ -185,7 +185,7 @@ def authorization(request)
request.env['REDIRECT_X_HTTP_AUTHORIZATION']
end
# Raises error unless the request credentials response value matches the expected value.
# Returns false 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)
@ -194,6 +194,8 @@ def validate_digest_response(request, realm, &password_procedure)
if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque]
password = password_procedure.call(credentials[:username])
return false unless password
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
[true, false].any? do |password_is_ha1|

@ -0,0 +1,192 @@
module ActionController
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
# repeated setups. The inclusion pattern has pages that look like this:
#
# <%= render "shared/header" %>
# Hello World
# <%= render "shared/footer" %>
#
# This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
# and if you ever want to change the structure of these two includes, you'll have to change all the templates.
#
# With layouts, you can flip it around and have the common structure know where to insert changing content. This means
# that the header and footer are only mentioned in one place, like this:
#
# // The header part of this layout
# <%= yield %>
# // The footer part of this layout
#
# And then you have content pages that look like this:
#
# hello world
#
# At rendering time, the content page is computed and then inserted in the layout, like this:
#
# // The header part of this layout
# hello world
# // The footer part of this layout
#
# == Accessing shared variables
#
# Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
# references that won't materialize before rendering time:
#
# <h1><%= @page_title %></h1>
# <%= yield %>
#
# ...and content pages that fulfill these references _at_ rendering time:
#
# <% @page_title = "Welcome" %>
# Off-world colonies offers you a chance to start a new life
#
# The result after rendering is:
#
# <h1>Welcome</h1>
# Off-world colonies offers you a chance to start a new life
#
# == Layout assignment
#
# You can either specify a layout declaratively (using the #layout class method) or give
# it the same name as your controller, and place it in <tt>app/views/layouts</tt>.
# If a subclass does not have a layout specified, it inherits its layout using normal Ruby inheritance.
#
# For instance, if you have PostsController and a template named <tt>app/views/layouts/posts.html.erb</tt>,
# that template will be used for all actions in PostsController and controllers inheriting
# from PostsController.
#
# If you use a module, for instance Weblog::PostsController, you will need a template named
# <tt>app/views/layouts/weblog/posts.html.erb</tt>.
#
# Since all your controllers inherit from ApplicationController, they will use
# <tt>app/views/layouts/application.html.erb</tt> if no other layout is specified
# or provided.
#
# == Inheritance Examples
#
# class BankController < ActionController::Base
# layout "bank_standard"
#
# class InformationController < BankController
#
# class TellerController < BankController
# # teller.html.erb exists
#
# class TillController < TellerController
#
# class VaultController < BankController
# layout :access_level_layout
#
# class EmployeeController < BankController
# layout nil
#
# The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites
# and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all.
#
# The TellerController uses +teller.html.erb+, and TillController inherits that layout and
# uses it as well.
#
# == Types of layouts
#
# Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
# you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
# be done either by specifying a method reference as a symbol or using an inline method (as a proc).
#
# The method reference is the preferred approach to variable layouts and is used like this:
#
# class WeblogController < ActionController::Base
# layout :writers_and_readers
#
# def index
# # fetching posts
# end
#
# private
# def writers_and_readers
# logged_in? ? "writer_layout" : "reader_layout"
# end
#
# Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
# is logged in or not.
#
# If you want to use an inline method, such as a proc, do something like this:
#
# class WeblogController < ActionController::Base
# layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
#
# Of course, the most common way of specifying a layout is still just as a plain template name:
#
# class WeblogController < ActionController::Base
# layout "weblog_standard"
#
# If no directory is specified for the template name, the template will by default be looked for in <tt>app/views/layouts/</tt>.
# Otherwise, it will be looked up relative to the template root.
#
# == Conditional layouts
#
# If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
# a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
# <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
#
# class WeblogController < ActionController::Base
# layout "weblog_standard", :except => :rss
#
# # ...
#
# end
#
# This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout
# around the rendered view.
#
# Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
# #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
#
# == Using a different layout in the action render call
#
# If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
# Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
# You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
#
# class WeblogController < ActionController::Base
# layout "weblog_standard"
#
# def help
# render :action => "help", :layout => "help"
# end
# end
#
# This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout.
module Layouts
extend ActiveSupport::Concern
include ActionController::Renderer
include AbstractController::Layouts
module ClassMethods
# If no layout is provided, look for a layout with this name.
def _implied_layout_name
controller_path
end
end
private
def _determine_template(options)
super
return if (options.key?(:text) || options.key?(:inline) || options.key?(:partial)) && !options.key?(:layout)
layout = options.key?(:layout) ? options[:layout] : :default
options[:_layout] = _layout_for_option(layout, options[:_template].details)
end
def _layout_for_option(name, details)
case name
when String then _layout_for_name(name, details)
when true then _default_layout(details, true)
when :default then _default_layout(details, false)
when false, nil then nil
else
raise ArgumentError,
"String, true, or false, expected for `layout'; you passed #{name.inspect}"
end
end
end
end

@ -120,12 +120,9 @@ def custom(mime_type, &block)
@responses[mime_type] ||= Proc.new do
# TODO: Remove this when new base is merged in
if defined?(Http)
@controller.formats = [mime_type.to_sym]
end
@controller.formats = [mime_type.to_sym]
@controller.content_type = mime_type
@controller.template.formats = [mime_type.to_sym]
@response.content_type = mime_type.to_s
block_given? ? block.call : @controller.send(:render, :action => @controller.action_name)
end

@ -3,7 +3,7 @@ module RackConvenience
extend ActiveSupport::Concern
included do
delegate :headers, :status=, :location=,
delegate :headers, :status=, :location=, :content_type=,
:status, :location, :content_type, :to => "@_response"
attr_internal :request, :response
end

@ -1,89 +0,0 @@
module ActionController
class RedirectBackError < ActionControllerError #:nodoc:
DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
def initialize(message = nil)
super(message || DEFAULT_MESSAGE)
end
end
module Redirector
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
#
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
#
# Examples:
# redirect_to :action => "show", :id => 5
# redirect_to post
# redirect_to "http://www.rubyonrails.org"
# redirect_to "/images/screenshot.jpg"
# redirect_to articles_url
# redirect_to :back
#
# The redirection happens as a "302 Moved" header unless otherwise specified.
#
# Examples:
# redirect_to post_url(@post), :status=>:found
# redirect_to :action=>'atom', :status=>:moved_permanently
# redirect_to post_url(@post), :status=>301
# redirect_to :action=>'atom', :status=>302
#
# When using <tt>redirect_to :back</tt>, if there is no referrer,
# RedirectBackError will be raised. You may specify some fallback
# behavior for this case by rescuing RedirectBackError.
def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
if options.is_a?(Hash) && options[:status]
status = options.delete(:status)
elsif response_status[:status]
status = response_status[:status]
else
status = 302
end
case options
# The scheme name consist of a letter followed by any combination of
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
# characters; and is terminated by a colon (":").
when %r{^\w[\w\d+.-]*:.*}
redirect_to_full_url(options, status)
when String
redirect_to_full_url(request.protocol + request.host_with_port + options, status)
when :back
if referer = request.headers["Referer"]
redirect_to(referer, :status=>status)
else
raise RedirectBackError
end
else
redirect_to_full_url(url_for(options), status)
end
end
def redirect_to_full_url(url, status)
raise DoubleRenderError if performed?
logger.info("Redirected to #{url}") if logger && logger.info?
response.status = interpret_status(status)
response.location = url.gsub(/[\r\n]/, '')
response.body = "<html><body>You are being <a href=\"#{CGI.escapeHTML(url)}\">redirected</a>.</body></html>"
@performed_redirect = true
end
# Clears the redirected results from the headers, resets the status to 200 and returns
# the URL that was used to redirect or nil if there was no redirected URL
# Note that +redirect_to+ will change the body of the response to indicate a redirection.
# The response body is not reset here, see +erase_render_results+
def erase_redirect_results #:nodoc:
@performed_redirect = false
response.status = DEFAULT_RENDER_STATUS_CODE
response.headers.delete('Location')
end
end
end

@ -1,403 +0,0 @@
require 'action_controller/abstract/renderer'
module ActionController
DEFAULT_RENDER_STATUS_CODE = "200 OK"
class DoubleRenderError < ActionControllerError #:nodoc:
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
def initialize(message = nil)
super(message || DEFAULT_MESSAGE)
end
end
module Renderer
protected
# Renders the content that will be returned to the browser as the response body.
#
# === Rendering an action
#
# Action rendering is the most common form and the type used automatically by Action Controller when nothing else is
# specified. By default, actions are rendered within the current layout (if one exists).
#
# # Renders the template for the action "goal" within the current controller
# render :action => "goal"
#
# # Renders the template for the action "short_goal" within the current controller,
# # but without the current active layout
# render :action => "short_goal", :layout => false
#
# # Renders the template for the action "long_goal" within the current controller,
# # but with a custom layout
# render :action => "long_goal", :layout => "spectacular"
#
# === Rendering partials
#
# Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page
# without reloading. Rendering of partials from the controller makes it possible to use the same partial template in
# both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the
# controller action responding to Ajax calls). By default, the current layout is not used.
#
# # Renders the same partial with a local variable.
# render :partial => "person", :locals => { :name => "david" }
#
# # Renders the partial, making @new_person available through
# # the local variable 'person'
# render :partial => "person", :object => @new_person
#
# # Renders a collection of the same partial by making each element
# # of @winners available through the local variable "person" as it
# # builds the complete response.
# render :partial => "person", :collection => @winners
#
# # Renders a collection of partials but with a custom local variable name
# render :partial => "admin_person", :collection => @winners, :as => :person
#
# # Renders the same collection of partials, but also renders the
# # person_divider partial between each person partial.
# render :partial => "person", :collection => @winners, :spacer_template => "person_divider"
#
# # Renders a collection of partials located in a view subfolder
# # outside of our current controller. In this example we will be
# # rendering app/views/shared/_note.r(html|xml) Inside the partial
# # each element of @new_notes is available as the local var "note".
# render :partial => "shared/note", :collection => @new_notes
#
# # Renders the partial with a status code of 500 (internal error).
# render :partial => "broken", :status => 500
#
# Note that the partial filename must also be a valid Ruby variable name,
# so e.g. 2005 and register-user are invalid.
#
#
# == 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. No etag header will be inserted if it's already set.
#
# === Rendering a template
#
# Template rendering works just like action rendering except that it takes a path relative to the template root.
# The current layout is automatically applied.
#
# # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
# render :template => "weblog/show"
#
# # Renders the template with a local variable
# render :template => "weblog/show", :locals => {:customer => Customer.new}
#
# === Rendering a file
#
# File rendering works just like action rendering except that it takes a filesystem path. By default, the path
# is assumed to be absolute, and the current layout is not applied.
#
# # Renders the template located at the absolute filesystem path
# render :file => "/path/to/some/template.erb"
# render :file => "c:/path/to/some/template.erb"
#
# # Renders a template within the current layout, and with a 404 status code
# render :file => "/path/to/some/template.erb", :layout => true, :status => 404
# render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404
#
# === Rendering text
#
# Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
# rendering is not done within the active layout.
#
# # Renders the clear text "hello world" with status code 200
# render :text => "hello world!"
#
# # Renders the clear text "Explosion!" with status code 500
# render :text => "Explosion!", :status => 500
#
# # Renders the clear text "Hi there!" within the current active layout (if one exists)
# render :text => "Hi there!", :layout => true
#
# # Renders the clear text "Hi there!" within the layout
# # placed in "app/views/layouts/special.r(html|xml)"
# render :text => "Hi there!", :layout => "special"
#
# The <tt>:text</tt> option can also accept a Proc object, which can be used to manually control the page generation. This should
# generally be avoided, as it violates the separation between code and content, and because almost everything that can be
# done with this method can also be done more cleanly using one of the other rendering methods, most notably templates.
#
# # Renders "Hello from code!"
# render :text => proc { |response, output| output.write("Hello from code!") }
#
# === Rendering XML
#
# Rendering XML sets the content type to application/xml.
#
# # Renders '<name>David</name>'
# render :xml => {:name => "David"}.to_xml
#
# It's not necessary to call <tt>to_xml</tt> on the object you want to render, since <tt>render</tt> will
# automatically do that for you:
#
# # Also renders '<name>David</name>'
# render :xml => {:name => "David"}
#
# === Rendering JSON
#
# Rendering JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected
# that the response will be parsed (or eval'd) for use as a data structure.
#
# # Renders '{"name": "David"}'
# render :json => {:name => "David"}.to_json
#
# It's not necessary to call <tt>to_json</tt> on the object you want to render, since <tt>render</tt> will
# automatically do that for you:
#
# # Also renders '{"name": "David"}'
# render :json => {:name => "David"}
#
# Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag),
# so the <tt>:callback</tt> option is provided for these cases.
#
# # Renders 'show({"name": "David"})'
# render :json => {:name => "David"}.to_json, :callback => 'show'
#
# === Rendering an inline template
#
# Rendering of an inline template works as a cross between text and action rendering where the source for the template
# is supplied inline, like text, but its interpreted with ERb or Builder, like action. By default, ERb is used for rendering
# and the current layout is not used.
#
# # Renders "hello, hello, hello, again"
# render :inline => "<%= 'hello, ' * 3 + 'again' %>"
#
# # Renders "<p>Good seeing you!</p>" using Builder
# render :inline => "xml.p { 'Good seeing you!' }", :type => :builder
#
# # Renders "hello david"
# render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" }
#
# === Rendering inline JavaScriptGenerator page updates
#
# In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details),
# you can also pass the <tt>:update</tt> parameter to +render+, along with a block, to render page updates inline.
#
# render :update do |page|
# page.replace_html 'user_list', :partial => 'user', :collection => @users
# page.visual_effect :highlight, 'user_list'
# end
#
# === Rendering vanilla JavaScript
#
# In addition to using RJS with render :update, you can also just render vanilla JavaScript with :js.
#
# # Renders "alert('hello')" and sets the mime type to text/javascript
# render :js => "alert('hello')"
#
# === Rendering with status and location headers
# All renders take the <tt>:status</tt> and <tt>:location</tt> options and turn them into headers. They can even be used together:
#
# render :xml => post.to_xml, :status => :created, :location => post_url(post)
def render(options = nil, extra_options = {}, &block) #:doc:
raise DoubleRenderError, "Can only render or redirect once per action" if performed?
options = { :layout => true } if options.nil?
# This handles render "string", render :symbol, and render object
# render string and symbol are handled by render_for_name
# render object becomes render :partial => object
unless options.is_a?(Hash)
if options.is_a?(String) || options.is_a?(Symbol)
original, options = options, extra_options
else
extra_options[:partial], options = options, extra_options
end
end
layout_name = options.delete(:layout)
_process_options(options)
if block_given?
@template.send(:_evaluate_assigns_and_ivars)
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
response.content_type = Mime::JS
return render_for_text(generator.to_s)
end
if original
return render_for_name(original, layout_name, options) unless block_given?
end
if options.key?(:text)
return render_for_text(@template._render_text(options[:text],
_pick_layout(layout_name), options))
end
file, template = options.values_at(:file, :template)
if file || template
file = template.sub(/^\//, '') if template
return render_for_file(file, [layout_name, !!template], options)
end
if action_option = options[:action]
return render_for_action(action_option, [layout_name, true], options)
end
if inline = options[:inline]
render_for_text(@template._render_inline(inline, _pick_layout(layout_name), options))
elsif xml = options[:xml]
response.content_type ||= Mime::XML
render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml)
elsif js = options[:js]
response.content_type ||= Mime::JS
render_for_text(js)
elsif options.include?(:json)
json = options[:json]
json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
response.content_type ||= Mime::JSON
render_for_text(json)
elsif partial = options[:partial]
if partial == true
parts = [action_name_base, formats, controller_name, true]
elsif partial.is_a?(String)
parts = partial_parts(partial, options)
else
return render_for_text(@template._render_partial(options))
end
render_for_parts(parts, layout_name, options)
elsif options[:nothing]
render_for_text(nil)
else
render_for_parts([action_name, formats, controller_path], layout_name, options)
end
end
def partial_parts(name, options)
segments = name.split("/")
parts = segments.pop.split(".")
case parts.size
when 1
parts
when 2, 3
extension = parts.delete_at(1).to_sym
if formats.include?(extension)
self.formats.replace [extension]
end
parts.pop if parts.size == 2
end
path = parts.join(".")
prefix = segments[0..-1].join("/")
prefix = prefix.blank? ? controller_path : prefix
parts = [path, formats, prefix]
parts.push options[:object] || true
end
def formats
@_request.formats.map {|f| f.symbol }.compact
end
def action_name_base(name = action_name)
(name.is_a?(String) ? name.sub(/^#{controller_path}\//, '') : name).to_s
end
# Same rules as <tt>render</tt>, but returns a Rack-compatible body
# instead of sending the response.
def render_to_body(options = nil, &block) #:doc:
render(options, &block)
response.body
ensure
response.content_type = nil
erase_render_results
reset_variables_added_to_assigns
end
def render_to_string(options = {})
AbstractController::Renderer.body_to_s(render_to_body(options))
end
# Clears the rendered results, allowing for another render to be performed.
def erase_render_results #:nodoc:
response.body = []
@performed_render = false
end
# Erase both render and redirect results
def erase_results #:nodoc:
erase_render_results
erase_redirect_results
end
# Return a response that has no content (merely headers). The options
# argument is interpreted to be a hash of header names and values.
# This allows you to easily return a response that consists only of
# significant headers:
#
# head :created, :location => person_path(@person)
#
# It can also be used to return exceptional conditions:
#
# return head(:method_not_allowed) unless request.post?
# return head(:bad_request) unless valid_request?
# render
def head(*args)
if args.length > 2
raise ArgumentError, "too many arguments to head"
elsif args.empty?
raise ArgumentError, "too few arguments to head"
end
options = args.extract_options!
status = interpret_status(args.shift || options.delete(:status) || :ok)
options.each do |key, value|
headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
end
render :nothing => true, :status => status
end
private
def render_for_name(name, layout, options)
case name.to_s.index('/')
when 0
render_for_file(name, layout, options)
when nil
render_for_action(name, layout, options)
else
render_for_file(name.sub(/^\//, ''), [layout, true], options)
end
end
# ==== Arguments
# parts<Array[String, Array[Symbol*], String, Boolean]>::
# Example: ["show", [:html, :xml], "users", false]
def render_for_parts(parts, layout_details, options = {})
parts[1] = {:formats => parts[1], :locales => [I18n.locale]}
tmp = view_paths.find_by_parts(*parts)
layout = _pick_layout(*layout_details) unless
self.class.exempt_from_layout.include?(tmp.handler)
render_for_text(
@template._render_template_with_layout(tmp, layout, options, parts[3]))
end
def render_for_file(file, layout, options)
render_for_parts([file, [request.format.to_sym]], layout, options)
end
def render_for_action(name, layout, options)
parts = [action_name_base(name), formats, controller_name]
render_for_parts(parts, layout, options)
end
end
end

@ -6,20 +6,16 @@ module RequestForgeryProtection
extend ActiveSupport::Concern
# TODO : Remove the defined? check when new base is the main base
if defined?(ActionController::Http)
include AbstractController::Helpers, Session
end
include AbstractController::Helpers, Session
included do
if defined?(ActionController::Http)
# Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
# sets it to <tt>:authenticity_token</tt> by default.
cattr_accessor :request_forgery_protection_token
# Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
# sets it to <tt>:authenticity_token</tt> by default.
cattr_accessor :request_forgery_protection_token
# Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
class_inheritable_accessor :allow_forgery_protection
self.allow_forgery_protection = true
end
# Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
class_inheritable_accessor :allow_forgery_protection
self.allow_forgery_protection = true
helper_method :form_authenticity_token
helper_method :protect_against_forgery?

@ -1,50 +0,0 @@
module ActionController #:nodoc:
# Actions that fail to perform as expected throw exceptions. These
# exceptions can either be rescued for the public view (with a nice
# user-friendly explanation) or for the developers view (with tons of
# debugging information). The developers view is already implemented by
# the Action Controller, but the public view should be tailored to your
# specific application.
#
# The default behavior for public exceptions is to render a static html
# file with the name of the error code thrown. If no such file exists, an
# empty response is sent with the correct status code.
#
# You can override what constitutes a local request by overriding the
# <tt>local_request?</tt> method in your own controller. Custom rescue
# behavior is achieved by overriding the <tt>rescue_action_in_public</tt>
# and <tt>rescue_action_locally</tt> methods.
module Rescue
def self.included(base) #:nodoc:
base.send :include, ActiveSupport::Rescuable
base.extend(ClassMethods)
base.class_eval do
alias_method_chain :perform_action, :rescue
end
end
module ClassMethods
def rescue_action(env)
exception = env.delete('action_dispatch.rescue.exception')
request = ActionDispatch::Request.new(env)
response = ActionDispatch::Response.new
new.process(request, response, :rescue_action, exception).to_a
end
end
protected
# Exception handler called when the performance of an action raises
# an exception.
def rescue_action(exception)
rescue_with_handler(exception) || raise(exception)
end
private
def perform_action_with_rescue
perform_action_without_rescue
rescue Exception => exception
rescue_action(exception)
end
end
end

@ -1,43 +0,0 @@
module ActionController
module Responder
def self.included(klass)
klass.extend ClassMethods
end
private
def render_for_text(text) #:nodoc:
@performed_render = true
case text
when Proc
response.body = text
when nil
# Safari 2 doesn't pass response headers if the response is zero-length
if response.body_parts.empty?
response.body_parts << ' '
end
else
response.body_parts << text
end
end
# Returns a set of the methods defined as actions in your controller
def action_methods
self.class.action_methods
end
module ClassMethods
def action_methods
@action_methods ||=
# All public instance methods of this class, including ancestors
public_instance_methods(true).map { |m| m.to_s }.to_set -
# Except for public instance methods of Base and its ancestors
Base.public_instance_methods(true).map { |m| m.to_s } +
# Be sure to include shadowed public instance methods of this class
public_instance_methods(false).map { |m| m.to_s } -
# And always exclude explicitly hidden actions
hidden_actions
end
end
end
end

@ -4,10 +4,7 @@ module ActionController #:nodoc:
module Streaming
extend ActiveSupport::Concern
# TODO : Remove the defined? check when new base is the main base
if defined?(ActionController::Http)
include ActionController::Renderer
end
include ActionController::Renderer
DEFAULT_SEND_FILE_OPTIONS = {
:type => 'application/octet-stream'.freeze,
@ -162,15 +159,16 @@ def send_file_headers!(options)
disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
content_type = options[:type]
if content_type.is_a?(Symbol)
raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.has_key?(content_type.to_s)
content_type = Mime::Type.lookup_by_extension(content_type.to_s)
raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.key?(content_type.to_s)
self.content_type = Mime::Type.lookup_by_extension(content_type.to_s)
else
self.content_type = content_type
end
content_type = content_type.to_s.strip # fixes a problem with extra '\r' with some browsers
headers.merge!(
'Content-Length' => options[:length],
'Content-Type' => content_type,
'Content-Length' => options[:length].to_s,
'Content-Disposition' => disposition,
'Content-Transfer-Encoding' => 'binary'
)

@ -2,10 +2,7 @@ module ActionController #:nodoc:
module Verification #:nodoc:
extend ActiveSupport::Concern
# TODO : Remove the defined? check when new base is the main base
if defined?(ActionController::Http)
include AbstractController::Callbacks, Session, Flash, Renderer
end
include AbstractController::Callbacks, Session, Flash, Renderer
# This module provides a class-level method for specifying that certain
# actions are guarded against being called without certain prerequisites

@ -62,14 +62,7 @@ def caches_action(*actions)
cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path), :store_options => options)
# TODO: Remove this once new base is swapped in.
if defined?(ActionController::Http)
around_filter cache_filter, filter_options
else
around_filter(filter_options) do |controller, action|
cache_filter.filter(controller, action)
end
end
around_filter cache_filter, filter_options
end
end
@ -91,19 +84,10 @@ def initialize(options, &block)
@options = options
end
# TODO: Remove once New Base is merged
if defined?(ActionController::Http)
def filter(controller)
should_continue = before(controller)
yield if should_continue
after(controller)
end
else
def filter(controller, action)
should_continue = before(controller)
action.call if should_continue
after(controller)
end
def filter(controller)
should_continue = before(controller)
yield if should_continue
after(controller)
end
def before(controller)

@ -1,8 +1,16 @@
require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/class'
require 'active_support/core_ext/class/delegating_attributes'
require 'active_support/core_ext/class/inheritable_attributes'
module ActionController #:nodoc:
# MegasuperultraHAX
# plz refactor ActionMailer
class Base
@@exempt_from_layout = [ActionView::TemplateHandlers::RJS]
cattr_accessor :exempt_from_layout
end
module Layout #:nodoc:
def self.included(base)
base.extend(ClassMethods)
@ -36,9 +44,6 @@ def self.included(base)
# hello world
# // The footer part of this layout
#
# NOTE: The old notation for rendering the view from a layout was to expose the magic <tt>@content_for_layout</tt> instance
# variable. The preferred notation now is to use <tt>yield</tt>, as documented above.
#
# == Accessing shared variables
#
# Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with

@ -1,47 +0,0 @@
module ActionController
autoload :Base, "action_controller/new_base/base"
autoload :ConditionalGet, "action_controller/new_base/conditional_get"
autoload :HideActions, "action_controller/new_base/hide_actions"
autoload :Http, "action_controller/new_base/http"
autoload :Layouts, "action_controller/new_base/layouts"
autoload :RackConvenience, "action_controller/new_base/rack_convenience"
autoload :Rails2Compatibility, "action_controller/new_base/compatibility"
autoload :Redirector, "action_controller/new_base/redirector"
autoload :Renderer, "action_controller/new_base/renderer"
autoload :RenderOptions, "action_controller/new_base/render_options"
autoload :Renderers, "action_controller/new_base/render_options"
autoload :Rescue, "action_controller/new_base/rescuable"
autoload :Testing, "action_controller/new_base/testing"
autoload :UrlFor, "action_controller/new_base/url_for"
autoload :Session, "action_controller/new_base/session"
autoload :Helpers, "action_controller/new_base/helpers"
# Ported modules
# require 'action_controller/routing'
autoload :Caching, 'action_controller/caching'
autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
autoload :MimeResponds, 'action_controller/base/mime_responds'
autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes'
autoload :RecordIdentifier, 'action_controller/record_identifier'
autoload :Resources, 'action_controller/routing/resources'
autoload :SessionManagement, 'action_controller/base/session_management'
autoload :TestCase, 'action_controller/testing/test_case'
autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter'
autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter'
autoload :Verification, 'action_controller/base/verification'
autoload :Flash, 'action_controller/base/chained/flash'
autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection'
autoload :Streaming, 'action_controller/base/streaming'
autoload :HttpAuthentication, 'action_controller/base/http_authentication'
autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging'
autoload :Translation, 'action_controller/translation'
autoload :Cookies, 'action_controller/base/cookies'
require 'action_controller/routing'
end
autoload :HTML, 'action_controller/vendor/html-scanner'
require 'action_dispatch'
require 'action_view'

@ -1,173 +0,0 @@
module ActionController
class Base < Http
abstract!
include AbstractController::Benchmarker
include AbstractController::Callbacks
include AbstractController::Logger
include ActionController::Helpers
include ActionController::HideActions
include ActionController::UrlFor
include ActionController::Redirector
include ActionController::Renderer
include ActionController::Renderers::All
include ActionController::Layouts
include ActionController::ConditionalGet
include ActionController::RackConvenience
# Legacy modules
include SessionManagement
include ActionDispatch::StatusCodes
include ActionController::Caching
include ActionController::MimeResponds
# Rails 2.x compatibility
include ActionController::Rails2Compatibility
include ActionController::Cookies
include ActionController::Session
include ActionController::Flash
include ActionController::Verification
include ActionController::RequestForgeryProtection
include ActionController::Streaming
include ActionController::HttpAuthentication::Basic::ControllerMethods
include ActionController::HttpAuthentication::Digest::ControllerMethods
include ActionController::FilterParameterLogging
include ActionController::Translation
# TODO: Extract into its own module
# This should be moved together with other normalizing behavior
module ImplicitRender
def send_action(method_name)
ret = super
default_render unless performed?
ret
end
def default_render
render
end
def method_for_action(action_name)
super || begin
if view_paths.find_by_parts?(action_name.to_s, {:formats => formats, :locales => [I18n.locale]}, controller_path)
"default_render"
end
end
end
end
include ImplicitRender
include ActionController::Rescue
def self.inherited(klass)
::ActionController::Base.subclasses << klass.to_s
super
end
def self.subclasses
@subclasses ||= []
end
def self.app_loaded!
@subclasses.each do |subclass|
subclass.constantize._write_layout_method
end
end
def _normalize_options(action = nil, options = {}, &blk)
if action.is_a?(Hash)
options, action = action, nil
elsif action.is_a?(String) || action.is_a?(Symbol)
key = case action = action.to_s
when %r{^/} then :file
when %r{/} then :template
else :action
end
options.merge! key => action
elsif action
options.merge! :partial => action
end
if options.key?(:action) && options[:action].to_s.index("/")
options[:template] = options.delete(:action)
end
if options[:status]
options[:status] = interpret_status(options[:status]).to_i
end
options[:update] = blk if block_given?
options
end
def render(action = nil, options = {}, &blk)
options = _normalize_options(action, options, &blk)
super(options)
end
def render_to_string(action = nil, options = {}, &blk)
options = _normalize_options(action, options, &blk)
super(options)
end
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
#
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
#
# Examples:
# redirect_to :action => "show", :id => 5
# redirect_to post
# redirect_to "http://www.rubyonrails.org"
# redirect_to "/images/screenshot.jpg"
# redirect_to articles_url
# redirect_to :back
#
# The redirection happens as a "302 Moved" header unless otherwise specified.
#
# Examples:
# redirect_to post_url(@post), :status=>:found
# redirect_to :action=>'atom', :status=>:moved_permanently
# redirect_to post_url(@post), :status=>301
# redirect_to :action=>'atom', :status=>302
#
# When using <tt>redirect_to :back</tt>, if there is no referrer,
# RedirectBackError will be raised. You may specify some fallback
# behavior for this case by rescuing RedirectBackError.
def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
status = if options.is_a?(Hash) && options.key?(:status)
interpret_status(options.delete(:status))
elsif response_status.key?(:status)
interpret_status(response_status[:status])
else
302
end
url = case options
# The scheme name consist of a letter followed by any combination of
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
# characters; and is terminated by a colon (":").
when %r{^\w[\w\d+.-]*:.*}
options
when String
request.protocol + request.host_with_port + options
when :back
raise RedirectBackError unless refer = request.headers["Referer"]
refer
else
url_for(options)
end
super(url, status)
end
end
end

@ -1,129 +0,0 @@
require 'active_support/core_ext/load_error'
require 'active_support/core_ext/name_error'
require 'active_support/dependencies'
module ActionController
module Helpers
extend ActiveSupport::Concern
include AbstractController::Helpers
included do
# Set the default directory for helpers
class_inheritable_accessor :helpers_dir
self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
end
module ClassMethods
def inherited(klass)
klass.__send__ :default_helper_module!
super
end
# The +helper+ class method can take a series of helper module names, a block, or both.
#
# * <tt>*args</tt>: One or more modules, strings or symbols, or the special symbol <tt>:all</tt>.
# * <tt>&block</tt>: A block defining helper methods.
#
# ==== Examples
# When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
# and include the module in the template class. The second form illustrates how to include custom helpers
# when working with namespaced controllers, or other cases where the file containing the helper definition is not
# in one of Rails' standard load paths:
# helper :foo # => requires 'foo_helper' and includes FooHelper
# helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
#
# When the argument is a module it will be included directly in the template class.
# helper FooHelper # => includes FooHelper
#
# When the argument is the symbol <tt>:all</tt>, the controller will include all helpers beneath
# <tt>ActionController::Base.helpers_dir</tt> (defaults to <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT).
# helper :all
#
# Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
# to the template.
# # One line
# helper { def hello() "Hello, world!" end }
# # Multi-line
# helper do
# def foo(bar)
# "#{bar} is the very best"
# end
# end
#
# Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
# +symbols+, +strings+, +modules+ and blocks.
# helper(:three, BlindHelper) { def mice() 'mice' end }
#
def helper(*args, &block)
args.flatten.each do |arg|
case arg
when :all
helper all_application_helpers
when String, Symbol
file_name = arg.to_s.underscore + '_helper'
class_name = file_name.camelize
begin
require_dependency(file_name)
rescue LoadError => load_error
requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
if requiree == file_name
msg = "Missing helper file helpers/#{file_name}.rb"
raise LoadError.new(msg).copy_blame!(load_error)
else
raise
end
end
super class_name.constantize
else
super args
end
end
# Evaluate block in template class if given.
master_helper_module.module_eval(&block) if block_given?
end
# Declares helper accessors for controller attributes. For example, the
# following adds new +name+ and <tt>name=</tt> instance methods to a
# controller and makes them available to the view:
# helper_attr :name
# attr_accessor :name
def helper_attr(*attrs)
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
end
# Provides a proxy to access helpers methods from outside the view.
def helpers
unless @helper_proxy
@helper_proxy = ActionView::Base.new
@helper_proxy.extend master_helper_module
else
@helper_proxy
end
end
private
def default_helper_module!
unless name.blank?
module_name = name.sub(/Controller$|$/, 'Helper')
module_path = module_name.split('::').map { |m| m.underscore }.join('/')
require_dependency module_path
helper module_name.constantize
end
rescue MissingSourceFile => e
raise unless e.is_missing? module_path
rescue NameError => e
raise unless e.missing_name? module_name
end
# Extract helper names from files in app/helpers/**/*.rb
def all_application_helpers
extract = /^#{Regexp.quote(helpers_dir)}\/?(.*)_helper.rb$/
Dir["#{helpers_dir}/**/*_helper.rb"].map { |file| file.sub extract, '\1' }
end
end
end
end

@ -1,39 +0,0 @@
module ActionController
module HideActions
extend ActiveSupport::Concern
included do
extlib_inheritable_accessor :hidden_actions
self.hidden_actions ||= Set.new
end
def action_methods
self.class.action_names
end
def action_names
action_methods
end
private
def action_method?(action_name)
!hidden_actions.include?(action_name) && super
end
module ClassMethods
def hide_action(*args)
args.each do |arg|
self.hidden_actions << arg.to_s
end
end
def action_methods
@action_names ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)})
end
def self.action_names
action_methods
end
end
end
end

@ -1,34 +0,0 @@
module ActionController
module Layouts
extend ActiveSupport::Concern
include ActionController::Renderer
include AbstractController::Layouts
module ClassMethods
def _implied_layout_name
controller_path
end
end
private
def _determine_template(options)
super
if (!options.key?(:text) && !options.key?(:inline) && !options.key?(:partial)) || options.key?(:layout)
options[:_layout] = _layout_for_option(options.key?(:layout) ? options[:layout] : :none, options[:_template].details)
end
end
def _layout_for_option(name, details)
case name
when String then _layout_for_name(name, details)
when true then _default_layout(true, details)
when :none then _default_layout(false, details)
when false, nil then nil
else
raise ArgumentError,
"String, true, or false, expected for `layout'; you passed #{name.inspect}"
end
end
end
end

@ -0,0 +1,84 @@
#--
# Copyright (c) 2004-2009 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
$:.unshift activesupport_path if File.directory?(activesupport_path)
require 'active_support'
require File.join(File.dirname(__FILE__), "action_pack")
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.
def self.load_all!
[Base, Request, Response, UrlRewriter, UrlWriter]
[ActionDispatch::Http::Headers]
end
autoload :Base, 'action_controller/base/base'
autoload :Benchmarking, 'action_controller/base/chained/benchmarking'
autoload :Caching, 'action_controller/caching'
autoload :Cookies, 'action_controller/base/cookies'
autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
autoload :Filters, 'action_controller/base/chained/filters'
autoload :Flash, 'action_controller/base/chained/flash'
autoload :Helpers, 'action_controller/base/helpers'
autoload :HttpAuthentication, 'action_controller/base/http_authentication'
autoload :Integration, 'action_controller/testing/integration'
autoload :IntegrationTest, 'action_controller/testing/integration'
autoload :Layout, 'action_controller/base/layout'
autoload :MimeResponds, 'action_controller/base/mime_responds'
autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes'
autoload :RecordIdentifier, 'action_controller/record_identifier'
autoload :Redirector, 'action_controller/base/redirect'
autoload :Renderer, 'action_controller/base/render'
autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection'
autoload :Rescue, 'action_controller/base/rescue'
autoload :Resources, 'action_controller/routing/resources'
autoload :Responder, 'action_controller/base/responder'
autoload :Routing, 'action_controller/routing'
autoload :SessionManagement, 'action_controller/base/session_management'
autoload :Streaming, 'action_controller/base/streaming'
autoload :TestCase, 'action_controller/testing/test_case'
autoload :TestProcess, 'action_controller/testing/process'
autoload :Translation, 'action_controller/translation'
autoload :UrlEncodedPairParser, 'action_controller/dispatch/url_encoded_pair_parser'
autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter'
autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter'
autoload :Verification, 'action_controller/base/verification'
autoload :FilterParameterLogging, 'action_controller/base/filter_parameter_logging'
module Assertions
autoload :DomAssertions, 'action_controller/testing/assertions/dom'
autoload :ModelAssertions, 'action_controller/testing/assertions/model'
autoload :ResponseAssertions, 'action_controller/testing/assertions/response'
autoload :RoutingAssertions, 'action_controller/testing/assertions/routing'
autoload :SelectorAssertions, 'action_controller/testing/assertions/selector'
autoload :TagAssertions, 'action_controller/testing/assertions/tag'
end
end
autoload :HTML, 'action_controller/vendor/html-scanner'
require 'action_dispatch'
require 'action_view'

@ -112,8 +112,7 @@ def polymorphic_url(record_or_hash_or_array, options = {})
# Returns the path component of a URL for the given record. It uses
# <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
def polymorphic_path(record_or_hash_or_array, options = {})
options[:routing_type] = :path
polymorphic_url(record_or_hash_or_array, options)
polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path))
end
%w(edit new).each do |action|

@ -150,9 +150,9 @@ def set_allowed_actions
end
if only
@allowed_actions[:only] = Array(only).map(&:to_sym)
@allowed_actions[:only] = Array(only).map {|a| a.to_sym }
elsif except
@allowed_actions[:except] = Array(except).map(&:to_sym)
@allowed_actions[:except] = Array(except).map {|a| a.to_sym }
end
end

@ -305,6 +305,7 @@ def routes_changed_at
end
def add_route(path, options = {})
options.each { |k, v| options[k] = v.to_s if [:controller, :action].include?(k) && v.is_a?(Symbol) }
route = builder.build(path, options)
routes << route
route
@ -436,7 +437,7 @@ def call(env)
def recognize(request)
params = recognize_path(request.path, extract_request_environment(request))
request.path_parameters = params.with_indifferent_access
"#{params[:controller].camelize}Controller".constantize
"#{params[:controller].to_s.camelize}Controller".constantize
end
def recognize_path(path, environment={})

@ -1,49 +1,4 @@
class Object
def to_param
to_s
end
end
class TrueClass
def to_param
self
end
end
class FalseClass
def to_param
self
end
end
class NilClass
def to_param
self
end
end
class Regexp #:nodoc:
def number_of_captures
Regexp.new("|#{source}").match('').captures.length
end
def multiline?
options & MULTILINE == MULTILINE
end
class << self
def optionalize(pattern)
case unoptionalize(pattern)
when /\A(.|\(.*\))\Z/ then "#{pattern}?"
else "(?:#{pattern})?"
end
end
def unoptionalize(pattern)
[/\A\(\?:(.*)\)\?\Z/, /\A(.|\(.*\))\?\Z/].each do |regexp|
return $1 if regexp =~ pattern
end
return pattern
end
end
end
require 'active_support/core_ext/object/conversions'
require 'active_support/core_ext/boolean/conversions'
require 'active_support/core_ext/nil/conversions'
require 'active_support/core_ext/regexp'

@ -1,6 +1,7 @@
require 'stringio'
require 'uri'
require 'active_support/test_case'
require 'active_support/core_ext/object/metaclass'
require 'rack/mock_session'
require 'rack/test/cookie_jar'
@ -191,7 +192,7 @@ def reset!
unless defined? @named_routes_configured
# install the named routes in this session instance.
klass = class << self; self; end
klass = metaclass
Routing::Routes.install_helpers(klass)
# the helpers are made protected by default--we make them public for
@ -244,7 +245,7 @@ def process(method, path, parameters = nil, rack_environment = nil)
path = location.query ? "#{location.path}?#{location.query}" : location.path
end
[ControllerCapture, ActionController::ProcessWithTest].each do |mod|
[ControllerCapture, ActionController::Testing].each do |mod|
unless ActionController::Base < mod
ActionController::Base.class_eval { include mod }
end

@ -56,6 +56,8 @@ def recycle!
@block = nil
@length = 0
@body = []
@charset = nil
@content_type = nil
@request = @template = nil
end
@ -122,30 +124,24 @@ def process(action, parameters = nil, session = nil, flash = nil, http_method =
@request.recycle!
@response.recycle!
@controller.response_body = nil
@controller.formats = nil
@controller.params = nil
@html_document = nil
@request.request_method = http_method
@request.env['REQUEST_METHOD'] = http_method
parameters ||= {}
@request.assign_parameters(@controller.class.controller_path, action.to_s, parameters)
@request.session = ActionController::TestSession.new(session) unless session.nil?
@request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
@controller.request = @request
@controller.params.merge!(parameters)
build_request_uri(action, parameters)
Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest
env = @request.env
app = @controller
# TODO: Enable Lint
# app = Rack::Lint.new(app)
status, headers, body = app.action(action, env)
response = Rack::MockResponse.new(status, headers, body)
@response.request, @response.template = @request, @controller.template
@response.status, @response.headers, @response.body = response.status, response.headers, response.body
Base.class_eval { include Testing }
@controller.process_with_new_base_test(@request, @response)
@response
end
@ -165,7 +161,7 @@ def assigns(key = nil)
next if ActionController::Base.protected_instance_variables.include?(ivar)
assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar)
end
key.nil? ? assigns : assigns[key.to_s]
end
@ -261,27 +257,4 @@ def with_routing
ActionController::Routing.const_set(:Routes, real_routes) if real_routes
end
end
module ProcessWithTest #:nodoc:
def self.included(base)
base.class_eval {
attr_reader :assigns
alias_method_chain :process, :test
}
end
def process_with_test(*args)
process_without_test(*args).tap { set_test_assigns }
end
private
def set_test_assigns
@assigns = {}
(instance_variable_names - self.class.protected_instance_variables).each do |var|
name, value = var[1..-1], instance_variable_get(var)
@assigns[name] = value
@template.assigns[name] = value if response
end
end
end
end
end

@ -1,74 +0,0 @@
require "action_controller/testing/process"
module ActionController
module TestProcess
# Executes a request simulating GET HTTP method and set/volley the response
def get(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "GET")
end
# Executes a request simulating POST HTTP method and set/volley the response
def post(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "POST")
end
# Executes a request simulating PUT HTTP method and set/volley the response
def put(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "PUT")
end
# Executes a request simulating DELETE HTTP method and set/volley the response
def delete(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "DELETE")
end
# Executes a request simulating HEAD HTTP method and set/volley the response
def head(action, parameters = nil, session = nil, flash = nil)
process(action, parameters, session, flash, "HEAD")
end
def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
# Sanity check for required instance variables so we can give an
# understandable error message.
%w(@controller @request @response).each do |iv_name|
if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
raise "#{iv_name} is nil: make sure you set it in your test's setup method."
end
end
@request.recycle!
@response.recycle!
@controller.response_body = nil
@controller.formats = nil
@controller.params = nil
@html_document = nil
@request.env['REQUEST_METHOD'] = http_method
parameters ||= {}
@request.assign_parameters(@controller.class.controller_path, action.to_s, parameters)
@request.session = ActionController::TestSession.new(session) unless session.nil?
@request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
@controller.request = @request
@controller.params.merge!(parameters)
build_request_uri(action, parameters)
# Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest
@controller.process_with_new_base_test(@request, @response)
@response
end
def build_request_uri(action, parameters)
unless @request.env['REQUEST_URI']
options = @controller.__send__(:rewrite_options, parameters)
options.update(:only_path => true, :action => action)
url = ActionController::UrlRewriter.new(@request, parameters)
@request.request_uri = url.rewrite(options)
end
end
end
end

@ -2,7 +2,19 @@
require 'active_support/core_ext/class/attribute_accessors'
module Mime
SET = []
class Mimes < Array
def symbols
@symbols ||= map {|m| m.to_sym }
end
%w(<< concat shift unshift push pop []= clear compact! collect!
delete delete_at delete_if flatten! map! insert reject! reverse!
replace slice! sort! uniq!).each do |method|
define_method(method) {|*args| @symbols = nil; super(*args) }
end
end
SET = Mimes.new
EXTENSION_LOOKUP = {}
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }

@ -180,12 +180,10 @@ def formats
else
accepts.dup
end.tap do |ret|
if defined?(ActionController::Http)
if ret == ONLY_ALL
ret.replace Mime::SET
elsif all = ret.index(Mime::ALL)
ret.delete_at(all) && ret.insert(all, *Mime::SET)
end
if ret == ONLY_ALL
ret.replace Mime::SET
elsif all = ret.index(Mime::ALL)
ret.delete_at(all) && ret.insert(all, *Mime::SET)
end
end
else

@ -67,12 +67,7 @@ def body
end
def body=(body)
@body =
if body.respond_to?(:to_str)
[body]
else
body
end
@body = body.respond_to?(:to_str) ? [body] : body
end
def body_parts
@ -96,36 +91,7 @@ def location=(url)
# If a character set has been defined for this response (see charset=) then
# the character set information will also be included in the content type
# information.
def content_type=(mime_type)
self.headers["Content-Type"] =
if mime_type =~ /charset/ || (c = charset).nil?
mime_type.to_s
else
"#{mime_type}; charset=#{c}"
end
end
# Returns the response's content MIME type, or nil if content type has been set.
def content_type
content_type = String(headers["Content-Type"] || headers["type"]).split(";")[0]
content_type.blank? ? nil : content_type
end
# Set the charset of the Content-Type header. Set to nil to remove it.
# If no content type is set, it defaults to HTML.
def charset=(charset)
headers["Content-Type"] =
if charset
"#{content_type || Mime::HTML}; charset=#{charset}"
else
content_type || Mime::HTML.to_s
end
end
def charset
charset = String(headers["Content-Type"] || headers["type"]).split(";")[1]
charset.blank? ? nil : charset.strip.split("=")[1]
end
attr_accessor :charset, :content_type
def last_modified
if last = headers['Last-Modified']
@ -162,15 +128,15 @@ def sending_file?
end
def assign_default_content_type_and_charset!
if type = headers['Content-Type'] || headers['type']
unless type =~ /charset=/ || sending_file?
headers['Content-Type'] = "#{type}; charset=#{default_charset}"
end
else
type = Mime::HTML.to_s
type += "; charset=#{default_charset}" unless sending_file?
headers['Content-Type'] = type
end
return if !headers["Content-Type"].blank?
@content_type ||= Mime::HTML
@charset ||= default_charset
type = @content_type.to_s.dup
type << "; charset=#{@charset}" unless sending_file?
headers["Content-Type"] = type
end
def prepare!

@ -1,3 +1,4 @@
require "active_support/core_ext/kernel/requires"
begin
require_library_or_gem 'memcache'

@ -1,3 +1,5 @@
require "active_support/core_ext/exception"
module ActionDispatch
class ShowExceptions
include StatusCodes

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

@ -33,21 +33,22 @@ def self.load_all!
[Base, InlineTemplate, TemplateError]
end
autoload :Base, 'action_view/base'
autoload :Helpers, 'action_view/helpers'
autoload :InlineTemplate, 'action_view/template/inline'
autoload :Partials, 'action_view/render/partials'
autoload :Path, 'action_view/template/path'
autoload :PathSet, 'action_view/paths'
autoload :Rendering, 'action_view/render/rendering'
autoload :Renderable, 'action_view/template/renderable'
autoload :Base, 'action_view/base'
autoload :Helpers, 'action_view/helpers'
autoload :InlineTemplate, 'action_view/template/inline'
autoload :Partials, 'action_view/render/partials'
autoload :Resolver, 'action_view/template/resolver'
autoload :PathSet, 'action_view/paths'
autoload :Rendering, 'action_view/render/rendering'
autoload :Renderable, 'action_view/template/renderable'
autoload :RenderablePartial, 'action_view/template/partial'
autoload :Template, 'action_view/template/template'
autoload :TemplateError, 'action_view/template/error'
autoload :TemplateHandler, 'action_view/template/handler'
autoload :TemplateHandlers, 'action_view/template/handlers'
autoload :TextTemplate, 'action_view/template/text'
autoload :Helpers, 'action_view/helpers'
autoload :Template, 'action_view/template/template'
autoload :TemplateError, 'action_view/template/error'
autoload :TemplateHandler, 'action_view/template/handler'
autoload :TemplateHandlers, 'action_view/template/handlers'
autoload :TextTemplate, 'action_view/template/text'
autoload :Helpers, 'action_view/helpers'
autoload :FileSystemResolverWithFallback, 'action_view/template/resolver'
end
class ERB

@ -170,12 +170,13 @@ class Base
attr_accessor :base_path, :assigns, :template_extension, :formats
attr_accessor :controller
attr_internal :captures
attr_accessor :output_buffer
class << self
delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB'
delegate :logger, :to => 'ActionController::Base'
delegate :logger, :to => 'ActionController::Base', :allow_nil => true
end
@@debug_rjs = false
@ -229,38 +230,27 @@ def include(*args)
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc:
@formats = formats || [:html]
@assigns = assigns_for_first_render
@assigns_added = nil
@assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) }
@controller = controller
@helpers = ProxyModule.new(self)
@_content_for = Hash.new {|h,k| h[k] = "" }
self.view_paths = view_paths
@_first_render = nil
@_current_render = nil
end
attr_internal :template
attr_reader :view_paths
def view_paths=(paths)
@view_paths = self.class.process_view_paths(paths)
end
# Access the current template being rendered.
# Returns a ActionView::Template object.
def template
@_current_render
end
def template=(template) #:nodoc:
@_first_render ||= template
@_current_render = template
end
def with_template(current_template)
_evaluate_assigns_and_ivars
last_template, self.template = template, current_template
last_formats, self.formats = formats, [current_template.mime_type.to_sym] + Mime::SET.symbols
yield
ensure
self.template = last_template
self.template, self.formats = last_template, last_formats
end
def punctuate_body!(part)
@ -271,30 +261,19 @@ def punctuate_body!(part)
# Evaluates the local assigns and controller ivars, pushes them to the view.
def _evaluate_assigns_and_ivars #:nodoc:
unless @assigns_added
@assigns.each { |key, value| instance_variable_set("@#{key}", value) }
_copy_ivars_from_controller
@assigns_added = true
end
@assigns_added ||= _copy_ivars_from_controller
end
private
private
def _copy_ivars_from_controller #:nodoc:
if @controller
variables = @controller.instance_variable_names
variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) }
end
def _copy_ivars_from_controller #:nodoc:
if @controller
variables = @controller.instance_variable_names
variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) }
end
true
end
def _set_controller_content_type(content_type) #:nodoc:
# TODO: Remove this method when new base is switched
unless defined?(ActionController::Http)
if controller.respond_to?(:response)
controller.response.content_type ||= content_type
end
end
end
end
end

@ -11,7 +11,7 @@ module Helpers #:nodoc:
autoload :FormHelper, 'action_view/helpers/form_helper'
autoload :FormOptionsHelper, 'action_view/helpers/form_options_helper'
autoload :FormTagHelper, 'action_view/helpers/form_tag_helper'
autoload :JavascriptHelper, 'action_view/helpers/javascript_helper'
autoload :JavaScriptHelper, 'action_view/helpers/javascript_helper'
autoload :NumberHelper, 'action_view/helpers/number_helper'
autoload :PrototypeHelper, 'action_view/helpers/prototype_helper'
autoload :RecordIdentificationHelper, 'action_view/helpers/record_identification_helper'

@ -1,6 +1,8 @@
require 'cgi'
require 'action_view/helpers/form_helper'
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/kernel/reporting'
module ActionView
class Base
@ -120,9 +122,9 @@ def error_message_on(object, method, *args)
options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError')
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
(errors = obj.errors.on(method))
(errors = obj.errors[method])
content_tag("div",
"#{options[:prepend_text]}#{ERB::Util.html_escape(errors.is_a?(Array) ? errors.first : errors)}#{options[:append_text]}",
"#{options[:prepend_text]}#{ERB::Util.html_escape(errors.first)}#{options[:append_text]}",
:class => options[:css_class]
)
else
@ -245,59 +247,22 @@ def to_tag(options = {})
end
end
alias_method :tag_without_error_wrapping, :tag
def tag(name, options)
if object.respond_to?(:errors) && object.errors.respond_to?(:on)
error_wrapping(tag_without_error_wrapping(name, options), object.errors.on(@method_name))
%w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth|
without = "#{meth}_without_error_wrapping"
define_method "#{meth}_with_error_wrapping" do |*args|
error_wrapping(send(without, *args))
end
alias_method_chain meth, :error_wrapping
end
def error_wrapping(html_tag)
if object.respond_to?(:errors) && object.errors.respond_to?(:full_messages) && object.errors[@method_name].any?
Base.field_error_proc.call(html_tag, self)
else
tag_without_error_wrapping(name, options)
html_tag
end
end
alias_method :content_tag_without_error_wrapping, :content_tag
def content_tag(name, value, options)
if object.respond_to?(:errors) && object.errors.respond_to?(:on)
error_wrapping(content_tag_without_error_wrapping(name, value, options), object.errors.on(@method_name))
else
content_tag_without_error_wrapping(name, value, options)
end
end
alias_method :to_date_select_tag_without_error_wrapping, :to_date_select_tag
def to_date_select_tag(options = {}, html_options = {})
if object.respond_to?(:errors) && object.errors.respond_to?(:on)
error_wrapping(to_date_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
else
to_date_select_tag_without_error_wrapping(options, html_options)
end
end
alias_method :to_datetime_select_tag_without_error_wrapping, :to_datetime_select_tag
def to_datetime_select_tag(options = {}, html_options = {})
if object.respond_to?(:errors) && object.errors.respond_to?(:on)
error_wrapping(to_datetime_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
else
to_datetime_select_tag_without_error_wrapping(options, html_options)
end
end
alias_method :to_time_select_tag_without_error_wrapping, :to_time_select_tag
def to_time_select_tag(options = {}, html_options = {})
if object.respond_to?(:errors) && object.errors.respond_to?(:on)
error_wrapping(to_time_select_tag_without_error_wrapping(options, html_options), object.errors.on(@method_name))
else
to_time_select_tag_without_error_wrapping(options, html_options)
end
end
def error_wrapping(html_tag, has_error)
has_error ? Base.field_error_proc.call(html_tag, self) : html_tag
end
def error_message
object.errors.on(@method_name)
end
def column_type
object.send(:column_for_attribute, @method_name).type
end

@ -1,6 +1,7 @@
require 'cgi'
require 'action_view/helpers/url_helper'
require 'action_view/helpers/tag_helper'
require 'active_support/core_ext/file'
module ActionView
module Helpers #:nodoc:
@ -273,17 +274,20 @@ def javascript_path(source)
# javascript_include_tag :all, :cache => true, :recursive => true
def javascript_include_tag(*sources)
options = sources.extract_options!.stringify_keys
cache = options.delete("cache")
concat = options.delete("concat")
cache = concat || options.delete("cache")
recursive = options.delete("recursive")
if ActionController::Base.perform_caching && cache
if concat || (ActionController::Base.perform_caching && cache)
joined_javascript_name = (cache == true ? "all" : cache) + ".js"
joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name)
joined_javascript_path = File.join(joined_javascript_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : JAVASCRIPTS_DIR, joined_javascript_name)
write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path)
unless ActionController::Base.perform_caching && File.exists?(joined_javascript_path)
write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive))
end
javascript_src_tag(joined_javascript_name, options)
else
expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n")
ensure_javascript_sources!(expand_javascript_sources(sources, recursive)).collect { |source| javascript_src_tag(source, options) }.join("\n")
end
end
@ -412,19 +416,28 @@ def stylesheet_path(source)
# The <tt>:recursive</tt> option is also available for caching:
#
# stylesheet_link_tag :all, :cache => true, :recursive => true
#
# To force concatenation (even in development mode) set <tt>:concat</tt> to true. This is useful if
# you have too many stylesheets for IE to load.
#
# stylesheet_link_tag :all, :concat => true
#
def stylesheet_link_tag(*sources)
options = sources.extract_options!.stringify_keys
cache = options.delete("cache")
concat = options.delete("concat")
cache = concat || options.delete("cache")
recursive = options.delete("recursive")
if ActionController::Base.perform_caching && cache
if concat || (ActionController::Base.perform_caching && cache)
joined_stylesheet_name = (cache == true ? "all" : cache) + ".css"
joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name)
joined_stylesheet_path = File.join(joined_stylesheet_name[/^#{File::SEPARATOR}/] ? ASSETS_DIR : STYLESHEETS_DIR, joined_stylesheet_name)
write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path)
unless ActionController::Base.perform_caching && File.exists?(joined_stylesheet_path)
write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive))
end
stylesheet_tag(joined_stylesheet_name, options)
else
expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n")
ensure_stylesheet_sources!(expand_stylesheet_sources(sources, recursive)).collect { |source| stylesheet_tag(source, options) }.join("\n")
end
end
@ -444,6 +457,21 @@ def image_path(source)
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
# Computes the path to a video asset in the public videos directory.
# Full paths from the document root will be passed through.
# Used internally by +video_tag+ to build the video path.
#
# ==== Examples
# video_path("hd") # => /videos/hd
# video_path("hd.avi") # => /videos/hd.avi
# video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
# video_path("/trailers/hd.avi") # => /videos/hd.avi
# video_path("http://www.railsapplication.com/vid/hd.avi") # => http://www.railsapplication.com/vid/hd.avi
def video_path(source)
compute_public_path(source, 'videos')
end
alias_method :path_to_video, :video_path # aliased to avoid conflicts with an video_path named route
# Returns an html image tag for the +source+. The +source+ can be a full
# path or a file that exists in your public images directory.
#
@ -480,8 +508,8 @@ def image_path(source)
def image_tag(source, options = {})
options.symbolize_keys!
options[:src] = path_to_image(source)
options[:alt] ||= File.basename(options[:src], '.*').split('.').first.to_s.capitalize
src = options[:src] = path_to_image(source)
options[:alt] ||= File.basename(src, '.*').split('.').first.to_s.capitalize
if size = options.delete(:size)
options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
@ -489,12 +517,64 @@ def image_tag(source, options = {})
if mouseover = options.delete(:mouseover)
options[:onmouseover] = "this.src='#{image_path(mouseover)}'"
options[:onmouseout] = "this.src='#{image_path(options[:src])}'"
options[:onmouseout] = "this.src='#{src}'"
end
tag("img", options)
end
# Returns an html video tag for the +sources+. If +sources+ is a string,
# a single video tag will be returned. If +sources+ is an array, a video
# tag with nested source tags for each source will be returned. The
# +sources+ can be full paths or files that exists in your public videos
# directory.
#
# ==== Options
# You can add HTML attributes using the +options+. The +options+ supports
# two additional keys for convenience and conformance:
#
# * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
# before the video loads. The path is calculated like the +src+ of +image_tag+.
# * <tt>:size</tt> - Supplied as "{Width}x{Height}", so "30x45" becomes
# width="30" and height="45". <tt>:size</tt> will be ignored if the
# value is not in the correct format.
#
# ==== Examples
# video_tag("trailer") # =>
# <video src="/videos/trailer" />
# video_tag("trailer.ogg") # =>
# <video src="/videos/trailer.ogg" />
# video_tag("trailer.ogg", :controls => true, :autobuffer => true) # =>
# <video autobuffer="autobuffer" controls="controls" src="/videos/trailer.ogg" />
# video_tag("trailer.m4v", :size => "16x10", :poster => "screenshot.png") # =>
# <video src="/videos/trailer.m4v" width="16" height="10" poster="/images/screenshot.png" />
# video_tag("/trailers/hd.avi", :size => "16x16") # =>
# <video src="/trailers/hd.avi" width="16" height="16" />
# video_tag("/trailers/hd.avi", :height => '32', :width => '32') # =>
# <video height="32" src="/trailers/hd.avi" width="32" />
# video_tag(["trailer.ogg", "trailer.flv"]) # =>
# <video><source src="trailer.ogg" /><source src="trailer.ogg" /><source src="trailer.flv" /></video>
# video_tag(["trailer.ogg", "trailer.flv"] :size => "160x120") # =>
# <video height="120" width="160"><source src="trailer.ogg" /><source src="trailer.flv" /></video>
def video_tag(sources, options = {})
options.symbolize_keys!
options[:poster] = path_to_image(options[:poster]) if options[:poster]
if size = options.delete(:size)
options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
end
if sources.is_a?(Array)
content_tag("video", options) do
sources.map { |source| tag("source", :src => source) }.join
end
else
options[:src] = path_to_video(sources)
tag("video", options)
end
end
def self.cache_asset_timestamps
@@cache_asset_timestamps
end
@ -655,13 +735,28 @@ def determine_source(source, collection)
end
end
def ensure_stylesheet_sources!(sources)
sources.each do |source|
asset_file_path!(path_to_stylesheet(source))
end
return sources
end
def ensure_javascript_sources!(sources)
sources.each do |source|
asset_file_path!(path_to_javascript(source))
end
return sources
end
def join_asset_file_contents(paths)
paths.collect { |path| File.read(asset_file_path(path)) }.join("\n\n")
paths.collect { |path| File.read(asset_file_path!(path)) }.join("\n\n")
end
def write_asset_file_contents(joined_asset_path, asset_paths)
FileUtils.mkdir_p(File.dirname(joined_asset_path))
File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) }
File.atomic_write(joined_asset_path) { |cache| cache.write(join_asset_file_contents(asset_paths)) }
# Set mtime to the latest of the combined files to allow for
# consistent ETag without a shared filesystem.
@ -673,6 +768,14 @@ def asset_file_path(path)
File.join(ASSETS_DIR, path.split('?').first)
end
def asset_file_path!(path)
unless path =~ %r{^[-a-z]+://}
absolute_path = asset_file_path(path)
raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path)
return absolute_path
end
end
def collect_asset_files(*path)
dir = path.first
@ -682,4 +785,4 @@ def collect_asset_files(*path)
end
end
end
end
end

@ -1,4 +1,4 @@
require 'benchmark'
require 'active_support/core_ext/benchmark'
module ActionView
module Helpers

@ -111,15 +111,32 @@ def capture(*args, &block)
#
# WARNING: content_for is ignored in caches. So you shouldn't use it
# for elements that will be fragment cached.
#
# The deprecated way of accessing a content_for block is to use an instance variable
# named <tt>@content_for_#{name_of_the_content_block}</tt>. The preferred usage is now
# <tt><%= yield :footer %></tt>.
def content_for(name, content = nil, &block)
ivar = "@content_for_#{name}"
content = capture(&block) if block_given?
instance_variable_set(ivar, "#{instance_variable_get(ivar)}#{content}")
nil
return @_content_for[name] << content if content
@_content_for[name]
end
# content_for? simply checks whether any content has been captured yet using content_for
# Useful to render parts of your layout differently based on what is in your views.
#
# ==== Examples
#
# Perhaps you will use different css in you layout if no content_for :right_column
#
# <%# This is the layout %>
# <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
# <head>
# <title>My Website</title>
# <%= yield :script %>
# </head>
# <body class="<%= content_for?(:right_col) ? 'one-column' : 'two-column' %>">
# <%= yield %>
# <%= yield :right_col %>
# </body>
# </html>
def content_for?(name)
@_content_for[name].present?
end
# Use an alternate output buffer for the duration of the block.

@ -3,6 +3,7 @@
require 'action_view/helpers/tag_helper'
require 'action_view/helpers/form_tag_helper'
require 'active_support/core_ext/class/inheritable_attributes'
require 'active_support/core_ext/hash/slice'
module ActionView
module Helpers
@ -494,7 +495,8 @@ def fields_for(record_or_name_or_array, *args, &block)
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
# assigned to the template (identified by +object+). The text of label will default to the attribute name unless you specify
# it explicitly. Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
# onto the HTML as an HTML element attribute as in the example shown.
# onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
# target labels for radio_button tags (where the value is used in the ID of the input tag).
#
# ==== Examples
# label(:post, :title)
@ -506,6 +508,9 @@ def fields_for(record_or_name_or_array, *args, &block)
# label(:post, :title, "A short title", :class => "title_label")
# # => <label for="post_title" class="title_label">A short title</label>
#
# label(:post, :privacy, "Public Post", :value => "public")
# # => <label for="post_privacy_public">Public Post</label>
#
def label(object_name, method, text = nil, options = {})
InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options)
end
@ -728,8 +733,9 @@ def initialize(object_name, method_name, template_object, object = nil)
def to_label_tag(text = nil, options = {})
options = options.stringify_keys
tag_value = options.delete("value")
name_and_id = options.dup
add_default_name_and_id(name_and_id)
add_default_name_and_id_for_value(tag_value, name_and_id)
options.delete("index")
options["for"] ||= name_and_id["id"]
content = (text.blank? ? nil : text.to_s) || method_name.humanize
@ -761,11 +767,7 @@ def to_radio_button_tag(tag_value, options = {})
checked = self.class.radio_button_checked?(value(object), tag_value)
end
options["checked"] = "checked" if checked
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
options["id"] ||= defined?(@auto_index) ?
"#{tag_id_with_index(@auto_index)}_#{pretty_tag_value}" :
"#{tag_id}_#{pretty_tag_value}"
add_default_name_and_id(options)
add_default_name_and_id_for_value(tag_value, options)
tag("input", options)
end
@ -866,6 +868,17 @@ def radio_button_checked?(value, checked_value)
end
private
def add_default_name_and_id_for_value(tag_value, options)
if tag_value
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/\W/, "").downcase
specified_id = options["id"]
add_default_name_and_id(options)
options["id"] += "_#{pretty_tag_value}" unless specified_id
else
add_default_name_and_id(options)
end
end
def add_default_name_and_id(options)
if options.has_key?("index")
options["name"] ||= tag_name_with_index(options["index"])
@ -913,6 +926,7 @@ class FormBuilder #:nodoc:
attr_accessor :object_name, :object, :options
def initialize(object_name, object, template, options, proc)
@nested_child_index = {}
@object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
@default_options = @options ? @options.slice(:index) : {}
if @object_name.to_s.match(/\[\]$/)
@ -1015,7 +1029,7 @@ def fields_for_with_nested_attributes(association_name, args, block)
explicit_child_index = args.last[:child_index] if args.last.is_a?(Hash)
children.map do |child|
fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index}]", child, args, block)
fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, args, block)
end.join
else
fields_for_nested_model(name, explicit_object || association, args, block)
@ -1033,9 +1047,9 @@ def fields_for_nested_model(name, object, args, block)
end
end
def nested_child_index
@nested_child_index ||= -1
@nested_child_index += 1
def nested_child_index(name)
@nested_child_index[name] ||= -1
@nested_child_index[name] += 1
end
end
end
@ -1043,5 +1057,6 @@ def nested_child_index
class << Base
attr_accessor :default_form_builder
end
Base.default_form_builder = ::ActionView::Helpers::FormBuilder
end

@ -1,5 +1,6 @@
require 'cgi'
require 'action_view/helpers/tag_helper'
require 'active_support/core_ext/object/returning'
module ActionView
module Helpers
@ -230,6 +231,8 @@ def password_field_tag(name = "password", value = nil, options = {})
# * <tt>:rows</tt> - Specify the number of rows in the textarea
# * <tt>:cols</tt> - Specify the number of columns in the textarea
# * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
# * <tt>:escape</tt> - By default, the contents of the text input are HTML escaped.
# If you need unescaped contents, set this to false.
# * Any other key creates standard HTML attributes for the tag.
#
# ==== Examples
@ -257,7 +260,10 @@ def text_area_tag(name, content = nil, options = {})
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
end
content_tag :textarea, content, { "name" => name, "id" => name }.update(options.stringify_keys)
escape = options.key?("escape") ? options.delete("escape") : true
content = html_escape(content) if escape
content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
end
# Creates a check box form input tag.
@ -445,10 +451,10 @@ def extra_tags_for_form(html_options)
''
when /^post$/i, "", nil
html_options["method"] = "post"
protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0') : ''
protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0;display:inline') : ''
else
html_options["method"] = "post"
content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0')
content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0;display:inline')
end
end

@ -1,6 +1,7 @@
require 'set'
require 'active_support/json'
require 'active_support/core_ext/object/extending'
require 'active_support/core_ext/object/returning'
module ActionView
module Helpers
@ -1175,7 +1176,7 @@ def reload(options_for_replace = {})
class JavaScriptVariableProxy < JavaScriptProxy #:nodoc:
def initialize(generator, variable)
@variable = variable
@variable = ::ActiveSupport::JSON::Variable.new(variable)
@empty = true # only record lines if we have to. gets rid of unnecessary linebreaks
super(generator)
end
@ -1186,7 +1187,7 @@ def respond_to?(*)
true
end
def rails_to_json(*)
def as_json(options = nil)
@variable
end

@ -8,7 +8,8 @@ module Helpers #:nodoc:
module TagHelper
include ERB::Util
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked).to_set
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
autoplay controls loop).to_set
BOOLEAN_ATTRIBUTES.merge(BOOLEAN_ATTRIBUTES.map {|attr| attr.to_sym })
# Returns an empty HTML tag of type +name+ which by default is XHTML

@ -1,4 +1,5 @@
require 'action_view/helpers/javascript_helper'
require 'active_support/core_ext/hash/keys'
module ActionView
module Helpers #:nodoc:
@ -220,9 +221,9 @@ def link_to(*args, &block)
html_options = args.second
concat(link_to(capture(&block), options, html_options))
else
name = args.first
options = args.second || {}
html_options = args.third
name = args[0]
options = args[1] || {}
html_options = args[2]
url = url_for(options)

@ -3,7 +3,7 @@ class PathSet < Array #:nodoc:
def self.type_cast(obj)
if obj.is_a?(String)
cache = !defined?(Rails) || !Rails.respond_to?(:configuration) || Rails.configuration.cache_classes
Template::FileSystemPathWithFallback.new(obj, :cache => cache)
FileSystemResolverWithFallback.new(obj, :cache => cache)
else
obj
end

@ -232,26 +232,7 @@ def _render_partial_with_block(layout, block, options)
ensure
@_proc_for_layout = nil
end
def _deprecated_ivar_assign(template)
if respond_to?(:controller)
ivar = :"@#{template.variable_name}"
object =
if controller.instance_variable_defined?(ivar)
ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
controller.instance_variable_get(ivar),
"#{ivar} will no longer be implicitly assigned to #{template.variable_name}")
end
end
end
def _render_partial_with_block(layout, block, options)
@_proc_for_layout = block
concat(_render_partial(options.merge(:partial => layout)))
ensure
@_proc_for_layout = nil
end
def _render_partial_with_layout(layout, options)
if layout
prefix = controller && !layout.include?("/") ? controller.controller_path : nil
@ -260,18 +241,6 @@ def _render_partial_with_layout(layout, options)
content = _render_partial(options)
return _render_content_with_layout(content, layout, options[:locals])
end
def _deprecated_ivar_assign(template)
if respond_to?(:controller)
ivar = :"@#{template.variable_name}"
object =
if controller.instance_variable_defined?(ivar)
ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
controller.instance_variable_get(ivar),
"#{ivar} will no longer be implicitly assigned to #{template.variable_name}")
end
end
end
def _array_like_objects
array_like = [Array]
@ -297,8 +266,6 @@ def _render_partial_object(template, options, object = nil)
end
def _set_locals(object, locals, template, options)
object ||= _deprecated_ivar_assign(template)
locals[:object] = locals[template.variable_name] = object
locals[options[:as]] = object if options[:as]
end

@ -51,32 +51,60 @@ def _render_content_with_layout(content, layout, locals)
end
begin
original_content_for_layout = @content_for_layout if defined?(@content_for_layout)
@content_for_layout = content
old_content, @_content_for[:layout] = @_content_for[:layout], content
@cached_content_for_layout = @content_for_layout
@cached_content_for_layout = @_content_for[:layout]
_render_template(layout, locals)
ensure
@content_for_layout = original_content_for_layout
@_content_for[:layout] = old_content
end
end
# You can think of a layout as a method that is called with a block. This method
# returns the block that the layout is called with. If the user calls yield :some_name,
# the block, by default, returns content_for(:some_name). If the user calls yield,
# the default block returns content_for(:layout).
#
# The user can override this default by passing a block to the layout.
#
# ==== Example
#
# # The template
# <% render :layout => "my_layout" do %>Content<% end %>
#
# # The layout
# <html><% yield %></html>
#
# In this case, instead of the default block, which would return content_for(:layout),
# this method returns the block that was passed in to render layout, and the response
# would be <html>Content</html>.
#
# Finally, the block can take block arguments, which can be passed in by yield.
#
# ==== Example
#
# # The template
# <% render :layout => "my_layout" do |customer| %>Hello <%= customer.name %><% end %>
#
# # The layout
# <html><% yield Struct.new(:name).new("David") %></html>
#
# In this case, the layout would receive the block passed into <tt>render :layout</tt>,
# and the Struct specified in the layout would be passed into the block. The result
# would be <html>Hello David</html>.
def layout_proc(name)
@_default_layout ||= proc { |*names| @_content_for[names.first || :layout] }
!@_content_for.key?(name) && @_proc_for_layout || @_default_layout
end
def _render_template(template, local_assigns = {})
with_template(template) do
_evaluate_assigns_and_ivars
_set_controller_content_type(template.mime_type) if template.respond_to?(:mime_type)
template.render(self, local_assigns) do |*names|
if !instance_variable_defined?(:"@content_for_#{names.first}") &&
instance_variable_defined?(:@_proc_for_layout) && (proc = @_proc_for_layout)
capture(*names, &proc)
elsif instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
instance_variable_get(ivar)
end
capture(*names, &layout_proc(names.first))
end
end
rescue Exception => e
if TemplateError === e
if e.is_a?(TemplateError)
e.sub_template_of(template)
raise e
else
@ -101,20 +129,18 @@ def _render_template_from_controller(*args)
end
def _render_template_with_layout(template, layout = nil, options = {}, partial = false)
if controller && logger
logger.info("Rendering #{template.identifier}" +
(options[:status] ? " (#{options[:status]})" : ''))
end
logger && logger.info("Rendering #{template.identifier}#{' (#{options[:status]})' if options[:status]}")
locals = options[:locals] || {}
content = if partial
object = partial unless partial == true
_render_partial_object(template, options, object)
else
_render_template(template, options[:locals] || {})
_render_template(template, locals)
end
return content unless layout
_render_content_with_layout(content, layout, options[:locals] || {})
layout ? _render_content_with_layout(content, layout, locals) : content
end
end
end

@ -1,3 +1,5 @@
require "active_support/core_ext/enumerable"
module ActionView
# The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a
# bunch of intimate details and uses it to report a very precise exception message.

@ -1,5 +1,3 @@
require 'builder'
module ActionView
module TemplateHandlers
class Builder < TemplateHandler
@ -8,8 +6,8 @@ class Builder < TemplateHandler
self.default_format = Mime::XML
def compile(template)
"_set_controller_content_type(Mime::XML);" +
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
require 'builder'
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +
"self.output_buffer = xml.target!;" +
template.source +
";xml.target!;"

@ -1,4 +1,3 @@
require 'erb'
require 'active_support/core_ext/class/attribute_accessors'
module ActionView
@ -16,7 +15,11 @@ class ERB < TemplateHandler
self.default_format = Mime::HTML
def compile(template)
::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src
require 'erb'
magic = $1 if template.source =~ /\A(<%#.*coding[:=]\s*(\S+)\s*-?%>)/
erb = "#{magic}<% __in_erb_template=true %>#{template.source}"
::ERB.new(erb, nil, erb_trim_mode, '@output_buffer').src
end
end
end

@ -6,7 +6,6 @@ class RJS < TemplateHandler
self.default_format = Mime::JS
def compile(template)
"@formats = [:html];" +
"controller.response.content_type ||= Mime::JS;" +
"update_page do |page|;#{template.source}\nend"
end

@ -1,152 +0,0 @@
require "pathname"
module ActionView
class Template
# Abstract super class
class Path
def initialize(options)
@cache = options[:cache]
@cached = {}
end
# Normalizes the arguments and passes it on to find_template
def find_by_parts(*args)
find_all_by_parts(*args).first
end
def find_all_by_parts(name, details = {}, prefix = nil, partial = nil)
details[:locales] = [I18n.locale]
name = name.to_s.gsub(handler_matcher, '').split("/")
find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial)
end
private
# This is what child classes implement. No defaults are needed
# because Path guarantees that the arguments are present and
# normalized.
def find_templates(name, details, prefix, partial)
raise NotImplementedError
end
def valid_handlers
@valid_handlers ||= TemplateHandlers.extensions
end
def handler_matcher
@handler_matcher ||= begin
e = valid_handlers.join('|')
/\.(?:#{e})$/
end
end
def handler_glob
e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join
"{#{e}}"
end
def formats_glob
@formats_glob ||= begin
formats = Mime::SET.map { |m| m.symbol }
'{' + formats.map { |l| ".#{l}," }.join + '}'
end
end
def cached(key)
return yield unless @cache
return @cached[key] if @cached.key?(key)
@cached[key] = yield
end
end
class FileSystemPath < Path
def initialize(path, options = {})
raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
super(options)
@path = Pathname.new(path).expand_path
end
# TODO: This is the currently needed API. Make this suck less
# ==== <suck>
attr_reader :path
def to_s
path.to_s
end
def to_str
path.to_s
end
def ==(path)
to_str == path.to_str
end
def eql?(path)
to_str == path.to_str
end
# ==== </suck>
def find_templates(name, details, prefix, partial, root = "#{@path}/")
if glob = details_to_glob(name, details, prefix, partial, root)
cached(glob) do
Dir[glob].map do |path|
next if File.directory?(path)
source = File.read(path)
identifier = Pathname.new(path).expand_path.to_s
Template.new(source, identifier, *path_to_details(path))
end.compact
end
end
end
private
# :api: plugin
def details_to_glob(name, details, prefix, partial, root)
path = ""
path << "#{prefix}/" unless prefix.empty?
path << (partial ? "_#{name}" : name)
extensions = ""
[:locales, :formats].each do |k|
extensions << if exts = details[k]
'{' + exts.map {|e| ".#{e},"}.join + '}'
else
k == :formats ? formats_glob : ''
end
end
"#{root}#{path}#{extensions}#{handler_glob}"
end
# TODO: fix me
# :api: plugin
def path_to_details(path)
# [:erb, :format => :html, :locale => :en, :partial => true/false]
if m = path.match(%r'/(_)?[\w-]+(\.[\w-]+)*\.(\w+)$')
partial = m[1] == '_'
details = (m[2]||"").split('.').reject { |e| e.empty? }
handler = Template.handler_class_for_extension(m[3])
format = Mime[details.last] && details.pop.to_sym
locale = details.last && details.pop.to_sym
return handler, :format => format, :locale => locale, :partial => partial
end
end
end
class FileSystemPathWithFallback < FileSystemPath
def find_templates(name, details, prefix, partial)
templates = super
return super(name, details, prefix, partial, '') if templates.empty?
templates
end
end
end
end

@ -0,0 +1,150 @@
require "pathname"
require "action_view/template/template"
module ActionView
# Abstract superclass
class Resolver
def initialize(options)
@cache = options[:cache]
@cached = {}
end
# Normalizes the arguments and passes it on to find_template
def find_by_parts(*args)
find_all_by_parts(*args).first
end
def find_all_by_parts(name, details = {}, prefix = nil, partial = nil)
details[:locales] = [I18n.locale]
name = name.to_s.gsub(handler_matcher, '').split("/")
find_templates(name.pop, details, [prefix, *name].compact.join("/"), partial)
end
private
# This is what child classes implement. No defaults are needed
# because Resolver guarantees that the arguments are present and
# normalized.
def find_templates(name, details, prefix, partial)
raise NotImplementedError
end
def valid_handlers
@valid_handlers ||= TemplateHandlers.extensions
end
def handler_matcher
@handler_matcher ||= begin
e = valid_handlers.join('|')
/\.(?:#{e})$/
end
end
def handler_glob
e = TemplateHandlers.extensions.map{|h| ".#{h},"}.join
"{#{e}}"
end
def formats_glob
@formats_glob ||= begin
'{' + Mime::SET.symbols.map { |l| ".#{l}," }.join + '}'
end
end
def cached(key)
return yield unless @cache
return @cached[key] if @cached.key?(key)
@cached[key] = yield
end
end
class FileSystemResolver < Resolver
def initialize(path, options = {})
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
super(options)
@path = Pathname.new(path).expand_path
end
# TODO: This is the currently needed API. Make this suck less
# ==== <suck>
attr_reader :path
def to_s
path.to_s
end
def to_str
path.to_s
end
def ==(path)
to_str == path.to_str
end
def eql?(path)
to_str == path.to_str
end
# ==== </suck>
def find_templates(name, details, prefix, partial, root = "#{@path}/")
if glob = details_to_glob(name, details, prefix, partial, root)
cached(glob) do
Dir[glob].map do |path|
next if File.directory?(path)
source = File.read(path)
identifier = Pathname.new(path).expand_path.to_s
Template.new(source, identifier, *path_to_details(path))
end.compact
end
end
end
private
# :api: plugin
def details_to_glob(name, details, prefix, partial, root)
path = ""
path << "#{prefix}/" unless prefix.empty?
path << (partial ? "_#{name}" : name)
extensions = ""
[:locales, :formats].each do |k|
extensions << if exts = details[k]
'{' + exts.map {|e| ".#{e},"}.join + '}'
else
k == :formats ? formats_glob : ''
end
end
"#{root}#{path}#{extensions}#{handler_glob}"
end
# TODO: fix me
# :api: plugin
def path_to_details(path)
# [:erb, :format => :html, :locale => :en, :partial => true/false]
if m = path.match(%r'/(_)?[\w-]+(\.[\w-]+)*\.(\w+)$')
partial = m[1] == '_'
details = (m[2]||"").split('.').reject { |e| e.empty? }
handler = Template.handler_class_for_extension(m[3])
format = Mime[details.last] && details.pop.to_sym
locale = details.last && details.pop.to_sym
return handler, :format => format, :locale => locale, :partial => partial
end
end
end
class FileSystemResolverWithFallback < FileSystemResolver
def find_templates(name, details, prefix, partial)
templates = super
return super(name, details, prefix, partial, '') if templates.empty?
templates
end
end
end

@ -2,7 +2,7 @@
# This is so that templates compiled in this file are UTF-8
require 'set'
require "action_view/template/path"
require "action_view/template/resolver"
module ActionView
class Template
@ -20,7 +20,7 @@ def initialize(source, identifier, handler, details)
handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html"
end
@mime_type = Mime::Type.lookup_by_extension(format.to_s)
@details[:formats] = Array.wrap(format && format.to_sym)
@details[:formats] = Array.wrap(format.to_sym)
end
def render(view, locals, &blk)
@ -53,7 +53,11 @@ def compile(locals, view)
locals_code = locals.keys.map! { |key| "#{key} = local_assigns[:#{key}];" }.join
code = @handler.call(self)
encoding_comment = $1 if code.sub!(/\A(#.*coding.*)\n/, '')
if code.sub!(/\A(#.*coding.*)\n/, '')
encoding_comment = $1
elsif defined?(Encoding) && Encoding.respond_to?(:default_external)
encoding_comment = "#coding:#{Encoding.default_external}"
end
source = <<-end_src
def #{method_name}(local_assigns)

@ -3,7 +3,7 @@ class TextTemplate < String #:nodoc:
def initialize(string, content_type = Mime[:html])
super(string.to_s)
@content_type = Mime[content_type]
@content_type = Mime[content_type] || content_type
end
def details

@ -19,7 +19,7 @@ def index
class TestBasic < ActiveSupport::TestCase
test "dispatching works" do
result = Me.process(:index)
result = Me.new.process(:index)
assert_equal "Hello world", result.response_body
end
end
@ -68,27 +68,27 @@ def rendering_to_string
class TestRenderer < ActiveSupport::TestCase
test "rendering templates works" do
result = Me2.process(:index)
result = Me2.new.process(:index)
assert_equal "Hello from index.erb", result.response_body
end
test "rendering passes ivars to the view" do
result = Me2.process(:action_with_ivars)
result = Me2.new.process(:action_with_ivars)
assert_equal "Hello from index_with_ivars.erb", result.response_body
end
test "rendering with no template name" do
result = Me2.process(:naked_render)
result = Me2.new.process(:naked_render)
assert_equal "Hello from naked_render.erb", result.response_body
end
test "rendering to a rack body" do
result = Me2.process(:rendering_to_body)
result = Me2.new.process(:rendering_to_body)
assert_equal "Hello from naked_render.erb", result.response_body
end
test "rendering to a string" do
result = Me2.process(:rendering_to_string)
result = Me2.new.process(:rendering_to_string)
assert_equal "Hello from naked_render.erb", result.response_body
end
end
@ -120,12 +120,12 @@ def formatted
class TestPrefixedViews < ActiveSupport::TestCase
test "templates are located inside their 'prefix' folder" do
result = Me3.process(:index)
result = Me3.new.process(:index)
assert_equal "Hello from me3/index.erb", result.response_body
end
test "templates included their format" do
result = Me3.process(:formatted)
result = Me3.new.process(:formatted)
assert_equal "Hello from me3/formatted.html.erb", result.response_body
end
end
@ -136,11 +136,6 @@ class TestPrefixedViews < ActiveSupport::TestCase
class WithLayouts < PrefixedViews
include Layouts
def self.inherited(klass)
klass._write_layout_method
super
end
private
def self.layout(formats)
begin
@ -154,7 +149,7 @@ def self.layout(formats)
end
def render_to_body(options = {})
options[:_layout] = options[:layout] || _default_layout
options[:_layout] = options[:layout] || _default_layout({})
super
end
end
@ -173,7 +168,7 @@ def index
class TestLayouts < ActiveSupport::TestCase
test "layouts are included" do
result = Me4.process(:index)
result = Me4.new.process(:index)
assert_equal "Me4 Enter : Hello from me4/index.erb : Exit", result.response_body
end
end
@ -210,7 +205,7 @@ def method_for_action(action_name)
class TestRespondToAction < ActiveSupport::TestCase
def assert_dispatch(klass, body = "success", action = :index)
response = klass.process(action).response_body
response = klass.new.process(action).response_body
assert_equal body, response
end
@ -219,7 +214,7 @@ def assert_dispatch(klass, body = "success", action = :index)
end
test "raises ActionNotFound when method does not exist and action_missing is not defined" do
assert_raise(ActionNotFound) { DefaultRespondToActionController.process(:fail) }
assert_raise(ActionNotFound) { DefaultRespondToActionController.new.process(:fail) }
end
test "dispatches to action_missing when method does not exist and action_missing is defined" do
@ -231,7 +226,7 @@ def assert_dispatch(klass, body = "success", action = :index)
end
test "raises ActionNotFound if method is defined but respond_to_action? returns false" do
assert_raise(ActionNotFound) { RespondToActionController.process(:fail) }
assert_raise(ActionNotFound) { RespondToActionController.new.process(:fail) }
end
end

@ -8,7 +8,7 @@ class ControllerWithCallbacks < AbstractController::Base
end
class Callback1 < ControllerWithCallbacks
process_action_callback :before, :first
set_callback :process_action, :before, :first
def first
@text = "Hello world"
@ -21,7 +21,7 @@ def index
class TestCallbacks < ActiveSupport::TestCase
test "basic callbacks work" do
result = Callback1.process(:index)
result = Callback1.new.process(:index)
assert_equal "Hello world", result.response_body
end
end
@ -52,17 +52,17 @@ def index
class TestCallbacks < ActiveSupport::TestCase
test "before_filter works" do
result = Callback2.process(:index)
result = Callback2.new.process(:index)
assert_equal "Hello world", result.response_body
end
test "after_filter works" do
result = Callback2.process(:index)
result = Callback2.new.process(:index)
assert_equal "Goodbye", result.instance_variable_get("@second")
end
test "around_filter works" do
result = Callback2.process(:index)
result = Callback2.new.process(:index)
assert_equal "FIRSTSECOND", result.instance_variable_get("@aroundz")
end
end
@ -83,12 +83,12 @@ def index
class TestCallbacks < ActiveSupport::TestCase
test "before_filter works with procs" do
result = Callback3.process(:index)
result = Callback3.new.process(:index)
assert_equal "Hello world", result.response_body
end
test "after_filter works with procs" do
result = Callback3.process(:index)
result = Callback3.new.process(:index)
assert_equal "Goodbye", result.instance_variable_get("@second")
end
end
@ -118,17 +118,17 @@ def authenticate
class TestCallbacks < ActiveSupport::TestCase
test "when :only is specified, a before filter is triggered on that action" do
result = CallbacksWithConditions.process(:index)
result = CallbacksWithConditions.new.process(:index)
assert_equal "Hello, World", result.response_body
end
test "when :only is specified, a before filter is not triggered on other actions" do
result = CallbacksWithConditions.process(:sekrit_data)
result = CallbacksWithConditions.new.process(:sekrit_data)
assert_equal "true", result.response_body
end
test "when :except is specified, an after filter is not triggered on that action" do
result = CallbacksWithConditions.process(:index)
result = CallbacksWithConditions.new.process(:index)
assert_nil result.instance_variable_get("@authenticated")
end
end
@ -158,17 +158,17 @@ def authenticate
class TestCallbacks < ActiveSupport::TestCase
test "when :only is specified with an array, a before filter is triggered on that action" do
result = CallbacksWithArrayConditions.process(:index)
result = CallbacksWithArrayConditions.new.process(:index)
assert_equal "Hello, World", result.response_body
end
test "when :only is specified with an array, a before filter is not triggered on other actions" do
result = CallbacksWithArrayConditions.process(:sekrit_data)
result = CallbacksWithArrayConditions.new.process(:sekrit_data)
assert_equal "true", result.response_body
end
test "when :except is specified with an array, an after filter is not triggered on that action" do
result = CallbacksWithArrayConditions.process(:index)
result = CallbacksWithArrayConditions.new.process(:index)
assert_nil result.instance_variable_get("@authenticated")
end
end
@ -183,12 +183,12 @@ def not_index
class TestCallbacks < ActiveSupport::TestCase
test "when a callback is modified in a child with :only, it works for the :only action" do
result = ChangedConditions.process(:index)
result = ChangedConditions.new.process(:index)
assert_equal "Hello world", result.response_body
end
test "when a callback is modified in a child with :only, it does not work for other actions" do
result = ChangedConditions.process(:not_index)
result = ChangedConditions.new.process(:not_index)
assert_equal "", result.response_body
end
end
@ -207,7 +207,7 @@ def set_body
class TestHalting < ActiveSupport::TestCase
test "when a callback sets the response body, the action should not be invoked" do
result = SetsResponseBody.process(:index)
result = SetsResponseBody.new.process(:index)
assert_equal "Success", result.response_body
end
end

@ -34,7 +34,7 @@ def index
class TestHelpers < ActiveSupport::TestCase
def test_helpers
result = MyHelpers1.process(:index)
result = MyHelpers1.new.process(:index)
assert_equal "Hello World : Included", result.response_body
end
end

@ -9,7 +9,7 @@ class Base < AbstractController::Base
include AbstractController::Renderer
include AbstractController::Layouts
self.view_paths = [ActionView::Template::FixturePath.new(
self.view_paths = [ActionView::FixtureResolver.new(
"layouts/hello.erb" => "With String <%= yield %>",
"layouts/hello_override.erb" => "With Override <%= yield %>",
"layouts/abstract_controller_tests/layouts/with_string_implied_child.erb" =>
@ -25,7 +25,7 @@ def self.controller_path
def controller_path() self.class.controller_path end
def render_to_body(options)
options[:_layout] = _default_layout
options[:_layout] = _default_layout({})
super
end
end
@ -141,91 +141,82 @@ def index
end
end
# TODO Move to bootloader
AbstractController::Base.subclasses.each do |klass|
klass = klass.constantize
next unless klass < AbstractController::Layouts
klass.class_eval do
_write_layout_method
end
end
class TestBase < ActiveSupport::TestCase
test "when no layout is specified, and no default is available, render without a layout" do
result = Blank.process(:index)
result = Blank.new.process(:index)
assert_equal "Hello blank!", result.response_body
end
test "when layout is specified as a string, render with that layout" do
result = WithString.process(:index)
result = WithString.new.process(:index)
assert_equal "With String Hello string!", result.response_body
end
test "when layout is specified as a string, but the layout is missing, raise an exception" do
assert_raises(ActionView::MissingTemplate) { WithMissingLayout.process(:index) }
assert_raises(ActionView::MissingTemplate) { WithMissingLayout.new.process(:index) }
end
test "when layout is specified as false, do not use a layout" do
result = WithFalseLayout.process(:index)
result = WithFalseLayout.new.process(:index)
assert_equal "Hello false!", result.response_body
end
test "when layout is specified as nil, do not use a layout" do
result = WithNilLayout.process(:index)
result = WithNilLayout.new.process(:index)
assert_equal "Hello nil!", result.response_body
end
test "when layout is specified as a symbol, call the requested method and use the layout returned" do
result = WithSymbol.process(:index)
result = WithSymbol.new.process(:index)
assert_equal "OMGHI2U Hello symbol!", result.response_body
end
test "when layout is specified as a symbol and the method returns nil, don't use a layout" do
result = WithSymbolReturningNil.process(:index)
result = WithSymbolReturningNil.new.process(:index)
assert_equal "Hello nilz!", result.response_body
end
test "when the layout is specified as a symbol and the method doesn't exist, raise an exception" do
assert_raises(NoMethodError, /:nilz/) { WithSymbolAndNoMethod.process(:index) }
assert_raises(NoMethodError, /:nilz/) { WithSymbolAndNoMethod.new.process(:index) }
end
test "when the layout is specified as a symbol and the method returns something besides a string/false/nil, raise an exception" do
assert_raises(ArgumentError) { WithSymbolReturningObj.process(:index) }
assert_raises(ArgumentError) { WithSymbolReturningObj.new.process(:index) }
end
test "when a child controller does not have a layout, use the parent controller layout" do
result = WithStringChild.process(:index)
result = WithStringChild.new.process(:index)
assert_equal "With String Hello string!", result.response_body
end
test "when a child controller has specified a layout, use that layout and not the parent controller layout" do
result = WithStringOverriddenChild.process(:index)
result = WithStringOverriddenChild.new.process(:index)
assert_equal "With Override Hello string!", result.response_body
end
test "when a child controller has an implied layout, use that layout and not the parent controller layout" do
result = WithStringImpliedChild.process(:index)
result = WithStringImpliedChild.new.process(:index)
assert_equal "With Implied Hello string!", result.response_body
end
test "when a child controller specifies layout nil, do not use the parent layout" do
result = WithNilChild.process(:index)
result = WithNilChild.new.process(:index)
assert_equal "Hello string!", result.response_body
end
test "when a grandchild has no layout specified, the child has an implied layout, and the " \
"parent has specified a layout, use the child controller layout" do
result = WithChildOfImplied.process(:index)
result = WithChildOfImplied.new.process(:index)
assert_equal "With Implied Hello string!", result.response_body
end
test "raises an exception when specifying layout true" do
assert_raises ArgumentError do
Object.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
Object.class_eval do
class ::BadOmgFailLolLayout < AbstractControllerTests::Layouts::Base
layout true
end
RUBY_EVAL
end
end
end
end

@ -1,19 +1,27 @@
if ENV['new_base']
puts *caller
raise 'new_base/abstract_unit already loaded'
end
$:.unshift(File.dirname(__FILE__) + '/../lib')
$:.unshift(File.dirname(__FILE__) + '/../../activesupport/lib')
$:.unshift(File.dirname(__FILE__) + '/../../activemodel/lib')
$:.unshift(File.dirname(__FILE__) + '/lib')
$:.unshift(File.dirname(__FILE__) + '/fixtures/helpers')
$:.unshift(File.dirname(__FILE__) + '/fixtures/alternate_helpers')
require 'rubygems'
require 'yaml'
require 'stringio'
require 'test/unit'
ENV['new_base'] = "true"
$stderr.puts "Running old tests on new_base"
gem 'mocha', '>= 0.9.5'
require 'mocha'
require 'test/unit'
require 'active_support'
require 'active_support/test_case'
require 'action_controller/abstract'
require 'action_controller'
require 'fixture_template'
require 'action_controller/testing/process'
require 'action_view/test_case'
require 'action_controller/testing/integration'
require 'active_support/dependencies'
$tags[:new_base] = true
begin
require 'ruby-debug'
@ -23,24 +31,88 @@
# Debugging disabled. `gem install ruby-debug` to enable.
end
require 'action_controller'
require 'action_controller/testing/process'
require 'action_view/test_case'
$tags[:old_base] = true
ActiveSupport::Dependencies.hook!
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
ActionController::Base.logger = nil
ActionController::Routing::Routes.reload rescue nil
ActionController::Base.session_store = nil
# Register danish language for testing
I18n.backend.store_translations 'da', {}
I18n.backend.store_translations 'pt-BR', {}
ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
ActionController::Base.view_paths = FIXTURE_LOAD_PATH
module ActionView
class TestCase
setup do
ActionController::Routing::Routes.draw do |map|
map.connect ':controller/:action/:id'
end
end
end
end
module ActionController
Base.session = {
:key => '_testing_session',
:secret => '8273f16463985e2b3747dc25e30f2528'
}
Base.session_store = nil
class Base
include ActionController::Testing
end
Base.view_paths = FIXTURE_LOAD_PATH
class TestCase
include TestProcess
setup do
ActionController::Routing::Routes.draw do |map|
map.connect ':controller/:action/:id'
end
end
def assert_template(options = {}, message = nil)
validate_request!
hax = @controller._action_view.instance_variable_get(:@_rendered)
case options
when NilClass, String
rendered = (hax[:template] || []).map { |t| t.identifier }
msg = build_message(message,
"expecting <?> but rendering with <?>",
options, rendered.join(', '))
assert_block(msg) do
if options.nil?
hax[:template].blank?
else
rendered.any? { |t| t.match(options) }
end
end
when Hash
if expected_partial = options[:partial]
partials = hax[:partials]
if expected_count = options[:count]
found = partials.detect { |p, _| p.identifier.match(expected_partial) }
actual_count = found.nil? ? 0 : found.second
msg = build_message(message,
"expecting ? to be rendered ? time(s) but rendered ? time(s)",
expected_partial, expected_count, actual_count)
assert(actual_count == expected_count.to_i, msg)
else
msg = build_message(message,
"expecting partial <?> but action rendered <?>",
options[:partial], partials.keys)
assert(partials.keys.any? { |p| p.identifier.match(expected_partial) }, msg)
end
else
assert hax[:partials].empty?,
"Expected no partials to be rendered"
end
end
end
end
end

@ -234,10 +234,13 @@ def test_polymorphic_path_does_not_modify_arguments
with_admin_test_routes do
@project.save
@task.save
options = {}
object_array = [:admin, @project, @task]
assert_no_difference 'object_array.size' do
polymorphic_url(object_array)
end
original_args = [object_array.dup, options.dup]
assert_no_difference('object_array.size') { polymorphic_path(object_array, options) }
assert_equal original_args, [object_array, options]
end
end

@ -126,6 +126,7 @@ def render_with_record_collection
end
class Game < Struct.new(:name, :id)
extend ActiveModel::Naming
def to_param
id.to_s
end

@ -13,6 +13,18 @@ def hello_world() render :template => "test/hello_world"; end
# a standard template
def hello_xml_world() render :template => "test/hello_xml_world"; end
# a standard template rendering PDF
def hello_xml_world_pdf
self.content_type = "application/pdf"
render :template => "test/hello_xml_world"
end
# a standard template rendering PDF
def hello_xml_world_pdf_header
response.headers["Content-Type"] = "application/pdf; charset=utf-8"
render :template => "test/hello_xml_world"
end
# a standard partial
def partial() render :partial => 'test/partial'; end
@ -537,11 +549,13 @@ def test_rendering_xml_sets_content_type
end
def test_rendering_xml_respects_content_type
pending do
@response.headers['type'] = 'application/pdf'
process :hello_xml_world
assert_equal('application/pdf; charset=utf-8', @response.headers['Content-Type'])
end
process :hello_xml_world_pdf
assert_equal('application/pdf; charset=utf-8', @response.headers['Content-Type'])
end
def test_rendering_xml_respects_content_type_when_set_in_the_header
process :hello_xml_world_pdf_header
assert_equal('application/pdf; charset=utf-8', @response.headers['Content-Type'])
end
def test_render_text_with_custom_content_type

@ -87,11 +87,11 @@ def setup
def test_action_methods
@empty_controllers.each do |c|
hide_mocha_methods_from_controller(c)
assert_equal Set.new, c.__send__(:action_methods), "#{c.controller_path} should be empty!"
assert_equal Set.new, c.class.__send__(:action_methods), "#{c.controller_path} should be empty!"
end
@non_empty_controllers.each do |c|
hide_mocha_methods_from_controller(c)
assert_equal Set.new(%w(public_action)), c.__send__(:action_methods), "#{c.controller_path} should not be empty!"
assert_equal Set.new(%w(public_action)), c.class.__send__(:action_methods), "#{c.controller_path} should not be empty!"
end
end
@ -145,7 +145,8 @@ def test_get_on_priv_should_show_selector
def test_method_missing_is_not_an_action_name
use_controller MethodMissingController
assert ! @controller.__send__(:action_methods).include?('method_missing')
assert ! @controller.__send__(:action_method?, 'method_missing')
get :method_missing
assert_response :success

Some files were not shown because too many files have changed in this diff Show More