Merge branch 'master' into active_model

Conflicts:
	activeresource/lib/active_resource/validations.rb
This commit is contained in:
Pratik Naik
2009-04-22 15:26:03 +01:00
485 changed files with 10667 additions and 7054 deletions

1
.gitignore vendored
View File

@@ -15,6 +15,7 @@ railties/pkg
railties/test/500.html
railties/doc/guides/html/images
railties/doc/guides/html/stylesheets
benches
railties/guides/output
*.rbc
*.swp

View File

@@ -4,7 +4,7 @@ require 'rake/contrib/sshpublisher'
env = %(PKG_BUILD="#{ENV['PKG_BUILD']}") if ENV['PKG_BUILD']
PROJECTS = %w(activesupport actionpack actionmailer activeresource activerecord railties)
PROJECTS = %w(activesupport actionpack actionmailer activemodel activeresource activerecord railties)
Dir["#{File.dirname(__FILE__)}/*/lib/*/version.rb"].each do |version_path|
require version_path

View File

@@ -373,6 +373,14 @@ module ActionMailer #:nodoc:
attr_reader :mail
attr_reader :template_name, :default_template_name, :action_name
def controller_path
self.class.controller_path
end
def formats
@template.formats
end
class << self
attr_writer :mailer_name
@@ -466,7 +474,7 @@ module ActionMailer #:nodoc:
# have not already been specified manually.
if @parts.empty?
Dir.glob("#{template_path}/#{@template}.*").each do |path|
template = template_root["#{mailer_name}/#{File.basename(path)}"]
template = template_root.find_by_parts("#{mailer_name}/#{File.basename(path)}")
# Skip unless template has a multipart format
next unless template && template.multipart?
@@ -475,7 +483,7 @@ module ActionMailer #:nodoc:
:content_type => template.content_type,
:disposition => "inline",
:charset => charset,
:body => render_message(template, @body)
:body => render_template(template, @body)
)
end
unless @parts.empty?
@@ -489,7 +497,7 @@ module ActionMailer #:nodoc:
# normal template exists (or if there were no implicit parts) we render
# it.
template_exists = @parts.empty?
template_exists ||= template_root["#{mailer_name}/#{@template}"]
template_exists ||= template_root.find_by_parts("#{mailer_name}/#{@template}")
@body = render_message(@template, @body) if template_exists
# Finally, if there are other message parts and a textual body exists,
@@ -514,6 +522,7 @@ module ActionMailer #:nodoc:
# no alternate has been given as the parameter, this will fail.
def deliver!(mail = @mail)
raise "no mail object available for delivery!" unless mail
unless logger.nil?
logger.info "Sent mail to #{Array(recipients).join(', ')}"
logger.debug "\n#{mail.encoded}"
@@ -545,27 +554,43 @@ module ActionMailer #:nodoc:
@mime_version = @@default_mime_version.dup if @@default_mime_version
end
def render_message(method_name, body)
if method_name.respond_to?(:content_type)
@current_template_content_type = method_name.content_type
def render_template(template, body)
if template.respond_to?(:content_type)
@current_template_content_type = template.content_type
end
@template = initialize_template_class(body)
layout = _pick_layout(layout, true) unless template.exempt_from_layout?
@template._render_template_with_layout(template, layout, {})
ensure
@current_template_content_type = nil
end
def render_message(method_name, body)
render :file => method_name, :body => body
ensure
@current_template_content_type = nil
end
def render(opts)
body = opts.delete(:body)
if opts[:file] && (opts[:file] !~ /\// && !opts[:file].respond_to?(:render))
opts[:file] = "#{mailer_name}/#{opts[:file]}"
end
layout, file = opts.delete(:layout), opts[:file]
begin
old_template, @template = @template, initialize_template_class(body)
layout = respond_to?(:pick_layout, true) ? pick_layout(opts) : false
@template.render(opts.merge(:layout => layout))
ensure
@template = old_template
@template = initialize_template_class(opts.delete(:body))
if file
prefix = mailer_name unless file =~ /\//
template = view_paths.find_by_parts(file, formats, prefix)
end
layout = _pick_layout(layout,
!template || !template.exempt_from_layout?)
if template
@template._render_template_with_layout(template, layout, opts)
elsif inline = opts[:inline]
@template._render_inline(inline, layout, opts)
end
end
end
@@ -577,12 +602,6 @@ module ActionMailer #:nodoc:
end
end
def candidate_for_layout?(options)
!self.view_paths.find_template(default_template_name, default_template_format).exempt_from_layout?
rescue ActionView::MissingTemplate
return true
end
def template_root
self.class.template_root
end
@@ -597,7 +616,7 @@ module ActionMailer #:nodoc:
def initialize_template_class(assigns)
template = ActionView::Base.new(self.class.view_paths, assigns, self)
template.template_format = default_template_format
template.formats = [default_template_format]
template
end

View File

@@ -19,7 +19,6 @@ ActionView::Template.register_template_handler :bak, lambda { |template| "Lame b
$:.unshift "#{File.dirname(__FILE__)}/fixtures/helpers"
ActionView::Base.cache_template_loading = true
FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures')
ActionMailer::Base.template_root = FIXTURE_LOAD_PATH

View File

@@ -565,13 +565,31 @@ class ActionMailerTest < Test::Unit::TestCase
TestMailer.deliver_signed_up(@recipient)
end
class FakeLogger
attr_reader :info_contents, :debug_contents
def initialize
@info_contents, @debug_contents = "", ""
end
def info(str)
@info_contents << str
end
def debug(str)
@debug_contents << str
end
end
def test_delivery_logs_sent_mail
mail = TestMailer.create_signed_up(@recipient)
logger = mock()
logger.expects(:info).with("Sent mail to #{@recipient}")
logger.expects(:debug).with("\n#{mail.encoded}")
TestMailer.logger = logger
# logger = mock()
# logger.expects(:info).with("Sent mail to #{@recipient}")
# logger.expects(:debug).with("\n#{mail.encoded}")
TestMailer.logger = FakeLogger.new
TestMailer.deliver_signed_up(@recipient)
assert(TestMailer.logger.info_contents =~ /Sent mail to #{@recipient}/)
assert_equal(TestMailer.logger.debug_contents, "\n#{mail.encoded}")
end
def test_unquote_quoted_printable_subject

View File

@@ -1,7 +1,14 @@
*Edge*
* Fixed that TestResponse.cookies was returning cookies unescaped #1867 [Doug McInnes]
*2.3.2 [Final] (March 15, 2009)*
* Fixed that redirection would just log the options, not the final url (which lead to "Redirected to #<Post:0x23150b8>") [DHH]
* Don't check authenticity tokens for any AJAX requests [Ross Kaffenberger/Bryan Helmkamp]
* Added ability to pass in :public => true to fresh_when, stale?, and expires_in to make the request proxy cachable #2095 [Gregg Pollack]
* Fixed that passing a custom form builder would be forwarded to nested fields_for calls #2023 [Eloy Duran/Nate Wiger]

View File

@@ -30,7 +30,7 @@ Rake::TestTask.new(:test_action_pack) do |t|
# 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/[cft]*/**/*_test.rb" ).sort
t.test_files = Dir.glob( "test/[cdft]*/**/*_test.rb" ).sort
t.verbose = true
#t.warning = true

View File

@@ -31,86 +31,59 @@ rescue LoadError
end
end
begin
gem 'rack', '~> 1.0.0'
require 'rack'
rescue Gem::LoadError
require 'action_controller/vendor/rack-1.0/rack'
end
require File.join(File.dirname(__FILE__), "action_pack")
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.
def self.load_all!
[Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
[Base, Request, Response, UrlRewriter, UrlWriter]
[ActionDispatch::Http::Headers]
end
autoload :AbstractRequest, 'action_controller/request'
autoload :Base, 'action_controller/base'
autoload :Benchmarking, 'action_controller/benchmarking'
autoload :Base, 'action_controller/base/base'
autoload :Benchmarking, 'action_controller/base/chained/benchmarking'
autoload :Caching, 'action_controller/caching'
autoload :Cookies, 'action_controller/cookies'
autoload :Dispatcher, 'action_controller/dispatcher'
autoload :Failsafe, 'action_controller/failsafe'
autoload :Filters, 'action_controller/filters'
autoload :Flash, 'action_controller/flash'
autoload :Helpers, 'action_controller/helpers'
autoload :HttpAuthentication, 'action_controller/http_authentication'
autoload :Integration, 'action_controller/integration'
autoload :IntegrationTest, 'action_controller/integration'
autoload :Layout, 'action_controller/layout'
autoload :MiddlewareStack, 'action_controller/middleware_stack'
autoload :MimeResponds, 'action_controller/mime_responds'
autoload :ParamsParser, 'action_controller/params_parser'
autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes'
autoload :Cookies, 'action_controller/base/cookies'
autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
autoload :Filters, 'action_controller/base/chained/filters'
autoload :Flash, 'action_controller/base/chained/flash'
autoload :Helpers, 'action_controller/base/helpers'
autoload :HttpAuthentication, 'action_controller/base/http_authentication'
autoload :Integration, 'action_controller/testing/integration'
autoload :IntegrationTest, 'action_controller/testing/integration'
autoload :Layout, 'action_controller/base/layout'
autoload :MimeResponds, 'action_controller/base/mime_responds'
autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes'
autoload :RecordIdentifier, 'action_controller/record_identifier'
autoload :Reloader, 'action_controller/reloader'
autoload :Request, 'action_controller/request'
autoload :RequestForgeryProtection, 'action_controller/request_forgery_protection'
autoload :Rescue, 'action_controller/rescue'
autoload :Resources, 'action_controller/resources'
autoload :Response, 'action_controller/response'
autoload :RewindableInput, 'action_controller/rewindable_input'
autoload :Redirector, 'action_controller/base/redirect'
autoload :Renderer, 'action_controller/base/render'
autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection'
autoload :Rescue, 'action_controller/dispatch/rescue'
autoload :Resources, 'action_controller/routing/resources'
autoload :Responder, 'action_controller/base/responder'
autoload :Routing, 'action_controller/routing'
autoload :SessionManagement, 'action_controller/session_management'
autoload :StatusCodes, 'action_controller/status_codes'
autoload :Streaming, 'action_controller/streaming'
autoload :TestCase, 'action_controller/test_case'
autoload :TestProcess, 'action_controller/test_process'
autoload :SessionManagement, 'action_controller/base/session_management'
autoload :Streaming, 'action_controller/base/streaming'
autoload :TestCase, 'action_controller/testing/test_case'
autoload :TestProcess, 'action_controller/testing/process'
autoload :Translation, 'action_controller/translation'
autoload :UploadedFile, 'action_controller/uploaded_file'
autoload :UploadedStringIO, 'action_controller/uploaded_file'
autoload :UploadedTempfile, 'action_controller/uploaded_file'
autoload :UrlRewriter, 'action_controller/url_rewriter'
autoload :UrlWriter, 'action_controller/url_rewriter'
autoload :Verification, 'action_controller/verification'
autoload :UrlEncodedPairParser, 'action_controller/dispatch/url_encoded_pair_parser'
autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter'
autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter'
autoload :Verification, 'action_controller/base/verification'
module Assertions
autoload :DomAssertions, 'action_controller/assertions/dom_assertions'
autoload :ModelAssertions, 'action_controller/assertions/model_assertions'
autoload :ResponseAssertions, 'action_controller/assertions/response_assertions'
autoload :RoutingAssertions, 'action_controller/assertions/routing_assertions'
autoload :SelectorAssertions, 'action_controller/assertions/selector_assertions'
autoload :TagAssertions, 'action_controller/assertions/tag_assertions'
autoload :DomAssertions, 'action_controller/testing/assertions/dom'
autoload :ModelAssertions, 'action_controller/testing/assertions/model'
autoload :ResponseAssertions, 'action_controller/testing/assertions/response'
autoload :RoutingAssertions, 'action_controller/testing/assertions/routing'
autoload :SelectorAssertions, 'action_controller/testing/assertions/selector'
autoload :TagAssertions, 'action_controller/testing/assertions/tag'
end
module Http
autoload :Headers, 'action_controller/headers'
end
module Session
autoload :AbstractStore, 'action_controller/session/abstract_store'
autoload :CookieStore, 'action_controller/session/cookie_store'
autoload :MemCacheStore, 'action_controller/session/mem_cache_store'
end
# DEPRECATE: Remove CGI support
autoload :CgiRequest, 'action_controller/cgi_process'
autoload :CGIHandler, 'action_controller/cgi_process'
end
autoload :Mime, 'action_controller/mime_type'
autoload :HTML, 'action_controller/vendor/html-scanner'
require 'action_dispatch'
require 'action_view'

View File

@@ -0,0 +1,10 @@
module AbstractController
autoload :Base, "action_controller/abstract/base"
autoload :Callbacks, "action_controller/abstract/callbacks"
autoload :Helpers, "action_controller/abstract/helpers"
autoload :Layouts, "action_controller/abstract/layouts"
autoload :Logger, "action_controller/abstract/logger"
autoload :Renderer, "action_controller/abstract/renderer"
# === Exceptions
autoload :ActionNotFound, "action_controller/abstract/exceptions"
end

View File

@@ -0,0 +1,41 @@
module AbstractController
class Base
attr_internal :response_body
attr_internal :response_obj
attr_internal :action_name
def self.process(action)
new.process(action)
end
def self.inherited(klass)
end
def initialize
self.response_obj = {}
end
def process(action_name)
unless respond_to_action?(action_name)
raise ActionNotFound, "The action '#{action_name}' could not be found"
end
@_action_name = action_name
process_action
self.response_obj[:body] = self.response_body
self
end
private
def process_action
respond_to?(action_name) ? send(action_name) : send(:action_missing, action_name)
end
def respond_to_action?(action_name)
respond_to?(action_name) || respond_to?(:action_missing, true)
end
end
end

View File

@@ -0,0 +1,40 @@
module AbstractController
module Callbacks
setup do
include ActiveSupport::NewCallbacks
define_callbacks :process_action
end
def process_action
_run_process_action_callbacks(action_name) do
super
end
end
module ClassMethods
def _normalize_callback_options(options)
if only = options[:only]
only = Array(only).map {|o| "action_name == :#{o}"}.join(" || ")
options[:per_key] = {:if => only}
end
if except = options[:except]
except = Array(except).map {|e| "action_name == :#{e}"}.join(" || ")
options[:per_key] = {:unless => except}
end
end
[:before, :after, :around].each do |filter|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{filter}_filter(*names, &blk)
options = names.last.is_a?(Hash) ? names.pop : {}
_normalize_callback_options(options)
names.push(blk) if block_given?
names.each do |name|
process_action_callback(:#{filter}, name, options)
end
end
RUBY_EVAL
end
end
end
end

View File

@@ -0,0 +1,3 @@
module AbstractController
class ActionNotFound < StandardError ; end
end

View File

@@ -0,0 +1,59 @@
module AbstractController
module Helpers
depends_on Renderer
setup do
extlib_inheritable_accessor :master_helper_module
self.master_helper_module = Module.new
end
# def self.included(klass)
# klass.class_eval do
# extlib_inheritable_accessor :master_helper_module
# self.master_helper_module = Module.new
# end
# end
def _action_view
@_action_view ||= begin
av = super
av.helpers.send(:include, master_helper_module)
av
end
end
module ClassMethods
def inherited(klass)
klass.master_helper_module = Module.new
klass.master_helper_module.__send__ :include, master_helper_module
super
end
def add_template_helper(mod)
master_helper_module.module_eval { include mod }
end
def helper_method(*meths)
meths.flatten.each do |meth|
master_helper_module.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
def #{meth}(*args, &blk)
controller.send(%(#{meth}), *args, &blk)
end
ruby_eval
end
end
def helper(*args, &blk)
args.flatten.each do |arg|
case arg
when Module
add_template_helper(arg)
end
end
master_helper_module.module_eval(&blk) if block_given?
end
end
end
end

View File

@@ -0,0 +1,82 @@
module AbstractController
module Layouts
depends_on Renderer
module ClassMethods
def layout(layout)
unless [String, Symbol, FalseClass, NilClass].include?(layout.class)
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
end
@_layout = layout || false # Converts nil to false
_write_layout_method
end
def _implied_layout_name
name.underscore
end
# Takes the specified layout and creates a _layout method to be called
# by _default_layout
#
# If the specified layout is a:
# String:: return the string
# Symbol:: call the method specified by the symbol
# false:: return nil
# none:: If a layout is found in the view paths with the controller's
# name, return that string. Otherwise, use the superclass'
# layout (which might also be implied)
def _write_layout_method
case @_layout
when String
self.class_eval %{def _layout() #{@_layout.inspect} end}
when Symbol
self.class_eval %{def _layout() #{@_layout} end}
when false
self.class_eval %{def _layout() end}
else
self.class_eval %{
def _layout
if view_paths.find_by_parts?("#{_implied_layout_name}", formats, "layouts")
"#{_implied_layout_name}"
else
super
end
end
}
end
end
end
def _render_template(template, options)
_action_view._render_template_with_layout(template, options[:_layout])
end
private
def _layout() end # This will be overwritten
def _layout_for_name(name)
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, "layouts")
end
def _default_layout(require_layout = false)
if require_layout && !_layout
raise ArgumentError,
"There was no default layout for #{self.class} in #{view_paths.inspect}"
end
begin
layout = _layout_for_name(_layout)
rescue NameError => e
raise NoMethodError,
"You specified #{@_layout.inspect} as the layout, but no such method was found"
end
end
end
end

View File

@@ -0,0 +1,7 @@
module AbstractController
module Logger
setup do
cattr_accessor :logger
end
end
end

View File

@@ -0,0 +1,80 @@
require "action_controller/abstract/logger"
module AbstractController
module Renderer
depends_on AbstractController::Logger
setup do
attr_internal :formats
extlib_inheritable_accessor :_view_paths
self._view_paths ||= ActionView::PathSet.new
end
def _action_view
@_action_view ||= ActionView::Base.new(self.class.view_paths, {}, self)
end
def render(options = {})
self.response_body = render_to_body(options)
end
# Raw rendering of a template to a Rack-compatible body.
# ====
# @option _prefix<String> The template's path prefix
# @option _layout<String> The relative path to the layout template to use
#
# :api: plugin
def render_to_body(options = {})
name = options[:_template_name] || action_name
template = options[:_template] || view_paths.find_by_parts(name.to_s, formats, options[:_prefix])
_render_template(template, options)
end
# Raw rendering of a template to a string.
# ====
# @option _prefix<String> The template's path prefix
# @option _layout<String> The relative path to the layout template to use
#
# :api: plugin
def render_to_string(options = {})
AbstractController::Renderer.body_to_s(render_to_body(options))
end
def _render_template(template, options)
_action_view._render_template_with_layout(template)
end
def view_paths() _view_paths end
# Return a string representation of a Rack-compatible response body.
def self.body_to_s(body)
if body.respond_to?(:to_str)
body
else
strings = []
body.each { |part| strings << part.to_s }
body.close if body.respond_to?(:close)
strings.join
end
end
module ClassMethods
def append_view_path(path)
self.view_paths << path
end
def view_paths
self._view_paths
end
def view_paths=(paths)
self._view_paths = paths.is_a?(ActionView::PathSet) ?
paths : ActionView::Base.process_view_paths(paths)
end
end
end
end

View File

@@ -1,3 +1,4 @@
require 'action_controller/deprecated'
require 'set'
module ActionController #:nodoc:
@@ -58,22 +59,6 @@ module ActionController #:nodoc:
end
end
class DoubleRenderError < ActionControllerError #:nodoc:
DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
def initialize(message = nil)
super(message || DEFAULT_MESSAGE)
end
end
class RedirectBackError < ActionControllerError #:nodoc:
DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
def initialize(message = nil)
super(message || DEFAULT_MESSAGE)
end
end
class UnknownHttpMethod < ActionControllerError #:nodoc:
end
@@ -247,9 +232,8 @@ module ActionController #:nodoc:
# end
#
class Base
DEFAULT_RENDER_STATUS_CODE = "200 OK"
include StatusCodes
include ActionDispatch::StatusCodes
cattr_reader :protected_instance_variables
# Controller specific instance variables which will not be accessible inside views.
@@ -301,7 +285,10 @@ module ActionController #:nodoc:
# A YAML parser is also available and can be turned on with:
#
# ActionController::Base.param_parsers[Mime::YAML] = :yaml
@@param_parsers = {}
@@param_parsers = { Mime::MULTIPART_FORM => :multipart_form,
Mime::URL_ENCODED_FORM => :url_encoded_form,
Mime::XML => :xml_simple,
Mime::JSON => :json }
cattr_accessor :param_parsers
# Controls the default charset for all renders.
@@ -381,8 +368,8 @@ module ActionController #:nodoc:
class << self
def call(env)
# HACK: For global rescue to have access to the original request and response
request = env["action_controller.rescue.request"] ||= Request.new(env)
response = env["action_controller.rescue.response"] ||= Response.new
request = env["action_controller.rescue.request"] ||= ActionDispatch::Request.new(env)
response = env["action_controller.rescue.response"] ||= ActionDispatch::Response.new
process(request, response)
end
@@ -408,7 +395,7 @@ module ActionController #:nodoc:
# Return an array containing the names of public methods that have been marked hidden from the action processor.
# By default, all methods defined in ActionController::Base and included modules are hidden.
# More methods can be hidden using <tt>hide_actions</tt>.
# More methods can be hidden using <tt>hide_action</tt>.
def hidden_actions
read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, [])
end
@@ -514,8 +501,8 @@ module ActionController #:nodoc:
def process(request, response, method = :perform_action, *arguments) #:nodoc:
response.request = request
initialize_template_class(response)
assign_shortcuts(request, response)
initialize_template_class(response)
initialize_current_url
assign_names
@@ -678,371 +665,6 @@ module ActionController #:nodoc:
@template.view_paths.push(*path)
end
protected
# Renders the content that will be returned to the browser as the response body.
#
# === Rendering an action
#
# Action rendering is the most common form and the type used automatically by Action Controller when nothing else is
# specified. By default, actions are rendered within the current layout (if one exists).
#
# # Renders the template for the action "goal" within the current controller
# render :action => "goal"
#
# # Renders the template for the action "short_goal" within the current controller,
# # but without the current active layout
# render :action => "short_goal", :layout => false
#
# # Renders the template for the action "long_goal" within the current controller,
# # but with a custom layout
# render :action => "long_goal", :layout => "spectacular"
#
# === Rendering partials
#
# Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page
# without reloading. Rendering of partials from the controller makes it possible to use the same partial template in
# both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the
# controller action responding to Ajax calls). By default, the current layout is not used.
#
# # Renders the same partial with a local variable.
# render :partial => "person", :locals => { :name => "david" }
#
# # Renders the partial, making @new_person available through
# # the local variable 'person'
# render :partial => "person", :object => @new_person
#
# # Renders a collection of the same partial by making each element
# # of @winners available through the local variable "person" as it
# # builds the complete response.
# render :partial => "person", :collection => @winners
#
# # Renders a collection of partials but with a custom local variable name
# render :partial => "admin_person", :collection => @winners, :as => :person
#
# # Renders the same collection of partials, but also renders the
# # person_divider partial between each person partial.
# render :partial => "person", :collection => @winners, :spacer_template => "person_divider"
#
# # Renders a collection of partials located in a view subfolder
# # outside of our current controller. In this example we will be
# # rendering app/views/shared/_note.r(html|xml) Inside the partial
# # each element of @new_notes is available as the local var "note".
# render :partial => "shared/note", :collection => @new_notes
#
# # Renders the partial with a status code of 500 (internal error).
# render :partial => "broken", :status => 500
#
# Note that the partial filename must also be a valid Ruby variable name,
# so e.g. 2005 and register-user are invalid.
#
#
# == Automatic etagging
#
# Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the
# response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified
# and the response body will be set to an empty string. No etag header will be inserted if it's already set.
#
# === Rendering a template
#
# Template rendering works just like action rendering except that it takes a path relative to the template root.
# The current layout is automatically applied.
#
# # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
# render :template => "weblog/show"
#
# # Renders the template with a local variable
# render :template => "weblog/show", :locals => {:customer => Customer.new}
#
# === Rendering a file
#
# File rendering works just like action rendering except that it takes a filesystem path. By default, the path
# is assumed to be absolute, and the current layout is not applied.
#
# # Renders the template located at the absolute filesystem path
# render :file => "/path/to/some/template.erb"
# render :file => "c:/path/to/some/template.erb"
#
# # Renders a template within the current layout, and with a 404 status code
# render :file => "/path/to/some/template.erb", :layout => true, :status => 404
# render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404
#
# === Rendering text
#
# Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
# rendering is not done within the active layout.
#
# # Renders the clear text "hello world" with status code 200
# render :text => "hello world!"
#
# # Renders the clear text "Explosion!" with status code 500
# render :text => "Explosion!", :status => 500
#
# # Renders the clear text "Hi there!" within the current active layout (if one exists)
# render :text => "Hi there!", :layout => true
#
# # Renders the clear text "Hi there!" within the layout
# # placed in "app/views/layouts/special.r(html|xml)"
# render :text => "Hi there!", :layout => "special"
#
# === Streaming data and/or controlling the page generation
#
# The <tt>:text</tt> option can also accept a Proc object, which can be used to:
#
# 1. stream on-the-fly generated data to the browser. Note that you should
# use the methods provided by ActionController::Steaming instead if you
# want to stream a buffer or a file.
# 2. manually control the page generation. This should generally be avoided,
# as it violates the separation between code and content, and because almost
# everything that can be done with this method can also be done more cleanly
# using one of the other rendering methods, most notably templates.
#
# Two arguments are passed to the proc, a <tt>response</tt> object and an
# <tt>output</tt> object. The response object is equivalent to the return
# value of the ActionController::Base#response method, and can be used to
# control various things in the HTTP response, such as setting the
# Content-Type header. The output object is an writable <tt>IO</tt>-like
# object, so one can call <tt>write</tt> and <tt>flush</tt> on it.
#
# The following example demonstrates how one can stream a large amount of
# on-the-fly generated data to the browser:
#
# # Streams about 180 MB of generated data to the browser.
# render :text => proc { |response, output|
# 10_000_000.times do |i|
# output.write("This is line #{i}\n")
# output.flush
# end
# }
#
# Another example:
#
# # Renders "Hello from code!"
# render :text => proc { |response, output| output.write("Hello from code!") }
#
# === Rendering XML
#
# Rendering XML sets the content type to application/xml.
#
# # Renders '<name>David</name>'
# render :xml => {:name => "David"}.to_xml
#
# It's not necessary to call <tt>to_xml</tt> on the object you want to render, since <tt>render</tt> will
# automatically do that for you:
#
# # Also renders '<name>David</name>'
# render :xml => {:name => "David"}
#
# === Rendering JSON
#
# Rendering JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected
# that the response will be parsed (or eval'd) for use as a data structure.
#
# # Renders '{"name": "David"}'
# render :json => {:name => "David"}.to_json
#
# It's not necessary to call <tt>to_json</tt> on the object you want to render, since <tt>render</tt> will
# automatically do that for you:
#
# # Also renders '{"name": "David"}'
# render :json => {:name => "David"}
#
# Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag),
# so the <tt>:callback</tt> option is provided for these cases.
#
# # Renders 'show({"name": "David"})'
# render :json => {:name => "David"}.to_json, :callback => 'show'
#
# === Rendering an inline template
#
# Rendering of an inline template works as a cross between text and action rendering where the source for the template
# is supplied inline, like text, but its interpreted with ERb or Builder, like action. By default, ERb is used for rendering
# and the current layout is not used.
#
# # Renders "hello, hello, hello, again"
# render :inline => "<%= 'hello, ' * 3 + 'again' %>"
#
# # Renders "<p>Good seeing you!</p>" using Builder
# render :inline => "xml.p { 'Good seeing you!' }", :type => :builder
#
# # Renders "hello david"
# render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" }
#
# === Rendering inline JavaScriptGenerator page updates
#
# In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details),
# you can also pass the <tt>:update</tt> parameter to +render+, along with a block, to render page updates inline.
#
# render :update do |page|
# page.replace_html 'user_list', :partial => 'user', :collection => @users
# page.visual_effect :highlight, 'user_list'
# end
#
# === Rendering vanilla JavaScript
#
# In addition to using RJS with render :update, you can also just render vanilla JavaScript with :js.
#
# # Renders "alert('hello')" and sets the mime type to text/javascript
# render :js => "alert('hello')"
#
# === Rendering with status and location headers
# All renders take the <tt>:status</tt> and <tt>:location</tt> options and turn them into headers. They can even be used together:
#
# render :xml => post.to_xml, :status => :created, :location => post_url(post)
def render(options = nil, extra_options = {}, &block) #:doc:
raise DoubleRenderError, "Can only render or redirect once per action" if performed?
validate_render_arguments(options, extra_options, block_given?)
if options.nil?
options = { :template => default_template, :layout => true }
elsif options == :update
options = extra_options.merge({ :update => true })
elsif options.is_a?(String) || options.is_a?(Symbol)
case options.to_s.index('/')
when 0
extra_options[:file] = options
when nil
extra_options[:action] = options
else
extra_options[:template] = options
end
options = extra_options
elsif !options.is_a?(Hash)
extra_options[:partial] = options
options = extra_options
end
layout = pick_layout(options)
response.layout = layout.path_without_format_and_extension if layout
logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger && layout
if content_type = options[:content_type]
response.content_type = content_type.to_s
end
if location = options[:location]
response.headers["Location"] = url_for(location)
end
if options.has_key?(:text)
text = layout ? @template.render(options.merge(:text => options[:text], :layout => layout)) : options[:text]
render_for_text(text, options[:status])
else
if file = options[:file]
render_for_file(file, options[:status], layout, options[:locals] || {})
elsif template = options[:template]
render_for_file(template, options[:status], layout, options[:locals] || {})
elsif inline = options[:inline]
render_for_text(@template.render(options.merge(:layout => layout)), options[:status])
elsif action_name = options[:action]
render_for_file(default_template(action_name.to_s), options[:status], layout)
elsif xml = options[:xml]
response.content_type ||= Mime::XML
render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
elsif js = options[:js]
response.content_type ||= Mime::JS
render_for_text(js, options[:status])
elsif json = options[:json]
json = json.to_json unless json.is_a?(String)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
response.content_type ||= Mime::JSON
render_for_text(json, options[:status])
elsif options[:partial]
options[:partial] = default_template_name if options[:partial] == true
if layout
render_for_text(@template.render(:text => @template.render(options), :layout => layout), options[:status])
else
render_for_text(@template.render(options), options[:status])
end
elsif options[:update]
@template.send(:_evaluate_assigns_and_ivars)
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
response.content_type = Mime::JS
render_for_text(generator.to_s, options[:status])
elsif options[:nothing]
render_for_text(nil, options[:status])
else
render_for_file(default_template, options[:status], layout)
end
end
end
# Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead
# of sending it as the response body to the browser.
def render_to_string(options = nil, &block) #:doc:
render(options, &block)
response.body
ensure
response.content_type = nil
erase_render_results
reset_variables_added_to_assigns
end
# Return a response that has no content (merely headers). The options
# argument is interpreted to be a hash of header names and values.
# This allows you to easily return a response that consists only of
# significant headers:
#
# head :created, :location => person_path(@person)
#
# It can also be used to return exceptional conditions:
#
# return head(:method_not_allowed) unless request.post?
# return head(:bad_request) unless valid_request?
# render
def head(*args)
if args.length > 2
raise ArgumentError, "too many arguments to head"
elsif args.empty?
raise ArgumentError, "too few arguments to head"
end
options = args.extract_options!
status = interpret_status(args.shift || options.delete(:status) || :ok)
options.each do |key, value|
headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
end
render :nothing => true, :status => status
end
# Clears the rendered results, allowing for another render to be performed.
def erase_render_results #:nodoc:
response.body = []
@performed_render = false
end
# Clears the redirected results from the headers, resets the status to 200 and returns
# the URL that was used to redirect or nil if there was no redirected URL
# Note that +redirect_to+ will change the body of the response to indicate a redirection.
# The response body is not reset here, see +erase_render_results+
def erase_redirect_results #:nodoc:
@performed_redirect = false
response.redirected_to = nil
response.redirected_to_method_params = nil
response.status = DEFAULT_RENDER_STATUS_CODE
response.headers.delete('Location')
end
# Erase both render and redirect results
def erase_results #:nodoc:
erase_render_results
erase_redirect_results
end
def rewrite_options(options) #:nodoc:
if defaults = default_url_options(options)
defaults.merge(options)
@@ -1064,73 +686,6 @@ module ActionController #:nodoc:
def default_url_options(options = nil)
end
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
#
# * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
#
# Examples:
# redirect_to :action => "show", :id => 5
# redirect_to post
# redirect_to "http://www.rubyonrails.org"
# redirect_to "/images/screenshot.jpg"
# redirect_to articles_url
# redirect_to :back
#
# The redirection happens as a "302 Moved" header unless otherwise specified.
#
# Examples:
# redirect_to post_url(@post), :status=>:found
# redirect_to :action=>'atom', :status=>:moved_permanently
# redirect_to post_url(@post), :status=>301
# redirect_to :action=>'atom', :status=>302
#
# When using <tt>redirect_to :back</tt>, if there is no referrer,
# RedirectBackError will be raised. You may specify some fallback
# behavior for this case by rescuing RedirectBackError.
def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
if options.is_a?(Hash) && options[:status]
status = options.delete(:status)
elsif response_status[:status]
status = response_status[:status]
else
status = 302
end
response.redirected_to = options
case options
# The scheme name consist of a letter followed by any combination of
# letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
# characters; and is terminated by a colon (":").
when %r{^\w[\w\d+.-]*:.*}
redirect_to_full_url(options, status)
when String
redirect_to_full_url(request.protocol + request.host_with_port + options, status)
when :back
if referer = request.headers["Referer"]
redirect_to(referer, :status=>status)
else
raise RedirectBackError
end
else
redirect_to_full_url(url_for(options), status)
end
end
def redirect_to_full_url(url, status)
raise DoubleRenderError if performed?
logger.info("Redirected to #{url}") if logger && logger.info?
response.redirect(url, interpret_status(status))
@performed_redirect = true
end
# Sets the etag and/or last_modified on the response and checks it against
# the client request. If the request doesn't match the options provided, the
# request is considered stale and should be generated from scratch. Otherwise,
@@ -1236,40 +791,20 @@ module ActionController #:nodoc:
end
private
def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc:
path = template_path.respond_to?(:path_without_format_and_extension) ? template_path.path_without_format_and_extension : template_path
logger.info("Rendering #{path}" + (status ? " (#{status})" : '')) if logger
render_for_text @template.render(:file => template_path, :locals => locals, :layout => layout), status
end
def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
@performed_render = true
response.status = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
if append_response
response.body_parts << text.to_s
else
response.body = case text
when Proc then text
when nil then [" "] # Safari doesn't pass the headers of the return if the response is zero length
else [text.to_s]
end
end
end
def validate_render_arguments(options, extra_options, has_block)
if options && (has_block && options != :update) && !options.is_a?(String) && !options.is_a?(Hash) && !options.is_a?(Symbol)
raise RenderError, "You called render with invalid options : #{options.inspect}"
def _process_options(options)
if content_type = options[:content_type]
response.content_type = content_type.to_s
end
if !extra_options.is_a?(Hash)
raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}"
if location = options[:location]
response.headers["Location"] = url_for(location)
end
response.status = interpret_status(options[:status] || DEFAULT_RENDER_STATUS_CODE)
end
def initialize_template_class(response)
response.template = ActionView::Base.new(self.class.view_paths, {}, self)
@template = response.template = ActionView::Base.new(self.class.view_paths, {}, self, formats)
response.template.helpers.send :include, self.class.master_helper_module
response.redirected_to = nil
@performed_render = @performed_redirect = false
@@ -1282,7 +817,6 @@ module ActionController #:nodoc:
@_response.session = request.session
@_session = @_response.session
@template = @_response.template
@_headers = @_response.headers
end
@@ -1318,26 +852,25 @@ module ActionController #:nodoc:
end
def perform_action
if action_methods.include?(action_name)
send(action_name)
default_render unless performed?
elsif respond_to? :method_missing
method_missing action_name
default_render unless performed?
else
begin
default_render
rescue ActionView::MissingTemplate => e
# Was the implicit template missing, or was it another template?
if e.path == default_template_name
raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence(:locale => :en)}", caller
else
raise e
end
end
if called = action_methods.include?(action_name)
ret = send(action_name)
elsif called = respond_to?(:method_missing)
ret = method_missing(action_name)
end
return (performed? ? ret : default_render) if called
begin
default_render
rescue ActionView::MissingTemplate => e
raise e unless e.action_name == action_name
# If the path is the same as the action_name, the action is completely missing
raise UnknownAction, "No action responded to #{action_name}. Actions: " +
"#{action_methods.sort.to_sentence}", caller
end
end
# Returns true if a render or redirect has already been performed.
def performed?
@performed_render || @performed_redirect
end
@@ -1346,22 +879,6 @@ module ActionController #:nodoc:
@action_name = (params['action'] || 'index')
end
def action_methods
self.class.action_methods
end
def self.action_methods
@action_methods ||=
# All public instance methods of this class, including ancestors
public_instance_methods(true).map { |m| m.to_s }.to_set -
# Except for public instance methods of Base and its ancestors
Base.public_instance_methods(true).map { |m| m.to_s } +
# Be sure to include shadowed public instance methods of this class
public_instance_methods(false).map { |m| m.to_s } -
# And always exclude explicitly hidden actions
hidden_actions
end
def reset_variables_added_to_assigns
@template.instance_variable_set("@assigns_added", nil)
end
@@ -1372,10 +889,15 @@ module ActionController #:nodoc:
@request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}"
end
# Returns the request URI used to get to the current location
def complete_request_uri
"#{request.protocol}#{request.host}#{request.request_uri}"
end
def close_session
# @_session.close if @_session && @_session.respond_to?(:close)
end
def default_template(action_name = self.action_name)
self.view_paths.find_template(default_template_name(action_name), default_template_format)
end
@@ -1387,7 +909,7 @@ module ActionController #:nodoc:
action_name = strip_out_controller(action_name)
end
end
"#{self.controller_path}/#{action_name}"
"#{controller_path}/#{action_name}"
end
def strip_out_controller(path)
@@ -1399,14 +921,15 @@ module ActionController #:nodoc:
end
def process_cleanup
close_session
end
end
Base.class_eval do
[ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
[ Filters, Layout, Renderer, Redirector, Responder, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
Cookies, Caching, Verification, Streaming, SessionManagement,
HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods,
RecordIdentifier, RequestForgeryProtection, Translation
HttpAuthentication::Basic::ControllerMethods, HttpAuthentication::Digest::ControllerMethods, RecordIdentifier,
RequestForgeryProtection, Translation
].each do |mod|
include mod
end

View File

@@ -64,7 +64,7 @@ module ActionController #:nodoc:
private
def perform_action_with_benchmark
if logger
if logger && logger.info?
ms = [Benchmark.ms { perform_action_without_benchmark }, 0.01].max
logging_view = defined?(@view_runtime)
logging_active_record = Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?

View File

@@ -2,11 +2,7 @@ module ActionController #:nodoc:
module Layout #:nodoc:
def self.included(base)
base.extend(ClassMethods)
base.class_eval do
class << self
alias_method_chain :inherited, :layout
end
end
base.class_inheritable_accessor :layout_name, :layout_conditions
end
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
@@ -159,123 +155,93 @@ module ActionController #:nodoc:
#
# This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout.
module ClassMethods
extend ActiveSupport::Memoizable
# If a layout is specified, all rendered actions will have their result rendered
# when the layout <tt>yield</tt>s. This layout can itself depend on instance variables assigned during action
# performance and have access to them as any normal template would.
def layout(template_name, conditions = {}, auto = false)
add_layout_conditions(conditions)
write_inheritable_attribute(:layout, template_name)
write_inheritable_attribute(:auto_layout, auto)
self.layout_name = template_name
end
def layout_conditions #:nodoc:
@layout_conditions ||= read_inheritable_attribute(:layout_conditions)
def memoized_default_layout(formats) #:nodoc:
self.layout_name || begin
layout = default_layout_name
layout.is_a?(String) ? find_layout(layout, formats) : layout
rescue ActionView::MissingTemplate
end
end
def default_layout(*args)
memoized_default_layout(*args)
@_memoized_default_layout ||= ::ActiveSupport::ConcurrentHash.new
@_memoized_default_layout[args] ||= memoized_default_layout(*args)
end
def memoized_find_layout(layout, formats) #:nodoc:
return layout if layout.nil? || layout.respond_to?(:render)
prefix = layout.to_s =~ /layouts\// ? nil : "layouts"
view_paths.find_by_parts(layout.to_s, formats, prefix)
end
def find_layout(*args)
@_memoized_find_layout ||= ::ActiveSupport::ConcurrentHash.new
@_memoized_find_layout[args] ||= memoized_find_layout(*args)
end
def layout_list #:nodoc:
Array(view_paths).sum([]) { |path| Dir["#{path.to_str}/layouts/**/*"] }
end
memoize :layout_list
def default_layout_name
layout_match = name.underscore.sub(/_controller$/, '')
if layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
superclass.default_layout_name if superclass.respond_to?(:default_layout_name)
else
layout_match
end
end
memoize :default_layout_name
private
def inherited_with_layout(child)
inherited_without_layout(child)
unless child.name.blank?
layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
end
end
def add_layout_conditions(conditions)
write_inheritable_hash(:layout_conditions, normalize_conditions(conditions))
end
def normalize_conditions(conditions)
conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})}
# :except => :foo == :except => [:foo] == :except => "foo" == :except => ["foo"]
conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
write_inheritable_hash(:layout_conditions, conditions)
end
end
# Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
# is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method
# object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
# weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
def active_layout(passed_layout = nil, options = {})
layout = passed_layout || default_layout
return layout if layout.respond_to?(:render)
active_layout = case layout
when Symbol then __send__(layout)
when Proc then layout.call(self)
else layout
def active_layout(name)
name = self.class.default_layout(formats) if name == true
layout_name = case name
when Symbol then __send__(name)
when Proc then name.call(self)
else name
end
find_layout(active_layout, default_template_format, options[:html_fallback]) if active_layout
self.class.find_layout(layout_name, formats)
end
def _pick_layout(layout_name = nil, implicit = false)
return unless layout_name || implicit
layout_name = true if layout_name.nil?
active_layout(layout_name) if action_has_layout? && layout_name
end
private
def default_layout #:nodoc:
layout = self.class.read_inheritable_attribute(:layout)
return layout unless self.class.read_inheritable_attribute(:auto_layout)
find_layout(layout, default_template_format)
rescue ActionView::MissingTemplate
nil
end
def find_layout(layout, format, html_fallback=false) #:nodoc:
view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", format, html_fallback)
rescue ActionView::MissingTemplate
raise if Mime::Type.lookup_by_extension(format.to_s).html?
end
def pick_layout(options)
if options.has_key?(:layout)
case layout = options.delete(:layout)
when FalseClass
nil
when NilClass, TrueClass
active_layout if action_has_layout? && candidate_for_layout?(:template => default_template_name)
else
active_layout(layout, :html_fallback => true)
end
else
active_layout if action_has_layout? && candidate_for_layout?(options)
end
end
def action_has_layout?
if conditions = self.class.layout_conditions
case
when only = conditions[:only]
only.include?(action_name)
when except = conditions[:except]
!except.include?(action_name)
else
true
end
else
true
end
end
def candidate_for_layout?(options)
template = options[:template] || default_template(options[:action])
if options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty?
begin
template_object = self.view_paths.find_template(template, default_template_format)
# this restores the behavior from 2.2.2, where response.template.template_format was reset
# to :html for :js requests with a matching html template.
# see v2.2.2, ActionView::Base, lines 328-330
@real_format = :html if response.template.template_format == :js && template_object.format == "html"
!template_object.exempt_from_layout?
rescue ActionView::MissingTemplate
true
if only = conditions[:only]
return only.include?(action_name)
elsif except = conditions[:except]
return !except.include?(action_name)
end
end
rescue ActionView::MissingTemplate
false
true
end
def default_template_format
@real_format || response.template.template_format
end
end
end

View File

@@ -109,16 +109,13 @@ module ActionController #:nodoc:
end
class Responder #:nodoc:
def initialize(controller)
@controller = controller
@request = controller.request
@response = controller.response
if ActionController::Base.use_accept_header
@mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
else
@mime_type_priority = [@request.format]
end
@mime_type_priority = @request.formats
@order = []
@responses = {}
@@ -130,7 +127,7 @@ module ActionController #:nodoc:
@order << mime_type
@responses[mime_type] ||= Proc.new do
@response.template.template_format = mime_type.to_sym
@response.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

View File

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

View File

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

View File

@@ -81,12 +81,13 @@ module ActionController #:nodoc:
# Returns true or false if a request is verified. Checks:
#
# * is the format restricted? By default, only HTML and AJAX requests are checked.
# * is the format restricted? By default, only HTML requests are checked.
# * is it a GET request? Gets should be safe and idempotent
# * Does the form_authenticity_token match the given token value from the params?
def verified_request?
!protect_against_forgery? ||
request.method == :get ||
request.xhr? ||
!verifiable_request_format? ||
form_authenticity_token == params[request_forgery_protection_token]
end

View File

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

View File

@@ -26,7 +26,7 @@ module ActionController #:nodoc:
if defined? @@session_store
@@session_store
else
Session::CookieStore
ActionDispatch::Session::CookieStore
end
end

View File

@@ -13,7 +13,7 @@ module ActionController #:nodoc:
#
# == Caching stores
#
# All the caching stores from ActiveSupport::Cache is available to be used as backends for Action Controller caching. This setting only
# All the caching stores from ActiveSupport::Cache are available to be used as backends for Action Controller caching. This setting only
# affects action and fragment caching as page caching is always written to disk.
#
# Configuration examples (MemoryStore is the default):

View File

@@ -134,7 +134,7 @@ module ActionController #:nodoc:
end
end
# When true, infer_extension will look up the cache path extension from the request's path & format.
# If +infer_extension+ is true, the cache path extension is looked up from the request's path & format.
# This is desirable when reading and writing the cache, but not when expiring the cache -
# expire_action should expire the same files regardless of the request format.
def initialize(controller, options = {}, infer_extension = true)

View File

@@ -1,15 +0,0 @@
require 'action_controller/cgi_ext/stdinput'
require 'action_controller/cgi_ext/query_extension'
require 'action_controller/cgi_ext/cookie'
class CGI #:nodoc:
include ActionController::CgiExt::Stdinput
class << self
alias :escapeHTML_fail_on_nil :escapeHTML
def escapeHTML(string)
escapeHTML_fail_on_nil(string) unless string.nil?
end
end
end

View File

@@ -1,112 +0,0 @@
require 'delegate'
CGI.module_eval { remove_const "Cookie" }
# TODO: document how this differs from stdlib CGI::Cookie
class CGI #:nodoc:
class Cookie < DelegateClass(Array)
attr_accessor :name, :value, :path, :domain, :expires
attr_reader :secure, :http_only
# Creates a new CGI::Cookie object.
#
# The contents of the cookie can be specified as a +name+ and one
# or more +value+ arguments. Alternatively, the contents can
# be specified as a single hash argument. The possible keywords of
# this hash are as follows:
#
# * <tt>:name</tt> - The name of the cookie. Required.
# * <tt>:value</tt> - The cookie's value or list of values.
# * <tt>:path</tt> - The path for which this cookie applies. Defaults to the
# base directory of the CGI script.
# * <tt>:domain</tt> - The domain for which this cookie applies.
# * <tt>:expires</tt> - The time at which this cookie expires, as a Time object.
# * <tt>:secure</tt> - Whether this cookie is a secure cookie or not (defaults to
# +false+). Secure cookies are only transmitted to HTTPS servers.
# * <tt>:http_only</tt> - Whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP.
# More details in http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx. Defaults to +false+.
#
# These keywords correspond to attributes of the cookie object.
def initialize(name = '', *value)
if name.kind_of?(String)
@name = name
@value = Array(value)
@domain = nil
@expires = nil
@secure = false
@http_only = false
@path = nil
else
@name = name['name']
@value = (name['value'].kind_of?(String) ? [name['value']] : Array(name['value'])).delete_if(&:blank?)
@domain = name['domain']
@expires = name['expires']
@secure = name['secure'] || false
@http_only = name['http_only'] || false
@path = name['path']
end
raise ArgumentError, "`name' required" unless @name
# simple support for IE
unless @path
%r|^(.*/)|.match(ENV['SCRIPT_NAME'])
@path = ($1 or '')
end
super(@value)
end
# Sets whether the Cookie is a secure cookie or not.
def secure=(val)
@secure = val == true
end
# Sets whether the Cookie is an HTTP only cookie or not.
def http_only=(val)
@http_only = val == true
end
# Converts the Cookie to its string representation.
def to_s
buf = ''
buf << @name << '='
buf << (@value.kind_of?(String) ? CGI::escape(@value) : @value.collect{|v| CGI::escape(v) }.join("&"))
buf << '; domain=' << @domain if @domain
buf << '; path=' << @path if @path
buf << '; expires=' << CGI::rfc1123_date(@expires) if @expires
buf << '; secure' if @secure
buf << '; HttpOnly' if @http_only
buf
end
# FIXME: work around broken 1.8.7 DelegateClass#respond_to?
def respond_to?(method, include_private = false)
return true if super(method)
return __getobj__.respond_to?(method, include_private)
end
# Parses a raw cookie string into a hash of <tt>cookie-name => cookie-object</tt>
# pairs.
#
# cookies = CGI::Cookie::parse("raw_cookie_string")
# # => { "name1" => cookie1, "name2" => cookie2, ... }
#
def self.parse(raw_cookie)
cookies = Hash.new([])
if raw_cookie
raw_cookie.split(/;\s?/).each do |pairs|
name, value = pairs.split('=',2)
next unless name and value
name = CGI::unescape(name)
unless cookies.has_key?(name)
cookies[name] = new(name, CGI::unescape(value))
end
end
end
cookies
end
end # class Cookie
end

View File

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

View File

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

View File

@@ -1,77 +0,0 @@
require 'action_controller/cgi_ext'
module ActionController #:nodoc:
class CGIHandler
module ProperStream
def each
while line = gets
yield line
end
end
def read(*args)
if args.empty?
super || ""
else
super
end
end
end
def self.dispatch_cgi(app, cgi, out = $stdout)
env = cgi.__send__(:env_table)
env.delete "HTTP_CONTENT_LENGTH"
cgi.stdinput.extend ProperStream
env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/"
env.update({
"rack.version" => [0,1],
"rack.input" => cgi.stdinput,
"rack.errors" => $stderr,
"rack.multithread" => false,
"rack.multiprocess" => true,
"rack.run_once" => false,
"rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http"
})
env["QUERY_STRING"] ||= ""
env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"]
env["REQUEST_PATH"] ||= "/"
env.delete "PATH_INFO" if env["PATH_INFO"] == ""
status, headers, body = app.call(env)
begin
out.binmode if out.respond_to?(:binmode)
out.sync = false if out.respond_to?(:sync=)
headers['Status'] = status.to_s
if headers.include?('Set-Cookie')
headers['cookie'] = headers.delete('Set-Cookie').split("\n")
end
out.write(cgi.header(headers))
body.each { |part|
out.write part
out.flush if out.respond_to?(:flush)
}
ensure
body.close if body.respond_to?(:close)
end
end
end
class CgiRequest #:nodoc:
DEFAULT_SESSION_OPTIONS = {
:database_manager => nil,
:prefix => "ruby_sess.",
:session_path => "/",
:session_key => "_session_id",
:cookie_only => true,
:session_http_only => true
}
end
end

View File

@@ -0,0 +1,2 @@
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response

View File

@@ -5,8 +5,8 @@ module ActionController
class << self
def define_dispatcher_callbacks(cache_classes)
unless cache_classes
unless self.middleware.include?(Reloader)
self.middleware.insert_after(Failsafe, Reloader)
unless self.middleware.include?(ActionDispatch::Reloader)
self.middleware.insert_after(ActionDispatch::Failsafe, ActionDispatch::Reloader)
end
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
@@ -23,11 +23,6 @@ module ActionController
end
end
# DEPRECATE: Remove CGI support
def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout)
new(output).dispatch_cgi(cgi, session_options)
end
# Add a preparation callback. Preparation callbacks are run before every
# request in development mode, and before the first request in production
# mode.
@@ -43,13 +38,7 @@ module ActionController
end
def run_prepare_callbacks
if defined?(Rails) && Rails.logger
logger = Rails.logger
else
logger = Logger.new($stderr)
end
new(logger).send :run_callbacks, :prepare_dispatch
new.send :run_callbacks, :prepare_dispatch
end
def reload_application
@@ -68,7 +57,7 @@ module ActionController
end
cattr_accessor :middleware
self.middleware = MiddlewareStack.new do |middleware|
self.middleware = ActionDispatch::MiddlewareStack.new do |middleware|
middlewares = File.join(File.dirname(__FILE__), "middlewares.rb")
middleware.instance_eval(File.read(middlewares))
end
@@ -76,19 +65,22 @@ module ActionController
include ActiveSupport::Callbacks
define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch
# DEPRECATE: Remove arguments, since they are only used by CGI
def initialize(output = $stdout, request = nil, response = nil)
@output = output
@app = @@middleware.build(lambda { |env| self.dup._call(env) })
def initialize
@app = @@middleware.build(lambda { |env| self._call(env) })
freeze
end
def dispatch
def call(env)
@app.call(env)
end
def _call(env)
begin
run_callbacks :before_dispatch
Routing::Routes.call(@env)
Routing::Routes.call(env)
rescue Exception => exception
if controller ||= (::ApplicationController rescue Base)
controller.call_with_exception(@env, exception).to_a
controller.call_with_exception(env, exception).to_a
else
raise exception
end
@@ -97,20 +89,6 @@ module ActionController
end
end
# DEPRECATE: Remove CGI support
def dispatch_cgi(cgi, session_options)
CGIHandler.dispatch_cgi(self, cgi, @output)
end
def call(env)
@app.call(env)
end
def _call(env)
@env = env
dispatch
end
def flush_logger
Base.logger.flush
end

View File

@@ -2,12 +2,12 @@ use "Rack::Lock", :if => lambda {
!ActionController::Base.allow_concurrency
}
use "ActionController::Failsafe"
use "ActionDispatch::Failsafe"
use lambda { ActionController::Base.session_store },
lambda { ActionController::Base.session_options }
use "ActionController::RewindableInput"
use "ActionController::ParamsParser"
use "ActionDispatch::RewindableInput"
use "ActionDispatch::ParamsParser"
use "Rack::MethodOverride"
use "Rack::Head"

View File

@@ -38,7 +38,7 @@ module ActionController #:nodoc:
'ActionView::TemplateError' => 'template_error'
}
RESCUES_TEMPLATE_PATH = ActionView::Template::EagerPath.new_and_loaded(
RESCUES_TEMPLATE_PATH = ActionView::Template::FileSystemPath.new(
File.join(File.dirname(__FILE__), "templates"))
def self.included(base) #:nodoc:
@@ -60,8 +60,8 @@ module ActionController #:nodoc:
module ClassMethods
def call_with_exception(env, exception) #:nodoc:
request = env["action_controller.rescue.request"] ||= Request.new(env)
response = env["action_controller.rescue.response"] ||= Response.new
request = env["action_controller.rescue.request"] ||= ActionDispatch::Request.new(env)
response = env["action_controller.rescue.response"] ||= ActionDispatch::Response.new
new.process(request, response, :rescue_action, exception)
end
end
@@ -131,11 +131,13 @@ module ActionController #:nodoc:
@template.instance_variable_set("@exception", exception)
@template.instance_variable_set("@rescues_path", RESCUES_TEMPLATE_PATH)
@template.instance_variable_set("@contents",
@template.render(:file => template_path_for_local_rescue(exception)))
@template._render_template(template_path_for_local_rescue(exception)))
response.content_type = Mime::HTML
render_for_file(rescues_path("layout"),
response_code_for_rescue(exception))
response.status = interpret_status(response_code_for_rescue(exception))
content = @template._render_template(rescues_path("layout"))
render_for_text(content)
end
def rescue_action_without_handler(exception)
@@ -163,7 +165,7 @@ module ActionController #:nodoc:
end
def rescues_path(template_name)
RESCUES_TEMPLATE_PATH["rescues/#{template_name}.erb"]
RESCUES_TEMPLATE_PATH.find_by_parts("rescues/#{template_name}.erb")
end
def template_path_for_local_rescue(exception)

