aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r--actionpack/lib/action_controller/base.rb9
-rw-r--r--actionpack/lib/action_controller/caching.rb11
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb18
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb8
-rw-r--r--actionpack/lib/action_controller/deprecated/dispatcher.rb31
-rw-r--r--actionpack/lib/action_controller/dispatch/dispatcher.rb52
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb12
-rw-r--r--actionpack/lib/action_controller/metal/filter_parameter_logging.rb22
-rw-r--r--actionpack/lib/action_controller/metal/flash.rb177
-rw-r--r--actionpack/lib/action_controller/metal/head.rb3
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb102
-rw-r--r--actionpack/lib/action_controller/metal/logger.rb89
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb9
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb4
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb4
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb38
-rw-r--r--actionpack/lib/action_controller/railtie.rb67
-rw-r--r--actionpack/lib/action_controller/railties/subscriber.rb60
-rw-r--r--actionpack/lib/action_controller/test_case.rb7
-rw-r--r--actionpack/lib/action_controller/url_rewriter.rb226
20 files changed, 330 insertions, 619 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index b23be66910..260e5da336 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -6,6 +6,8 @@ module ActionController
include AbstractController::Layouts
include ActionController::Helpers
+ helper :all # By default, all helpers should be included
+
include ActionController::HideActions
include ActionController::UrlFor
include ActionController::Redirecting
@@ -13,7 +15,6 @@ module ActionController
include ActionController::Renderers::All
include ActionController::ConditionalGet
include ActionController::RackDelegation
- include ActionController::Logger
include ActionController::Configuration
# Legacy modules
@@ -31,9 +32,13 @@ module ActionController
include ActionController::Streaming
include ActionController::HttpAuthentication::Basic::ControllerMethods
include ActionController::HttpAuthentication::Digest::ControllerMethods
- include ActionController::FilterParameterLogging
include ActionController::Translation
+ # Add instrumentations hooks at the bottom, to ensure they instrument
+ # all the methods properly.
+ include ActionController::Instrumentation
+ include ActionController::FilterParameterLogging
+
# TODO: Extract into its own module
# This should be moved together with other normalizing behavior
module ImplicitRender
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 69ed84da95..d784138ebe 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -60,17 +60,6 @@ module ActionController #:nodoc:
def cache_configured?
perform_caching && cache_store
end
-
- def log_event(name, before, after, instrumenter_id, payload)
- if name.to_s =~ /(read|write|cache|expire|exist)_(fragment|page)\??/
- key_or_path = payload[:key] || payload[:path]
- human_name = name.to_s.humanize
- duration = (after - before) * 1000
- logger.info("#{human_name} #{key_or_path.inspect} (%.1fms)" % duration)
- else
- super
- end
- end
end
def caching_allowed?
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index f569d0dd8b..00a7f034d3 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -36,8 +36,8 @@ module ActionController #:nodoc:
def fragment_for(buffer, name = {}, options = nil, &block) #:nodoc:
if perform_caching
- if fragment_exist?(name,options)
- buffer.concat(read_fragment(name, options))
+ if fragment_exist?(name, options)
+ buffer.safe_concat(read_fragment(name, options))
else
pos = buffer.length
block.call
@@ -53,7 +53,7 @@ module ActionController #:nodoc:
return content unless cache_configured?
key = fragment_cache_key(key)
- ActiveSupport::Notifications.instrument(:write_fragment, :key => key) do
+ instrument_fragment_cache :write_fragment, key do
cache_store.write(key, content, options)
end
content
@@ -64,7 +64,7 @@ module ActionController #:nodoc:
return unless cache_configured?
key = fragment_cache_key(key)
- ActiveSupport::Notifications.instrument(:read_fragment, :key => key) do
+ instrument_fragment_cache :read_fragment, key do
cache_store.read(key, options)
end
end
@@ -74,7 +74,7 @@ module ActionController #:nodoc:
return unless cache_configured?
key = fragment_cache_key(key)
- ActiveSupport::Notifications.instrument(:exist_fragment?, :key => key) do
+ instrument_fragment_cache :exist_fragment?, key do
cache_store.exist?(key, options)
end
end
@@ -101,16 +101,18 @@ module ActionController #:nodoc:
key = fragment_cache_key(key) unless key.is_a?(Regexp)
message = nil
- ActiveSupport::Notifications.instrument(:expire_fragment, :key => key) do
+ instrument_fragment_cache :expire_fragment, key do
if key.is_a?(Regexp)
- message = "Expired fragments matching: #{key.source}"
cache_store.delete_matched(key, options)
else
- message = "Expired fragment: #{key}"
cache_store.delete(key, options)
end
end
end
+
+ def instrument_fragment_cache(name, key)
+ ActiveSupport::Notifications.instrument("action_controller.#{name}", :key => key){ yield }
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index d46f528c7e..5797eeebd6 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -64,7 +64,7 @@ module ActionController #:nodoc:
return unless perform_caching
path = page_cache_path(path)
- ActiveSupport::Notifications.instrument(:expire_page, :path => path) do
+ instrument_page_cache :expire_page, path do
File.delete(path) if File.exist?(path)
end
end
@@ -75,7 +75,7 @@ module ActionController #:nodoc:
return unless perform_caching
path = page_cache_path(path)
- ActiveSupport::Notifications.instrument(:cache_page, :path => path) do
+ instrument_page_cache :write_page, path do
FileUtils.makedirs(File.dirname(path))
File.open(path, "wb+") { |f| f.write(content) }
end
@@ -107,6 +107,10 @@ module ActionController #:nodoc:
def page_cache_path(path)
page_cache_directory + page_cache_file(path)
end
+
+ def instrument_page_cache(name, path)
+ ActiveSupport::Notifications.instrument("action_controller.#{name}", :path => path){ yield }
+ end
end
# Expires the page that was cached with the +options+ as a key. Example:
diff --git a/actionpack/lib/action_controller/deprecated/dispatcher.rb b/actionpack/lib/action_controller/deprecated/dispatcher.rb
new file mode 100644
index 0000000000..3da3c8ce7d
--- /dev/null
+++ b/actionpack/lib/action_controller/deprecated/dispatcher.rb
@@ -0,0 +1,31 @@
+module ActionController
+ class Dispatcher
+ cattr_accessor :prepare_each_request
+ self.prepare_each_request = false
+
+ class << self
+ def before_dispatch(*args, &block)
+ ActiveSupport::Deprecation.warn "ActionController::Dispatcher.before_dispatch is deprecated. " <<
+ "Please use ActionDispatch::Callbacks.before instead.", caller
+ ActionDispatch::Callbacks.before(*args, &block)
+ end
+
+ def after_dispatch(*args, &block)
+ ActiveSupport::Deprecation.warn "ActionController::Dispatcher.after_dispatch is deprecated. " <<
+ "Please use ActionDispatch::Callbacks.after instead.", caller
+ ActionDispatch::Callbacks.after(*args, &block)
+ end
+
+ def to_prepare(*args, &block)
+ ActiveSupport::Deprecation.warn "ActionController::Dispatcher.to_prepare is deprecated. " <<
+ "Please use ActionDispatch::Callbacks.to_prepare instead.", caller
+ ActionDispatch::Callbacks.after(*args, &block)
+ end
+
+ def new
+ ActiveSupport::Deprecation.warn "ActionController::Dispatcher.new is deprecated, use Rails.application instead."
+ Rails.application
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/dispatch/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb
deleted file mode 100644
index cf02757cf6..0000000000
--- a/actionpack/lib/action_controller/dispatch/dispatcher.rb
+++ /dev/null
@@ -1,52 +0,0 @@
-require 'active_support/core_ext/module/delegation'
-
-module ActionController
- # Dispatches requests to the appropriate controller and takes care of
- # reloading the app after each request when Dependencies.load? is true.
- class Dispatcher
- cattr_accessor :prepare_each_request
- self.prepare_each_request = false
-
- class << self
- def define_dispatcher_callbacks(cache_classes)
- unless cache_classes
- # Run prepare callbacks before every request in development mode
- self.prepare_each_request = true
-
- ActionDispatch::Callbacks.after_dispatch do
- # Cleanup the application before processing the current request.
- ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
- ActiveSupport::Dependencies.clear
- ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord)
- end
-
- ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
- end
-
- if defined?(ActiveRecord)
- to_prepare(:activerecord_instantiate_observers) do
- ActiveRecord::Base.instantiate_observers
- end
- end
-
- if Base.logger && Base.logger.respond_to?(:flush)
- after_dispatch do
- Base.logger.flush
- end
- end
-
- to_prepare do
- I18n.reload!
- end
- end
-
- delegate :to_prepare, :before_dispatch, :around_dispatch, :after_dispatch,
- :to => ActionDispatch::Callbacks
-
- def new
- # DEPRECATE Rails application fallback
- Rails.application
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index a90f798cd5..0e869e4e87 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -21,6 +21,8 @@ module ActionController
class << self
delegate :default_charset=, :to => "ActionDispatch::Response"
+ delegate :resources_path_names, :to => "ActionController::Routing::Routes"
+ delegate :resources_path_names=, :to => "ActionController::Routing::Routes"
end
# cattr_reader :protected_instance_variables
@@ -29,15 +31,7 @@ module ActionController
@variables_added @request_origin @url
@parent_controller @action_name
@before_filter_chain_aborted @_headers @_params
- @_flash @_response)
-
- # Indicates whether or not optimise the generated named
- # route helper methods
- cattr_accessor :optimise_named_routes
- self.optimise_named_routes = true
-
- cattr_accessor :resources_path_names
- self.resources_path_names = { :new => 'new', :edit => 'edit' }
+ @_response)
# Controls the resource action separator
cattr_accessor :resource_action_separator
diff --git a/actionpack/lib/action_controller/metal/filter_parameter_logging.rb b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb
index 59e200396a..0b1e1ee6ab 100644
--- a/actionpack/lib/action_controller/metal/filter_parameter_logging.rb
+++ b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb
@@ -2,6 +2,8 @@ module ActionController
module FilterParameterLogging
extend ActiveSupport::Concern
+ INTERNAL_PARAMS = %w(controller action format _method only_path)
+
module ClassMethods
# Replace sensitive parameter data from the request log.
# Filters parameters that have any of the arguments as a substring.
@@ -48,27 +50,19 @@ module ActionController
filtered_params[key] = value
end
- filtered_params
+ filtered_params.except!(*INTERNAL_PARAMS)
end
protected :filter_parameters
end
-
- protected
-
- # Overwrite log_process_action to include parameters information.
- # If this method is invoked, it means logger is defined, so don't
- # worry with such scenario here.
- def log_process_action(controller) #:nodoc:
- params = controller.send(:filter_parameters, controller.request.params)
- logger.info " Parameters: #{params.inspect}" unless params.empty?
- super
- end
end
- INTERNAL_PARAMS = [:controller, :action, :format, :_method, :only_path]
-
protected
+ def append_info_to_payload(payload)
+ super
+ payload[:params] = filter_parameters(request.params)
+ end
+
def filter_parameters(params)
params.dup.except!(*INTERNAL_PARAMS)
end
diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb
index 25e25940a7..bd768b634e 100644
--- a/actionpack/lib/action_controller/metal/flash.rb
+++ b/actionpack/lib/action_controller/metal/flash.rb
@@ -1,187 +1,14 @@
module ActionController #:nodoc:
- # The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed
- # to the very next action and then cleared out. This is a great way of doing notices and alerts, such as a create
- # action that sets <tt>flash[:notice] = "Successfully created"</tt> before redirecting to a display action that can
- # then expose the flash to its template. Actually, that exposure is automatically done. Example:
- #
- # class PostsController < ActionController::Base
- # def create
- # # save post
- # flash[:notice] = "Successfully created post"
- # redirect_to @post
- # end
- #
- # def show
- # # doesn't need to assign the flash notice to the template, that's done automatically
- # end
- # end
- #
- # show.html.erb
- # <% if flash[:notice] %>
- # <div class="notice"><%= flash[:notice] %></div>
- # <% end %>
- #
- # This example just places a string in the flash, but you can put any object in there. And of course, you can put as
- # many as you like at a time too. Just remember: They'll be gone by the time the next action has been performed.
- #
- # See docs on the FlashHash class for more details about the flash.
module Flash
extend ActiveSupport::Concern
included do
+ delegate :flash, :to => :request
+ delegate :alert, :notice, :to => "request.flash"
helper_method :alert, :notice
end
- class FlashNow #:nodoc:
- def initialize(flash)
- @flash = flash
- end
-
- def []=(k, v)
- @flash[k] = v
- @flash.discard(k)
- v
- end
-
- def [](k)
- @flash[k]
- end
- end
-
- class FlashHash < Hash
- def initialize #:nodoc:
- super
- @used = Set.new
- end
-
- def []=(k, v) #:nodoc:
- keep(k)
- super
- end
-
- def update(h) #:nodoc:
- h.keys.each { |k| keep(k) }
- super
- end
-
- alias :merge! :update
-
- def replace(h) #:nodoc:
- @used = Set.new
- super
- end
-
- # Sets a flash that will not be available to the next action, only to the current.
- #
- # flash.now[:message] = "Hello current action"
- #
- # This method enables you to use the flash as a central messaging system in your app.
- # When you need to pass an object to the next action, you use the standard flash assign (<tt>[]=</tt>).
- # When you need to pass an object to the current action, you use <tt>now</tt>, and your object will
- # vanish when the current action is done.
- #
- # Entries set via <tt>now</tt> are accessed the same way as standard entries: <tt>flash['my-key']</tt>.
- def now
- FlashNow.new(self)
- end
-
- # Keeps either the entire current flash or a specific flash entry available for the next action:
- #
- # flash.keep # keeps the entire flash
- # flash.keep(:notice) # keeps only the "notice" entry, the rest of the flash is discarded
- def keep(k = nil)
- use(k, false)
- end
-
- # Marks the entire flash or a single flash entry to be discarded by the end of the current action:
- #
- # flash.discard # discard the entire flash at the end of the current action
- # flash.discard(:warning) # discard only the "warning" entry at the end of the current action
- def discard(k = nil)
- use(k)
- end
-
- # Mark for removal entries that were kept, and delete unkept ones.
- #
- # This method is called automatically by filters, so you generally don't need to care about it.
- def sweep #:nodoc:
- keys.each do |k|
- unless @used.include?(k)
- @used << k
- else
- delete(k)
- @used.delete(k)
- end
- end
-
- # clean up after keys that could have been left over by calling reject! or shift on the flash
- (@used - keys).each{ |k| @used.delete(k) }
- end
-
- def store(session)
- return if self.empty?
- session["flash"] = self
- end
-
- private
- # Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
- # use() # marks the entire flash as used
- # use('msg') # marks the "msg" entry as used
- # use(nil, false) # marks the entire flash as unused (keeps it around for one more action)
- # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
- # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself
- # if no key is passed.
- def use(key = nil, used = true)
- Array(key || keys).each { |k| used ? @used << k : @used.delete(k) }
- return key ? self[key] : self
- end
- end
-
- # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
- # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
- # to put a new one.
- def flash #:doc:
- unless @_flash
- @_flash = session["flash"] || FlashHash.new
- @_flash.sweep
- end
-
- @_flash
- end
-
- # Convenience accessor for flash[:alert]
- def alert
- flash[:alert]
- end
-
- # Convenience accessor for flash[:alert]=
- def alert=(message)
- flash[:alert] = message
- end
-
- # Convenience accessor for flash[:notice]
- def notice
- flash[:notice]
- end
-
- # Convenience accessor for flash[:notice]=
- def notice=(message)
- flash[:notice] = message
- end
-
protected
- def process_action(method_name)
- @_flash = nil
- super
- @_flash.store(session) if @_flash
- @_flash = nil
- end
-
- def reset_session
- super
- @_flash = nil
- end
-
def redirect_to(options = {}, response_status_and_flash = {}) #:doc:
if alert = response_status_and_flash.delete(:alert)
flash[:alert] = alert
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index c82d9cf369..37be8b3999 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -1,6 +1,7 @@
module ActionController
module Head
- include UrlFor
+ extend ActiveSupport::Concern
+ include ActionController::UrlFor
# Return a response that has no content (merely headers). The options
# argument is interpreted to be a hash of header names and values.
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
new file mode 100644
index 0000000000..7222b7b2fa
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -0,0 +1,102 @@
+require 'abstract_controller/logger'
+
+module ActionController
+ # Adds instrumentation to several ends in ActionController::Base. It also provides
+ # some hooks related with process_action, this allows an ORM like ActiveRecord
+ # and/or DataMapper to plug in ActionController and show related information.
+ #
+ # Check ActiveRecord::Railties::ControllerRuntime for an example.
+ module Instrumentation
+ extend ActiveSupport::Concern
+
+ included do
+ include AbstractController::Logger
+ end
+
+ attr_internal :view_runtime
+
+ def process_action(action, *args)
+ ActiveSupport::Notifications.instrument("action_controller.process_action") do |payload|
+ result = super
+ payload[:controller] = self.class.name
+ payload[:action] = self.action_name
+ payload[:formats] = request.formats.map(&:to_s)
+ payload[:remote_ip] = request.remote_ip
+ payload[:method] = request.method
+ payload[:status] = response.status
+ payload[:request_uri] = request.request_uri rescue "unknown"
+ append_info_to_payload(payload)
+ result
+ end
+ end
+
+ def render(*args, &block)
+ if logger
+ render_output = nil
+
+ self.view_runtime = cleanup_view_runtime do
+ Benchmark.ms { render_output = super }
+ end
+
+ render_output
+ else
+ super
+ end
+ end
+
+ def send_file(path, options={})
+ ActiveSupport::Notifications.instrument("action_controller.send_file",
+ options.merge(:path => path)) do
+ super
+ end
+ end
+
+ def send_data(data, options = {})
+ ActiveSupport::Notifications.instrument("action_controller.send_data", options) do
+ super
+ end
+ end
+
+ def redirect_to(*args)
+ ActiveSupport::Notifications.instrument("action_controller.redirect_to") do |payload|
+ result = super
+ payload[:status] = self.status
+ payload[:location] = self.location
+ result
+ end
+ end
+
+ protected
+
+ # A hook which allows you to clean up any time taken into account in
+ # views wrongly, like database querying time.
+ #
+ # def cleanup_view_runtime
+ # super - time_taken_in_something_expensive
+ # end
+ #
+ # :api: plugin
+ def cleanup_view_runtime #:nodoc:
+ yield
+ end
+
+ # Everytime after an action is processed, this method is invoked
+ # with the payload, so you can add more information.
+ # :api: plugin
+ def append_info_to_payload(payload) #:nodoc:
+ payload[:view_runtime] = view_runtime
+ end
+
+ module ClassMethods
+ # A hook which allows other frameworks to log what happened during
+ # controller process action. This method should return an array
+ # with the messages to be added.
+ # :api: plugin
+ def log_process_action(payload) #:nodoc:
+ messages, view_runtime = [], payload[:view_runtime]
+ messages << ("Views: %.1fms" % view_runtime.to_f) if view_runtime
+ messages
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/logger.rb b/actionpack/lib/action_controller/metal/logger.rb
deleted file mode 100644
index 4f4370e5f0..0000000000
--- a/actionpack/lib/action_controller/metal/logger.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-require 'abstract_controller/logger'
-
-module ActionController
- # Adds instrumentation to <tt>process_action</tt> and a <tt>log_event</tt> method
- # responsible to log events from ActiveSupport::Notifications. This module handles
- # :process_action and :render_template events but allows any other module to hook
- # into log_event and provide its own logging facilities (as in ActionController::Caching).
- module Logger
- extend ActiveSupport::Concern
-
- included do
- include AbstractController::Logger
- end
-
- attr_internal :view_runtime
-
- def process_action(action)
- ActiveSupport::Notifications.instrument(:process_action, :controller => self, :action => action) do
- super
- end
- end
-
- def render(*args, &block)
- if logger
- render_output = nil
-
- self.view_runtime = cleanup_view_runtime do
- Benchmark.ms { render_output = super }
- end
-
- render_output
- else
- super
- end
- end
-
- # If you want to remove any time taken into account in :view_runtime
- # wrongly, you can do it here:
- #
- # def cleanup_view_runtime
- # super - time_taken_in_something_expensive
- # end
- #
- # :api: plugin
- def cleanup_view_runtime #:nodoc:
- yield
- end
-
- module ClassMethods
- # This is the hook invoked by ActiveSupport::Notifications.subscribe.
- # If you need to log any event, overwrite the method and do it here.
- def log_event(name, before, after, instrumenter_id, payload) #:nodoc:
- if name == :process_action
- duration = [(after - before) * 1000, 0.01].max
- controller = payload[:controller]
- request = controller.request
-
- logger.info "\n\nProcessed #{controller.class.name}##{payload[:action]} " \
- "to #{request.formats} (for #{request.remote_ip} at #{before.to_s(:db)}) " \
- "[#{request.method.to_s.upcase}]"
-
- log_process_action(controller)
-
- message = "Completed in %.0fms" % duration
- message << " | #{controller.response.status}"
- message << " [#{request.request_uri rescue "unknown"}]"
-
- logger.info(message)
- elsif name == :render_template
- # TODO Make render_template logging work if you are using just ActionView
- duration = (after - before) * 1000
- message = "Rendered #{payload[:identifier]}"
- message << " within #{payload[:layout]}" if payload[:layout]
- message << (" (%.1fms)" % duration)
- logger.info(message)
- end
- end
-
- protected
-
- # A hook which allows logging what happened during controller process action.
- # :api: plugin
- def log_process_action(controller) #:nodoc:
- view_runtime = controller.send :view_runtime
- logger.info(" View runtime: %.1fms" % view_runtime.to_f) if view_runtime
- end
- end
- end
-end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 468c5f4fae..4c02677729 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -215,7 +215,10 @@ module ActionController #:nodoc:
# a proc to it.
#
def respond_with(*resources, &block)
- if response = retrieve_response_from_mimes([], &block)
+ raise "In order to use respond_with, first you need to declare the formats your " <<
+ "controller responds to in the class level" if mimes_for_respond_to.empty?
+
+ if response = retrieve_response_from_mimes(&block)
options = resources.extract_options!
options.merge!(:default_response => response)
(options.delete(:responder) || responder).call(self, resources, options)
@@ -246,9 +249,9 @@ module ActionController #:nodoc:
# Collects mimes and return the response for the negotiated format. Returns
# nil if :not_acceptable was sent to the client.
#
- def retrieve_response_from_mimes(mimes, &block)
+ def retrieve_response_from_mimes(mimes=nil, &block)
collector = Collector.new { default_render }
- mimes = collect_mimes_from_class_level if mimes.empty?
+ mimes ||= collect_mimes_from_class_level
mimes.each { |mime| collector.send(mime) }
block.call(collector) if block_given?
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index 7a2f9a6fc5..faf0589fd2 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -9,7 +9,9 @@ module ActionController
module Redirecting
extend ActiveSupport::Concern
+
include AbstractController::Logger
+ include ActionController::UrlFor
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
#
@@ -55,8 +57,6 @@ module ActionController
self.status = _extract_redirect_to_status(options, response_status)
self.location = _compute_redirect_to_location(options)
self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
-
- logger.info("Redirected to #{location}") if logger && logger.info?
end
private
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 288b5d7c99..8f03b8bb17 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -88,13 +88,11 @@ module ActionController #:nodoc:
@performed_render = false
if options[:x_sendfile]
- logger.info "Sending #{X_SENDFILE_HEADER} header #{path}" if logger
head options[:status], X_SENDFILE_HEADER => path
else
if options[:stream]
# TODO : Make render :text => proc {} work with the new base
render :status => options[:status], :text => Proc.new { |response, output|
- logger.info "Streaming file #{path}" unless logger.nil?
len = options[:buffer_size] || 4096
File.open(path, 'rb') do |file|
while buf = file.read(len)
@@ -103,7 +101,6 @@ module ActionController #:nodoc:
end
}
else
- logger.info "Sending file #{path}" unless logger.nil?
File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read }
end
end
@@ -141,7 +138,6 @@ module ActionController #:nodoc:
# data to the browser, then use <tt>render :text => proc { ... }</tt>
# instead. See ActionController::Base#render for more information.
def send_data(data, options = {}) #:doc:
- logger.info "Sending data #{options[:filename]}" if logger
send_file_headers! options.merge(:length => data.bytesize)
render :status => options[:status], :text => data
end
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 8c3810ebcb..73feacb872 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -2,40 +2,14 @@ module ActionController
module UrlFor
extend ActiveSupport::Concern
- include RackDelegation
+ include AbstractController::UrlFor
+ include ActionController::RackDelegation
- # 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
+ protected
- def url_for(options = {})
- options ||= {}
- case options
- when String
- options
- when Hash
- @url ||= UrlRewriter.new(request, params)
- @url.rewrite(rewrite_options(options))
- else
- polymorphic_url(options)
- end
+ def _url_rewriter
+ return ActionController::UrlRewriter unless request
+ @_url_rewriter ||= ActionController::UrlRewriter.new(request, params)
end
end
end
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index f861d12905..741101a210 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -5,6 +5,9 @@ module ActionController
class Railtie < Rails::Railtie
plugin_name :action_controller
+ require "action_controller/railties/subscriber"
+ subscriber ActionController::Railties::Subscriber.new
+
initializer "action_controller.set_configs" do |app|
app.config.action_controller.each do |k,v|
ActionController::Base.send "#{k}=", v
@@ -23,26 +26,6 @@ module ActionController
app.reload_routes!
end
- # Include middleware to serve up static assets
- initializer "action_controller.initialize_static_server" do |app|
- if app.config.serve_static_assets
- app.config.middleware.use(ActionDispatch::Static, Rails.public_path)
- end
- end
-
- initializer "action_controller.initialize_middleware_stack" do |app|
- middleware = app.config.middleware
- middleware.use(::Rack::Lock, :if => lambda { ActionController::Base.allow_concurrency })
- middleware.use(::Rack::Runtime)
- middleware.use(ActionDispatch::ShowExceptions, lambda { ActionController::Base.consider_all_requests_local })
- middleware.use(ActionDispatch::Callbacks, lambda { ActionController::Dispatcher.prepare_each_request })
- middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options })
- middleware.use(ActionDispatch::ParamsParser)
- middleware.use(::Rack::MethodOverride)
- middleware.use(::Rack::Head)
- middleware.use(ActionDispatch::StringCoercion)
- end
-
initializer "action_controller.initialize_framework_caches" do
ActionController::Base.cache_store ||= RAILS_CACHE
end
@@ -57,19 +40,41 @@ module ActionController
ActionController::Base.view_paths = view_path if ActionController::Base.view_paths.blank?
end
+ class MetalMiddlewareBuilder
+ def initialize(metals)
+ @metals = metals
+ end
+
+ def new(app)
+ ActionDispatch::Cascade.new(@metals, app)
+ end
+
+ def name
+ ActionDispatch::Cascade.name
+ end
+ alias_method :to_s, :name
+ end
+
initializer "action_controller.initialize_metal" do |app|
- Rails::Rack::Metal.requested_metals = app.config.metals
+ metal_root = "#{Rails.root}/app/metal"
+ load_list = app.config.metals || Dir["#{metal_root}/**/*.rb"]
- app.config.middleware.insert_before(:"ActionDispatch::ParamsParser",
- Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?)
+ metals = load_list.map { |metal|
+ metal = File.basename(metal.gsub("#{metal_root}/", ''), '.rb')
+ require_dependency metal
+ metal.camelize.constantize
+ }.compact
+
+ middleware = MetalMiddlewareBuilder.new(metals)
+ app.config.middleware.insert_before(:"ActionDispatch::ParamsParser", middleware)
end
- # # Prepare dispatcher callbacks and run 'prepare' callbacks
+ # Prepare dispatcher callbacks and run 'prepare' callbacks
initializer "action_controller.prepare_dispatcher" do |app|
# TODO: This used to say unless defined?(Dispatcher). Find out why and fix.
+ # Notice that at this point, ActionDispatch::Callbacks were already loaded.
require 'rails/dispatcher'
-
- Dispatcher.define_dispatcher_callbacks(app.config.cache_classes)
+ ActionController::Dispatcher.prepare_each_request = true unless app.config.cache_classes
unless app.config.cache_classes
# Setup dev mode route reloading
@@ -80,15 +85,7 @@ module ActionController
app.reload_routes!
end
end
- ActionDispatch::Callbacks.before_dispatch { |callbacks| reload_routes.call }
- end
- end
-
- initializer "action_controller.notifications" do |app|
- require 'active_support/notifications'
-
- ActiveSupport::Notifications.subscribe do |*args|
- ActionController::Base.log_event(*args) if ActionController::Base.logger
+ ActionDispatch::Callbacks.before { |callbacks| reload_routes.call }
end
end
diff --git a/actionpack/lib/action_controller/railties/subscriber.rb b/actionpack/lib/action_controller/railties/subscriber.rb
new file mode 100644
index 0000000000..a9f5d16c58
--- /dev/null
+++ b/actionpack/lib/action_controller/railties/subscriber.rb
@@ -0,0 +1,60 @@
+module ActionController
+ module Railties
+ class Subscriber < Rails::Subscriber
+ def process_action(event)
+ payload = event.payload
+
+ info "\nProcessed #{payload[:controller]}##{payload[:action]} " \
+ "to #{payload[:formats].join(', ')} (for #{payload[:remote_ip]} at #{event.time.to_s(:db)}) " \
+ "[#{payload[:method].to_s.upcase}]"
+
+ info " Parameters: #{payload[:params].inspect}" unless payload[:params].blank?
+
+ additions = ActionController::Base.log_process_action(payload)
+
+ message = "Completed in %.0fms" % event.duration
+ message << " (#{additions.join(" | ")})" unless additions.blank?
+ message << " | #{payload[:status]} [#{payload[:request_uri]}]\n\n"
+
+ info(message)
+ end
+
+ def send_file(event)
+ message = if event.payload[:x_sendfile]
+ header = ActionController::Streaming::X_SENDFILE_HEADER
+ "Sent #{header} header %s"
+ elsif event.payload[:stream]
+ "Streamed file %s"
+ else
+ "Sent file %s"
+ end
+
+ message << " (%.1fms)"
+ info(message % [event.payload[:path], event.duration])
+ end
+
+ def redirect_to(event)
+ info "Redirected to #{event.payload[:location]}"
+ end
+
+ def send_data(event)
+ info("Sent data %s (%.1fms)" % [event.payload[:filename], event.duration])
+ end
+
+ %w(write_fragment read_fragment exist_fragment?
+ expire_fragment expire_page write_page).each do |method|
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{method}(event)
+ key_or_path = event.payload[:key] || event.payload[:path]
+ human_name = #{method.to_s.humanize.inspect}
+ info("\#{human_name} \#{key_or_path} (%.1fms)" % event.duration)
+ end
+ METHOD
+ end
+
+ def logger
+ ActionController::Base.logger
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 398ea52495..14557ca782 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -1,6 +1,5 @@
-require 'active_support/test_case'
require 'rack/session/abstract/id'
-require 'action_controller/metal/testing'
+require 'action_view/test_case'
module ActionController
class TestRequest < ActionDispatch::TestRequest #:nodoc:
@@ -240,13 +239,15 @@ module ActionController
@request.assign_parameters(@controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
@request.session = ActionController::TestSession.new(session) unless session.nil?
- @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
+ @request.session["flash"] = @request.flash.update(flash || {})
+ @request.session["flash"].sweep
@controller.request = @request
@controller.params.merge!(parameters)
build_request_uri(action, parameters)
Base.class_eval { include Testing }
@controller.process_with_new_base_test(@request, @response)
+ @request.session.delete('flash') if @request.session['flash'].blank?
@response
end
diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb
index 52b66c9303..933a1fa8f9 100644
--- a/actionpack/lib/action_controller/url_rewriter.rb
+++ b/actionpack/lib/action_controller/url_rewriter.rb
@@ -1,153 +1,21 @@
-module ActionController
- # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
- # is also possible: an URL can be generated from one of your routing definitions.
- # URL generation functionality is centralized in this module.
- #
- # See ActionController::Routing and ActionController::Resources for general
- # information about routing and routes.rb.
- #
- # <b>Tip:</b> If you need to generate URLs from your models or some other place,
- # then ActionController::UrlWriter is what you're looking for. Read on for
- # an introduction.
- #
- # == URL generation from parameters
- #
- # As you may know, some functions - such as ActionController::Base#url_for
- # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
- # of parameters. For example, you've probably had the chance to write code
- # like this in one of your views:
- #
- # <%= link_to('Click here', :controller => 'users',
- # :action => 'new', :message => 'Welcome!') %>
- #
- # #=> Generates a link to: /users/new?message=Welcome%21
- #
- # link_to, and all other functions that require URL generation functionality,
- # actually use ActionController::UrlWriter under the hood. And in particular,
- # they use the ActionController::UrlWriter#url_for method. One can generate
- # the same path as the above example by using the following code:
- #
- # include UrlWriter
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :only_path => true)
- # # => "/users/new?message=Welcome%21"
- #
- # Notice the <tt>:only_path => true</tt> part. This is because UrlWriter has no
- # information about the website hostname that your Rails app is serving. So if you
- # want to include the hostname as well, then you must also pass the <tt>:host</tt>
- # argument:
- #
- # include UrlWriter
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :host => 'www.example.com') # Changed this.
- # # => "http://www.example.com/users/new?message=Welcome%21"
- #
- # By default, all controllers and views have access to a special version of url_for,
- # that already knows what the current hostname is. So if you use url_for in your
- # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
- # argument.
- #
- # For convenience reasons, mailers provide a shortcut for ActionController::UrlWriter#url_for.
- # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlWriter#url_for'
- # in full. However, mailers don't have hostname information, and what's why you'll still
- # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
- #
- #
- # == URL generation for named routes
- #
- # UrlWriter also allows one to access methods that have been auto-generated from
- # named routes. For example, suppose that you have a 'users' resource in your
- # <b>routes.rb</b>:
- #
- # map.resources :users
- #
- # 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 by including ActionController::UrlWriter in your class:
- #
- # class User < ActiveRecord::Base
- # include ActionController::UrlWriter
- #
- # def base_uri
- # user_path(self)
- # end
- # end
- #
- # User.find(1).base_uri # => "/users/1"
- module UrlWriter
- def self.included(base) #:nodoc:
- ActionController::Routing::Routes.install_helpers(base)
- base.mattr_accessor :default_url_options
-
- # The default options for urls written by this writer. Typically a <tt>:host</tt> pair is provided.
- base.default_url_options ||= {}
- end
-
- # Generate a url based on the options provided, default_url_options and the
- # routes defined in routes.rb. The following options are supported:
- #
- # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
- # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
- # * <tt>:host</tt> - Specifies the host the link should be targeted at.
- # If <tt>:only_path</tt> is false, this option must be
- # provided either explicitly, or via +default_url_options+.
- # * <tt>:port</tt> - Optionally specify the port to connect to.
- # * <tt>:anchor</tt> - An anchor name to be appended to the path.
- # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
- # +relative_url_root+ set in ActionController::Base.relative_url_root.
- # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
- #
- # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
- # +url_for+ is forwarded to the Routes module.
- #
- # Examples:
- #
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
- # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
- def url_for(options)
- options = self.class.default_url_options.merge(options)
-
- url = ''
-
- unless options.delete(:only_path)
- url << (options.delete(:protocol) || 'http')
- url << '://' unless url.match("://")
-
- raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
-
- url << options.delete(:host)
- url << ":#{options.delete(:port)}" if options.key?(:port)
- else
- # Delete the unused options to prevent their appearance in the query string.
- [:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) }
- end
- trailing_slash = options.delete(:trailing_slash) if options.key?(:trailing_slash)
- url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
- anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
- generated = Routing::Routes.generate(options, {})
- url << (trailing_slash ? generated.sub(/\?|\z/) { "/" + $& } : generated)
- url << anchor if anchor
-
- url
- end
- end
+require 'active_support/core_ext/hash/except'
+module ActionController
# Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
class UrlRewriter #:nodoc:
RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :skip_relative_url_root]
+
def initialize(request, parameters)
@request, @parameters = request, parameters
end
def rewrite(options = {})
- rewrite_url(options)
+ options[:host] ||= @request.host_with_port
+ options[:protocol] ||= @request.protocol
+
+ self.class.rewrite(options, @request.symbolized_path_parameters) do |options|
+ process_path_options(options)
+ end
end
def to_str
@@ -156,49 +24,53 @@ module ActionController
alias_method :to_s, :to_str
- private
- # Given a path and options, returns a rewritten URL string
- def rewrite_url(options)
- rewritten_url = ""
-
- unless options[:only_path]
- rewritten_url << (options[:protocol] || @request.protocol)
- rewritten_url << "://" unless rewritten_url.match("://")
- rewritten_url << rewrite_authentication(options)
- rewritten_url << (options[:host] || @request.host_with_port)
- rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
- end
-
- path = rewrite_path(options)
- rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
- rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
- rewritten_url << "##{CGI.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
-
- rewritten_url
+ def self.rewrite(options, path_segments=nil)
+ rewritten_url = ""
+
+ unless options[:only_path]
+ rewritten_url << (options[:protocol] || "http")
+ rewritten_url << "://" unless rewritten_url.match("://")
+ rewritten_url << rewrite_authentication(options)
+
+ raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
+
+ rewritten_url << options[:host]
+ rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
end
- # Given a Hash of options, generates a route
- def rewrite_path(options)
- options = options.symbolize_keys
- options.update(options[:params].symbolize_keys) if options[:params]
+ path_options = options.except(*RESERVED_OPTIONS)
+ path_options = yield(path_options) if block_given?
+ path = Routing::Routes.generate(path_options, path_segments || {})
+
+ rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
+ rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
+ rewritten_url << "##{Rack::Utils.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
- if (overwrite = options.delete(:overwrite_params))
- options.update(@parameters.symbolize_keys)
- options.update(overwrite.symbolize_keys)
- end
+ rewritten_url
+ end
- RESERVED_OPTIONS.each { |k| options.delete(k) }
+ protected
- # Generates the query string, too
- Routing::Routes.generate(options, @request.symbolized_path_parameters)
+ def self.rewrite_authentication(options)
+ if options[:user] && options[:password]
+ "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@"
+ else
+ ""
end
+ end
+
+ # Given a Hash of options, generates a route
+ def process_path_options(options)
+ options = options.symbolize_keys
+ options.update(options[:params].symbolize_keys) if options[:params]
- def rewrite_authentication(options)
- if options[:user] && options[:password]
- "#{CGI.escape(options.delete(:user))}:#{CGI.escape(options.delete(:password))}@"
- else
- ""
- end
+ if (overwrite = options.delete(:overwrite_params))
+ options.update(@parameters.symbolize_keys)
+ options.update(overwrite.symbolize_keys)
end
+
+ options
+ end
+
end
end