Reorganize ActionController folder structure

This commit is contained in:
Joshua Peek 2009-01-27 18:17:39 -06:00
parent eb9af20b7c
commit a0f2b1d95d
68 changed files with 976 additions and 559 deletions

1
.gitignore vendored
View File

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

View File

@ -44,57 +44,61 @@ module ActionController
[Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
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 :Failsafe, 'action_controller/dispatch/rack/failsafe'
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 :Lock, 'action_controller/dispatch/rack/lock'
autoload :MiddlewareStack, 'action_controller/dispatch/rack/middleware_stack'
autoload :MimeResponds, 'action_controller/mime/responds'
autoload :ParamsParser, 'action_controller/dispatch/params_parser'
autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes'
autoload :RecordIdentifier, 'action_controller/record_identifier'
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 :Request, 'action_controller/dispatch/request'
autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection'
autoload :RequestParser, 'action_controller/dispatch/request_parser'
autoload :Rescue, 'action_controller/dispatch/rescue'
autoload :Resources, 'action_controller/routing/resources'
autoload :Responder, 'action_controller/base/responder'
autoload :Response, 'action_controller/dispatch/response'
autoload :RewindableInput, 'action_controller/dispatch/rewindable_input'
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/session/management'
autoload :StatusCodes, 'action_controller/dispatch/status_codes'
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 :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser'
autoload :UrlRewriter, 'action_controller/url_rewriter'
autoload :UrlWriter, 'action_controller/url_rewriter'
autoload :Verification, 'action_controller/verification'
autoload :UploadedFile, 'action_controller/dispatch/uploaded_file'
autoload :UploadedStringIO, 'action_controller/dispatch/uploaded_file'
autoload :UploadedTempfile, 'action_controller/dispatch/uploaded_file'
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'
autoload :Headers, 'action_controller/base/headers'
end
module Session
@ -102,13 +106,9 @@ module ActionController
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 :Mime, 'action_controller/mime/type'
autoload :HTML, 'action_controller/vendor/html-scanner'

View File

@ -58,22 +58,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,7 +231,6 @@ module ActionController #:nodoc:
# end
#
class Base
DEFAULT_RENDER_STATUS_CODE = "200 OK"
include StatusCodes
@ -301,7 +284,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.
@ -644,7 +630,7 @@ module ActionController #:nodoc:
end
def session_enabled?
ActiveSupport::Deprecation.warn("Sessions are now lazy loaded. So if you don't access them, consider them disabled.", caller)
request.session_options && request.session_options[:disabled] != false
end
self.view_paths = []
@ -678,331 +664,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"
#
# 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?
original, options = options, extra_options unless options.is_a?(Hash)
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 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
# 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)
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 = nil
@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)
@ -1024,73 +685,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
logger.info("Redirected to #{options}") if logger && logger.info?
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?
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,
@ -1178,71 +772,6 @@ module ActionController #:nodoc:
response.status = interpret_status(options[:status] || DEFAULT_RENDER_STATUS_CODE)
end
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 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 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
def render_for_text(text = nil, append_response = false) #:nodoc:
@performed_render = true
if append_response
response.body ||= ''
response.body << 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 initialize_template_class(response)
@template = response.template = ActionView::Base.new(self.class.view_paths, {}, self, formats)
response.template.helpers.send :include, self.class.master_helper_module
@ -1318,22 +847,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
@ -1363,14 +876,13 @@ 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)
path.split('/', 2).last
end
def template_path_includes_controller?(path)
self.controller_path.split('/')[-1] == path.split('/')[0]
end
@ -1381,7 +893,7 @@ module ActionController #:nodoc:
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, RecordIdentifier,
RequestForgeryProtection, Translation

View File

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

View File