View File

@@ -6,6 +6,5 @@
</h1>
<pre><%=h @exception.clean_message %></pre>
<%= render :file => @rescues_path["rescues/_trace.erb"] %>
<%= render :file => @rescues_path["rescues/_request_and_response.erb"] %>
<%= @template._render_template(@rescues_path.find_by_parts("rescues/_trace.erb")) %>
<%= @template._render_template(@rescues_path.find_by_parts("rescues/_request_and_response.erb")) %>

View File

@@ -0,0 +1,7 @@
module ActionController
autoload :AbstractBase, "action_controller/new_base/base"
autoload :HideActions, "action_controller/new_base/hide_actions"
autoload :Layouts, "action_controller/new_base/layouts"
autoload :Renderer, "action_controller/new_base/renderer"
autoload :UrlFor, "action_controller/new_base/url_for"
end

View File

@@ -0,0 +1,60 @@
module ActionController
class AbstractBase < AbstractController::Base
# :api: public
attr_internal :request, :response, :params
# :api: public
def self.controller_name
@controller_name ||= controller_path.split("/").last
end
# :api: public
def controller_name() self.class.controller_name end
# :api: public
def self.controller_path
@controller_path ||= self.name.sub(/Controller$/, '').underscore
end
# :api: public
def controller_path() self.class.controller_path end
# :api: private
def self.action_methods
@action_names ||= Set.new(self.public_instance_methods - self::CORE_METHODS)
end
# :api: private
def self.action_names() action_methods end
# :api: private
def action_methods() self.class.action_names end
# :api: private
def action_names() action_methods end
# :api: plugin
def self.call(env)
controller = new
controller.call(env).to_rack
end
# :api: plugin
def response_body=(body)
@_response.body = body
end
# :api: private
def call(env)
@_request = ActionDispatch::Request.new(env)
@_response = ActionDispatch::Response.new
process(@_request.parameters[:action])
end
# :api: private
def to_rack
response.to_a
end
end
end

