Merge commit 'mainstream/master'

Conflicts:
	actionpack/lib/action_controller/base/mime_responds.rb
This commit is contained in:
Pratik Naik 2009-05-24 11:54:27 +02:00
commit 7324e46a3f
216 changed files with 3486 additions and 1770 deletions

@ -15,9 +15,11 @@ task :default => :test
%w(test isolated_test rdoc pgem package release).each do |task_name|
desc "Run #{task_name} task for all projects"
task task_name do
errors = []
PROJECTS.each do |project|
system %(cd #{project} && #{env} #{$0} #{task_name})
system(%(cd #{project} && #{env} #{$0} #{task_name})) || errors << project
end
fail("Errors in #{errors.join(', ')}") unless errors.empty?
end
end

@ -1,4 +1,4 @@
require File.dirname(__FILE__) + '/abstract_unit'
require 'abstract_unit'
require 'action_mailer/adv_attr_accessor'
class AdvAttrTest < Test::Unit::TestCase
@ -15,6 +15,4 @@ def test_adv_attr
assert_raise(ArgumentError) {bob.name 'x', 'y'}
end
end
end

@ -1,5 +1,12 @@
*Edge*
* Change integration test helpers to accept Rack environment instead of just HTTP Headers [Pratik Naik]
Before : get '/path', {}, 'Accept' => 'text/javascript'
After : get '/path', {}, 'HTTP_ACCEPT' => 'text/javascript'
* Instead of checking Rails.env.test? in Failsafe middleware, check env["rails.raise_exceptions"] [Bryan Helmkamp]
* Fixed that TestResponse.cookies was returning cookies unescaped #1867 [Doug McInnes]

@ -22,14 +22,15 @@ task :default => [ :test ]
# Run the unit tests
desc "Run all unit tests"
task :test => [:test_action_pack, :test_active_record_integration, :test_new_base]
task :test => [:test_action_pack, :test_active_record_integration, :test_new_base, :test_new_base_on_old_tests]
test_lib_dirs = [ENV["NEW"] ? "test/new_base" : "test", "test/lib"]
Rake::TestTask.new(:test_action_pack) do |t|
t.libs << "test"
t.libs.concat test_lib_dirs
# make sure we include the tests in alphabetical order as on some systems
# this will not happen automatically and the tests (as a whole) will error
t.test_files = Dir.glob( "test/{controller,dispatch,template}/**/*_test.rb" ).sort
t.test_files = Dir.glob( "test/{controller,dispatch,template,html-scanner}/**/*_test.rb" ).sort
t.verbose = true
#t.warning = true
@ -37,24 +38,46 @@ 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|
system(ruby, '-Ilib:test', file)
system(ruby, "-Ilib:#{test_lib_dirs * ':'}", file)
end or raise "Failures"
end
desc 'ActiveRecord Integration Tests'
Rake::TestTask.new(:test_active_record_integration) do |t|
t.libs << "test"
t.libs.concat test_lib_dirs
t.test_files = Dir.glob("test/activerecord/*_test.rb")
t.verbose = true
end
desc 'New Controller Tests'
Rake::TestTask.new(:test_new_base) do |t|
t.libs << "test"
t.libs << "test/new_base" << "test/lib"
t.test_files = Dir.glob("test/{abstract_controller,new_base}/*_test.rb")
t.verbose = true
end
desc 'Old Controller Tests on New Base'
Rake::TestTask.new(:test_new_base_on_old_tests) do |t|
t.libs << "test/new_base" << "test/lib"
# layout
# Dir.glob( "test/{dispatch,template}/**/*_test.rb" ).sort +
# ==== Not ported
# * filters
# * integration
# * test
# * view_paths
t.test_files = %w(
action_pack_assertions addresses_render assert_select
base benchmark caching capture content_type cookie dispatcher
filter_params flash helper http_basic_authentication
http_digest_authentication layout logging mime_responds
record_identifier redirect render render_js render_json
render_other render_xml request_forgery_protection rescue
resources routing selector send_file url_rewriter verification webservice
).map { |name| "test/controller/#{name}_test.rb" }
t.verbose = true
end
# Genereate the RDoc documentation

@ -1,3 +1,7 @@
# Pass NEW=1 to run with the new Base
ENV['RAILS_ENV'] ||= 'production'
ENV['NO_RELOAD'] ||= '1'
$LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
require 'action_controller'
require 'action_controller/new_base' if ENV['NEW']
@ -9,26 +13,30 @@ def index
end
end
n = (ENV['N'] || 10000).to_i
input = StringIO.new('')
def call_index(controller, input, n)
n.times do
controller.action(:index).call({ 'rack.input' => input })
class Runner
def initialize(app)
@app = app
end
puts controller.name
status, headers, body = controller.action(:index).call({ 'rack.input' => input })
puts status
puts headers.to_yaml
puts '---'
body.each do |part|
puts part
def call(env)
env['n'].to_i.times { @app.call(env) }
@app.call(env).tap { |response| report(env, response) }
end
def report(env, response)
out = env['rack.errors']
out.puts response[0], response[1].to_yaml, '---'
response[2].each { |part| out.puts part }
out.puts '---'
end
puts '---'
end
elapsed = Benchmark.realtime { call_index BaseController, input, n }
n = (ENV['N'] || 1000).to_i
input = StringIO.new('')
puts "%dms elapsed, %d requests/sec" % [1000 * elapsed, n / elapsed]
elapsed = Benchmark.realtime do
Runner.new(BaseController.action(:index)).
call('n' => n, 'rack.input' => input, 'rack.errors' => $stdout)
end
puts "%dms elapsed, %d req/sec, %.2f msec/req" %
[1000 * elapsed, n / elapsed, 1000 * elapsed / n]

@ -66,6 +66,7 @@ def self.load_all!
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'

@ -3,6 +3,7 @@
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"

@ -68,11 +68,11 @@ def initialize
self.response_obj = {}
end
def process(action_name)
@_action_name = action_name = action_name.to_s
def process(action)
@_action_name = action_name = action.to_s
unless action_name = method_for_action(action_name)
raise ActionNotFound, "The action '#{action_name}' could not be found"
raise ActionNotFound, "The action '#{action}' could not be found"
end
process_action(action_name)

@ -0,0 +1,28 @@
module AbstractController
module Benchmarker
extend ActiveSupport::DependencyModule
depends_on Logger
module ClassMethods
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
end
end

@ -36,6 +36,17 @@ def #{filter}_filter(*names, &blk)
process_action_callback(:#{filter}, name, options)
end
end
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)
end
end
alias_method :append_#{filter}_filter, :#{filter}_filter
RUBY_EVAL
end
end

@ -24,11 +24,30 @@ def inherited(klass)
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
# 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(*meths)
meths.flatten.each do |meth|
master_helper_module.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
@ -39,14 +58,14 @@ def #{meth}(*args, &blk)
end
end
def helper(*args, &blk)
def helper(*args, &block)
args.flatten.each do |arg|
case arg
when Module
add_template_helper(arg)
end
end
master_helper_module.module_eval(&blk) if block_given?
master_helper_module.module_eval(&block) if block_given?
end
end

@ -4,11 +4,19 @@ module Layouts
depends_on Renderer
included do
extlib_inheritable_accessor :_layout_conditions
self._layout_conditions = {}
end
module ClassMethods
def layout(layout)
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
conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
self._layout_conditions = conditions
@_layout = layout || false # Converts nil to false
_write_layout_method
@ -31,15 +39,15 @@ def _implied_layout_name
def _write_layout_method
case @_layout
when String
self.class_eval %{def _layout() #{@_layout.inspect} end}
self.class_eval %{def _layout(details) #{@_layout.inspect} end}
when Symbol
self.class_eval %{def _layout() #{@_layout} end}
self.class_eval %{def _layout(details) #{@_layout} end}
when false
self.class_eval %{def _layout() end}
self.class_eval %{def _layout(details) end}
else
self.class_eval %{
def _layout
if view_paths.find_by_parts?("#{_implied_layout_name}", {:formats => formats}, "layouts")
def _layout(details)
if view_paths.find_by_parts?("#{_implied_layout_name}", details, "layouts")
"#{_implied_layout_name}"
else
super
@ -49,38 +57,50 @@ def _layout
end
end
end
def _render_template(template, options)
_action_view._render_template_from_controller(template, options[:_layout], options, options[:_partial])
end
private
def _layout() end # This will be overwritten
def _layout(details) end # This will be overwritten
# :api: plugin
# ====
# Override this to mutate the inbound layout name
def _layout_for_name(name)
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, {:formats => formats}, "layouts")
name && view_paths.find_by_parts(name, details, _layout_prefix(name))
end
# TODO: Decide if this is the best hook point for the feature
def _layout_prefix(name)
"layouts"
end
def _default_layout(require_layout = false)
if require_layout && !_layout
def _default_layout(require_layout = false, details = {:formats => formats})
if require_layout && _action_has_layout? && !_layout(details)
raise ArgumentError,
"There was no default layout for #{self.class} in #{view_paths.inspect}"
end
begin
layout = _layout_for_name(_layout)
_layout_for_name(_layout(details), details) if _action_has_layout?
rescue NameError => e
raise NoMethodError,
"You specified #{@_layout.inspect} as the layout, but no such method was found"
end
end
def _action_has_layout?
conditions = _layout_conditions
if only = conditions[:only]
only.include?(action_name)
elsif except = conditions[:except]
!except.include?(action_name)
else
true
end
end
end
end

@ -1,4 +1,5 @@
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/logger'
module AbstractController
module Logger

@ -33,16 +33,12 @@ def render(*args)
#
# :api: plugin
def render_to_body(options = {})
name = options[:_template_name] || action_name
# TODO: Refactor so we can just use the normal template logic for this
if options[:_partial_object]
_action_view._render_partial_from_controller(options)
else
options[:_template] ||= view_paths.find_by_parts(name.to_s, {:formats => formats},
options[:_prefix], options[:_partial])
_render_template(options[:_template], options)
else
_determine_template(options)
_render_template(options)
end
end
@ -56,8 +52,8 @@ def render_to_string(options = {})
AbstractController::Renderer.body_to_s(render_to_body(options))
end
def _render_template(template, options)
_action_view._render_template_from_controller(template, nil, options, options[:_partial])
def _render_template(options)
_action_view._render_template_from_controller(options[:_template], options[:_layout], options, options[:_partial])
end
def view_paths() _view_paths end
@ -74,6 +70,16 @@ def self.body_to_s(body)
end
end
private
def _determine_template(options)
name = (options[:_template_name] || action_name).to_s
options[:_template] ||= view_paths.find_by_parts(
name, { :formats => formats }, options[:_prefix], options[:_partial]
)
end
module ClassMethods
def append_view_path(path)

@ -242,7 +242,6 @@ class Base
# Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
# and images to a dedicated asset server away from the main web server. Example:
# ActionController::Base.asset_host = "http://assets.example.com"
@@asset_host = ""
cattr_accessor :asset_host
# All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors.
@ -368,9 +367,8 @@ def session
attr_reader :template
def action(name, env)
# HACK: For global rescue to have access to the original request and response
request = env["action_controller.rescue.request"] ||= ActionDispatch::Request.new(env)
response = env["action_controller.rescue.response"] ||= ActionDispatch::Response.new
request = ActionDispatch::Request.new(env)
response = ActionDispatch::Response.new
self.action_name = name && name.to_s
process(request, response).to_a
end
@ -449,55 +447,6 @@ def append_view_path(path)
@view_paths = superclass.view_paths.dup if @view_paths.nil?
@view_paths.push(*path)
end
# Replace sensitive parameter data from the request log.
# Filters parameters that have any of the arguments as a substring.
# Looks in all subhashes of the param hash for keys to filter.
# If a block is given, each key and value of the parameter hash and all
# subhashes is passed to it, the value or key
# can be replaced using String#replace or similar method.
#
# Examples:
# filter_parameter_logging
# => Does nothing, just slows the logging process down
#
# filter_parameter_logging :password
# => replaces the value to all keys matching /password/i with "[FILTERED]"
#
# filter_parameter_logging :foo, "bar"
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
#
# filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i }
# => reverses the value to all keys matching /secret/i
#
# filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
# => reverses the value to all keys matching /secret/i, and
# replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
def filter_parameter_logging(*filter_words, &block)
parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
define_method(:filter_parameters) do |unfiltered_parameters|
filtered_parameters = {}
unfiltered_parameters.each do |key, value|
if key =~ parameter_filter
filtered_parameters[key] = '[FILTERED]'
elsif value.is_a?(Hash)
filtered_parameters[key] = filter_parameters(value)
elsif block_given?
key = key.dup
value = value.dup if value
yield key, value
filtered_parameters[key] = value
else
filtered_parameters[key] = value
end
end
filtered_parameters
end
protected :filter_parameters
end
@@exempt_from_layout = [ActionView::TemplateHandlers::RJS]
@ -854,13 +803,6 @@ def log_processing_for_request_id
logger.info(request_id)
end
def log_processing_for_parameters
parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
parameters = parameters.except!(:controller, :action, :format, :_method)
logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
end
def default_render #:nodoc:
render
end
@ -934,7 +876,7 @@ def process_cleanup
[ Filters, Layout, Renderer, Redirector, Responder, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
Cookies, Caching, Verification, Streaming, SessionManagement,
HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, RecordIdentifier,
RequestForgeryProtection, Translation
RequestForgeryProtection, Translation, FilterParameterLogging
].each do |mod|
include mod
end

@ -21,7 +21,7 @@ module ClassMethods
# 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
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)")

@ -571,12 +571,7 @@ def skip_filter(*filters)
# Returns an array of Filter objects for this controller.
def filter_chain
if chain = read_inheritable_attribute('filter_chain')
return chain
else
write_inheritable_attribute('filter_chain', FilterChain.new)
return filter_chain
end
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.

@ -26,9 +26,18 @@ module ActionController #:nodoc:
#
# See docs on the FlashHash class for more details about the flash.
module Flash
def self.included(base)
base.class_eval do
include InstanceMethods
extend ActiveSupport::DependencyModule
# TODO : Remove the defined? check when new base is the main base
depends_on 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
@ -135,29 +144,50 @@ def use(k=nil, v=true)
end
end
module InstanceMethods #:nodoc:
module InstanceMethodsForBase #:nodoc:
protected
def perform_action_with_flash
perform_action_without_flash
remove_instance_variable(:@_flash) if defined? @_flash
end
def reset_session_with_flash
reset_session_without_flash
remove_instance_variable(:@_flash) if defined? @_flash
end
def perform_action_with_flash
perform_action_without_flash
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:
unless defined? @_flash
@_flash = session["flash"] ||= FlashHash.new
@_flash.sweep
end
def reset_session_with_flash
reset_session_without_flash
remove_instance_variable(:@_flash) if defined?(@_flash)
end
end
@_flash
end
module InstanceMethodsForNewBase #:nodoc:
protected
def reset_session
super
remove_flash_instance_variable
end
def process_action(method_name)
super
remove_flash_instance_variable
end
def remove_flash_instance_variable
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:
unless defined?(@_flash)
@_flash = session["flash"] ||= FlashHash.new
@_flash.sweep
end
@_flash
end
end
end

@ -0,0 +1,97 @@
module ActionController
module FilterParameterLogging
extend ActiveSupport::DependencyModule
# TODO : Remove the defined? check when new base is the main base
if defined?(ActionController::Http)
depends_on AbstractController::Logger
end
included do
if defined?(ActionController::Http)
include InstanceMethodsForNewBase
end
end
module ClassMethods
# Replace sensitive parameter data from the request log.
# Filters parameters that have any of the arguments as a substring.
# Looks in all subhashes of the param hash for keys to filter.
# If a block is given, each key and value of the parameter hash and all
# subhashes is passed to it, the value or key
# can be replaced using String#replace or similar method.
#
# Examples:
# filter_parameter_logging
# => Does nothing, just slows the logging process down
#
# filter_parameter_logging :password
# => replaces the value to all keys matching /password/i with "[FILTERED]"
#
# filter_parameter_logging :foo, "bar"
# => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
#
# filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i }
# => reverses the value to all keys matching /secret/i
#
# filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
# => reverses the value to all keys matching /secret/i, and
# replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
def filter_parameter_logging(*filter_words, &block)
parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
define_method(:filter_parameters) do |unfiltered_parameters|
filtered_parameters = {}
unfiltered_parameters.each do |key, value|
if key =~ parameter_filter
filtered_parameters[key] = '[FILTERED]'
elsif value.is_a?(Hash)
filtered_parameters[key] = filter_parameters(value)
elsif block_given?
key = key.dup
value = value.dup if value
yield key, value
filtered_parameters[key] = value
else
filtered_parameters[key] = value
end
end
filtered_parameters
end
protected :filter_parameters
end
end
module InstanceMethodsForNewBase
# TODO : Fix the order of information inside such that it's exactly same as the old base
def process(*)
ret = super
if logger
parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
parameters = parameters.except!(:controller, :action, :format, :_method, :only_path)
unless parameters.empty?
# TODO : Move DelayedLog to AS
log = AbstractController::Logger::DelayedLog.new { " Parameters: #{parameters.inspect}" }
logger.info(log)
end
end
ret
end
end
private
# TODO : This method is not needed for the new base
def log_processing_for_parameters
parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
parameters = parameters.except!(:controller, :action, :format, :_method)
logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
end
end
end

@ -3,23 +3,19 @@
# FIXME: helper { ... } is broken on Ruby 1.9
module ActionController #:nodoc:
module Helpers #:nodoc:
def self.included(base)
extend ActiveSupport::DependencyModule
included do
# Initialize the base module to aggregate its helpers.
base.class_inheritable_accessor :master_helper_module
base.master_helper_module = Module.new
class_inheritable_accessor :master_helper_module
self.master_helper_module = Module.new
# Set the default directory for helpers
base.class_inheritable_accessor :helpers_dir
base.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
class_inheritable_accessor :helpers_dir
self.helpers_dir = (defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers")
# Extend base with class methods to declare helpers.
base.extend(ClassMethods)
base.class_eval do
# Wrap inherited to create a new master helper module for subclasses.
class << self
alias_method_chain :inherited, :helper
end
class << self
alias_method_chain :inherited, :helper
end
end

@ -194,9 +194,10 @@ def validate_digest_response(request, realm, &password_procedure)
if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque]
password = password_procedure.call(credentials[:username])
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
[true, false].any? do |password_is_ha1|
expected = expected_response(request.env['REQUEST_METHOD'], request.env['REQUEST_URI'], credentials, password, password_is_ha1)
expected = expected_response(method, request.env['REQUEST_URI'], credentials, password, password_is_ha1)
expected == credentials[:response]
end
end

@ -1,123 +1,103 @@
module ActionController #:nodoc:
module MimeResponds #:nodoc:
def self.included(base)
base.module_eval do
include ActionController::MimeResponds::InstanceMethods
end
end
module InstanceMethods
# Without web-service support, an action which collects the data for displaying a list of people
# might look something like this:
#
# def index
# @people = Person.find(:all)
# end
#
# Here's the same action, with web-service support baked in:
#
# def index
# @people = Person.find(:all)
#
# respond_to do |format|
# format.html
# format.xml { render :xml => @people.to_xml }
# end
# end
#
# What that says is, "if the client wants HTML in response to this action, just respond as we
# would have before, but if the client wants XML, return them the list of people in XML format."
# (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
#
# Supposing you have an action that adds a new person, optionally creating their company
# (by name) if it does not already exist, without web-services, it might look like this:
#
# def create
# @company = Company.find_or_create_by_name(params[:company][:name])
# @person = @company.people.create(params[:person])
#
# redirect_to(person_list_url)
# end
#
# Here's the same action, with web-service support baked in:
#
# def create
# company = params[:person].delete(:company)
# @company = Company.find_or_create_by_name(company[:name])
# @person = @company.people.create(params[:person])
#
# respond_to do |format|
# format.html { redirect_to(person_list_url) }
# format.js
# format.xml { render :xml => @person.to_xml(:include => @company) }
# end
# end
#
# If the client wants HTML, we just redirect them back to the person list. If they want Javascript
# (format.js), then it is an RJS request and we render the RJS template associated with this action.
# Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
# include the person's company in the rendered XML, so you get something like this:
#
# <person>
# <id>...</id>
# ...
# <company>
# <id>...</id>
# <name>...</name>
# ...
# </company>
# </person>
#
# Note, however, the extra bit at the top of that action:
#
# company = params[:person].delete(:company)
# @company = Company.find_or_create_by_name(company[:name])
#
# This is because the incoming XML document (if a web-service request is in process) can only contain a
# single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
#
# person[name]=...&person[company][name]=...&...
#
# And, like this (xml-encoded):
#
# <person>
# <name>...</name>
# <company>
# <name>...</name>
# </company>
# </person>
#
# In other words, we make the request so that it operates on a single entity's person. Then, in the action,
# we extract the company data from the request, find or create the company, and then create the new person
# with the remaining data.
#
# Note that you can define your own XML parameter parser which would allow you to describe multiple entities
# in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
# and accept Rails' defaults, life will be much easier.
#
# Further more, you may call the #any method on the block's object in order to run the same code for different responses.
# def index
#
# respond_to do |format|
# format.html { @people = People.all(:limit => 10) }
# format.any(:xml, :atom) { @people = People.all }
# end
# end
#
# This will limit the @people variable to 10 people records if we're requesting HTML, but will list all the
# people for any xml or atom request.
#
# If you need to use a MIME type which isn't supported by default, you can register your own handlers in
# environment.rb as follows.
#
# Mime::Type.register "image/jpg", :jpg
def respond_to(*types, &block)
raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
block ||= lambda { |responder| types.each { |type| responder.send(type) } }
responder = Responder.new(self)
block.call(responder)
responder.respond
end
# Without web-service support, an action which collects the data for displaying a list of people
# might look something like this:
#
# def index
# @people = Person.find(:all)
# end
#
# Here's the same action, with web-service support baked in:
#
# def index
# @people = Person.find(:all)
#
# respond_to do |format|
# format.html
# format.xml { render :xml => @people.to_xml }
# end
# end
#
# What that says is, "if the client wants HTML in response to this action, just respond as we
# would have before, but if the client wants XML, return them the list of people in XML format."
# (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
#
# Supposing you have an action that adds a new person, optionally creating their company
# (by name) if it does not already exist, without web-services, it might look like this:
#
# def create
# @company = Company.find_or_create_by_name(params[:company][:name])
# @person = @company.people.create(params[:person])
#
# redirect_to(person_list_url)
# end
#
# Here's the same action, with web-service support baked in:
#
# def create
# company = params[:person].delete(:company)
# @company = Company.find_or_create_by_name(company[:name])
# @person = @company.people.create(params[:person])
#
# respond_to do |format|
# format.html { redirect_to(person_list_url) }
# format.js
# format.xml { render :xml => @person.to_xml(:include => @company) }
# end
# end
#
# If the client wants HTML, we just redirect them back to the person list. If they want Javascript
# (format.js), then it is an RJS request and we render the RJS template associated with this action.
# Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
# include the person's company in the rendered XML, so you get something like this:
#
# <person>
# <id>...</id>
# ...
# <company>
# <id>...</id>
# <name>...</name>
# ...
# </company>
# </person>
#
# Note, however, the extra bit at the top of that action:
#
# company = params[:person].delete(:company)
# @company = Company.find_or_create_by_name(company[:name])
#
# This is because the incoming XML document (if a web-service request is in process) can only contain a
# single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
#
# person[name]=...&person[company][name]=...&...
#
# And, like this (xml-encoded):
#
# <person>
# <name>...</name>
# <company>
# <name>...</name>
# </company>
# </person>
#
# In other words, we make the request so that it operates on a single entity's person. Then, in the action,
# we extract the company data from the request, find or create the company, and then create the new person
# with the remaining data.
#
# Note that you can define your own XML parameter parser which would allow you to describe multiple entities
# in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow
# and accept Rails' defaults, life will be much easier.
#
# If you need to use a MIME type which isn't supported by default, you can register your own handlers in
# environment.rb as follows.
#
# Mime::Type.register "image/jpg", :jpg
def respond_to(*types, &block)
raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
block ||= lambda { |responder| types.each { |type| responder.send(type) } }
responder = Responder.new(self)
block.call(responder)
responder.respond
end
class Responder #:nodoc:
@ -139,8 +119,14 @@ def custom(mime_type, &block)
@order << mime_type
@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.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
end

@ -3,12 +3,26 @@ class InvalidAuthenticityToken < ActionControllerError #:nodoc:
end
module RequestForgeryProtection
def self.included(base)
base.class_eval do
helper_method :form_authenticity_token
helper_method :protect_against_forgery?
extend ActiveSupport::DependencyModule
# TODO : Remove the defined? check when new base is the main base
if defined?(ActionController::Http)
depends_on AbstractController::Helpers, Session
end
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
# 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
base.extend(ClassMethods)
helper_method :form_authenticity_token
helper_method :protect_against_forgery?
end
# Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a

@ -2,6 +2,13 @@ module ActionController #:nodoc:
# Methods for sending arbitrary data and for streaming files to the browser,
# instead of rendering.
module Streaming
extend ActiveSupport::DependencyModule
# TODO : Remove the defined? check when new base is the main base
if defined?(ActionController::Http)
depends_on ActionController::Renderer
end
DEFAULT_SEND_FILE_OPTIONS = {
:type => 'application/octet-stream'.freeze,
:disposition => 'attachment'.freeze,
@ -88,6 +95,7 @@ def send_file(path, options = {}) #:doc:
head options[:status], X_SENDFILE_HEADER => path
else
if options[:stream]
# TODO : Make render :text => proc {} work with the new base
render :status => options[:status], :text => Proc.new { |response, output|
logger.info "Streaming file #{path}" unless logger.nil?
len = options[:buffer_size] || 4096

@ -1,7 +1,10 @@
module ActionController #:nodoc:
module Verification #:nodoc:
def self.included(base) #:nodoc:
base.extend(ClassMethods)
extend ActiveSupport::DependencyModule
# TODO : Remove the defined? check when new base is the main base
if defined?(ActionController::Http)
depends_on AbstractController::Callbacks, Session, Flash, Renderer
end
# This module provides a class-level method for specifying that certain
@ -102,7 +105,7 @@ def prereqs_invalid?(options) # :nodoc:
end
def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
[*options[:params] ].find { |v| params[v].nil? } ||
[*options[:params] ].find { |v| v && params[v.to_sym].nil? } ||
[*options[:session]].find { |v| session[v].nil? } ||
[*options[:flash] ].find { |v| flash[v].nil? }
end

@ -24,31 +24,31 @@ module ActionController #:nodoc:
# ActionController::Base.cache_store = :mem_cache_store, "localhost"
# ActionController::Base.cache_store = MyOwnStore.new("parameter")
module Caching
extend ActiveSupport::DependencyModule
autoload :Actions, 'action_controller/caching/actions'
autoload :Fragments, 'action_controller/caching/fragments'
autoload :Pages, 'action_controller/caching/pages'
autoload :Sweeper, 'action_controller/caching/sweeping'
autoload :Sweeping, 'action_controller/caching/sweeping'
def self.included(base) #:nodoc:
base.class_eval do
@@cache_store = nil
cattr_reader :cache_store
included do
@@cache_store = nil
cattr_reader :cache_store
# Defines the storage option for cached fragments
def self.cache_store=(store_option)
@@cache_store = ActiveSupport::Cache.lookup_store(store_option)
end
# Defines the storage option for cached fragments
def self.cache_store=(store_option)
@@cache_store = ActiveSupport::Cache.lookup_store(store_option)
end
include Pages, Actions, Fragments
include Sweeping if defined?(ActiveRecord)
include Pages, Actions, Fragments
include Sweeping if defined?(ActiveRecord)
@@perform_caching = true
cattr_accessor :perform_caching
@@perform_caching = true
cattr_accessor :perform_caching
def self.cache_configured?
perform_caching && cache_store
end
def self.cache_configured?
perform_caching && cache_store
end
end

@ -61,8 +61,14 @@ def caches_action(*actions)
filter_options = { :only => actions, :if => options.delete(:if), :unless => options.delete(:unless) }
cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path), :store_options => options)
around_filter(filter_options) do |controller, action|
cache_filter.filter(controller, action)
# TODO: Remove this once new base is swapped in.
if defined?(Http)
around_filter cache_filter, filter_options
else
around_filter(filter_options) do |controller, action|
cache_filter.filter(controller, action)
end
end
end
end
@ -85,14 +91,22 @@ def initialize(options, &block)
@options = options
end
# TODO: Remove once New Base is merged
def filter(controller, action)
should_continue = before(controller)
action.call if should_continue
after(controller)
end
def around_process_action(controller)
should_continue = before(controller)
yield if should_continue
after(controller)
end
def before(controller)
cache_path = ActionCachePath.new(controller, path_options_for(controller, @options.slice(:cache_path)))
if cache = controller.read_fragment(cache_path.path, @options[:store_options])
controller.rendered_action_cache = true
set_content_type!(controller, cache_path.extension)
@ -129,7 +143,9 @@ def cache_layout?
end
def content_for_layout(controller)
controller.template.layout && controller.template.instance_variable_get('@cached_content_for_layout')
# TODO: Remove this when new base is merged in
template = controller.respond_to?(:template) ? controller.template : controller._action_view
template.layout && template.instance_variable_get('@cached_content_for_layout')
end
end

@ -1,46 +1,11 @@
require 'active_support/core_ext/module/delegation'
module ActionController
# Dispatches requests to the appropriate controller and takes care of
# reloading the app after each request when Dependencies.load? is true.
class Dispatcher
class << self
def define_dispatcher_callbacks(cache_classes)
unless cache_classes
# Development mode callbacks
before_dispatch :reload_application
after_dispatch :cleanup_application
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
end
if defined?(ActiveRecord)
to_prepare(:activerecord_instantiate_observers) { ActiveRecord::Base.instantiate_observers }
end
after_dispatch :flush_logger if Base.logger && Base.logger.respond_to?(:flush)
to_prepare do
I18n.reload!
end
end
# Add a preparation callback. Preparation callbacks are run before every
# request in development mode, and before the first request in production
# mode.
#
# An optional identifier may be supplied for the callback. If provided,
# to_prepare may be called again with the same identifier to replace the
# existing callback. Passing an identifier is a suggested practice if the
# code adding a preparation block may be reloaded.
def to_prepare(identifier = nil, &block)
@prepare_dispatch_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier)
@prepare_dispatch_callbacks.replace_or_append!(callback)
end
def run_prepare_callbacks
new.send :run_callbacks, :prepare_dispatch
end
end
cattr_accessor :prepare_each_request
self.prepare_each_request = false
cattr_accessor :router
self.router = Routing::Routes
@ -51,37 +16,50 @@ def run_prepare_callbacks
middleware.instance_eval(File.read(middlewares), middlewares, 1)
end
include ActiveSupport::Callbacks
define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
class << self
def define_dispatcher_callbacks(cache_classes)
unless cache_classes
# Run prepare callbacks before every request in development mode
self.prepare_each_request = true
def initialize
@app = @@middleware.build(@@router)
freeze
end
# Development mode callbacks
ActionDispatch::Callbacks.before_dispatch do |app|
ActionController::Dispatcher.router.reload
end
def call(env)
run_callbacks :before_dispatch
@app.call(env)
ensure
run_callbacks :after_dispatch, :enumerator => :reverse_each
end
ActionDispatch::Callbacks.after_dispatch do
# Cleanup the application before processing the current request.
ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
ActiveSupport::Dependencies.clear
ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
end
def reload_application
# Run prepare callbacks before every request in development mode
run_callbacks :prepare_dispatch
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
end
@@router.reload
end
if defined?(ActiveRecord)
to_prepare(:activerecord_instantiate_observers) do
ActiveRecord::Base.instantiate_observers
end
end
def cleanup_application
# Cleanup the application before processing the current request.
ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
ActiveSupport::Dependencies.clear
ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
end
if Base.logger && Base.logger.respond_to?(:flush)
after_dispatch do
Base.logger.flush
end
end
def flush_logger
Base.logger.flush
to_prepare do
I18n.reload!
end
end
delegate :to_prepare, :prepare_dispatch, :before_dispatch, :after_dispatch,
:to => ActionDispatch::Callbacks
def new
@@middleware.build(@@router)
end
end
end
end

@ -2,8 +2,8 @@
!ActionController::Base.allow_concurrency
}
use "ActionDispatch::Failsafe"
use "ActionDispatch::ShowExceptions", lambda { ActionController::Base.consider_all_requests_local }
use "ActionDispatch::Callbacks", lambda { ActionController::Dispatcher.prepare_each_request }
use "ActionDispatch::Rescue", lambda {
controller = (::ApplicationController rescue ActionController::Base)
# TODO: Replace with controller.action(:_rescue_action)

@ -7,13 +7,19 @@ module ActionController
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'
@ -21,9 +27,18 @@ module ActionController
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
require 'action_dispatch'
require 'action_view'
require 'action_view'

@ -2,41 +2,56 @@ module ActionController
class Base < Http
abstract!
include AbstractController::Benchmarker
include AbstractController::Callbacks
include AbstractController::Helpers
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
# 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 process_action(method_name)
ret = super
render if response_body.nil?
default_render if response_body.nil?
ret
end
def _implicit_render
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)
"_implicit_render"
"default_render"
end
end
end
@ -61,7 +76,7 @@ def self.app_loaded!
end
end
def render_to_body(action = nil, options = {})
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)
@ -78,11 +93,25 @@ def render_to_body(action = nil, options = {})
if options.key?(:action) && options[:action].to_s.index("/")
options[:template] = options.delete(:action)
end
# options = {:template => options.to_s} if options.is_a?(String) || options.is_a?(Symbol)
super(options) || " "
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+.
@ -140,4 +169,4 @@ def redirect_to(options = {}, response_status = {}) #:doc:
super(url, status)
end
end
end
end

@ -1,7 +1,10 @@
module ActionController
module Rails2Compatibility
extend ActiveSupport::DependencyModule
class ::ActionController::ActionControllerError < StandardError #:nodoc:
end
# Temporary hax
included do
::ActionController::UnknownAction = ::AbstractController::ActionNotFound
@ -53,10 +56,23 @@ module Rails2Compatibility
cattr_accessor :consider_all_requests_local
self.consider_all_requests_local = true
# Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
# and images to a dedicated asset server away from the main web server. Example:
# ActionController::Base.asset_host = "http://assets.example.com"
cattr_accessor :asset_host
end
# For old tests
def initialize_template_class(*) end
def assign_shortcuts(*) end
# TODO: Remove this after we flip
def template
_action_view
end
module ClassMethods
def protect_from_forgery() end
def consider_all_requests_local() end
def rescue_action(env)
raise env["action_dispatch.rescue.exception"]
@ -80,7 +96,9 @@ def render_to_body(options)
options[:text] = nil if options[:nothing] == true
super
body = super
body = [' '] if body.blank?
body
end
def _handle_method_missing
@ -91,10 +109,12 @@ def method_for_action(action_name)
super || (respond_to?(:method_missing) && "_handle_method_missing")
end
def _layout_for_name(name)
name &&= name.sub(%r{^/?layouts/}, '')
super
def _layout_prefix(name)
super unless name =~ /\blayouts/
end
def performed?
response_body
end
end
end
end

@ -57,7 +57,7 @@ def head(*args)
raise ArgumentError, "too few arguments to head"
end
options = args.extract_options!
status = interpret_status(args.shift || options.delete(:status) || :ok)
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

@ -0,0 +1,130 @@
require 'active_support/core_ext/load_error'
require 'active_support/core_ext/name_error'
require 'active_support/dependencies'
module ActionController
module Helpers
extend ActiveSupport::DependencyModule
depends_on 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 # ClassMethods
end
end

@ -37,7 +37,7 @@ def self.call(env)
end
delegate :headers, :to => "@_response"
def params
@_params ||= @_request.parameters
end
@ -60,7 +60,6 @@ def self.action(name)
# :api: private
def to_rack
@_response.body = response_body
@_response.prepare!
@_response.to_a
end

@ -11,23 +11,20 @@ def _implied_layout_name
end
end
def render_to_body(options)
# render :text => ..., :layout => ...
# or
# render :anything_else
if (!options.key?(:text) && !options.key?(:inline) && !options.key?(:partial)) || options.key?(:layout)
options[:_layout] = options.key?(:layout) ? _layout_for_option(options[:layout]) : _default_layout
end
super
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)
def _layout_for_option(name, details)
case name
when String then _layout_for_name(name)
when true then _default_layout(true)
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,

@ -0,0 +1,107 @@
module ActionController
module RenderOptions
extend ActiveSupport::DependencyModule
included do
extlib_inheritable_accessor :_renderers
self._renderers = []
end
module ClassMethods
def _write_render_options
renderers = _renderers.map do |r|
<<-RUBY_EVAL
if options.key?(:#{r})
_process_options(options)
return _render_#{r}(options[:#{r}], options)
end
RUBY_EVAL
end
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def _handle_render_options(options)
#{renderers.join}
end
RUBY_EVAL
end
def _add_render_option(name)
_renderers << name
_write_render_options
end
end
def render_to_body(options)
_handle_render_options(options) || super
end
end
module RenderOption
extend ActiveSupport::DependencyModule
included do
extend ActiveSupport::DependencyModule
depends_on ::ActionController::RenderOptions
def self.register_renderer(name)
included { _add_render_option(name) }
end
end
end
module Renderers
module Json
include RenderOption
register_renderer :json
def _render_json(json, options)
json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
response.content_type ||= Mime::JSON
self.response_body = json
end
end
module Js
include RenderOption
register_renderer :js
def _render_js(js, options)
response.content_type ||= Mime::JS
self.response_body = js
end
end
module Xml
include RenderOption
register_renderer :xml
def _render_xml(xml, options)
response.content_type ||= Mime::XML
self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml
end
end
module Rjs
include RenderOption
register_renderer :update
def _render_update(proc, options)
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(_action_view, &proc)
response.content_type = Mime::JS
self.response_body = generator.to_s
end
end
module All
extend ActiveSupport::DependencyModule
included do
include ::ActionController::Renderers::Json
include ::ActionController::Renderers::Js
include ::ActionController::Renderers::Xml
include ::ActionController::Renderers::Rjs
end
end
end
end

@ -4,17 +4,45 @@ module Renderer
depends_on AbstractController::Renderer
def initialize(*)
self.formats = [:html]
def process_action(*)
self.formats = request.formats.map {|x| x.to_sym}
super
end
def response_body=(body)
response.body = body if response
super
end
def render(options)
super
options[:_template] ||= _action_view._partial
response.content_type ||= begin
mime = options[:_template].mime_type
formats.include?(mime && mime.to_sym) || formats.include?(:all) ? mime : Mime::Type.lookup_by_extension(formats.first)
end
response_body
end
def render_to_body(options)
_process_options(options)
if options.key?(:partial)
_render_partial(options[:partial], options)
end
super
end
private
def _prefix
controller_path
end
def _determine_template(options)
if options.key?(:text)
options[:_template] = ActionView::TextTemplate.new(_text(options))
template = nil
options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first)
elsif options.key?(:inline)
handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {})
@ -23,35 +51,14 @@ def render_to_body(options)
options[:_template_name] = options[:template]
elsif options.key?(:file)
options[:_template_name] = options[:file]
elsif options.key?(:partial)
_render_partial(options[:partial], options)
else
elsif !options.key?(:partial)
options[:_template_name] = (options[:action] || action_name).to_s
options[:_prefix] = _prefix
end
ret = super(options)
options[:_template] ||= _action_view._partial
response.content_type ||= options[:_template].mime_type
ret
super
end
private
def _prefix
controller_path
end
def _text(options)
text = options[:text]
case text
when nil then " "
else text.to_s
end
end
def _render_partial(partial, options)
case partial
when true
@ -68,9 +75,10 @@ def _render_partial(partial, options)
end
def _process_options(options)
status, content_type = options.values_at(:status, :content_type)
response.status = status.to_i if status
status, content_type, location = options.values_at(:status, :content_type, :location)
response.status = status if status
response.content_type = content_type if content_type
response.headers["Location"] = url_for(location) if location
end
end
end

@ -0,0 +1,11 @@
module ActionController
module Session
def session
@_request.session
end
def reset_session
@_request.reset_session
end
end
end

@ -1,13 +1,13 @@
module ActionController
module Testing
# OMG MEGA HAX
def process_with_test(request, response)
def process_with_new_base_test(request, response)
@_request = request
@_response = response
@_response.request = request
ret = process(request.parameters[:action])
@_response.body = self.response_body || " "
@_response.body ||= self.response_body
@_response.prepare!
set_test_assigns
ret
@ -20,6 +20,11 @@ def set_test_assigns
@assigns[name] = value
end
end
# TODO : Rewrite tests using controller.headers= to use Rack env
def headers=(new_headers)
@_response ||= ActionDispatch::Response.new
@_response.headers.replace(new_headers)
end
end
end
end

@ -1,5 +1,10 @@
module ActionController
module UrlFor
def process_action(*)
initialize_current_url
super
end
def initialize_current_url
@url = UrlRewriter.new(request, params.clone)
end

@ -2,6 +2,9 @@
require 'uri'
require 'active_support/test_case'
require 'rack/mock_session'
require 'rack/test/cookie_jar'
module ActionController
module Integration #:nodoc:
module RequestHelpers
@ -62,8 +65,8 @@ def head(path, parameters = nil, headers = nil)
# with 'HTTP_' if not already.
def xml_http_request(request_method, path, parameters = nil, headers = nil)
headers ||= {}
headers['X-Requested-With'] = 'XMLHttpRequest'
headers['Accept'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
headers['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
headers['HTTP_ACCEPT'] ||= [Mime::JS, Mime::HTML, Mime::XML, 'text/xml', Mime::ALL].join(', ')
process(request_method, path, parameters, headers)
end
alias xhr :xml_http_request
@ -121,6 +124,8 @@ def delete_via_redirect(path, parameters = nil, headers = nil)
# IntegrationTest#open_session, rather than instantiating
# Integration::Session directly.
class Session
DEFAULT_HOST = "www.example.com"
include Test::Unit::Assertions
include ActionDispatch::Assertions
include ActionController::TestProcess
@ -145,7 +150,9 @@ class Session
# A map of the cookies returned by the last response, and which will be
# sent with the next request.
attr_reader :cookies
def cookies
@mock_session.cookie_jar
end
# A reference to the controller instance used by the last request.
attr_reader :controller
@ -172,11 +179,11 @@ def initialize(app = nil)
# session.reset!
def reset!
@https = false
@cookies = {}
@mock_session = Rack::MockSession.new(@app, DEFAULT_HOST)
@controller = @request = @response = nil
@request_count = 0
self.host = "www.example.com"
self.host = DEFAULT_HOST
self.remote_addr = "127.0.0.1"
self.accept = "text/xml,application/xml,application/xhtml+xml," +
"text/html;q=0.9,text/plain;q=0.8,image/png," +
@ -227,8 +234,9 @@ def url_for(options)
end
private
# Performs the actual request.
def process(method, path, parameters = nil, headers = nil)
def process(method, path, parameters = nil, rack_environment = nil)
if path =~ %r{://}
location = URI.parse(path)
https! URI::HTTPS === location if location.scheme
@ -242,8 +250,6 @@ def process(method, path, parameters = nil, headers = nil)
end
end
ActionController::Base.clear_last_instantiation!
opts = {
:method => method,
:params => parameters,
@ -258,34 +264,23 @@ def process(method, path, parameters = nil, headers = nil)
"HTTP_HOST" => host,
"REMOTE_ADDR" => remote_addr,
"CONTENT_TYPE" => "application/x-www-form-urlencoded",
"HTTP_ACCEPT" => accept,
"HTTP_COOKIE" => cookies.inject("") { |string, (name, value)|
string << "#{name}=#{value}; "
}
"HTTP_ACCEPT" => accept
}
env = Rack::MockRequest.env_for(path, opts)
(headers || {}).each do |key, value|
key = key.to_s.upcase.gsub(/-/, "_")
key = "HTTP_#{key}" unless env.has_key?(key) || key =~ /^HTTP_/
(rack_environment || {}).each do |key, value|
env[key] = value
end
app = Rack::Lint.new(@app)
status, headers, body = app.call(env)
mock_response = ::Rack::MockResponse.new(status, headers, body)
@controller = ActionController::Base.capture_instantiation do
@mock_session.request(URI.parse(path), env)
end
@request_count += 1
@request = ActionDispatch::Request.new(env)
@response = ActionDispatch::TestResponse.from_response(mock_response)
@cookies.merge!(@response.cookies)
@response = ActionDispatch::TestResponse.from_response(@mock_session.last_response)
@html_document = nil
if @controller = ActionController::Base.last_instantiation
@controller.send(:set_test_assigns)
end
return response.status
end
@ -306,11 +301,10 @@ def generic_url_rewriter
# A module used to extend ActionController::Base, so that integration tests
# can capture the controller used to satisfy a request.
module ControllerCapture #:nodoc:
def self.included(base)
base.extend(ClassMethods)
base.class_eval do
alias_method_chain :initialize, :capture
end
extend ActiveSupport::DependencyModule
included do
alias_method_chain :initialize, :capture
end
def initialize_with_capture(*args)
@ -321,8 +315,10 @@ def initialize_with_capture(*args)
module ClassMethods #:nodoc:
mattr_accessor :last_instantiation
def clear_last_instantiation!
def capture_instantiation
self.last_instantiation = nil
yield
return last_instantiation
end
end
end

@ -41,6 +41,7 @@ def assign_parameters(controller_path, action, parameters)
end
def recycle!
@formats = nil
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
@env['action_dispatch.request.query_parameters'] = {}
@ -132,9 +133,6 @@ def process(action, parameters = nil, session = nil, flash = nil, http_method =
@request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
build_request_uri(action, parameters)
@request.env["action_controller.rescue.request"] = @request
@request.env["action_controller.rescue.response"] = @response
Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest
env = @request.env

@ -40,6 +40,8 @@ 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.env['REQUEST_METHOD'] = http_method
@ -53,7 +55,8 @@ def process(action, parameters = nil, session = nil, flash = nil, http_method =
@controller.request = @request
@controller.params.merge!(parameters)
# Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest
@controller.process_with_test(@request, @response)
@controller.process_with_new_base_test(@request, @response)
@response
end
def build_request_uri(action, parameters)

@ -73,7 +73,7 @@ class WhiteListSanitizer < Sanitizer
# Specifies the default Set of tags that the #sanitize helper will allow unscathed.
self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dt dd abbr
sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
acronym a img blockquote del ins))
# Specifies the default Set of html attributes that the #sanitize helper will leave

@ -27,23 +27,26 @@
begin
gem 'rack', '~> 1.1.pre'
rescue Gem::LoadError
rescue Gem::LoadError, ArgumentError
$:.unshift "#{File.dirname(__FILE__)}/action_dispatch/vendor/rack-1.1.pre"
end
require 'rack'
$:.unshift "#{File.dirname(__FILE__)}/action_dispatch/vendor/rack-test"
module ActionDispatch
autoload :Request, 'action_dispatch/http/request'
autoload :Response, 'action_dispatch/http/response'
autoload :StatusCodes, 'action_dispatch/http/status_codes'
autoload :Failsafe, 'action_dispatch/middleware/failsafe'
autoload :Callbacks, 'action_dispatch/middleware/callbacks'
autoload :ParamsParser, 'action_dispatch/middleware/params_parser'
autoload :Rescue, 'action_dispatch/middleware/rescue'
autoload :ShowExceptions, 'action_dispatch/middleware/show_exceptions'
autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
autoload :HTML, 'action_controller/vendor/html-scanner'
autoload :Assertions, 'action_dispatch/testing/assertions'
autoload :TestRequest, 'action_dispatch/testing/test_request'
autoload :TestResponse, 'action_dispatch/testing/test_response'

@ -3,7 +3,7 @@
module Mime
SET = []
EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
EXTENSION_LOOKUP = {}
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
def self.[](type)

@ -1,9 +1,9 @@
# Build list of Mime types for HTTP responses
# http://www.iana.org/assignments/media-types/
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
Mime::Type.register "*/*", :all
Mime::Type.register "text/plain", :text, [], %w(txt)
Mime::Type.register "text/html", :html, %w( application/xhtml+xml ), %w( xhtml )
Mime::Type.register "text/javascript", :js, %w( application/javascript application/x-javascript )
Mime::Type.register "text/css", :css
Mime::Type.register "text/calendar", :ics

@ -3,7 +3,9 @@
require 'strscan'
require 'active_support/memoizable'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/object/tap'
module ActionDispatch
class Request < Rack::Request
@ -173,9 +175,21 @@ def format(view_path = [])
def formats
if ActionController::Base.use_accept_header
Array(Mime[parameters[:format]] || accepts)
if param = parameters[:format]
Array.wrap(Mime[param])
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
end
end
else
[format]
[format] + Mime::SET
end
end

@ -0,0 +1,40 @@
module ActionDispatch
class Callbacks
include ActiveSupport::Callbacks
define_callbacks :prepare, :before, :after
class << self
# DEPRECATED
alias_method :prepare_dispatch, :prepare
alias_method :before_dispatch, :before
alias_method :after_dispatch, :after
end
# Add a preparation callback. Preparation callbacks are run before every
# request in development mode, and before the first request in production
# mode.
#
# An optional identifier may be supplied for the callback. If provided,
# to_prepare may be called again with the same identifier to replace the
# existing callback. Passing an identifier is a suggested practice if the
# code adding a preparation block may be reloaded.
def self.to_prepare(identifier = nil, &block)
@prepare_callbacks ||= ActiveSupport::Callbacks::CallbackChain.new
callback = ActiveSupport::Callbacks::Callback.new(:prepare, block, :identifier => identifier)
@prepare_callbacks.replace_or_append!(callback)
end
def initialize(app, prepare_each_request = false)
@app, @prepare_each_request = app, prepare_each_request
run_callbacks :prepare
end
def call(env)
run_callbacks :before
run_callbacks :prepare if @prepare_each_request
@app.call(env)
ensure
run_callbacks :after, :enumerator => :reverse_each
end
end
end

@ -1,52 +0,0 @@
module ActionDispatch
class Failsafe
cattr_accessor :error_file_path
self.error_file_path = Rails.public_path if defined?(Rails.public_path)
def initialize(app)
@app = app
end
def call(env)
@app.call(env)
rescue Exception => exception
# Reraise exception in test environment
if defined?(Rails) && Rails.env.test?
raise exception
else
failsafe_response(exception)
end
end
private
def failsafe_response(exception)
log_failsafe_exception(exception)
[500, {'Content-Type' => 'text/html'}, failsafe_response_body]
rescue Exception => failsafe_error # Logger or IO errors
$stderr.puts "Error during failsafe response: #{failsafe_error}"
end
def failsafe_response_body
error_path = "#{self.class.error_file_path}/500.html"
if File.exist?(error_path)
[File.read(error_path)]
else
["<html><body><h1>500 Internal Server Error</h1></body></html>"]
end
end
def log_failsafe_exception(exception)
message = "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: 500 Internal Server Error\n"
message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception
failsafe_logger.fatal(message)
end
def failsafe_logger
if defined?(Rails) && Rails.logger
Rails.logger
else
Logger.new($stderr)
end
end
end
end

@ -32,16 +32,14 @@ def parse_formatted_parameters(env)
when Proc
strategy.call(request.raw_post)
when :xml_simple, :xml_node
body = request.raw_post
body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
request.body.size == 0 ? {} : Hash.from_xml(request.body).with_indifferent_access
when :yaml
YAML.load(request.raw_post)
YAML.load(request.body)
when :json
body = request.raw_post
if body.blank?
if request.body.size == 0
{}
else
data = ActiveSupport::JSON.decode(body)
data = ActiveSupport::JSON.decode(request.body)
data = {:_json => data} unless data.is_a?(Hash)
data.with_indifferent_access
end

@ -4,8 +4,11 @@ class ShowExceptions
LOCALHOST = '127.0.0.1'.freeze
DEFAULT_RESCUE_RESPONSE = :internal_server_error
DEFAULT_RESCUE_RESPONSES = {
RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
cattr_accessor :rescue_responses
@@rescue_responses = Hash.new(:internal_server_error)
@@rescue_responses.update({
'ActionController::RoutingError' => :not_found,
'ActionController::UnknownAction' => :not_found,
'ActiveRecord::RecordNotFound' => :not_found,
@ -15,25 +18,19 @@ class ShowExceptions
'ActionController::MethodNotAllowed' => :method_not_allowed,
'ActionController::NotImplemented' => :not_implemented,
'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
}
})
DEFAULT_RESCUE_TEMPLATE = 'diagnostics'
DEFAULT_RESCUE_TEMPLATES = {
cattr_accessor :rescue_templates
@@rescue_templates = Hash.new('diagnostics')
@@rescue_templates.update({
'ActionView::MissingTemplate' => 'missing_template',
'ActionController::RoutingError' => 'routing_error',
'ActionController::UnknownAction' => 'unknown_action',
'ActionView::TemplateError' => 'template_error'
}
})
RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
cattr_accessor :rescue_responses
@@rescue_responses = Hash.new(DEFAULT_RESCUE_RESPONSE)
@@rescue_responses.update DEFAULT_RESCUE_RESPONSES
cattr_accessor :rescue_templates
@@rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE)
@@rescue_templates.update DEFAULT_RESCUE_TEMPLATES
FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'},
['<html><body><h1>500 Internal Server Error</h1></body></html>']]
def initialize(app, consider_all_requests_local = false)
@app = app
@ -43,34 +40,35 @@ def initialize(app, consider_all_requests_local = false)
def call(env)
@app.call(env)
rescue Exception => exception
raise exception if env['rack.test']
log_error(exception) if logger
request = Request.new(env)
if @consider_all_requests_local || local_request?(request)
rescue_action_locally(request, exception)
else
rescue_action_in_public(exception)
end
raise exception if env['action_dispatch.show_exceptions'] == false
render_exception(env, exception)
end
private
def render_exception(env, exception)
log_error(exception)
request = Request.new(env)
if @consider_all_requests_local || local_request?(request)
rescue_action_locally(request, exception)
else
rescue_action_in_public(exception)
end
rescue Exception => failsafe_error
$stderr.puts "Error during failsafe response: #{failsafe_error}"
FAILSAFE_RESPONSE
end
# Render detailed diagnostics for unhandled exceptions rescued from
# a controller action.
def rescue_action_locally(request, exception)
template = ActionView::Base.new([RESCUES_TEMPLATE_PATH],
:template => template,
:request => request,
:exception => exception
)
file = "rescues/#{@@rescue_templates[exception.class.name]}.erb"
body = template.render(:file => file, :layout => 'rescues/layout.erb')
headers = {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s}
status = status_code(exception)
[status, headers, body]
render(status_code(exception), body)
end
# Attempts to render a static error page based on the
@ -86,11 +84,11 @@ def rescue_action_in_public(exception)
path = "#{public_path}/#{status}.html"
if locale_path && File.exist?(locale_path)
render_public_file(status, locale_path)
render(status, File.read(locale_path))
elsif File.exist?(path)
render_public_file(status, path)
render(status, File.read(path))
else
[status, {'Content-Type' => 'text/html', 'Content-Length' => '0'}, []]
render(status, '')
end
end
@ -99,24 +97,21 @@ def local_request?(request)
request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST
end
def render_public_file(status, path)
body = File.read(path)
[status, {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s}, body]
end
def status_code(exception)
interpret_status(@@rescue_responses[exception.class.name]).to_i
end
def public_path
if defined?(Rails)
Rails.public_path
else
"public"
end
def render(status, body)
[status, {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s}, [body]]
end
def log_error(exception) #:doc:
def public_path
defined?(Rails.public_path) ? Rails.public_path : 'public_path'
end
def log_error(exception)
return unless logger
ActiveSupport::Deprecation.silence do
if ActionView::TemplateError === exception
logger.fatal(exception.to_s)
@ -136,9 +131,7 @@ def clean_backtrace(exception)
end
def logger
if defined?(Rails.logger)
Rails.logger
end
defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
end
end
end

@ -0,0 +1,50 @@
module Rack
class MockSession
attr_writer :cookie_jar
attr_reader :last_response
def initialize(app, default_host = Rack::Test::DEFAULT_HOST)
@app = app
@default_host = default_host
end
def clear_cookies
@cookie_jar = Rack::Test::CookieJar.new([], @default_host)
end
def set_cookie(cookie, uri = nil)
cookie_jar.merge(cookie, uri)
end
def request(uri, env)
env["HTTP_COOKIE"] ||= cookie_jar.for(uri)
@last_request = Rack::Request.new(env)
status, headers, body = @app.call(@last_request.env)
@last_response = MockResponse.new(status, headers, body, env["rack.errors"].flush)
cookie_jar.merge(last_response.headers["Set-Cookie"], uri)
@last_response
end
# Return the last request issued in the session. Raises an error if no
# requests have been sent yet.
def last_request
raise Rack::Test::Error.new("No request yet. Request a page first.") unless @last_request
@last_request
end
# Return the last response received in the session. Raises an error if
# no requests have been sent yet.
def last_response
raise Rack::Test::Error.new("No response yet. Request a page first.") unless @last_response
@last_response
end
def cookie_jar
@cookie_jar ||= Rack::Test::CookieJar.new([], @default_host)
end
end
end

@ -0,0 +1,239 @@
unless $LOAD_PATH.include?(File.expand_path(File.dirname(__FILE__) + "/.."))
$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/.."))
end
require "uri"
require "rack"
require "rack/mock_session"
require "rack/test/cookie_jar"
require "rack/test/mock_digest_request"
require "rack/test/utils"
require "rack/test/methods"
require "rack/test/uploaded_file"
module Rack
module Test
VERSION = "0.3.0"
DEFAULT_HOST = "example.org"
MULTIPART_BOUNDARY = "----------XnJLe9ZIbbGUYtzPQJ16u1"
# The common base class for exceptions raised by Rack::Test
class Error < StandardError; end
class Session
extend Forwardable
include Rack::Test::Utils
def_delegators :@rack_mock_session, :clear_cookies, :set_cookie, :last_response, :last_request
# Initialize a new session for the given Rack app
def initialize(app, default_host = DEFAULT_HOST)
@headers = {}
@default_host = default_host
@rack_mock_session = Rack::MockSession.new(app, default_host)
end
# Issue a GET request for the given URI with the given params and Rack
# environment. Stores the issues request object in #last_request and
# the app's response in #last_response. Yield #last_response to a block
# if given.
#
# Example:
# get "/"
def get(uri, params = {}, env = {}, &block)
env = env_for(uri, env.merge(:method => "GET", :params => params))
process_request(uri, env, &block)
end
# Issue a POST request for the given URI. See #get
#
# Example:
# post "/signup", "name" => "Bryan"
def post(uri, params = {}, env = {}, &block)
env = env_for(uri, env.merge(:method => "POST", :params => params))
process_request(uri, env, &block)
end
# Issue a PUT request for the given URI. See #get
#
# Example:
# put "/"
def put(uri, params = {}, env = {}, &block)
env = env_for(uri, env.merge(:method => "PUT", :params => params))
process_request(uri, env, &block)
end
# Issue a DELETE request for the given URI. See #get
#
# Example:
# delete "/"
def delete(uri, params = {}, env = {}, &block)
env = env_for(uri, env.merge(:method => "DELETE", :params => params))
process_request(uri, env, &block)
end
# Issue a HEAD request for the given URI. See #get
#
# Example:
# head "/"
def head(uri, params = {}, env = {}, &block)
env = env_for(uri, env.merge(:method => "HEAD", :params => params))
process_request(uri, env, &block)
end
# Issue a request to the Rack app for the given URI and optional Rack
# environment. Stores the issues request object in #last_request and
# the app's response in #last_response. Yield #last_response to a block
# if given.
#
# Example:
# request "/"
def request(uri, env = {}, &block)
env = env_for(uri, env)
process_request(uri, env, &block)
end
# Set a header to be included on all subsequent requests through the
# session. Use a value of nil to remove a previously configured header.
#
# Example:
# header "User-Agent", "Firefox"
def header(name, value)
if value.nil?
@headers.delete(name)
else
@headers[name] = value
end
end
# Set the username and password for HTTP Basic authorization, to be
# included in subsequent requests in the HTTP_AUTHORIZATION header.
#
# Example:
# basic_authorize "bryan", "secret"
def basic_authorize(username, password)
encoded_login = ["#{username}:#{password}"].pack("m*")
header('HTTP_AUTHORIZATION', "Basic #{encoded_login}")
end
alias_method :authorize, :basic_authorize
def digest_authorize(username, password)
@digest_username = username
@digest_password = password
end
# Rack::Test will not follow any redirects automatically. This method
# will follow the redirect returned in the last response. If the last
# response was not a redirect, an error will be raised.
def follow_redirect!
unless last_response.redirect?
raise Error.new("Last response was not a redirect. Cannot follow_redirect!")
end
get(last_response["Location"])
end
private
def env_for(path, env)
uri = URI.parse(path)
uri.host ||= @default_host
env = default_env.merge(env)
env.update("HTTPS" => "on") if URI::HTTPS === uri
env["X-Requested-With"] = "XMLHttpRequest" if env[:xhr]
if (env[:method] == "POST" || env["REQUEST_METHOD"] == "POST") && !env.has_key?(:input)
env["CONTENT_TYPE"] = "application/x-www-form-urlencoded"
multipart = (Hash === env[:params]) &&
env[:params].any? { |_, v| UploadedFile === v }
if multipart
env[:input] = multipart_body(env.delete(:params))
env["CONTENT_LENGTH"] ||= env[:input].length.to_s
env["CONTENT_TYPE"] = "multipart/form-data; boundary=#{MULTIPART_BOUNDARY}"
else
env[:input] = params_to_string(env.delete(:params))
end
end
params = env[:params] || {}
params.update(parse_query(uri.query))
uri.query = requestify(params)
if env.has_key?(:cookie)
set_cookie(env.delete(:cookie), uri)
end
Rack::MockRequest.env_for(uri.to_s, env)
end
def process_request(uri, env)
uri = URI.parse(uri)
uri.host ||= @default_host
@rack_mock_session.request(uri, env)
if retry_with_digest_auth?(env)
auth_env = env.merge({
"HTTP_AUTHORIZATION" => digest_auth_header,
"rack-test.digest_auth_retry" => true
})
auth_env.delete('rack.request')
process_request(uri.path, auth_env)
else
yield last_response if block_given?
last_response
end
end
def digest_auth_header
challenge = last_response["WWW-Authenticate"].split(" ", 2).last
params = Rack::Auth::Digest::Params.parse(challenge)
params.merge!({
"username" => @digest_username,
"nc" => "00000001",
"cnonce" => "nonsensenonce",
"uri" => last_request.path_info,
"method" => last_request.env["REQUEST_METHOD"],
})
params["response"] = MockDigestRequest.new(params).response(@digest_password)
"Digest #{params}"
end
def retry_with_digest_auth?(env)
last_response.status == 401 &&
digest_auth_configured? &&
!env["rack-test.digest_auth_retry"]
end
def digest_auth_configured?
@digest_username
end
def default_env
{ "rack.test" => true, "REMOTE_ADDR" => "127.0.0.1" }.merge(@headers)
end
def params_to_string(params)
case params
when Hash then requestify(params)
when nil then ""
else params
end
end
end
end
end

@ -0,0 +1,169 @@
require "uri"
module Rack
module Test
class Cookie
include Rack::Utils
# :api: private
attr_reader :name, :value
# :api: private
def initialize(raw, uri = nil, default_host = DEFAULT_HOST)
@default_host = default_host
uri ||= default_uri
# separate the name / value pair from the cookie options
@name_value_raw, options = raw.split(/[;,] */n, 2)
@name, @value = parse_query(@name_value_raw, ';').to_a.first
@options = parse_query(options, ';')
@options["domain"] ||= (uri.host || default_host)
@options["path"] ||= uri.path.sub(/\/[^\/]*\Z/, "")
end
def replaces?(other)
[name.downcase, domain, path] == [other.name.downcase, other.domain, other.path]
end
# :api: private
def raw
@name_value_raw
end
# :api: private
def empty?
@value.nil? || @value.empty?
end
# :api: private
def domain
@options["domain"]
end
def secure?
@options.has_key?("secure")
end
# :api: private
def path
@options["path"].strip || "/"
end
# :api: private
def expires
Time.parse(@options["expires"]) if @options["expires"]
end
# :api: private
def expired?
expires && expires < Time.now
end
# :api: private
def valid?(uri)
uri ||= default_uri
if uri.host.nil?
uri.host = @default_host
end
(!secure? || (secure? && uri.scheme == "https")) &&
uri.host =~ Regexp.new("#{Regexp.escape(domain)}$", Regexp::IGNORECASE) &&
uri.path =~ Regexp.new("^#{Regexp.escape(path)}")
end
# :api: private
def matches?(uri)
! expired? && valid?(uri)
end
# :api: private
def <=>(other)
# Orders the cookies from least specific to most
[name, path, domain.reverse] <=> [other.name, other.path, other.domain.reverse]
end
protected
def default_uri
URI.parse("//" + @default_host + "/")
end
end
class CookieJar
# :api: private
def initialize(cookies = [], default_host = DEFAULT_HOST)
@default_host = default_host
@cookies = cookies
@cookies.sort!
end
def [](name)
cookies = hash_for(nil)
# TODO: Should be case insensitive
cookies[name] && cookies[name].value
end
def []=(name, value)
# TODO: needs proper escaping
merge("#{name}=#{value}")
end
def merge(raw_cookies, uri = nil)
return unless raw_cookies
raw_cookies.each_line do |raw_cookie|
cookie = Cookie.new(raw_cookie, uri, @default_host)
self << cookie if cookie.valid?(uri)
end
end
def <<(new_cookie)
@cookies.reject! do |existing_cookie|
new_cookie.replaces?(existing_cookie)
end
@cookies << new_cookie
@cookies.sort!
end
# :api: private
def for(uri)
hash_for(uri).values.map { |c| c.raw }.join(';')
end
def to_hash
cookies = {}
hash_for(nil).each do |name, cookie|
cookies[name] = cookie.value
end
return cookies
end
protected
def hash_for(uri = nil)
cookies = {}
# The cookies are sorted by most specific first. So, we loop through
# all the cookies in order and add it to a hash by cookie name if
# the cookie can be sent to the current URI. It's added to the hash
# so that when we are done, the cookies will be unique by name and
# we'll have grabbed the most specific to the URI.
@cookies.each do |cookie|
cookies[cookie.name] = cookie if cookie.matches?(uri)
end
return cookies
end
end
end
end

@ -0,0 +1,45 @@
require "forwardable"
module Rack
module Test
module Methods
extend Forwardable
def rack_test_session
@_rack_test_session ||= Rack::Test::Session.new(app)
end
def rack_mock_session
@_rack_mock_session ||= Rack::MockSession.new(app)
end
METHODS = [
:request,
# HTTP verbs
:get,
:post,
:put,
:delete,
:head,
# Redirects
:follow_redirect!,
# Header-related features
:header,
:set_cookie,
:clear_cookies,
:authorize,
:basic_authorize,
:digest_authorize,
# Expose the last request and response
:last_response,
:last_request
]
def_delegators :rack_test_session, *METHODS
end
end
end

@ -0,0 +1,27 @@
module Rack
module Test
class MockDigestRequest
def initialize(params)
@params = params
end
def method_missing(sym)
if @params.has_key? k = sym.to_s
return @params[k]
end
super
end
def method
@params['method']
end
def response(password)
Rack::Auth::Digest::MD5.new(nil).send :digest, self, password
end
end
end
end

@ -0,0 +1,36 @@
require "tempfile"
module Rack
module Test
class UploadedFile
# The filename, *not* including the path, of the "uploaded" file
attr_reader :original_filename
# The content type of the "uploaded" file
attr_accessor :content_type
def initialize(path, content_type = "text/plain", binary = false)
raise "#{path} file does not exist" unless ::File.exist?(path)
@content_type = content_type
@original_filename = ::File.basename(path)
@tempfile = Tempfile.new(@original_filename)
@tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding)
@tempfile.binmode if binary
FileUtils.copy_file(path, @tempfile.path)
end
def path
@tempfile.path
end
alias_method :local_path, :path
def method_missing(method_name, *args, &block) #:nodoc:
@tempfile.__send__(method_name, *args, &block)
end
end
end
end

@ -0,0 +1,75 @@
module Rack
module Test
module Utils
include Rack::Utils
def requestify(value, prefix = nil)
case value
when Array
value.map do |v|
requestify(v, "#{prefix}[]")
end.join("&")
when Hash
value.map do |k, v|
requestify(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
end.join("&")
else
"#{prefix}=#{escape(value)}"
end
end
module_function :requestify
def multipart_requestify(params, first=true)
p = Hash.new
params.each do |key, value|
k = first ? key.to_s : "[#{key}]"
if Hash === value
multipart_requestify(value, false).each do |subkey, subvalue|
p[k + subkey] = subvalue
end
else
p[k] = value
end
end
return p
end
module_function :multipart_requestify
def multipart_body(params)
multipart_requestify(params).map do |key, value|
if value.respond_to?(:original_filename)
::File.open(value.path, "rb") do |f|
f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding)
<<-EOF
--#{MULTIPART_BOUNDARY}\r
Content-Disposition: form-data; name="#{key}"; filename="#{escape(value.original_filename)}"\r
Content-Type: #{value.content_type}\r
Content-Length: #{::File.stat(value.path).size}\r
\r
#{f.read}\r
EOF
end
else
<<-EOF
--#{MULTIPART_BOUNDARY}\r
Content-Disposition: form-data; name="#{key}"\r
\r
#{value}\r
EOF
end
end.join("")+"--#{MULTIPART_BOUNDARY}--\r"
end
module_function :multipart_body
end
end
end

@ -269,15 +269,16 @@ def punctuate_body!(part)
nil
end
private
# 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
# 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
end
private
def _copy_ivars_from_controller #:nodoc:
if @controller
@ -288,8 +289,11 @@ def _copy_ivars_from_controller #:nodoc:
end
def _set_controller_content_type(content_type) #:nodoc:
if controller.respond_to?(:response)
controller.response.content_type ||= content_type
# 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

@ -1,5 +1,6 @@
require 'cgi'
require 'action_view/helpers/form_helper'
require 'active_support/core_ext/class/attribute_accessors'
module ActionView
class Base

@ -248,6 +248,11 @@ def number_with_precision(number, *args)
# number_to_human_size(483989, :precision => 0) # => 473 KB
# number_to_human_size(1234567, :precision => 2, :separator => ',') # => 1,18 MB
#
# Zeros after the decimal point are always stripped out, regardless of the
# specified precision:
# helper.number_to_human_size(1234567890123, :precision => 5) # => "1.12283 TB"
# helper.number_to_human_size(524288000, :precision=>5) # => "500 MB"
#
# You can still use <tt>number_to_human_size</tt> with the old API that accepts the
# +precision+ as its optional second parameter:
# number_to_human_size(1234567, 2) # => 1.18 MB
@ -293,7 +298,7 @@ def number_to_human_size(number, *args)
:precision => precision,
:separator => separator,
:delimiter => delimiter
).sub(/(\d)(#{escaped_separator}[1-9]*)?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
rescue
number

@ -1,5 +1,6 @@
require 'set'
require 'active_support/json'
require 'active_support/core_ext/object/extending'
module ActionView
module Helpers
@ -572,6 +573,7 @@ def observe_form(form_id, options = {})
# #include_helpers_from_context has nothing to overwrite.
class JavaScriptGenerator #:nodoc:
def initialize(context, &block) #:nodoc:
context._evaluate_assigns_and_ivars
@context, @lines = context, []
include_helpers_from_context
@context.with_output_buffer(@lines) do

@ -34,12 +34,16 @@ def concat(string, unused_binding = nil)
# Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
# (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...").
# Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
#
# ==== Examples
#
# truncate("Once upon a time in a world far far away")
# # => Once upon a time in a world f...
#
# truncate("Once upon a time in a world far far away", :separator => ' ')
# # => Once upon a time in a world...
#
# truncate("Once upon a time in a world far far away", :length => 14)
# # => Once upon a...
#
@ -71,7 +75,8 @@ def truncate(text, *args)
if text
l = options[:length] - options[:omission].mb_chars.length
chars = text.mb_chars
(chars.length > options[:length] ? chars[0...l] + options[:omission] : text).to_s
stop = options[:separator] ? (chars.rindex(options[:separator].mb_chars, l) || l) : l
(chars.length > options[:length] ? chars[0...stop] + options[:omission] : text).to_s
end
end
@ -535,7 +540,7 @@ def auto_link_urls(text, html_options = {})
link_attributes = html_options.stringify_keys
text.gsub(AUTO_LINK_RE) do
href = $&
punctuation = ''
punctuation = []
left, right = $`, $'
# detect already linked URLs and URLs in the middle of a tag
if left =~ /<[^>]+$/ && right =~ /^[^>]*>/
@ -543,17 +548,18 @@ def auto_link_urls(text, html_options = {})
href
else
# don't include trailing punctuation character as part of the URL
if href.sub!(/[^\w\/-]$/, '') and punctuation = $& and opening = BRACKETS[punctuation]
if href.scan(opening).size > href.scan(punctuation).size
href << punctuation
punctuation = ''
while href.sub!(/[^\w\/-]$/, '')
punctuation.push $&
if opening = BRACKETS[punctuation.last] and href.scan(opening).size > href.scan(punctuation.last).size
href << punctuation.pop
break
end
end
link_text = block_given?? yield(href) : href
href = 'http://' + href unless href.index('http') == 0
content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation
content_tag(:a, h(link_text), link_attributes.merge('href' => href)) + punctuation.reverse.join('')
end
end
end

@ -1,3 +1,6 @@
require "active_support/core_ext/class/inheritable_attributes"
require "action_dispatch/http/mime_type"
# Legacy TemplateHandler stub
module ActionView
module TemplateHandlers #:nodoc:
@ -19,6 +22,9 @@ def compile(template)
end
class TemplateHandler
extlib_inheritable_accessor :default_format
self.default_format = Mime::HTML
def self.call(template)
"#{name}.new(self).render(template, local_assigns)"
end

@ -33,7 +33,7 @@ def register_template_handler(extension, klass)
end
def template_handler_extensions
@@template_handlers.keys.map(&:to_s).sort
@@template_handlers.keys.map {|key| key.to_s }.sort
end
def registered_template_handler(extension)

@ -5,6 +5,8 @@ module TemplateHandlers
class Builder < TemplateHandler
include Compilable
self.default_format = Mime::XML
def compile(template)
"_set_controller_content_type(Mime::XML);" +
"xml = ::Builder::XmlMarkup.new(:indent => 2);" +

@ -13,6 +13,8 @@ class ERB < TemplateHandler
cattr_accessor :erb_trim_mode
self.erb_trim_mode = '-'
self.default_format = Mime::HTML
def compile(template)
src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src

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

@ -7,13 +7,20 @@
module ActionView
class Template
extend TemplateHandlers
attr_reader :source, :identifier, :handler
attr_reader :source, :identifier, :handler, :mime_type, :details
def initialize(source, identifier, handler, details)
@source = source
@identifier = identifier
@handler = handler
@details = details
format = details.delete(:format) || begin
# TODO: Clean this up
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)
end
def render(view, locals, &blk)
@ -35,12 +42,7 @@ def counter_name
def partial?
@details[:partial]
end
# TODO: Move out of Template
def mime_type
Mime::Type.lookup_by_extension(@details[:format].to_s) if @details[:format]
end
private
def compile(locals, view)

@ -1,11 +1,20 @@
module ActionView #:nodoc:
class TextTemplate < String #:nodoc:
def initialize(string, content_type = Mime[:html])
super(string.to_s)
@content_type = Mime[content_type]
end
def details
{:formats => [@content_type.to_sym]}
end
def identifier() self end
def render(*) self end
def mime_type() Mime::HTML end
def mime_type() @content_type end
def partial?() false end
end

@ -11,7 +11,7 @@ def initialize(*args)
attr_internal :rendered
alias_method :_render_template_without_template_tracking, :_render_template
def _render_template(template, local_assigns = {})
if template.respond_to?(:identifier)
if template.respond_to?(:identifier) && template.present?
@_rendered[:partials][template] += 1 if template.partial?
@_rendered[:template] ||= []
@_rendered[:template] << template

@ -1,4 +1,5 @@
require File.join(File.expand_path(File.dirname(__FILE__)), "test_helper")
require 'active_support/core_ext/class/removal'
module AbstractControllerTests
module Layouts

@ -1,6 +1,7 @@
if ENV["new_base"]
require "abstract_unit2"
else
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__) + '/fixtures/helpers')
@ -26,6 +27,8 @@
require 'action_controller/testing/process'
require 'action_view/test_case'
$tags[:old_base] = true
# Show backtraces for deprecated behavior for quicker cleanup.
ActiveSupport::Deprecation.debug = true
@ -41,4 +44,3 @@
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
ActionController::Base.view_paths = FIXTURE_LOAD_PATH
end

@ -28,9 +28,9 @@ def get_session_id
end
def call_reset_session
session[:bar]
session[:foo]
reset_session
session[:bar] = "baz"
session[:foo] = "baz"
head :ok
end
@ -91,7 +91,7 @@ def test_setting_session_value_after_session_reset
get '/get_session_value'
assert_response :success
assert_equal 'foo: nil', response.body
assert_equal 'foo: "baz"', response.body
get '/get_session_id'
assert_response :success

@ -1,4 +1,5 @@
require 'abstract_unit'
require 'action_controller/vendor/html-scanner'
# a controller class to facilitate the tests
class ActionPackAssertionsController < ActionController::Base
@ -12,6 +13,9 @@ 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 partial
def partial() render :partial => 'test/partial'; end
# a redirect to an internal location
def redirect_internal() redirect_to "/nothing"; end
@ -292,8 +296,8 @@ def test_assert_redirected_to_top_level_named_route_with_same_controller_name_in
# make sure that the template objects exist
def test_template_objects_alive
process :assign_this
assert !@controller.template.assigns['hi']
assert @controller.template.assigns['howdy']
assert !@controller.template.instance_variable_get(:"@hi")
assert @controller.template.instance_variable_get(:"@howdy")
end
# make sure we don't have template objects when we shouldn't
@ -331,6 +335,26 @@ def test_flash_have_nots
end
end
def test_assert_template_with_partial
get :partial
assert_template :partial => '_partial'
end
def test_assert_template_with_nil
get :nothing
assert_template nil
end
def test_assert_template_with_string
get :hello_world
assert_template 'hello_world'
end
def test_assert_template_with_symbol
get :hello_world
assert_template :hello_world
end
# check if we were rendered by a file-based template?
def test_rendered_action
process :nothing
@ -421,7 +445,6 @@ def test_render_based_on_parameters
assert_equal "Mr. David", @response.body
end
def test_assert_redirection_fails_with_incorrect_controller
process :redirect_to_controller
assert_raise(ActiveSupport::TestCase::Assertion) do

@ -48,6 +48,8 @@ def setup
super
ActionController::Base.perform_caching = true
ActionController::Routing::Routes.clear!
ActionController::Routing::Routes.draw do |map|
map.main '', :controller => 'posts'
map.formatted_posts 'posts.:format', :controller => 'posts'

@ -148,12 +148,13 @@ class AcceptBasedContentTypeTest < ActionController::TestCase
def setup
super
@_old_accept_header = ActionController::Base.use_accept_header
ActionController::Base.use_accept_header = true
end
def teardown
super
ActionController::Base.use_accept_header = false
ActionController::Base.use_accept_header = @_old_accept_header
end

@ -4,44 +4,48 @@ class CookieTest < ActionController::TestCase
class TestController < ActionController::Base
def authenticate
cookies["user_name"] = "david"
head :ok
end
def set_with_with_escapable_characters
cookies["that & guy"] = "foo & bar => baz"
head :ok
end
def authenticate_for_fourteen_days
cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) }
head :ok
end
def authenticate_for_fourteen_days_with_symbols
cookies[:user_name] = { :value => "david", :expires => Time.utc(2005, 10, 10,5) }
head :ok
end
def set_multiple_cookies
cookies["user_name"] = { "value" => "david", "expires" => Time.utc(2005, 10, 10,5) }
cookies["login"] = "XJ-122"
head :ok
end
def access_frozen_cookies
cookies["will"] = "work"
head :ok
end
def logout
cookies.delete("user_name")
head :ok
end
def delete_cookie_with_path
cookies.delete("user_name", :path => '/beaten')
render :text => "hello world"
head :ok
end
def authenticate_with_http_only
cookies["user_name"] = { :value => "david", :httponly => true }
end
def rescue_action(e)
raise unless ActionView::MissingTemplate # No templates here, and we don't care about the output
head :ok
end
end
@ -54,38 +58,38 @@ def setup
def test_setting_cookie
get :authenticate
assert_equal "user_name=david; path=/", @response.headers["Set-Cookie"]
assert_cookie_header "user_name=david; path=/"
assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_setting_with_escapable_characters
get :set_with_with_escapable_characters
assert_equal "that+%26+guy=foo+%26+bar+%3D%3E+baz; path=/", @response.headers["Set-Cookie"]
assert_cookie_header "that+%26+guy=foo+%26+bar+%3D%3E+baz; path=/"
assert_equal({"that & guy" => "foo & bar => baz"}, @response.cookies)
end
def test_setting_cookie_for_fourteen_days
get :authenticate_for_fourteen_days
assert_equal "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", @response.headers["Set-Cookie"]
assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"
assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_setting_cookie_for_fourteen_days_with_symbols
get :authenticate_for_fourteen_days_with_symbols
assert_equal "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT", @response.headers["Set-Cookie"]
assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT"
assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_setting_cookie_with_http_only
get :authenticate_with_http_only
assert_equal "user_name=david; path=/; HttpOnly", @response.headers["Set-Cookie"]
assert_cookie_header "user_name=david; path=/; HttpOnly"
assert_equal({"user_name" => "david"}, @response.cookies)
end
def test_multiple_cookies
get :set_multiple_cookies
assert_equal 2, @response.cookies.size
assert_equal "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT\nlogin=XJ-122; path=/", @response.headers["Set-Cookie"]
assert_cookie_header "user_name=david; path=/; expires=Mon, 10-Oct-2005 05:00:00 GMT\nlogin=XJ-122; path=/"
assert_equal({"login" => "XJ-122", "user_name" => "david"}, @response.cookies)
end
@ -95,7 +99,7 @@ def test_setting_test_cookie
def test_expiring_cookie
get :logout
assert_equal "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT", @response.headers["Set-Cookie"]
assert_cookie_header "user_name=; path=/; expires=Thu, 01-Jan-1970 00:00:00 GMT"
assert_equal({"user_name" => nil}, @response.cookies)
end
@ -116,6 +120,16 @@ def test_cookiejar_accessor_with_array_value
def test_delete_cookie_with_path
get :delete_cookie_with_path
assert_equal "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT", @response.headers["Set-Cookie"]
assert_cookie_header "user_name=; path=/beaten; expires=Thu, 01-Jan-1970 00:00:00 GMT"
end
private
def assert_cookie_header(expected)
header = @response.headers["Set-Cookie"]
if header.respond_to?(:to_str)
assert_equal expected, header
else
assert_equal expected.split("\n"), header
end
end
end

@ -6,20 +6,20 @@ class DispatcherTest < Test::Unit::TestCase
def setup
ENV['REQUEST_METHOD'] = 'GET'
Dispatcher.middleware = ActionDispatch::MiddlewareStack.new do |middleware|
middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/dispatch/middlewares.rb"))
middleware.instance_eval(File.read(middlewares))
end
# Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks
Dispatcher.instance_variable_set("@prepare_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
Dispatcher.instance_variable_set("@before_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
Dispatcher.instance_variable_set("@after_dispatch_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
ActionDispatch::Callbacks.instance_variable_set("@prepare_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
ActionDispatch::Callbacks.instance_variable_set("@before_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
ActionDispatch::Callbacks.instance_variable_set("@after_callbacks", ActiveSupport::Callbacks::CallbackChain.new)
@old_router, Dispatcher.router = Dispatcher.router, mock()
Dispatcher.router.stubs(:call).returns([200, {}, 'response'])
Dispatcher.router.stubs(:reload)
Dispatcher.stubs(:require_dependency)
end
def teardown
Dispatcher.router = @old_router
@dispatcher = nil
ENV.delete 'REQUEST_METHOD'
end
@ -29,12 +29,12 @@ def test_clears_dependencies_after_dispatch_if_in_loading_mode
end
def test_reloads_routes_before_dispatch_if_in_loading_mode
ActionController::Routing::Routes.expects(:reload).once
Dispatcher.router.expects(:reload).once
dispatch(false)
end
def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode
ActionController::Routing::Routes.expects(:reload).never
Dispatcher.router.expects(:reload).never
ActiveSupport::Dependencies.expects(:clear).never
dispatch
@ -55,7 +55,7 @@ def test_prepare_callbacks
assert_nil a || b || c
# Run callbacks
Dispatcher.run_prepare_callbacks
dispatch
assert_equal 1, a
assert_equal 2, b
@ -72,16 +72,22 @@ def test_to_prepare_with_identifier_replaces
Dispatcher.to_prepare(:unique_id) { |*args| a = b = 1 }
Dispatcher.to_prepare(:unique_id) { |*args| a = 2 }
Dispatcher.run_prepare_callbacks
dispatch
assert_equal 2, a
assert_equal nil, b
end
private
def dispatch(cache_classes = true)
ActionController::Routing::RouteSet.any_instance.stubs(:call).returns([200, {}, 'response'])
ActionController::Dispatcher.prepare_each_request = false
Dispatcher.define_dispatcher_callbacks(cache_classes)
Dispatcher.new.call({'rack.input' => StringIO.new('')})
Dispatcher.middleware = ActionDispatch::MiddlewareStack.new do |middleware|
middlewares = File.expand_path(File.join(File.dirname(__FILE__), "../../lib/action_controller/dispatch/middlewares.rb"))
middleware.instance_eval(File.read(middlewares))
end
@dispatcher ||= Dispatcher.new
@dispatcher.call({'rack.input' => StringIO.new(''), 'action_dispatch.show_exceptions' => false})
end
def assert_subclasses(howmany, klass, message = klass.subclasses.inspect)

@ -1,13 +1,30 @@
require 'abstract_unit'
class FilterParamController < ActionController::Base
def payment
head :ok
end
end
class FilterParamTest < Test::Unit::TestCase
def setup
@controller = FilterParamController.new
class FilterParamTest < ActionController::TestCase
tests FilterParamController
class MockLogger
attr_reader :logged
attr_accessor :level
def initialize
@level = Logger::DEBUG
end
def method_missing(method, *args)
@logged ||= []
@logged << args.first
end
end
setup :set_logger
def test_filter_parameters
assert FilterParamController.respond_to?(:filter_parameter_logging)
assert !@controller.respond_to?(:filter_parameters)
@ -46,4 +63,26 @@ def test_filter_parameters_is_protected
assert !FilterParamController.action_methods.include?('filter_parameters')
assert_raise(NoMethodError) { @controller.filter_parameters([{'password' => '[FILTERED]'}]) }
end
def test_filter_parameters_inside_logs
FilterParamController.filter_parameter_logging(:lifo, :amount)
get :payment, :lifo => 'Pratik', :amount => '420', :step => '1'
filtered_params_logs = logs.detect {|l| l =~ /\AParameters/ }
assert filtered_params_logs.index('"amount"=>"[FILTERED]"')
assert filtered_params_logs.index('"lifo"=>"[FILTERED]"')
assert filtered_params_logs.index('"step"=>"1"')
end
private
def set_logger
@controller.logger = MockLogger.new
end
def logs
@logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip}
end
end

@ -1,6 +1,13 @@
require 'abstract_unit'
# FIXME: crashes Ruby 1.9
class << ActionController::Base
%w(append_around_filter prepend_after_filter prepend_around_filter prepend_before_filter skip_after_filter skip_before_filter skip_filter).each do |pending|
define_method(pending) do |*args|
$stderr.puts "#{pending} unimplemented: #{args.inspect}"
end unless method_defined?(pending)
end
end
class FilterTest < Test::Unit::TestCase
include ActionController::TestProcess
@ -141,14 +148,6 @@ class ConditionalOptionsFilter < ConditionalFilterController
before_filter :clean_up_tmp, :if => Proc.new { |c| false }
end
class EmptyFilterChainController < TestController
self.filter_chain.clear
def show
@action_executed = true
render :text => "yawp!"
end
end
class PrependingController < TestController
prepend_before_filter :wonderful_life
# skip_before_filter :fire_flash
@ -455,12 +454,6 @@ def test_after_filters_are_not_run_if_around_filter_does_not_yield
assert_equal ["filter_one", "zomg it didn't yield"], controller.assigns['filters']
end
def test_empty_filter_chain
assert_equal 0, EmptyFilterChainController.filter_chain.size
test_process(EmptyFilterChainController)
assert @controller.template.assigns['action_executed']
end
def test_added_filter_to_inheritance_graph
assert_equal [ :ensure_login ], TestController.before_filters
end
@ -607,7 +600,6 @@ def test_filters_with_mixed_specialization_run_in_order
def test_dynamic_dispatch
%w(foo bar baz).each do |action|
request = ActionController::TestRequest.new
request.env["action_controller.rescue.request"] = request
request.query_parameters[:choose] = action
response = DynamicDispatchController.action.call(request.env).last
assert_equal action, response.body
@ -615,7 +607,6 @@ def test_dynamic_dispatch
end
def test_running_prepended_before_and_after_filter
assert_equal 3, PrependingBeforeAndAfterController.filter_chain.length
test_process(PrependingBeforeAndAfterController)
assert_equal %w( before_all between_before_all_and_after_all after_all ), @controller.template.assigns["ran_filter"]
end
@ -819,15 +810,6 @@ class YieldingAroundFiltersTest < Test::Unit::TestCase
include PostsController::AroundExceptions
include ActionController::TestProcess
def test_filters_registering
assert_equal 1, ControllerWithFilterMethod.filter_chain.size
assert_equal 1, ControllerWithFilterClass.filter_chain.size
assert_equal 1, ControllerWithFilterInstance.filter_chain.size
assert_equal 3, ControllerWithSymbolAsFilter.filter_chain.size
assert_equal 6, ControllerWithNestedFilters.filter_chain.size
assert_equal 4, ControllerWithAllTypesOfFilters.filter_chain.size
end
def test_base
controller = PostsController
assert_nothing_raised { test_process(controller,'no_raise') }

@ -60,6 +60,7 @@ def rescue_action(e)
def std_action
@flash_copy = {}.update(flash)
render :nothing => true
end
def filter_halting_action
@ -79,64 +80,64 @@ def test_flash
get :set_flash
get :use_flash
assert_equal "hello", @controller.template.assigns["flash_copy"]["that"]
assert_equal "hello", @controller.template.assigns["flashy"]
assert_equal "hello", assigns["flash_copy"]["that"]
assert_equal "hello", assigns["flashy"]
get :use_flash
assert_nil @controller.template.assigns["flash_copy"]["that"], "On second flash"
assert_nil assigns["flash_copy"]["that"], "On second flash"
end
def test_keep_flash
get :set_flash
get :use_flash_and_keep_it
assert_equal "hello", @controller.template.assigns["flash_copy"]["that"]
assert_equal "hello", @controller.template.assigns["flashy"]
assert_equal "hello", assigns["flash_copy"]["that"]
assert_equal "hello", assigns["flashy"]
get :use_flash
assert_equal "hello", @controller.template.assigns["flash_copy"]["that"], "On second flash"
assert_equal "hello", assigns["flash_copy"]["that"], "On second flash"
get :use_flash
assert_nil @controller.template.assigns["flash_copy"]["that"], "On third flash"
assert_nil assigns["flash_copy"]["that"], "On third flash"
end
def test_flash_now
get :set_flash_now
assert_equal "hello", @controller.template.assigns["flash_copy"]["that"]
assert_equal "bar" , @controller.template.assigns["flash_copy"]["foo"]
assert_equal "hello", @controller.template.assigns["flashy"]
assert_equal "hello", assigns["flash_copy"]["that"]
assert_equal "bar" , assigns["flash_copy"]["foo"]
assert_equal "hello", assigns["flashy"]
get :attempt_to_use_flash_now
assert_nil @controller.template.assigns["flash_copy"]["that"]
assert_nil @controller.template.assigns["flash_copy"]["foo"]
assert_nil @controller.template.assigns["flashy"]
assert_nil assigns["flash_copy"]["that"]
assert_nil assigns["flash_copy"]["foo"]
assert_nil assigns["flashy"]
end
def test_update_flash
get :set_flash
get :use_flash_and_update_it
assert_equal "hello", @controller.template.assigns["flash_copy"]["that"]
assert_equal "hello again", @controller.template.assigns["flash_copy"]["this"]
assert_equal "hello", assigns["flash_copy"]["that"]
assert_equal "hello again", assigns["flash_copy"]["this"]
get :use_flash
assert_nil @controller.template.assigns["flash_copy"]["that"], "On second flash"
assert_equal "hello again", @controller.template.assigns["flash_copy"]["this"], "On second flash"
assert_nil assigns["flash_copy"]["that"], "On second flash"
assert_equal "hello again", assigns["flash_copy"]["this"], "On second flash"
end
def test_flash_after_reset_session
get :use_flash_after_reset_session
assert_equal "hello", @controller.template.assigns["flashy_that"]
assert_equal "good-bye", @controller.template.assigns["flashy_this"]
assert_nil @controller.template.assigns["flashy_that_reset"]
assert_equal "hello", assigns["flashy_that"]
assert_equal "good-bye", assigns["flashy_this"]
assert_nil assigns["flashy_that_reset"]
end
def test_sweep_after_halted_filter_chain
get :std_action
assert_nil @controller.template.assigns["flash_copy"]["foo"]
assert_nil assigns["flash_copy"]["foo"]
get :filter_halting_action
assert_equal "bar", @controller.template.assigns["flash_copy"]["foo"]
assert_equal "bar", assigns["flash_copy"]["foo"]
get :std_action # follow redirection
assert_equal "bar", @controller.template.assigns["flash_copy"]["foo"]
assert_equal "bar", assigns["flash_copy"]["foo"]
get :std_action
assert_nil @controller.template.assigns["flash_copy"]["foo"]
assert_nil assigns["flash_copy"]["foo"]
end
end

@ -27,7 +27,7 @@ def rescue_action(e) raise end
end
end
class ApplicationController < ActionController::Base
class AllHelpersController < ActionController::Base
helper :all
end
@ -104,7 +104,6 @@ def test_helper_attr
def call_controller(klass, action)
request = ActionController::TestRequest.new
request.env["action_controller.rescue.request"] = request
klass.action(action).call(request.env)
end
@ -112,7 +111,6 @@ def test_helper_for_nested_controller
assert_equal 'hello: Iz guuut!',
call_controller(Fun::GamesController, "render_hello_world").last.body
# request = ActionController::TestRequest.new
# request.env["action_controller.rescue.request"] = request
#
# resp = Fun::GamesController.action(:render_hello_world).call(request.env)
# assert_equal 'hello: Iz guuut!', resp.last.body
@ -129,7 +127,7 @@ def test_helper_for_acronym_controller
end
def test_all_helpers
methods = ApplicationController.master_helper_module.instance_methods.map(&:to_s)
methods = AllHelpersController.master_helper_module.instance_methods.map(&:to_s)
# abc_helper.rb
assert methods.include?('bare_a')
@ -156,7 +154,7 @@ def test_all_helpers_with_alternate_helper_dir
end
def test_helper_proxy
methods = ApplicationController.helpers.methods.map(&:to_s)
methods = AllHelpersController.helpers.methods.map(&:to_s)
# ActionView
assert methods.include?('pluralize')
@ -217,7 +215,6 @@ def index
def call_controller(klass, action)
request = ActionController::TestRequest.new
request.env["action_controller.rescue.request"] = request
klass.action(action).call(request.env)
end

@ -38,6 +38,15 @@ def authenticate_with_request
tests DummyDigestController
setup do
# Used as secret in generating nonce to prevent tampering of timestamp
@old_secret, ActionController::Base.session_options[:secret] = ActionController::Base.session_options[:secret], "session_options_secret"
end
teardown do
ActionController::Base.session_options[:secret] = @old_secret
end
AUTH_HEADERS.each do |header|
test "successful authentication with #{header.downcase}" do
@request.env[header] = encode_credentials(:username => 'lifo', :password => 'world')
@ -149,25 +158,38 @@ def authenticate_with_request
assert_equal 'Definitely Maybe', @response.body
end
test "authentication request with _method" do
@request.env['HTTP_AUTHORIZATION'] = encode_credentials(:username => 'pretty', :password => 'please', :method => :post)
@request.env['rack.methodoverride.original_method'] = 'POST'
put :display
assert_response :success
assert assigns(:logged_in)
assert_equal 'Definitely Maybe', @response.body
end
private
def encode_credentials(options)
options.reverse_merge!(:nc => "00000001", :cnonce => "0a4f113b", :password_is_ha1 => false)
password = options.delete(:password)
# Set in /initializers/session_store.rb. Used as secret in generating nonce
# to prevent tampering of timestamp
ActionController::Base.session_options[:secret] = "session_options_secret"
# Perform unauthenticated request to retrieve digest parameters to use on subsequent request
method = options.delete(:method) || 'GET'
# Perform unauthenticated GET to retrieve digest parameters to use on subsequent request
get :index
case method.to_s.upcase
when 'GET'
get :index
when 'POST'
post :index
end
assert_response :unauthorized
credentials = decode_credentials(@response.headers['WWW-Authenticate'])
credentials.merge!(options)
credentials.reverse_merge!(:uri => "#{@request.env['REQUEST_URI']}")
ActionController::HttpAuthentication::Digest.encode_credentials("GET", credentials, password, options[:password_is_ha1])
ActionController::HttpAuthentication::Digest.encode_credentials(method, credentials, password, options[:password_is_ha1])
end
def decode_credentials(header)

@ -1,4 +1,5 @@
require 'abstract_unit'
require 'action_controller/vendor/html-scanner'
class SessionTest < Test::Unit::TestCase
StubApp = lambda { |env|
@ -123,8 +124,8 @@ def test_head
def test_xml_http_request_get
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
"X-Requested-With" => "XMLHttpRequest",
"Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
"HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:get,path,params,headers_after_xhr)
@session.xml_http_request(:get,path,params,headers)
@ -133,8 +134,8 @@ def test_xml_http_request_get
def test_xml_http_request_post
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
"X-Requested-With" => "XMLHttpRequest",
"Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
"HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:post,path,params,headers_after_xhr)
@session.xml_http_request(:post,path,params,headers)
@ -143,8 +144,8 @@ def test_xml_http_request_post
def test_xml_http_request_put
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
"X-Requested-With" => "XMLHttpRequest",
"Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
"HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:put,path,params,headers_after_xhr)
@session.xml_http_request(:put,path,params,headers)
@ -153,8 +154,8 @@ def test_xml_http_request_put
def test_xml_http_request_delete
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
"X-Requested-With" => "XMLHttpRequest",
"Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
"HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:delete,path,params,headers_after_xhr)
@session.xml_http_request(:delete,path,params,headers)
@ -163,17 +164,17 @@ def test_xml_http_request_delete
def test_xml_http_request_head
path = "/index"; params = "blah"; headers = {:location => 'blah'}
headers_after_xhr = headers.merge(
"X-Requested-With" => "XMLHttpRequest",
"Accept" => "text/javascript, text/html, application/xml, text/xml, */*"
"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest",
"HTTP_ACCEPT" => "text/javascript, text/html, application/xml, text/xml, */*"
)
@session.expects(:process).with(:head,path,params,headers_after_xhr)
@session.xml_http_request(:head,path,params,headers)
end
def test_xml_http_request_override_accept
path = "/index"; params = "blah"; headers = {:location => 'blah', "Accept" => "application/xml"}
path = "/index"; params = "blah"; headers = {:location => 'blah', "HTTP_ACCEPT" => "application/xml"}
headers_after_xhr = headers.merge(
"X-Requested-With" => "XMLHttpRequest"
"HTTP_X_REQUESTED_WITH" => "XMLHttpRequest"
)
@session.expects(:process).with(:post,path,params,headers_after_xhr)
@session.xml_http_request(:post,path,params,headers)
@ -253,7 +254,7 @@ def test_get
assert_response 200
assert_response :success
assert_response :ok
assert_equal({}, cookies)
assert_equal({}, cookies.to_hash)
assert_equal "OK", body
assert_equal "OK", response.body
assert_kind_of HTML::Document, html_document
@ -269,7 +270,7 @@ def test_post
assert_response 201
assert_response :success
assert_response :created
assert_equal({}, cookies)
assert_equal({}, cookies.to_hash)
assert_equal "Created", body
assert_equal "Created", response.body
assert_kind_of HTML::Document, html_document
@ -287,7 +288,7 @@ def test_cookie_monster
assert_response 410
assert_response :gone
assert_equal "cookie_1=; path=/\ncookie_3=chocolate; path=/", headers["Set-Cookie"]
assert_equal({"cookie_1"=>nil, "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies)
assert_equal({"cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies.to_hash)
assert_equal "Gone", response.body
end
end

@ -9,8 +9,11 @@
ActionController::Base.view_paths = [ File.dirname(__FILE__) + '/../fixtures/layout_tests/' ]
require "fixture_template"
class LayoutTest < ActionController::Base
def self.controller_path; 'views' end
def self._implied_layout_name; to_s.underscore.gsub(/_controller$/, '') ; end
self.view_paths = ActionController::Base.view_paths.dup
end
@ -35,6 +38,15 @@ class ControllerNameSpace::NestedController < LayoutTest
class MultipleExtensions < LayoutTest
end
if defined?(ActionController::Http)
LayoutTest._write_layout_method
ProductController._write_layout_method
ItemController._write_layout_method
ThirdPartyTemplateLibraryController._write_layout_method
MultipleExtensions._write_layout_method
ControllerNameSpace::NestedController._write_layout_method
end
class LayoutAutoDiscoveryTest < ActionController::TestCase
def setup
super
@ -56,23 +68,19 @@ def test_controller_name_layout_name_match
def test_third_party_template_library_auto_discovers_layout
@controller = ThirdPartyTemplateLibraryController.new
get :hello
assert @controller.active_layout(true).identifier.include?('layouts/third_party_template_library.mab')
assert @controller.template.layout.include?('layouts/third_party_template_library')
assert_response :success
assert_equal 'Mab', @response.body
assert_equal 'layouts/third_party_template_library.mab', @response.body
end
def test_namespaced_controllers_auto_detect_layouts
def test_namespaced_controllers_auto_detect_layouts1
@controller = ControllerNameSpace::NestedController.new
get :hello
assert_equal 'layouts/controller_name_space/nested', @controller.active_layout(true).to_s
assert_equal 'controller_name_space/nested.rhtml hello.rhtml', @response.body
end
def test_namespaced_controllers_auto_detect_layouts
def test_namespaced_controllers_auto_detect_layouts2
@controller = MultipleExtensions.new
get :hello
assert @controller.active_layout(true).identifier.include?('layouts/multiple_extensions.html.erb')
assert_equal 'multiple_extensions.html.erb hello.rhtml', @response.body.strip
end
end
@ -139,7 +147,7 @@ def test_layout_only_exception_when_included
def test_layout_only_exception_when_excepted
@controller = OnlyLayoutController.new
get :goodbye
assert_equal nil, @controller.template.layout
assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'"
end
def test_layout_except_exception_when_included
@ -151,7 +159,7 @@ def test_layout_except_exception_when_included
def test_layout_except_exception_when_excepted
@controller = ExceptLayoutController.new
get :goodbye
assert_equal nil, @controller.template.layout
assert !@response.body.include?("item.rhtml"), "#{@response.body.inspect} included 'item.rhtml'"
end
def test_layout_set_when_using_render
@ -166,15 +174,18 @@ def test_layout_is_not_set_when_none_rendered
assert_nil @controller.template.layout
end
def test_exempt_from_layout_honored_by_render_template
ActionController::Base.exempt_from_layout :erb
@controller = RenderWithTemplateOptionController.new
for_tag(:old_base) do
# exempt_from_layout is deprecated
def test_exempt_from_layout_honored_by_render_template
ActionController::Base.exempt_from_layout :erb
@controller = RenderWithTemplateOptionController.new
get :hello
assert_equal "alt/hello.rhtml", @response.body.strip
get :hello
assert_equal "alt/hello.rhtml", @response.body.strip
ensure
ActionController::Base.exempt_from_layout.delete(ERB)
ensure
ActionController::Base.exempt_from_layout.delete(ERB)
end
end
def test_layout_is_picked_from_the_controller_instances_view_path

@ -11,6 +11,11 @@ class LoggingTest < ActionController::TestCase
class MockLogger
attr_reader :logged
attr_accessor :level
def initialize
@level = Logger::DEBUG
end
def method_missing(method, *args)
@logged ||= []
@ -30,7 +35,7 @@ def test_logging_without_parameters
end
def test_logging_with_parameters
get :show, :id => 10
get :show, :id => '10'
assert_equal 3, logs.size
params = logs.detect {|l| l =~ /Parameters/ }
@ -44,6 +49,6 @@ def set_logger
end
def logs
@logs ||= @controller.logger.logged.compact.map {|l| l.strip}
@logs ||= @controller.logger.logged.compact.map {|l| l.to_s.strip}
end
end

@ -375,9 +375,11 @@ def test_handle_any_any_xml
end
def test_rjs_type_skips_layout
@request.accept = "text/javascript"
get :all_types_with_layout
assert_equal 'RJS for all_types_with_layout', @response.body
pending(:new_base) do
@request.accept = "text/javascript"
get :all_types_with_layout
assert_equal 'RJS for all_types_with_layout', @response.body
end
end
def test_html_type_with_layout
@ -437,7 +439,7 @@ def test_render_action_for_html
@controller.instance_eval do
def render(*args)
unless args.empty?
@action = args.first[:action]
@action = args.first[:action] || action_name
end
response.body = "#{@action} - #{@template.formats}"
end
@ -490,14 +492,15 @@ def index
end
end
protected
def with_iphone
Mime::Type.register_alias("text/html", :iphone)
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
yield
ensure
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
end
protected
def with_iphone
Mime::Type.register_alias("text/html", :iphone)
request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone"
yield
ensure
Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) }
end
end
class SuperPostController < PostController
@ -509,6 +512,11 @@ def index
end
end
if defined?(ActionController::Http)
PostController._write_layout_method
SuperPostController._write_layout_method
end
class MimeControllerLayoutsTest < ActionController::TestCase
tests PostController
@ -526,14 +534,16 @@ def test_missing_layout_renders_properly
assert_equal 'Hello iPhone', @response.body
end
def test_format_with_inherited_layouts
@controller = SuperPostController.new
for_tag(:old_base) do
def test_format_with_inherited_layouts
@controller = SuperPostController.new
get :index
assert_equal 'Super Firefox', @response.body
get :index
assert_equal 'Super Firefox', @response.body
@request.accept = "text/iphone"
get :index
assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body
@request.accept = "text/iphone"
get :index
assert_equal '<html><div id="super_iphone">Super iPhone</div></html>', @response.body
end
end
end

@ -19,6 +19,8 @@ def partial
end
class RenderTest < ActionController::TestCase
tests TestController
def test_render_vanilla_js
get :render_vanilla_js_hello
assert_equal "alert('hello')", @response.body

@ -4,6 +4,7 @@
class TestController < ActionController::Base
protect_from_forgery
layout :determine_layout
module RenderTestHelper
def rjs_helper_method_from_module
@ -103,11 +104,26 @@ def render_alternate_default
end
private
def default_render
if @alternate_default_render
@alternate_default_render.call
else
super
end
end
def determine_layout
case action_name
when "render_js_with_explicit_template",
"render_js_with_explicit_action_template",
"delete_with_js", "update_page", "update_page_with_instance_variables"
when "hello_world", "layout_test", "rendering_without_layout",
"rendering_nothing_on_layout", "render_text_hello_world",
"render_text_hello_world_with_layout",
"hello_world_with_layout_false",
"partial_only", "partial_only_with_layout",
"accessing_params_in_template",
"accessing_params_in_template_with_layout",
"render_with_explicit_template",
"render_with_explicit_string_template",
"update_page", "update_page_with_instance_variables"
"layouts/standard"
when "action_talk_to_layout", "layout_overriding_layout"

@ -625,9 +625,7 @@ def determine_layout
"accessing_params_in_template_with_layout",
"render_with_explicit_template",
"render_with_explicit_string_template",
"render_js_with_explicit_template",
"render_js_with_explicit_action_template",
"delete_with_js", "update_page", "update_page_with_instance_variables"
"update_page", "update_page_with_instance_variables"
"layouts/standard"
when "action_talk_to_layout", "layout_overriding_layout"

@ -1,5 +1,19 @@
require 'abstract_unit'
module ActionDispatch
class ShowExceptions
private
def public_path
"#{FIXTURE_LOAD_PATH}/public"
end
# Silence logger
def logger
nil
end
end
end
class RescueController < ActionController::Base
class NotAuthorized < StandardError
end

@ -1,5 +1,6 @@
require 'abstract_unit'
require 'controller/fake_controllers'
require 'active_support/dependencies'
class MilestonesController < ActionController::Base
def index() head :ok end

@ -5,6 +5,7 @@
require 'abstract_unit'
require 'controller/fake_controllers'
require 'action_controller/vendor/html-scanner'
class SelectorTest < Test::Unit::TestCase
#

@ -11,12 +11,17 @@ class SendFileController < ActionController::Base
layout "layouts/standard" # to make sure layouts don't interfere
attr_writer :options
def options() @options ||= {} end
def options
@options ||= {}
end
def file() send_file(file_path, options) end
def data() send_data(file_data, options) end
def file
send_file(file_path, options)
end
def rescue_action(e) raise end
def data
send_data(file_data, options)
end
end
class SendFileTest < ActionController::TestCase
@ -40,17 +45,19 @@ def test_file_nostream
assert_equal file_data, response.body
end
def test_file_stream
response = nil
assert_nothing_raised { response = process('file') }
assert_not_nil response
assert_kind_of Array, response.body_parts
for_tag(:old_base) do
def test_file_stream
response = nil
assert_nothing_raised { response = process('file') }
assert_not_nil response
assert_kind_of Array, response.body_parts
require 'stringio'
output = StringIO.new
output.binmode
assert_nothing_raised { response.body_parts.each { |part| output << part.to_s } }
assert_equal file_data, output.string
require 'stringio'
output = StringIO.new
output.binmode
assert_nothing_raised { response.body_parts.each { |part| output << part.to_s } }
assert_equal file_data, output.string
end
end
def test_file_url_based_filename

@ -103,17 +103,15 @@ def no_default_action
end
protected
def rescue_action(e) raise end
def unconditional_redirect
redirect_to :action => "unguarded"
end
def unconditional_redirect
redirect_to :action => "unguarded"
end
end
def setup
@controller = TestController.new
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
tests TestController
setup do
ActionController::Routing::Routes.add_named_route :foo, '/foo', :controller => 'test', :action => 'foo'
end
@ -184,7 +182,7 @@ def test_unguarded_with_params
def test_unguarded_without_params
get :unguarded
assert_equal "", @response.body
assert @response.body.blank?
end
def test_guarded_in_session_with_prereqs

@ -34,7 +34,7 @@ def teardown
def test_check_parameters
with_test_route_set do
get "/"
assert_equal '', @controller.response.body
assert @controller.response.body.blank?
end
end
@ -163,7 +163,7 @@ def test_use_xml_ximple_with_empty_request
with_test_route_set do
ActionController::Base.param_parsers[Mime::XML] = :xml_simple
assert_nothing_raised { post "/", "", {'CONTENT_TYPE' => 'application/xml'} }
assert_equal "", @controller.response.body
assert @controller.response.body.blank?
end
end

@ -6,6 +6,11 @@ class ShowExceptions
def public_path
"#{FIXTURE_LOAD_PATH}/public"
end
# Silence logger
def logger
nil
end
end
end

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