@ -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
logger.info("Redirected to #{options}") if logger && logger.info?
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?
response.redirect(url, interpret_status(status))
@performed_redirect = true
end
# Clears the redirected results from the headers, resets the status to 200 and returns
# the URL that was used to redirect or nil if there was no redirected URL
# Note that +redirect_to+ will change the body of the response to indicate a redirection.
# The response body is not reset here, see +erase_render_results+
def erase_redirect_results #:nodoc:
@performed_redirect = false
response.redirected_to = nil
response.redirected_to_method_params = nil
response.status = DEFAULT_RENDER_STATUS_CODE
response.headers.delete('Location')
end
end
end

View File

@ -0,0 +1,378 @@
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?
original, options = options, extra_options unless options.is_a?(Hash)
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
# 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)
ensure
response.content_type = nil
erase_render_results
reset_variables_added_to_assigns
end
# Clears the rendered results, allowing for another render to be performed.
def erase_render_results #:nodoc:
response.body = nil
@performed_render = false
end
# Erase both render and redirect results
def erase_results #:nodoc:
erase_render_results
erase_redirect_results
end
# Return a response that has no content (merely headers). The options
# argument is interpreted to be a hash of header names and values.
# This allows you to easily return a response that consists only of
# significant headers:
#
# head :created, :location => person_path(@person)
#
# It can also be used to return exceptional conditions:
#
# return head(:method_not_allowed) unless request.post?
# return head(:bad_request) unless valid_request?
# render
def head(*args)
if args.length > 2
raise ArgumentError, "too many arguments to head"
elsif args.empty?
raise ArgumentError, "too few arguments to head"
end
options = args.extract_options!
status = interpret_status(args.shift || options.delete(:status) || :ok)
options.each do |key, value|
headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
end
render :nothing => true, :status => status
end
private
def render_for_name(name, layout, options)
case name.to_s.index('/')
when 0
render_for_file(name, layout, options)
when nil
render_for_action(name, layout, options)
else
render_for_file(name.sub(/^\//, ''), [layout, true], options)
end
end
def render_for_parts(parts, layout, options = {})
tmp = view_paths.find_by_parts(*parts)
layout = _pick_layout(*layout) unless tmp.exempt_from_layout?
render_for_text(
@template._render_template_with_layout(tmp, layout, options, parts[3]))
end
def render_for_file(file, layout, options)
render_for_parts([file, [request.format.to_sym]], layout, options)
end
def render_for_action(name, layout, options)
parts = [action_name_base(name), formats, controller_name]
render_for_parts(parts, layout, options)
end
end
end

View File

@ -0,0 +1,41 @@
module ActionController
module Responder
def self.included(klass)
klass.extend ClassMethods
end
private
def render_for_text(text = nil, append_response = false) #:nodoc:
@performed_render = true
if append_response
response.body ||= ''
response.body << 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 action_methods
self.class.action_methods
end
module ClassMethods
def action_methods
@action_methods ||=
# All public instance methods of this class, including ancestors
public_instance_methods(true).map { |m| m.to_s }.to_set -
# Except for public instance methods of Base and its ancestors
Base.public_instance_methods(true).map { |m| m.to_s } +
# Be sure to include shadowed public instance methods of this class
public_instance_methods(false).map { |m| m.to_s } -
# And always exclude explicitly hidden actions
hidden_actions
end
end
end
end

View File

@ -1,6 +1,6 @@
require 'action_controller/cgi_ext/stdinput'
require 'action_controller/cgi_ext/query_extension'
require 'action_controller/cgi_ext/cookie'
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

View File

@ -1,5 +1,3 @@
require 'action_controller/cgi_ext'
module ActionController #:nodoc:
class CGIHandler
module ProperStream

View File

@ -46,7 +46,7 @@ module ActionController
cattr_accessor :middleware
self.middleware = MiddlewareStack.new do |middleware|
middlewares = File.join(File.dirname(__FILE__), "middlewares.rb")
middlewares = File.join(File.dirname(__FILE__), "rack", "middlewares.rb")
middleware.instance_eval(File.read(middlewares))
end

View File

@ -0,0 +1,16 @@
module ActionController
class Lock
FLAG = 'rack.multithread'.freeze
def initialize(app, lock = Mutex.new)
@app, @lock = app, lock
end
def call(env)
old, env[FLAG] = env[FLAG], false
@lock.synchronize { @app.call(env) }
ensure
env[FLAG] = old
end
end
end

View File

@ -0,0 +1,315 @@
module ActionController
class RequestParser
def initialize(env)
@env = env
freeze
end
def request_parameters
@env["action_controller.request_parser.request_parameters"] ||= parse_formatted_request_parameters
end
def query_parameters
@env["action_controller.request_parser.query_parameters"] ||= self.class.parse_query_parameters(query_string)
end
# Returns the query string, accounting for server idiosyncrasies.
def query_string
@env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].split('?', 2)[1] || '')
end
# The request body is an IO input stream. If the RAW_POST_DATA environment
# variable is already set, wrap it in a StringIO.
def body
if raw_post = @env['RAW_POST_DATA']
raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding)
StringIO.new(raw_post)
else
@env['rack.input']
end
end
# The raw content type string with its parameters stripped off.
def content_type_without_parameters
self.class.extract_content_type_without_parameters(content_type_with_parameters)
end
def raw_post
unless @env.include? 'RAW_POST_DATA'
@env['RAW_POST_DATA'] = body.read(content_length)
body.rewind if body.respond_to?(:rewind)
end
@env['RAW_POST_DATA']
end
private
def parse_formatted_request_parameters
return {} if content_length.zero?
content_type, boundary = self.class.extract_multipart_boundary(content_type_with_parameters)
# Don't parse params for unknown requests.
return {} if content_type.blank?
mime_type = Mime::Type.lookup(content_type)
strategy = ActionController::Base.param_parsers[mime_type]
# Only multipart form parsing expects a stream.
body = (strategy && strategy != :multipart_form) ? raw_post : self.body
case strategy
when Proc
strategy.call(body)
when :url_encoded_form
self.class.clean_up_ajax_request_body! body
self.class.parse_query_parameters(body)
when :multipart_form
self.class.parse_multipart_form_parameters(body, boundary, content_length, @env)
when :xml_simple, :xml_node
body.blank? ? {} : Hash.from_xml(body).with_indifferent_access
when :yaml
YAML.load(body)
when :json
if body.blank?
{}
else
data = ActiveSupport::JSON.decode(body)
data = {:_json => data} unless data.is_a?(Hash)
data.with_indifferent_access
end
else
{}
end
rescue Exception => e # YAML, XML or Ruby code block errors
raise
{ "body" => body,
"content_type" => content_type_with_parameters,
"content_length" => content_length,
"exception" => "#{e.message} (#{e.class})",
"backtrace" => e.backtrace }
end
def content_length
@env['CONTENT_LENGTH'].to_i
end
# The raw content type string. Use when you need parameters such as
# charset or boundary which aren't included in the content_type MIME type.
# Overridden by the X-POST_DATA_FORMAT header for backward compatibility.
def content_type_with_parameters
content_type_from_legacy_post_data_format_header || @env['CONTENT_TYPE'].to_s
end
def content_type_from_legacy_post_data_format_header
if x_post_format = @env['HTTP_X_POST_DATA_FORMAT']
case x_post_format.to_s.downcase
when 'yaml'; 'application/x-yaml'
when 'xml'; 'application/xml'
end
end
end
class << self
def parse_query_parameters(query_string)
return {} if query_string.blank?
pairs = query_string.split('&').collect do |chunk|
next if chunk.empty?
key, value = chunk.split('=', 2)
next if key.empty?
value = value.nil? ? nil : CGI.unescape(value)
[ CGI.unescape(key), value ]
end.compact
UrlEncodedPairParser.new(pairs).result
end
def parse_request_parameters(params)
parser = UrlEncodedPairParser.new
params = params.dup
until params.empty?
for key, value in params
if key.blank?
params.delete key
elsif !key.include?('[')
# much faster to test for the most common case first (GET)
# and avoid the call to build_deep_hash
parser.result[key] = get_typed_value(value[0])
params.delete key
elsif value.is_a?(Array)
parser.parse(key, get_typed_value(value.shift))
params.delete key if value.empty?
else
raise TypeError, "Expected array, found #{value.inspect}"
end
end
end
parser.result
end
def parse_multipart_form_parameters(body, boundary, body_size, env)
parse_request_parameters(read_multipart(body, boundary, body_size, env))
end
def extract_multipart_boundary(content_type_with_parameters)
if content_type_with_parameters =~ MULTIPART_BOUNDARY
['multipart/form-data', $1.dup]
else
extract_content_type_without_parameters(content_type_with_parameters)
end
end
def extract_content_type_without_parameters(content_type_with_parameters)
$1.strip.downcase if content_type_with_parameters =~ /^([^,\;]*)/
end
def clean_up_ajax_request_body!(body)
body.chop! if body[-1] == 0
body.gsub!(/&_=$/, '')
end
private
def get_typed_value(value)
case value
when String
value
when NilClass
''
when Array
value.map { |v| get_typed_value(v) }
else
if value.respond_to? :original_filename
# Uploaded file
if value.original_filename
value
# Multipart param
else
result = value.read
value.rewind
result
end
# Unknown value, neither string nor multipart.
else
raise "Unknown form value: #{value.inspect}"
end
end
end
MULTIPART_BOUNDARY = %r|\Amultipart/form-data.*boundary=\"?([^\";,]+)\"?|n
EOL = "\015\012"
def read_multipart(body, boundary, body_size, env)
params = Hash.new([])
boundary = "--" + boundary
quoted_boundary = Regexp.quote(boundary)
buf = ""
bufsize = 10 * 1024
boundary_end=""
# start multipart/form-data
body.binmode if defined? body.binmode
case body
when File
body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding)
when StringIO
body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding)
end
boundary_size = boundary.size + EOL.size
body_size -= boundary_size
status = body.read(boundary_size)
if nil == status
raise EOFError, "no content body"
elsif boundary + EOL != status
raise EOFError, "bad content body"
end
loop do
head = nil
content =
if 10240 < body_size
UploadedTempfile.new("CGI")
else
UploadedStringIO.new
end
content.binmode if defined? content.binmode
until head and /#{quoted_boundary}(?:#{EOL}|--)/n.match(buf)
if (not head) and /#{EOL}#{EOL}/n.match(buf)
buf = buf.sub(/\A((?:.|\n)*?#{EOL})#{EOL}/n) do
head = $1.dup
""
end
next
end
if head and ( (EOL + boundary + EOL).size < buf.size )
content.print buf[0 ... (buf.size - (EOL + boundary + EOL).size)]
buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = ""
end
c = if bufsize < body_size
body.read(bufsize)
else
body.read(body_size)
end
if c.nil? || c.empty?
raise EOFError, "bad content body"
end
buf.concat(c)
body_size -= c.size
end
buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do
content.print $1
if "--" == $2
body_size = -1
end
boundary_end = $2.dup
""
end
content.rewind
head =~ /Content-Disposition:.* filename=(?:"((?:\\.|[^\"])*)"|([^;]*))/ni
if filename = $1 || $2
if /Mac/ni.match(env['HTTP_USER_AGENT']) and
/Mozilla/ni.match(env['HTTP_USER_AGENT']) and
(not /MSIE/ni.match(env['HTTP_USER_AGENT']))
filename = CGI.unescape(filename)
end
content.original_path = filename.dup
end
head =~ /Content-Type: ([^\r]*)/ni
content.content_type = $1.dup if $1
head =~ /Content-Disposition:.* name="?([^\";]*)"?/ni
name = $1.dup if $1
if params.has_key?(name)
params[name].push(content)
else
params[name] = [content]
end
break if body_size == -1
end
raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/
begin
body.rewind if body.respond_to?(:rewind)
rescue Errno::ESPIPE
# Handles exceptions raised by input streams that cannot be rewound
# such as when using plain CGI under Apache
end
params
end
end # class << self
end
end

View File

@ -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"] ||= ActionController::Request.new(env)
response = env["action_controller.rescue.response"] ||= ActionController::Response.new
new.process(request, response, :rescue_action, exception)
end
end

View File

@ -211,4 +211,4 @@ module Mime
end
end
require 'action_controller/mime_types'
require 'action_controller/mime/default_types'

View File

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

View File

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

View File

@ -243,6 +243,34 @@ module ActionView
end
end
def _render_partial_with_block(layout, block, options)
@_proc_for_layout = block
concat(_render_partial(options.merge(:partial => layout)))
ensure
@_proc_for_layout = nil
end
def _render_partial_with_layout(layout, options)
if layout
prefix = controller && !layout.include?("/") ? controller.controller_path : nil
layout = find_by_parts(layout, formats, prefix, true)
end
content = _render_partial(options)
return _render_content_with_layout(content, layout, options[:locals])
end
def _deprecated_ivar_assign(template)
if respond_to?(:controller)
ivar = :"@#{template.variable_name}"
object =
if controller.instance_variable_defined?(ivar)
ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
controller.instance_variable_get(ivar),
"#{ivar} will no longer be implicitly assigned to #{template.variable_name}")
end
end
end
def _array_like_objects
array_like = [Array]
if defined?(ActiveRecord)

View File

@ -20,8 +20,7 @@ rescue LoadError
end
require 'action_controller'
require 'action_controller/cgi_ext'
require 'action_controller/test_process'
require 'action_controller/testing/process'
require 'action_view/test_case'
# Show backtraces for deprecated behavior for quicker cleanup.

View File

@ -155,7 +155,7 @@ class SendFileTest < ActionController::TestCase
define_method "test_default_send_#{method}_status" do
@controller.options = { :stream => false }
assert_nothing_raised { assert_not_nil process(method) }
assert_equal ActionController::Base::DEFAULT_RENDER_STATUS_CODE, @response.status
assert_equal ActionController::DEFAULT_RENDER_STATUS_CODE, @response.status
end
end
end

View File

@ -612,7 +612,7 @@ class CleanBacktraceTest < ActionController::TestCase
end
def test_should_clean_assertion_lines_from_backtrace
path = File.expand_path("#{File.dirname(__FILE__)}/../../lib/action_controller")
path = File.expand_path("#{File.dirname(__FILE__)}/../../lib/action_controller/testing")
exception = ActiveSupport::TestCase::Assertion.new('message')
exception.set_backtrace ["#{path}/abc", "#{path}/assertions/def"]
clean_backtrace { raise exception }

View File

@ -1,4 +1,42 @@
module ActiveSupport
class ConcurrentHash
def initialize(hash = {})
@backup_cache = hash.dup
@frozen_cache = hash.dup.freeze
@mutex = Mutex.new
end
def []=(k,v)
@mutex.synchronize { @backup_cache[k] = v }
@frozen_cache = @backup_cache.dup.freeze
end
def [](k)
if @frozen_cache.key?(k)
@frozen_cache[k]
else
@mutex.synchronize { @backup_cache[k] }
end
end
def empty?
@backup_cache.empty?
end
end
module SafelyMemoizable
def safely_memoize(*symbols)
symbols.each do |symbol|
class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{symbol}(*args)
memoized = @_memoized_#{symbol} || ::ActiveSupport::ConcurrentHash.new
memoized[args] ||= memoized_#{symbol}(*args)
end
RUBY
end
end
end
module Memoizable
def self.memoized_ivar_for(symbol)
"@_memoized_#{symbol.to_s.sub(/\?\Z/, '_query').sub(/!\Z/, '_bang')}".to_sym

View File

@ -20,5 +20,5 @@
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
require 'action_controller/dispatcher'
require 'action_controller/dispatch/dispatcher'
Dispatcher = ActionController::Dispatcher