View File

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

View File

@@ -0,0 +1,37 @@
module ActionController
module Layouts
depends_on ActionController::Renderer
depends_on AbstractController::Layouts
module ClassMethods
def _implied_layout_name
controller_path
end
end
def render_to_body(options)
# render :text => ..., :layout => ...
# or
# render :anything_else
if !options.key?(:text) || options.key?(:layout)
options[:_layout] = options.key?(:layout) ? _layout_for_option(options[:layout]) : _default_layout
end
super
end
private
def _layout_for_option(name)
case name
when String then _layout_for_name(name)
when true then _default_layout(true)
when false, nil then nil
else
raise ArgumentError,
"String, true, or false, expected for `layout'; you passed #{name.inspect}"
end
end
end
end

View File

@@ -0,0 +1,62 @@
module ActionController
module Renderer
depends_on AbstractController::Renderer
def initialize(*)
self.formats = [:html]
super
end
def render(action, options = {})
# TODO: Move this into #render_to_body
if action.is_a?(Hash)
options, action = action, nil
else
options.merge! :action => action
end
_process_options(options)
self.response_body = render_to_body(options)
end
def render_to_body(options)
unless options.is_a?(Hash)
options = {:action => options}
end
if options.key?(:text)
options[:_template] = ActionView::TextTemplate.new(_text(options))
template = nil
elsif options.key?(:template)
options[:_template_name] = options[:template]
elsif options.key?(:action)
options[:_template_name] = options[:action].to_s
options[:_prefix] = _prefix
end
super(options)
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 _process_options(options)
if status = options[:status]
response.status = status.to_i
end
end
end
end

