mirror of
https://github.com/github/rails.git
synced 2026-01-10 15:17:57 -05:00
Merge branch 'master' into active_model
Conflicts: activeresource/lib/active_resource/validations.rb
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -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
|
||||
|
||||
2
Rakefile
2
Rakefile
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
10
actionpack/lib/action_controller/abstract.rb
Normal file
10
actionpack/lib/action_controller/abstract.rb
Normal 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
|
||||
41
actionpack/lib/action_controller/abstract/base.rb
Normal file
41
actionpack/lib/action_controller/abstract/base.rb
Normal 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
|
||||
40
actionpack/lib/action_controller/abstract/callbacks.rb
Normal file
40
actionpack/lib/action_controller/abstract/callbacks.rb
Normal 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
|
||||
3
actionpack/lib/action_controller/abstract/exceptions.rb
Normal file
3
actionpack/lib/action_controller/abstract/exceptions.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
module AbstractController
|
||||
class ActionNotFound < StandardError ; end
|
||||
end
|
||||
59
actionpack/lib/action_controller/abstract/helpers.rb
Normal file
59
actionpack/lib/action_controller/abstract/helpers.rb
Normal 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
|
||||
82
actionpack/lib/action_controller/abstract/layouts.rb
Normal file
82
actionpack/lib/action_controller/abstract/layouts.rb
Normal 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
|
||||
7
actionpack/lib/action_controller/abstract/logger.rb
Normal file
7
actionpack/lib/action_controller/abstract/logger.rb
Normal file
@@ -0,0 +1,7 @@
|
||||
module AbstractController
|
||||
module Logger
|
||||
setup do
|
||||
cattr_accessor :logger
|
||||
end
|
||||
end
|
||||
end
|
||||
80
actionpack/lib/action_controller/abstract/renderer.rb
Normal file
80
actionpack/lib/action_controller/abstract/renderer.rb
Normal 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
|
||||
@@ -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
|
||||
@@ -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?
|
||||
@@ -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
|
||||
@@ -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
|
||||
91
actionpack/lib/action_controller/base/redirect.rb
Normal file
91
actionpack/lib/action_controller/base/redirect.rb
Normal 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
|
||||
396
actionpack/lib/action_controller/base/render.rb
Normal file
396
actionpack/lib/action_controller/base/render.rb
Normal 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
|
||||
@@ -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
|
||||
43
actionpack/lib/action_controller/base/responder.rb
Normal file
43
actionpack/lib/action_controller/base/responder.rb
Normal 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
|
||||
@@ -26,7 +26,7 @@ module ActionController #:nodoc:
|
||||
if defined? @@session_store
|
||||
@@session_store
|
||||
else
|
||||
Session::CookieStore
|
||||
ActionDispatch::Session::CookieStore
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
2
actionpack/lib/action_controller/deprecated.rb
Normal file
2
actionpack/lib/action_controller/deprecated.rb
Normal file
@@ -0,0 +1,2 @@
|
||||
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
|
||||
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
|
||||
@@ -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
|
||||
@@ -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"
|
||||
@@ -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)
|
||||
@@ -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")) %>
|
||||
7
actionpack/lib/action_controller/new_base.rb
Normal file
7
actionpack/lib/action_controller/new_base.rb
Normal 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
|
||||
60
actionpack/lib/action_controller/new_base/base.rb
Normal file
60
actionpack/lib/action_controller/new_base/base.rb
Normal 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
|
||||
31
actionpack/lib/action_controller/new_base/hide_actions.rb
Normal file
31
actionpack/lib/action_controller/new_base/hide_actions.rb
Normal 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
|
||||
37
actionpack/lib/action_controller/new_base/layouts.rb
Normal file
37
actionpack/lib/action_controller/new_base/layouts.rb
Normal 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
|
||||
62
actionpack/lib/action_controller/new_base/renderer.rb
Normal file
62
actionpack/lib/action_controller/new_base/renderer.rb
Normal 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
|
||||
40
actionpack/lib/action_controller/new_base/url_for.rb
Normal file
40
actionpack/lib/action_controller/new_base/url_for.rb
Normal 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
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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?
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
64
actionpack/lib/action_dispatch.rb
Normal file
64
actionpack/lib/action_dispatch.rb
Normal 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'
|
||||
@@ -1,6 +1,6 @@
|
||||
require 'active_support/memoizable'
|
||||
|
||||
module ActionController
|
||||
module ActionDispatch
|
||||
module Http
|
||||
class Headers < ::Hash
|
||||
extend ActiveSupport::Memoizable
|
||||
@@ -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'
|
||||
@@ -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)
|
||||
@@ -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
|
||||
40
actionpack/lib/action_dispatch/http/status_codes.rb
Normal file
40
actionpack/lib/action_dispatch/http/status_codes.rb
Normal 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
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
require 'rack/utils'
|
||||
|
||||
module ActionController
|
||||
module ActionDispatch
|
||||
module Session
|
||||
class AbstractStore
|
||||
ENV_SESSION_KEY = 'rack.session'.freeze
|
||||
@@ -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
|
||||
@@ -1,7 +1,7 @@
|
||||
begin
|
||||
require_library_or_gem 'memcache'
|
||||
|
||||
module ActionController
|
||||
module ActionDispatch
|
||||
module Session
|
||||
class MemCacheStore < AbstractStore
|
||||
def initialize(app, options = {})
|
||||
@@ -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
Reference in New Issue
Block a user