diff options
Diffstat (limited to 'actionpack/lib')
24 files changed, 379 insertions, 396 deletions
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb index 46760bba7c..4073e9b386 100644 --- a/actionpack/lib/abstract_controller/layouts.rb +++ b/actionpack/lib/abstract_controller/layouts.rb @@ -20,7 +20,7 @@ module AbstractController end def clear_template_caches! - @found_layouts.clear if @found_layouts + @found_layouts.clear if defined? @found_layouts super end diff --git a/actionpack/lib/abstract_controller/localized_cache.rb b/actionpack/lib/abstract_controller/localized_cache.rb index ee7b43cb9f..bf648af60a 100644 --- a/actionpack/lib/abstract_controller/localized_cache.rb +++ b/actionpack/lib/abstract_controller/localized_cache.rb @@ -1,6 +1,6 @@ module AbstractController class HashKey - @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } } + @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|sh,sk| sh[sk] = {} } } def self.get(klass, formats, locale) @hash_keys[klass][formats][locale] ||= new(klass, formats, locale) diff --git a/actionpack/lib/abstract_controller/logger.rb b/actionpack/lib/abstract_controller/logger.rb index e3bcd28da7..a23a13e1d6 100644 --- a/actionpack/lib/abstract_controller/logger.rb +++ b/actionpack/lib/abstract_controller/logger.rb @@ -9,25 +9,5 @@ module AbstractController cattr_accessor :logger extend ActiveSupport::Benchmarkable end - - # A class that allows you to defer expensive processing - # until the logger actually tries to log. Otherwise, you are - # forced to do the processing in advance, and send the - # entire processed String to the logger, which might - # just discard the String if the log level is too low. - # - # TODO: Require that Rails loggers accept a block. - class DelayedLog < ActiveSupport::BasicObject - def initialize(&block) - @str, @block = nil, block - end - - def method_missing(*args, &block) - unless @str - @str, @block = @block.call, nil - end - @str.send(*args, &block) - end - end end end diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index dbba69f637..67656110c4 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -15,7 +15,6 @@ module ActionController include ActionController::ConditionalGet include ActionController::RackDelegation include ActionController::Logger - include ActionController::Benchmarking include ActionController::Configuration # Legacy modules diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb index d784138ebe..69ed84da95 100644 --- a/actionpack/lib/action_controller/caching.rb +++ b/actionpack/lib/action_controller/caching.rb @@ -60,6 +60,17 @@ 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 8c1167d526..f569d0dd8b 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -74,7 +74,7 @@ module ActionController #:nodoc: return unless cache_configured? key = fragment_cache_key(key) - ActiveSupport::Notifications.instrument(:fragment_exist?, :key => key) do + ActiveSupport::Notifications.instrument(:exist_fragment?, :key => key) do cache_store.exist?(key, options) end end diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index b436de9878..f445ca70ee 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -85,7 +85,7 @@ module ActionController end class ActionEndpoint - @@endpoints = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } } + @@endpoints = Hash.new {|h,k| h[k] = Hash.new {|sh,sk| sh[sk] = {} } } def self.for(controller, action, stack) @@endpoints[controller][action][stack] ||= begin diff --git a/actionpack/lib/action_controller/metal/benchmarking.rb b/actionpack/lib/action_controller/metal/benchmarking.rb deleted file mode 100644 index f73f635b0d..0000000000 --- a/actionpack/lib/action_controller/metal/benchmarking.rb +++ /dev/null @@ -1,72 +0,0 @@ -require 'active_support/core_ext/benchmark' - -module ActionController #:nodoc: - # The benchmarking module times the performance of actions and reports to the logger. If the Active Record - # package has been included, a separate timing section for database calls will be added as well. - module Benchmarking #:nodoc: - extend ActiveSupport::Concern - - protected - def render(*args, &block) - if logger - if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? - db_runtime = ActiveRecord::Base.connection.reset_runtime - end - - render_output = nil - @view_runtime = Benchmark.ms { render_output = super } - - if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? - @db_rt_before_render = db_runtime - @db_rt_after_render = ActiveRecord::Base.connection.reset_runtime - @view_runtime -= @db_rt_after_render - end - - render_output - else - super - end - end - - private - def process_action(*args) - if logger - ms = [Benchmark.ms { super }, 0.01].max - logging_view = defined?(@view_runtime) - logging_active_record = Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected? - - log_message = 'Completed in %.0fms' % ms - - if logging_view || logging_active_record - log_message << " (" - log_message << view_runtime if logging_view - - if logging_active_record - log_message << ", " if logging_view - log_message << active_record_runtime + ")" - else - ")" - end - end - - log_message << " | #{response.status}" - log_message << " [#{complete_request_uri rescue "unknown"}]" - - logger.info(log_message) - else - super - end - end - - def view_runtime - "View: %.0f" % @view_runtime - end - - def active_record_runtime - db_runtime = ActiveRecord::Base.connection.reset_runtime - db_runtime += @db_rt_before_render if @db_rt_before_render - db_runtime += @db_rt_after_render if @db_rt_after_render - "DB: %.0f" % db_runtime - end - end -end diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb index 8d5f0d7199..5b51bd21d0 100644 --- a/actionpack/lib/action_controller/metal/cookies.rb +++ b/actionpack/lib/action_controller/metal/cookies.rb @@ -58,140 +58,140 @@ module ActionController #:nodoc: def cookies @cookies ||= CookieJar.build(request, response) end - end + end - class CookieJar < Hash #:nodoc: - def self.build(request, response) - new.tap do |hash| - hash.update(request.cookies) - hash.response = response - end + class CookieJar < Hash #:nodoc: + def self.build(request, response) + new.tap do |hash| + hash.update(request.cookies) + hash.response = response end + end - attr_accessor :response + attr_accessor :response - # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. - def [](name) - super(name.to_s) - end + # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists. + def [](name) + super(name.to_s) + end - # Sets the cookie named +name+. The second argument may be the very cookie - # value, or a hash of options as documented above. - def []=(key, options) - if options.is_a?(Hash) - options.symbolize_keys! - value = options[:value] - else - value = options - options = { :value => value } - end - - super(key.to_s, value) - - options[:path] ||= "/" - response.set_cookie(key, options) + # Sets the cookie named +name+. The second argument may be the very cookie + # value, or a hash of options as documented above. + def []=(key, options) + if options.is_a?(Hash) + options.symbolize_keys! + value = options[:value] + else + value = options + options = { :value => value } end - # Removes the cookie on the client machine by setting the value to an empty string - # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in - # an options hash to delete cookies with extra data such as a <tt>:path</tt>. - def delete(key, options = {}) + super(key.to_s, value) + + options[:path] ||= "/" + response.set_cookie(key, options) + end + + # Removes the cookie on the client machine by setting the value to an empty string + # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in + # an options hash to delete cookies with extra data such as a <tt>:path</tt>. + def delete(key, options = {}) + options.symbolize_keys! + options[:path] ||= "/" + value = super(key.to_s) + response.delete_cookie(key, options) + value + end + + # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example: + # + # cookies.permanent[:prefers_open_id] = true + # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + # + # This jar is only meant for writing. You'll read permanent cookies through the regular accessor. + # + # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples: + # + # cookies.permanent.signed[:remember_me] = current_user.id + # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT + def permanent + @permanent ||= PermanentCookieJar.new(self) + end + + # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from + # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed + # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will + # be raised. + # + # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret. + # + # Example: + # + # cookies.signed[:discount] = 45 + # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/ + # + # cookies.signed[:discount] # => 45 + def signed + @signed ||= SignedCookieJar.new(self) + end + end + + class PermanentCookieJar < CookieJar #:nodoc: + def initialize(parent_jar) + @parent_jar = parent_jar + end + + def []=(key, options) + if options.is_a?(Hash) options.symbolize_keys! - options[:path] ||= "/" - value = super(key.to_s) - response.delete_cookie(key, options) - value + else + options = { :value => options } end - # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example: - # - # cookies.permanent[:prefers_open_id] = true - # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT - # - # This jar is only meant for writing. You'll read permanent cookies through the regular accessor. - # - # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples: - # - # cookies.permanent.signed[:remember_me] = current_user.id - # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT - def permanent - @permanent ||= PermanentCookieJar.new(self) - end - - # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from - # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed - # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will - # be raised. - # - # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret. - # - # Example: - # - # cookies.signed[:discount] = 45 - # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/ - # - # cookies.signed[:discount] # => 45 - def signed - @signed ||= SignedCookieJar.new(self) - end + options[:expires] = 20.years.from_now + @parent_jar[key] = options end - - class PermanentCookieJar < CookieJar #:nodoc: - def initialize(parent_jar) - @parent_jar = parent_jar - end - - def []=(key, options) - if options.is_a?(Hash) - options.symbolize_keys! - else - options = { :value => options } - end - - options[:expires] = 20.years.from_now - @parent_jar[key] = options - end - - def signed - @signed ||= SignedCookieJar.new(self) - end - - def controller - @parent_jar.controller - end - - def method_missing(method, *arguments, &block) - @parent_jar.send(method, *arguments, &block) - end + + def signed + @signed ||= SignedCookieJar.new(self) end - - class SignedCookieJar < CookieJar #:nodoc: - def initialize(parent_jar) - unless ActionController::Base.cookie_verifier_secret - raise "You must set ActionController::Base.cookie_verifier_secret to use signed cookies" - end - - @parent_jar = parent_jar - @verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.cookie_verifier_secret) - end - - def [](name) - @verifier.verify(@parent_jar[name]) - end - - def []=(key, options) - if options.is_a?(Hash) - options.symbolize_keys! - options[:value] = @verifier.generate(options[:value]) - else - options = { :value => @verifier.generate(options) } - end - - @parent_jar[key] = options + + def controller + @parent_jar.controller + end + + def method_missing(method, *arguments, &block) + @parent_jar.send(method, *arguments, &block) + end + end + + class SignedCookieJar < CookieJar #:nodoc: + def initialize(parent_jar) + unless ActionController::Base.cookie_verifier_secret + raise "You must set ActionController::Base.cookie_verifier_secret to use signed cookies" end - - def method_missing(method, *arguments, &block) - @parent_jar.send(method, *arguments, &block) + + @parent_jar = parent_jar + @verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.cookie_verifier_secret) + end + + def [](name) + @verifier.verify(@parent_jar[name]) + end + + def []=(key, options) + if options.is_a?(Hash) + options.symbolize_keys! + options[:value] = @verifier.generate(options[:value]) + else + options = { :value => @verifier.generate(options) } end + + @parent_jar[key] = options + end + + def method_missing(method, *arguments, &block) + @parent_jar.send(method, *arguments, &block) + end end end diff --git a/actionpack/lib/action_controller/metal/filter_parameter_logging.rb b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb index a53c052075..59e200396a 100644 --- a/actionpack/lib/action_controller/metal/filter_parameter_logging.rb +++ b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb @@ -2,8 +2,6 @@ module ActionController module FilterParameterLogging extend ActiveSupport::Concern - include AbstractController::Logger - module ClassMethods # Replace sensitive parameter data from the request log. # Filters parameters that have any of the arguments as a substring. @@ -54,23 +52,25 @@ module ActionController end protected :filter_parameters end - end - INTERNAL_PARAMS = [:controller, :action, :format, :_method, :only_path] + protected - def process(*) - response = super - if logger - parameters = filter_parameters(params).except!(*INTERNAL_PARAMS) - logger.info { " Parameters: #{parameters.inspect}" } unless parameters.empty? + # 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 - response end + INTERNAL_PARAMS = [:controller, :action, :format, :_method, :only_path] + protected def filter_parameters(params) - params.dup + params.dup.except!(*INTERNAL_PARAMS) end end diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb index 581ff6109e..682f90e23b 100644 --- a/actionpack/lib/action_controller/metal/flash.rb +++ b/actionpack/lib/action_controller/metal/flash.rb @@ -123,80 +123,79 @@ module ActionController #:nodoc: 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 + 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 - @_flash - end - - # Convenience accessor for flash[:alert] - def alert - flash[:alert] - end - - # Convenience accessor for flash[:alert]= - def alert=(message) - flash[:alert] = message - 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 - # Convenience accessor for flash[:notice] - def notice - flash[:notice] - end + @_flash + end - # Convenience accessor for flash[:notice]= - def notice=(message) - flash[:notice] = message - end + # Convenience accessor for flash[:alert] + def alert + flash[:alert] + end + # Convenience accessor for flash[:alert]= + def alert=(message) + flash[:alert] = message + end - protected - def process_action(method_name) - @_flash = nil - super - @_flash.store(session) if @_flash - @_flash = nil + # Convenience accessor for flash[:notice] + def notice + flash[:notice] end - def reset_session - super - @_flash = nil + # Convenience accessor for flash[:notice]= + def notice=(message) + flash[:notice] = message end - def redirect_to(options = {}, response_status_and_flash = {}) #:doc: - if alert = response_status_and_flash.delete(:alert) - flash[:alert] = alert + protected + def process_action(method_name) + @_flash = nil + super + @_flash.store(session) if @_flash + @_flash = nil end - if notice = response_status_and_flash.delete(:notice) - flash[:notice] = notice + def reset_session + super + @_flash = nil end - if other_flashes = response_status_and_flash.delete(:flash) - flash.update(other_flashes) - end + def redirect_to(options = {}, response_status_and_flash = {}) #:doc: + if alert = response_status_and_flash.delete(:alert) + flash[:alert] = alert + end - super(options, response_status_and_flash) - end + if notice = response_status_and_flash.delete(:notice) + flash[:notice] = notice + end + + if other_flashes = response_status_and_flash.delete(:flash) + flash.update(other_flashes) + end + + super(options, response_status_and_flash) + end end end diff --git a/actionpack/lib/action_controller/metal/logger.rb b/actionpack/lib/action_controller/metal/logger.rb index 956d7dd371..e71f77fbb2 100644 --- a/actionpack/lib/action_controller/metal/logger.rb +++ b/actionpack/lib/action_controller/metal/logger.rb @@ -1,34 +1,85 @@ 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 - # Override process_action in the AbstractController::Base - # to log details about the method. + extend ActiveSupport::Concern + + attr_internal :view_runtime + def process_action(action) - result = ActiveSupport::Notifications.instrument(:process_action, - :controller => self, :action => action) do + ActiveSupport::Notifications.instrument(:process_action, :controller => self, :action => action) do super end + end + def render(*args, &block) if logger - log = AbstractController::Logger::DelayedLog.new do - "\n\nProcessing #{self.class.name}\##{action_name} " \ - "to #{request.formats} (for #{request_origin}) " \ - "[#{request.method.to_s.upcase}]" + render_output = nil + + self.view_runtime = cleanup_view_runtime do + Benchmark.ms { render_output = super } end - logger.info(log) + render_output + else + super end + end - result + # 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 - private + 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}]" - # Returns the request origin with the IP and time. This needs to be cached, - # otherwise we would get different results for each time it calls. - def request_origin - @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}" + 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 +end
\ No newline at end of file diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index 2826b1e34c..f1fb4d7ce5 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -13,7 +13,7 @@ module ActionController #:nodoc: cattr_accessor :request_forgery_protection_token # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. - class_inheritable_accessor :allow_forgery_protection + extlib_inheritable_accessor :allow_forgery_protection self.allow_forgery_protection = true helper_method :form_authenticity_token diff --git a/actionpack/lib/action_controller/rails.rb b/actionpack/lib/action_controller/rails.rb index 36a52b3149..6ebb50887b 100644 --- a/actionpack/lib/action_controller/rails.rb +++ b/actionpack/lib/action_controller/rails.rb @@ -1,6 +1,7 @@ module ActionController class Plugin < Rails::Plugin plugin_name :action_controller + include_modules_in "ActionController::Base" initializer "action_controller.set_configs" do |app| app.config.action_controller.each do |k,v| @@ -14,10 +15,6 @@ module ActionController end # Routing must be initialized after plugins to allow the former to extend the routes - # --- - # If Action Controller is not one of the loaded frameworks (Configuration#frameworks) - # this does nothing. Otherwise, it loads the routing definitions and sets up - # loading module used to lazily load controllers (Configuration#controller_paths). initializer "action_controller.initialize_routing" do |app| app.route_configuration_files << app.config.routes_configuration_file app.route_configuration_files << app.config.builtin_routes_configuration_file @@ -88,13 +85,8 @@ module ActionController initializer "action_controller.notifications" do |app| require 'active_support/notifications' - ActiveSupport::Notifications.subscribe(/(read|write|cache|expire|exist)_(fragment|page)\??/) do |*args| - event = ActiveSupport::Notifications::Event.new(*args) - - if logger = ActionController::Base.logger - human_name = event.name.to_s.humanize - logger.info("#{human_name} (%.1fms)" % event.duration) - end + ActiveSupport::Notifications.subscribe do |*args| + ActionController::Base.log_event(*args) if ActionController::Base.logger end end diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb index 0696cb017c..7f61ff5657 100644 --- a/actionpack/lib/action_dispatch.rb +++ b/actionpack/lib/action_dispatch.rb @@ -24,6 +24,7 @@ activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__) $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path) require 'active_support/ruby/shim' +require 'active_support/dependencies/autoload' require 'rack' diff --git a/actionpack/lib/action_dispatch/http/headers.rb b/actionpack/lib/action_dispatch/http/headers.rb index 2a41b4dbad..1e43104f0a 100644 --- a/actionpack/lib/action_dispatch/http/headers.rb +++ b/actionpack/lib/action_dispatch/http/headers.rb @@ -6,13 +6,13 @@ module ActionDispatch extend ActiveSupport::Memoizable def initialize(*args) - if args.size == 1 && args[0].is_a?(Hash) - super() - update(args[0]) - else - super - end - end + if args.size == 1 && args[0].is_a?(Hash) + super() + update(args[0]) + else + super + end + end def [](header_name) if include?(header_name) diff --git a/actionpack/lib/action_dispatch/http/mime_type.rb b/actionpack/lib/action_dispatch/http/mime_type.rb index c30897b32a..13c0f2bad0 100644 --- a/actionpack/lib/action_dispatch/http/mime_type.rb +++ b/actionpack/lib/action_dispatch/http/mime_type.rb @@ -10,8 +10,8 @@ module Mime %w(<< concat shift unshift push pop []= clear compact! collect! delete delete_at delete_if flatten! map! insert reject! reverse! replace slice! sort! uniq!).each do |method| - module_eval <<-CODE - def #{method}(*args) + module_eval <<-CODE, __FILE__, __LINE__ + 1 + def #{method}(*) @symbols = nil super end @@ -104,7 +104,7 @@ module Mime SET << Mime.const_get(symbol.to_s.upcase) - ([string] + mime_type_synonyms).each { |string| LOOKUP[string] = SET.last } unless skip_lookup + ([string] + mime_type_synonyms).each { |str| LOOKUP[str] = SET.last } unless skip_lookup ([symbol.to_s] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext] = SET.last } end diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb index 3b27309f58..24be4fee55 100644 --- a/actionpack/lib/action_dispatch/middleware/stack.rb +++ b/actionpack/lib/action_dispatch/middleware/stack.rb @@ -93,8 +93,9 @@ module ActionDispatch alias_method :insert_before, :insert def insert_after(index, *args, &block) - index = self.index(index) unless index.is_a?(Integer) - insert(index + 1, *args, &block) + i = index.is_a?(Integer) ? index : self.index(index) + raise "No such middleware to insert after: #{index.inspect}" unless i + insert(i + 1, *args, &block) end def swap(target, *args, &block) diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index e655d6a708..8f33346a4f 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -43,9 +43,13 @@ module ActionDispatch def extract_path_and_options(args) options = args.extract_options! - if args.empty? + case + when using_to_shorthand?(args, options) path, to = options.find { |name, value| name.is_a?(String) } options.merge!(:to => to).delete(path) if path + when using_match_shorthand?(args, options) + path = args.first + options = { :to => path.gsub("/", "#"), :as => path.gsub("/", "_") } else path = args.first end @@ -53,6 +57,16 @@ module ActionDispatch [ normalize_path(path), options ] end + # match "account" => "account#index" + def using_to_shorthand?(args, options) + args.empty? && options.present? + end + + # match "account/overview" + def using_match_shorthand?(args, options) + args.present? && options.except(:via).empty? && !args.first.include?(':') + end + def normalize_path(path) path = nil if path == "" path = "#{@scope[:path]}#{path}" if @scope[:path] diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index aeaf1ee4ff..5158415c20 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -191,45 +191,60 @@ module ActionView def setup(options, block) partial = options[:partial] - @options = options - @locals = options[:locals] || {} - @block = block + @options = options + @locals = options[:locals] || {} + @block = block if String === partial - @object = options[:object] - @path = partial + @object = options[:object] + @path = partial + @collection = collection else @object = partial - @path = partial_path(partial) + + if @collection = collection + paths = @collection_paths = @collection.map { |o| partial_path(o) } + @path = paths.uniq.size == 1 ? paths.first : nil + else + @path = partial_path + end end end def render - if @collection = collection - render_collection + options = @options + + if @collection + ActiveSupport::Notifications.instrument(:render_collection, :path => @path, + :count => @collection.size) do + render_collection + end else - @template = template = find_template - render_template(template, @object || @locals[template.variable_name]) + content = ActiveSupport::Notifications.instrument(:render_partial, :path => @path) do + render_partial + end + + if !@block && options[:layout] + content = @view._render_layout(find_template(options[:layout]), @locals){ content } + end + content end end def render_collection @template = template = find_template - return nil if @collection.blank? if @options.key?(:spacer_template) spacer = find_template(@options[:spacer_template]).render(@view, @locals) end - result = template ? collection_with_template(template) : collection_without_template + result = template ? collection_with_template : collection_without_template result.join(spacer).html_safe! end - def collection_with_template(template) - options = @options - - segments, locals, as = [], @locals, options[:as] || template.variable_name + def collection_with_template(template = @template) + segments, locals, as = [], @locals, @options[:as] || template.variable_name counter_name = template.counter_name locals[counter_name] = -1 @@ -245,16 +260,14 @@ module ActionView segments end - def collection_without_template - options = @options - - segments, locals, as = [], @locals, options[:as] + def collection_without_template(collection_paths = @collection_paths) + segments, locals, as = [], @locals, @options[:as] index, template = -1, nil - @collection.each do |object| - template = find_template(partial_path(object)) + @collection.each_with_index do |object, i| + template = find_template(collection_paths[i]) locals[template.counter_name] = (index += 1) - locals[template.variable_name] = object + locals[as || template.variable_name] = object segments << template.render(@view, locals) end @@ -263,18 +276,15 @@ module ActionView segments end - def render_template(template, object = @object) - options, locals, view = @options, @locals, @view - locals[options[:as] || template.variable_name] = object + def render_partial(object = @object) + @template = template = find_template + locals, view = @locals, @view - content = template.render(view, locals) do |*name| - @view._layout_for(*name, &@block) - end + object ||= locals[template.variable_name] + locals[@options[:as] || template.variable_name] = object - if @block || !options[:layout] - content - else - find_template(options[:layout]).render(@view, @locals) { content } + template.render(view, locals) do |*name| + view._layout_for(*name, &@block) end end @@ -305,9 +315,9 @@ module ActionView def partial_path(object = @object) @partial_names[object.class] ||= begin - return nil unless object.respond_to?(:to_model) + object = object.to_model if object.respond_to?(:to_model) - object.to_model.class.model_name.partial_path.dup.tap do |partial| + object.class.model_name.partial_path.dup.tap do |partial| path = @view.controller_path partial.insert(0, "#{File.dirname(path)}/") if path.include?(?/) end diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb index d4d16b4d98..0302e44b4e 100644 --- a/actionpack/lib/action_view/render/rendering.rb +++ b/actionpack/lib/action_view/render/rendering.rb @@ -73,25 +73,24 @@ module ActionView # would be <html>Hello David</html>. def _layout_for(name = nil, &block) return @_content_for[name || :layout] if !block_given? || name - capture(&block) end def _render_inline(inline, layout, options) - handler = Template.handler_class_for_extension(options[:type] || "erb") - template = Template.new(options[:inline], "inline template", handler, {}) + locals = options[:locals] - locals = options[:locals] - content = template.render(self, locals) + content = ActiveSupport::Notifications.instrument(:render_inline) do + handler = Template.handler_class_for_extension(options[:type] || "erb") + template = Template.new(options[:inline], "inline template", handler, {}) + template.render(self, locals) + end _render_text(content, layout, locals) end def _render_text(content, layout, locals) - content = layout.render(self, locals) do |*name| - _layout_for(*name) { content } - end if layout - + ActiveSupport::Notifications.instrument(:render_text) + content = _render_layout(layout, locals){ content } if layout content end @@ -108,23 +107,27 @@ module ActionView end def _render_template(template, layout = nil, options = {}, partial = nil) - logger && logger.info do - msg = "Rendering #{template.inspect}" - msg << " (#{options[:status]})" if options[:status] - msg + locals = options[:locals] || {} + + content = ActiveSupport::Notifications.instrument(:render_template, + :identifier => template.identifier, :layout => (layout ? layout.identifier : nil)) do + partial ? _render_partial_object(template, options) : template.render(self, locals) end - locals = options[:locals] || {} - content = partial ? _render_partial_object(template, options) : template.render(self, locals) @_content_for[:layout] = content if layout @_layout = layout.identifier - logger.info("Rendering template within #{layout.inspect}") if logger - content = layout.render(self, locals) { |*name| _layout_for(*name) } + content = _render_layout(layout, locals) end content end + + def _render_layout(layout, locals, &block) + ActiveSupport::Notifications.instrument(:render_layout, :identifier => layout.identifier) do + layout.render(self, locals){ |*name| _layout_for(*name, &block) } + end + end end end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index d46c989d11..adaf6544a7 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -36,10 +36,8 @@ module ActionView end def render(view, locals, &block) - ActiveSupport::Notifications.instrument(:render_template, :identifier => identifier) do - method_name = compile(locals, view) - view.send(method_name, locals, &block) - end + method_name = compile(locals, view) + view.send(method_name, locals, &block) rescue Exception => e if e.is_a?(Template::Error) e.sub_template_of(self) diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb index 5a46a27893..221d1bd5c5 100644 --- a/actionpack/lib/action_view/template/handler.rb +++ b/actionpack/lib/action_view/template/handler.rb @@ -17,8 +17,8 @@ module ActionView end def compile(template) - raise "Need to implement #{self.class.name}#compile(template)" - end + raise "Need to implement #{self.class.name}#compile(template)" + end end end diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb index a2f4ab2ef5..c6a17907ff 100644 --- a/actionpack/lib/action_view/template/resolver.rb +++ b/actionpack/lib/action_view/template/resolver.rb @@ -108,13 +108,9 @@ module ActionView query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << '}' end - Dir[query].map do |path| - next if File.directory?(path) - source = File.read(path) - identifier = Pathname.new(path).expand_path.to_s - - Template.new(source, identifier, *path_to_details(path)) - end.compact + Dir[query].reject { |p| File.directory?(p) }.map do |p| + Template.new(File.read(p), File.expand_path(p), *path_to_details(p)) + end end # # TODO: fix me @@ -162,4 +158,4 @@ module ActionView @paths.first.to_s end end -end
\ No newline at end of file +end |