View File

@@ -0,0 +1,40 @@
module ActionController
module UrlFor
def initialize_current_url
@url = UrlRewriter.new(request, params.clone)
end
# Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
# the form of a hash, just like the one you would use for url_for directly. Example:
#
# def default_url_options(options)
# { :project => @project.active? ? @project.url_name : "unknown" }
# end
#
# As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
# urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set
# by this method.
def default_url_options(options = nil)
end
def rewrite_options(options) #:nodoc:
if defaults = default_url_options(options)
defaults.merge(options)
else
options
end
end
def url_for(options = {})
options ||= {}
case options
when String
options
when Hash
@url.rewrite(rewrite_options(options))
else
polymorphic_url(options)
end
end
end
end

View File

@@ -1,4 +1,4 @@
module ActionController
module ActionController
# The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or
# Active Resources or pretty much any other model type that has an id. These patterns are then used to try elevate
# the view actions to a higher logical level. Example:

View File

@@ -1,28 +0,0 @@
module ActionController
class RewindableInput
class RewindableIO < ActiveSupport::BasicObject
def initialize(io)
@io = io
@rewindable = io.is_a?(::StringIO)
end
def method_missing(method, *args, &block)
unless @rewindable
@io = ::StringIO.new(@io.read)
@rewindable = true
end
@io.__send__(method, *args, &block)
end
end
def initialize(app)
@app = app
end
def call(env)
env['rack.input'] = RewindableIO.new(env['rack.input'])
@app.call(env)
end
end
end

View File

@@ -68,29 +68,17 @@ module ActionController
# This generates, among other things, the method <tt>users_path</tt>. By default,
# this method is accessible from your controllers, views and mailers. If you need
# to access this auto-generated method from other places (such as a model), then
# you can do that in two ways.
#
# The first way is to include ActionController::UrlWriter in your class:
# you can do that by including ActionController::UrlWriter in your class:
#
# class User < ActiveRecord::Base
# include ActionController::UrlWriter # !!!
# include ActionController::UrlWriter
#
# def name=(value)
# write_attribute('name', value)
# write_attribute('base_uri', users_path) # !!!
# def base_uri
# user_path(self)
# end
# end
#
# The second way is to access them through ActionController::UrlWriter.
# The autogenerated named routes methods are available as class methods:
#
# class User < ActiveRecord::Base
# def name=(value)
# write_attribute('name', value)
# path = ActionController::UrlWriter.users_path # !!!
# write_attribute('base_uri', path) # !!!
# end
# end
# User.find(1).base_uri # => "/users/1"
module UrlWriter
def self.included(base) #:nodoc:
ActionController::Routing::Routes.install_helpers(base)

View File

@@ -428,7 +428,7 @@ module ActionController
end
def call(env)
request = Request.new(env)
request = ActionDispatch::Request.new(env)
app = Routing::Routes.recognize(request)
app.call(env).to_a
end

View File

@@ -1,88 +0,0 @@
module ActionController
module StatusCodes #:nodoc:
# Defines the standard HTTP status codes, by integer, with their
# corresponding default message texts.
# Source: http://www.iana.org/assignments/http-status-codes
STATUS_CODES = {
100 => "Continue",
101 => "Switching Protocols",
102 => "Processing",
200 => "OK",
201 => "Created",
202 => "Accepted",
203 => "Non-Authoritative Information",
204 => "No Content",
205 => "Reset Content",
206 => "Partial Content",
207 => "Multi-Status",
226 => "IM Used",
300 => "Multiple Choices",
301 => "Moved Permanently",
302 => "Found",
303 => "See Other",
304 => "Not Modified",
305 => "Use Proxy",
307 => "Temporary Redirect",
400 => "Bad Request",
401 => "Unauthorized",
402 => "Payment Required",
403 => "Forbidden",
404 => "Not Found",
405 => "Method Not Allowed",
406 => "Not Acceptable",
407 => "Proxy Authentication Required",
408 => "Request Timeout",
409 => "Conflict",
410 => "Gone",
411 => "Length Required",
412 => "Precondition Failed",
413 => "Request Entity Too Large",
414 => "Request-URI Too Long",
415 => "Unsupported Media Type",
416 => "Requested Range Not Satisfiable",
417 => "Expectation Failed",
422 => "Unprocessable Entity",
423 => "Locked",
424 => "Failed Dependency",
426 => "Upgrade Required",
500 => "Internal Server Error",
501 => "Not Implemented",
502 => "Bad Gateway",
503 => "Service Unavailable",
504 => "Gateway Timeout",
505 => "HTTP Version Not Supported",
507 => "Insufficient Storage",
510 => "Not Extended"
}
# Provides a symbol-to-fixnum lookup for converting a symbol (like
# :created or :not_implemented) into its corresponding HTTP status
# code (like 200 or 501).
SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) do |hash, (code, message)|
hash[message.gsub(/ /, "").underscore.to_sym] = code
hash
end
# Given a status parameter, determine whether it needs to be converted
# to a string. If it is a fixnum, use the STATUS_CODES hash to lookup
# the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE
# hash to convert it.
def interpret_status(status)
case status
when Fixnum then
"#{status} #{STATUS_CODES[status]}".strip
when Symbol then
interpret_status(SYMBOL_TO_STATUS_CODE[status] ||
"500 Unknown Status #{status.inspect}")
else
status.to_s
end
end
private :interpret_status
end
end

View File

@@ -11,7 +11,7 @@ module ActionController
#
# You can also pass an explicit status number like assert_response(501)
# or its symbolic equivalent assert_response(:not_implemented).
# See ActionController::StatusCodes for a full list.
# See ActionDispatch::StatusCodes for a full list.
#
# ==== Examples
#
@@ -27,7 +27,7 @@ module ActionController
assert_block("") { true } # to count the assertion
elsif type.is_a?(Fixnum) && @response.response_code == type
assert_block("") { true } # to count the assertion
elsif type.is_a?(Symbol) && @response.response_code == ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
elsif type.is_a?(Symbol) && @response.response_code == ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
assert_block("") { true } # to count the assertion
else
if @response.error?

View File

@@ -323,7 +323,7 @@ module ActionController
@html_document = nil
@status = status.to_i
@status_message = StatusCodes::STATUS_CODES[@status]
@status_message = ActionDispatch::StatusCodes::STATUS_CODES[@status]
@headers = Rack::Utils::HeaderHash.new(headers)
@@ -348,7 +348,7 @@ module ActionController
else
# Decorate responses from Rack Middleware and Rails Metal
# as an Response for the purposes of integration testing
@response = Response.new
@response = ActionDispatch::Response.new
@response.status = status.to_s
@response.headers.replace(@headers)
@response.body = @body_parts
@@ -387,7 +387,7 @@ module ActionController
"SERVER_PORT" => https? ? "443" : "80",
"HTTPS" => https? ? "on" : "off"
}
UrlRewriter.new(Request.new(env), {})
UrlRewriter.new(ActionDispatch::Request.new(env), {})
end
def name_with_prefix(prefix, name)

View File

@@ -1,5 +1,6 @@
require 'rack/session/abstract/id'
module ActionController #:nodoc:
class TestRequest < Request #:nodoc:
class TestRequest < ActionDispatch::Request #:nodoc:
attr_accessor :cookies, :session_options
attr_accessor :query_parameters, :path, :session
attr_accessor :host
@@ -13,6 +14,8 @@ module ActionController #:nodoc:
@query_parameters = {}
@session = TestSession.new
default_rack_options = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
@session_options ||= {:id => generate_sid(default_rack_options[:sidbits])}.merge(default_rack_options)
initialize_default_values
initialize_containers
@@ -110,6 +113,7 @@ module ActionController #:nodoc:
end
def recycle!
@env["action_controller.request.request_parameters"] = {}
self.query_parameters = {}
self.path_parameters = {}
@headers, @request_method, @accepts, @content_type = nil, nil, nil, nil
@@ -120,6 +124,10 @@ module ActionController #:nodoc:
end
private
def generate_sid(sidbits)
"%0#{sidbits / 4}x" % rand(2**sidbits - 1)
end
def initialize_containers
@cookies = {}
end
@@ -250,7 +258,7 @@ module ActionController #:nodoc:
def cookies
cookies = {}
Array(headers['Set-Cookie']).each do |cookie|
key, value = cookie.split(";").first.split("=")
key, value = cookie.split(";").first.split("=").map {|val| Rack::Utils.unescape(val)}
cookies[key] = value
end
cookies
@@ -275,10 +283,11 @@ module ActionController #:nodoc:
# controller actions.
#
# See Response for more information on controller response objects.
class TestResponse < Response
class TestResponse < ActionDispatch::Response
include TestResponseBehavior
def recycle!
body_parts.clear
headers.delete('ETag')
headers.delete('Last-Modified')
end

View File

@@ -1,5 +1,5 @@
require 'active_support/test_case'
require 'action_controller/test_process'
require 'action_controller/testing/process'
module ActionController
# Superclass for ActionController functional tests. Functional tests allow you to

View File

@@ -1,44 +0,0 @@
module ActionController
module UploadedFile
def self.included(base)
base.class_eval do
attr_accessor :original_path, :content_type
alias_method :local_path, :path
end
end
def self.extended(object)
object.class_eval do
attr_accessor :original_path, :content_type
alias_method :local_path, :path
end
end
# Take the basename of the upload's original filename.
# This handles the full Windows paths given by Internet Explorer
# (and perhaps other broken user agents) without affecting
# those which give the lone filename.
# The Windows regexp is adapted from Perl's File::Basename.
def original_filename
unless defined? @original_filename
@original_filename =
unless original_path.blank?
if original_path =~ /^(?:.*[:\\\/])?(.*)/m
$1
else
File.basename original_path
end
end
end
@original_filename
end
end
class UploadedStringIO < StringIO
include UploadedFile
end
class UploadedTempfile < Tempfile
include UploadedFile
end
end

View File

@@ -0,0 +1,64 @@
#--
# Copyright (c) 2004-2009 David Heinemeier Hansson
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
begin
require 'active_support'
rescue LoadError
activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
if File.directory?(activesupport_path)
$:.unshift activesupport_path
require 'active_support'
end
end
$:.unshift "#{File.dirname(__FILE__)}/action_dispatch/vendor/rack-1.0"
begin
gem 'rack', '~> 1.0.0'
require 'rack'
rescue Gem::LoadError
require 'action_dispatch/vendor/rack-1.0/rack'
end
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 :ParamsParser, 'action_dispatch/middleware/params_parser'
autoload :Reloader, 'action_dispatch/middleware/reloader'
autoload :RewindableInput, 'action_dispatch/middleware/rewindable_input'
autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
module Http
autoload :Headers, 'action_dispatch/http/headers'
end
module Session
autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store'
autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
end
end
autoload :Mime, 'action_dispatch/http/mime_type'

View File

@@ -1,6 +1,6 @@
require 'active_support/memoizable'
module ActionController
module ActionDispatch
module Http
class Headers < ::Hash
extend ActiveSupport::Memoizable

View File

@@ -5,6 +5,10 @@ module Mime
EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
def self.[](type)
Type.lookup_by_extension(type.to_s)
end
# Encapsulates the notion of a mime type. Can be used at render time, for example, with:
#
# class PostsController < ActionController::Base
@@ -27,7 +31,7 @@ module Mime
# only needs to protect against these types.
@@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text]
cattr_reader :browser_generated_types
attr_reader :symbol
@@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
def self.unverifiable_types
@@ -187,17 +191,13 @@ module Mime
# Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
# ActionController::RequestForgeryProtection.
def verify_request?
browser_generated?
@@browser_generated_types.include?(to_sym)
end
def html?
@@html_types.include?(to_sym) || @string =~ /html/
end
def browser_generated?
@@browser_generated_types.include?(to_sym)
end
private
def method_missing(method, *args)
if method.to_s =~ /(\w+)\?$/
@@ -209,4 +209,4 @@ module Mime
end
end
require 'action_controller/mime_types'
require 'action_dispatch/http/mime_types'

View File

@@ -3,9 +3,8 @@ require 'stringio'
require 'strscan'
require 'active_support/memoizable'
require 'action_controller/cgi_ext'
module ActionController
module ActionDispatch
class Request < Rack::Request
%w[ AUTH_TYPE GATEWAY_INTERFACE
@@ -32,7 +31,7 @@ module ActionController
# <tt>:get</tt>. If the request \method is not listed in the HTTP_METHODS
# constant above, an UnknownHttpMethod exception is raised.
def request_method
@request_method ||= HTTP_METHOD_LOOKUP[super] || raise(UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
@request_method ||= HTTP_METHOD_LOOKUP[super] || raise(ActionController::UnknownHttpMethod, "#{super}, accepted HTTP methods are #{HTTP_METHODS.to_sentence(:locale => :en)}")
end
# Returns the HTTP request \method used for action processing as a
@@ -73,7 +72,7 @@ module ActionController
#
# request.headers["Content-Type"] # => "text/plain"
def headers
@headers ||= ActionController::Http::Headers.new(@env)
Http::Headers.new(@env)
end
# Returns the content length of the request as an integer.
@@ -94,20 +93,26 @@ module ActionController
end
end
end
# Returns the accepted MIME type for the request.
def accepts
@accepts ||= begin
header = @env['HTTP_ACCEPT'].to_s.strip
fallback = xhr? ? Mime::JS : Mime::HTML
if header.empty?
[content_type, Mime::ALL].compact
[content_type, fallback, Mime::ALL].compact
else
Mime::Type.parse(header)
ret = Mime::Type.parse(header)
if ret.last == Mime::ALL
ret.insert(-2, fallback)
end
ret
end
end
end
def if_modified_since
if since = env['HTTP_IF_MODIFIED_SINCE']
Time.rfc2822(since) rescue nil
@@ -142,24 +147,33 @@ module ActionController
end
end
ONLY_ALL = [Mime::ALL].freeze
# Returns the Mime type for the \format used in the request.
#
# GET /posts/5.xml | request.format => Mime::XML
# GET /posts/5.xhtml | request.format => Mime::HTML
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>
def format
def format(view_path = [])
@format ||=
if parameters[:format]
Mime::Type.lookup_by_extension(parameters[:format])
elsif ActionController::Base.use_accept_header
accepts.first
elsif xhr?
Mime::Type.lookup_by_extension("js")
else
Mime::Type.lookup_by_extension("html")
Mime[parameters[:format]]
elsif ActionController::Base.use_accept_header && !(accepts == ONLY_ALL)
accepts.first
elsif xhr? then Mime::JS
else Mime::HTML
end
end
def formats
@formats =
if ActionController::Base.use_accept_header
Array(Mime[parameters[:format]] || accepts)
else
[format]
end
end
# Sets the \format by string extension, which can be used to force custom formats
# that are not controlled by the extension.
@@ -226,7 +240,7 @@ module ActionController
if @env.include? 'HTTP_CLIENT_IP'
if ActionController::Base.ip_spoofing_check && remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP'])
# We don't know which came from the proxy, and which from the user
raise ActionControllerError.new(<<EOM)
raise ActionController::ActionControllerError.new(<<EOM)
IP spoofing attack?!
HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
@@ -463,6 +477,34 @@ EOM
!(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
end
module UploadedFile
def self.extended(object)
object.class_eval do
attr_accessor :original_path, :content_type
alias_method :local_path, :path
end
end
# Take the basename of the upload's original filename.
# This handles the full Windows paths given by Internet Explorer
# (and perhaps other broken user agents) without affecting
# those which give the lone filename.
# The Windows regexp is adapted from Perl's File::Basename.
def original_filename
unless defined? @original_filename
@original_filename =
unless original_path.blank?
if original_path =~ /^(?:.*[:\\\/])?(.*)/m
$1
else
File.basename original_path
end
end
end
@original_filename
end
end
# Convert nested Hashs to HashWithIndifferentAccess and replace
# file upload hashs with UploadedFile objects
def normalize_parameters(value)

View File

@@ -1,6 +1,6 @@
require 'digest/md5'
module ActionController # :nodoc:
module ActionDispatch # :nodoc:
# Represents an HTTP response generated by a controller action. One can use
# an ActionController::Response object to retrieve the current state
# of the response, or customize the response. An Response object can

View File

@@ -0,0 +1,40 @@
module ActionDispatch
module StatusCodes #:nodoc:
STATUS_CODES = Rack::Utils::HTTP_STATUS_CODES.merge({
102 => "Processing",
207 => "Multi-Status",
226 => "IM Used",
422 => "Unprocessable Entity",
423 => "Locked",
424 => "Failed Dependency",
426 => "Upgrade Required",
507 => "Insufficient Storage",
510 => "Not Extended"
}).freeze
# Provides a symbol-to-fixnum lookup for converting a symbol (like
# :created or :not_implemented) into its corresponding HTTP status
# code (like 200 or 501).
SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) { |hash, (code, message)|
hash[message.gsub(/ /, "").underscore.to_sym] = code
hash
}.freeze
private
# Given a status parameter, determine whether it needs to be converted
# to a string. If it is a fixnum, use the STATUS_CODES hash to lookup
# the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE
# hash to convert it.
def interpret_status(status)
case status
when Fixnum then
"#{status} #{STATUS_CODES[status]}".strip
when Symbol then
interpret_status(SYMBOL_TO_STATUS_CODE[status] ||
"500 Unknown Status #{status.inspect}")
else
status.to_s
end
end
end
end

View File

@@ -1,4 +1,4 @@
module ActionController
module ActionDispatch
class Failsafe
cattr_accessor :error_file_path
self.error_file_path = Rails.public_path if defined?(Rails.public_path)

View File

@@ -1,4 +1,4 @@
module ActionController
module ActionDispatch
class ParamsParser
ActionController::Base.param_parsers[Mime::XML] = :xml_simple
ActionController::Base.param_parsers[Mime::JSON] = :json

View File

@@ -1,14 +1,14 @@
module ActionController
module ActionDispatch
class Reloader
def initialize(app)
@app = app
end
def call(env)
Dispatcher.reload_application
ActionController::Dispatcher.reload_application
@app.call(env)
ensure
Dispatcher.cleanup_application
ActionController::Dispatcher.cleanup_application
end
end
end

View File

@@ -0,0 +1,19 @@
module ActionDispatch
class RewindableInput
def initialize(app)
@app = app
end
def call(env)
begin
env['rack.input'].rewind
rescue NoMethodError, Errno::ESPIPE
# Handles exceptions raised by input streams that cannot be rewound
# such as when using plain CGI under Apache
env['rack.input'] = StringIO.new(env['rack.input'].read)
end
@app.call(env)
end
end
end

View File

@@ -1,6 +1,6 @@
require 'rack/utils'
module ActionController
module ActionDispatch
module Session
class AbstractStore
ENV_SESSION_KEY = 'rack.session'.freeze

View File

@@ -1,4 +1,4 @@
module ActionController
module ActionDispatch
module Session
# This cookie-based session store is the Rails default. Sessions typically
# contain at most a user_id and flash message; both fit within the 4K cookie

View File

@@ -1,7 +1,7 @@
begin
require_library_or_gem 'memcache'
module ActionController
module ActionDispatch
module Session
class MemCacheStore < AbstractStore
def initialize(app, options = {})

View File

@@ -1,4 +1,4 @@
module ActionController
module ActionDispatch
class MiddlewareStack < Array
class Middleware
def self.new(klass, *args, &block)

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