Make many parts of Rails lazy. In order to facilitate this,

add lazy_load_hooks.rb, which allows us to declare code that
should be run at some later time. For instance, this allows
us to defer requiring ActiveRecord::Base at boot time purely
to apply configuration. Instead, we register a hook that should
apply configuration once ActiveRecord::Base is loaded.

With these changes, brings down total boot time of a
new app to 300ms in production and 400ms in dev.

TODO: rename base_hook
This commit is contained in:
wycats
2010-03-07 06:24:30 -08:00
parent a424f199a9
commit 39d6f9e112
34 changed files with 299 additions and 312 deletions

View File

@@ -34,6 +34,7 @@ require 'active_support/core_ext/array/uniq_by'
require 'active_support/core_ext/module/attr_internal' require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/string/inflections' require 'active_support/core_ext/string/inflections'
require 'active_support/lazy_load_hooks'
module ActionMailer module ActionMailer
extend ::ActiveSupport::Autoload extend ::ActiveSupport::Autoload

View File

@@ -291,6 +291,8 @@ module ActionMailer #:nodoc:
:parts_order => [ "text/plain", "text/enriched", "text/html" ] :parts_order => [ "text/plain", "text/enriched", "text/html" ]
}.freeze }.freeze
ActionMailer.run_base_hooks(self)
class << self class << self
def mailer_name def mailer_name

View File

@@ -6,19 +6,21 @@ module ActionMailer
railtie_name :action_mailer railtie_name :action_mailer
initializer "action_mailer.url_for", :before => :load_environment_config do |app| initializer "action_mailer.url_for", :before => :load_environment_config do |app|
ActionMailer::Base.send(:include, app.routes.url_helpers) ActionMailer.base_hook { include app.routes.url_helpers }
end end
require "action_mailer/railties/log_subscriber" require "action_mailer/railties/log_subscriber"
log_subscriber ActionMailer::Railties::LogSubscriber.new log_subscriber ActionMailer::Railties::LogSubscriber.new
initializer "action_mailer.logger" do initializer "action_mailer.logger" do
ActionMailer::Base.logger ||= Rails.logger ActionMailer.base_hook { self.logger ||= Rails.logger }
end end
initializer "action_mailer.set_configs" do |app| initializer "action_mailer.set_configs" do |app|
app.config.action_mailer.each do |k,v| ActionMailer.base_hook do
ActionMailer::Base.send "#{k}=", v app.config.action_mailer.each do |k,v|
send "#{k}=", v
end
end end
end end
end end

View File

@@ -58,6 +58,8 @@ module ActionController
filter filter
end end
ActionController.run_base_hooks(self)
end end
end end

View File

@@ -40,7 +40,7 @@ module ActionController
log_subscriber ActionController::Railties::LogSubscriber.new log_subscriber ActionController::Railties::LogSubscriber.new
initializer "action_controller.logger" do initializer "action_controller.logger" do
ActionController::Base.logger ||= Rails.logger ActionController.base_hook { self.logger ||= Rails.logger }
end end
initializer "action_controller.set_configs" do |app| initializer "action_controller.set_configs" do |app|
@@ -51,19 +51,23 @@ module ActionController
ac.stylesheets_dir = paths.public.stylesheets.to_a.first ac.stylesheets_dir = paths.public.stylesheets.to_a.first
ac.secret = app.config.cookie_secret ac.secret = app.config.cookie_secret
ActionController::Base.config.replace(ac) ActionController.base_hook { self.config.replace(ac) }
end end
initializer "action_controller.initialize_framework_caches" do initializer "action_controller.initialize_framework_caches" do
ActionController::Base.cache_store ||= RAILS_CACHE ActionController.base_hook { self.cache_store ||= RAILS_CACHE }
end end
initializer "action_controller.set_helpers_path" do |app| initializer "action_controller.set_helpers_path" do |app|
ActionController::Base.helpers_path = app.config.paths.app.helpers.to_a ActionController.base_hook do
self.helpers_path = app.config.paths.app.helpers.to_a
end
end end
initializer "action_controller.url_helpers" do |app| initializer "action_controller.url_helpers" do |app|
ActionController::Base.extend ::ActionController::Railtie::UrlHelpers.with(app.routes) ActionController.base_hook do
extend ::ActionController::Railtie::UrlHelpers.with(app.routes)
end
message = "ActionController::Routing::Routes is deprecated. " \ message = "ActionController::Routing::Routes is deprecated. " \
"Instead, use Rails.application.routes" "Instead, use Rails.application.routes"

View File

@@ -1,4 +1,3 @@
require 'active_support/json'
require 'action_dispatch/http/request' require 'action_dispatch/http/request'
module ActionDispatch module ActionDispatch

View File

@@ -1,5 +1,4 @@
require 'active_support/core_ext/hash/keys' require 'active_support/core_ext/hash/keys'
require 'rack/request'
module ActionDispatch module ActionDispatch
module Session module Session

View File

@@ -58,7 +58,7 @@ module ActionDispatch
if lazy_compare?(@klass) && lazy_compare?(middleware) if lazy_compare?(@klass) && lazy_compare?(middleware)
normalize(@klass) == normalize(middleware) normalize(@klass) == normalize(middleware)
else else
klass == ActiveSupport::Inflector.constantize(middleware.to_s) klass.name == middleware.to_s
end end
end end
end end

View File

@@ -1,3 +1,5 @@
require "active_support/core_ext/hash/except"
module ActionDispatch module ActionDispatch
module Routing module Routing
class Mapper class Mapper
@@ -85,7 +87,7 @@ module ActionDispatch
end end
def requirements def requirements
@requirements ||= returning(@options[:constraints] || {}) do |requirements| @requirements ||= (@options[:constraints] || {}).tap do |requirements|
requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints] requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
@options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) } @options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
end end

View File

@@ -41,6 +41,7 @@ module ActionView
autoload :Rendering autoload :Rendering
end end
autoload :Base
autoload :MissingTemplate, 'action_view/base' autoload :MissingTemplate, 'action_view/base'
autoload :Resolver, 'action_view/template/resolver' autoload :Resolver, 'action_view/template/resolver'
autoload :PathResolver, 'action_view/template/resolver' autoload :PathResolver, 'action_view/template/resolver'
@@ -56,6 +57,5 @@ module ActionView
end end
require 'active_support/core_ext/string/output_safety' require 'active_support/core_ext/string/output_safety'
require 'action_view/base'
I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml" I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"

View File

@@ -177,6 +177,8 @@ module ActionView #:nodoc:
extend ActiveSupport::Memoizable extend ActiveSupport::Memoizable
ActionView.run_base_hooks(self)
attr_accessor :base_path, :assigns, :template_extension attr_accessor :base_path, :assigns, :template_extension
attr_internal :captures attr_internal :captures

View File

@@ -5,9 +5,11 @@ require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/kernel/reporting' require 'active_support/core_ext/kernel/reporting'
module ActionView module ActionView
class Base ActionView.base_hook do
@@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe } class ActionView::Base
cattr_accessor :field_error_proc @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe }
cattr_accessor :field_error_proc
end
end end
module Helpers module Helpers

View File

@@ -1211,8 +1211,10 @@ module ActionView
end end
end end
class Base ActionView.base_hook do
cattr_accessor :default_form_builder class ActionView::Base
@@default_form_builder = ::ActionView::Helpers::FormBuilder cattr_accessor :default_form_builder
@@default_form_builder = ::ActionView::Helpers::FormBuilder
end
end end
end end

View File

@@ -10,7 +10,9 @@ module ActionView
initializer "action_view.cache_asset_timestamps" do |app| initializer "action_view.cache_asset_timestamps" do |app|
unless app.config.cache_classes unless app.config.cache_classes
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false ActionView.base_hook do
ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
end
end end
end end
end end

View File

@@ -1,4 +1,5 @@
require 'action_controller/test_case' require 'action_controller/test_case'
require 'action_view'
module ActionView module ActionView
class Base class Base

View File

@@ -16,7 +16,6 @@ require 'test/unit'
require 'abstract_controller' require 'abstract_controller'
require 'action_controller' require 'action_controller'
require 'action_view' require 'action_view'
require 'action_view/base'
require 'action_dispatch' require 'action_dispatch'
require 'fixture_template' require 'fixture_template'
require 'active_support/dependencies' require 'active_support/dependencies'

View File

@@ -30,7 +30,6 @@ $:.unshift(activemodel_path) if File.directory?(activemodel_path) && !$:.include
require 'active_support' require 'active_support'
require 'active_model' require 'active_model'
require 'arel'
module ActiveRecord module ActiveRecord
extend ActiveSupport::Autoload extend ActiveSupport::Autoload
@@ -38,8 +37,8 @@ module ActiveRecord
eager_autoload do eager_autoload do
autoload :VERSION autoload :VERSION
autoload :ActiveRecordError, 'active_record/base' autoload :ActiveRecordError, 'active_record/errors'
autoload :ConnectionNotEstablished, 'active_record/base' autoload :ConnectionNotEstablished, 'active_record/errors'
autoload :Aggregations autoload :Aggregations
autoload :AssociationPreload autoload :AssociationPreload
@@ -106,12 +105,16 @@ module ActiveRecord
eager_autoload do eager_autoload do
autoload :AbstractAdapter autoload :AbstractAdapter
autoload :ConnectionManagement, "active_record/connection_adapters/abstract/connection_pool"
end end
end end
autoload :TestCase autoload :TestCase
autoload :TestFixtures, 'active_record/fixtures' autoload :TestFixtures, 'active_record/fixtures'
base_hook do
Arel::Table.engine = Arel::Sql::Engine.new(self)
end
end end
Arel::Table.engine = Arel::Sql::Engine.new(ActiveRecord::Base) I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'
I18n.load_path << File.dirname(__FILE__) + '/active_record/locale/en.yml'

View File

@@ -13,172 +13,10 @@ require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/string/behavior' require 'active_support/core_ext/string/behavior'
require 'active_support/core_ext/object/singleton_class' require 'active_support/core_ext/object/singleton_class'
require 'active_support/core_ext/module/delegation' require 'active_support/core_ext/module/delegation'
require 'arel'
require 'active_record/errors'
module ActiveRecord #:nodoc: module ActiveRecord #:nodoc:
# Generic Active Record exception class.
class ActiveRecordError < StandardError
end
# Raised when the single-table inheritance mechanism fails to locate the subclass
# (for example due to improper usage of column that +inheritance_column+ points to).
class SubclassNotFound < ActiveRecordError #:nodoc:
end
# Raised when an object assigned to an association has an incorrect type.
#
# class Ticket < ActiveRecord::Base
# has_many :patches
# end
#
# class Patch < ActiveRecord::Base
# belongs_to :ticket
# end
#
# # Comments are not patches, this assignment raises AssociationTypeMismatch.
# @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
class AssociationTypeMismatch < ActiveRecordError
end
# Raised when unserialized object's type mismatches one specified for serializable field.
class SerializationTypeMismatch < ActiveRecordError
end
# Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt> misses adapter field).
class AdapterNotSpecified < ActiveRecordError
end
# Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
class AdapterNotFound < ActiveRecordError
end
# Raised when connection to the database could not been established (for example when <tt>connection=</tt> is given a nil object).
class ConnectionNotEstablished < ActiveRecordError
end
# Raised when Active Record cannot find record by given id or set of ids.
class RecordNotFound < ActiveRecordError
end
# Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
# saved because record is invalid.
class RecordNotSaved < ActiveRecordError
end
# Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old).
class StatementInvalid < ActiveRecordError
end
# Raised when SQL statement is invalid and the application gets a blank result.
class ThrowResult < ActiveRecordError
end
# Parent class for all specific exceptions which wrap database driver exceptions
# provides access to the original exception also.
class WrappedDatabaseException < StatementInvalid
attr_reader :original_exception
def initialize(message, original_exception)
super(message)
@original_exception = original_exception
end
end
# Raised when a record cannot be inserted because it would violate a uniqueness constraint.
class RecordNotUnique < WrappedDatabaseException
end
# Raised when a record cannot be inserted or updated because it references a non-existent record.
class InvalidForeignKey < WrappedDatabaseException
end
# Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example, when using +find+ method)
# does not match number of expected variables.
#
# For example, in
#
# Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
#
# two placeholders are given but only one variable to fill them.
class PreparedStatementInvalid < ActiveRecordError
end
# Raised on attempt to save stale record. Record is stale when it's being saved in another query after
# instantiation, for example, when two users edit the same wiki page and one starts editing and saves
# the page before the other.
#
# Read more about optimistic locking in ActiveRecord::Locking module RDoc.
class StaleObjectError < ActiveRecordError
end
# Raised when association is being configured improperly or
# user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
class ConfigurationError < ActiveRecordError
end
# Raised on attempt to update record that is instantiated as read only.
class ReadOnlyRecord < ActiveRecordError
end
# ActiveRecord::Transactions::ClassMethods.transaction uses this exception
# to distinguish a deliberate rollback from other exceptional situations.
# Normally, raising an exception will cause the +transaction+ method to rollback
# the database transaction *and* pass on the exception. But if you raise an
# ActiveRecord::Rollback exception, then the database transaction will be rolled back,
# without passing on the exception.
#
# For example, you could do this in your controller to rollback a transaction:
#
# class BooksController < ActionController::Base
# def create
# Book.transaction do
# book = Book.new(params[:book])
# book.save!
# if today_is_friday?
# # The system must fail on Friday so that our support department
# # won't be out of job. We silently rollback this transaction
# # without telling the user.
# raise ActiveRecord::Rollback, "Call tech support!"
# end
# end
# # ActiveRecord::Rollback is the only exception that won't be passed on
# # by ActiveRecord::Base.transaction, so this line will still be reached
# # even on Friday.
# redirect_to root_url
# end
# end
class Rollback < ActiveRecordError
end
# Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
class DangerousAttributeError < ActiveRecordError
end
# Raised when unknown attributes are supplied via mass assignment.
class UnknownAttributeError < NoMethodError
end
# Raised when an error occurred while doing a mass assignment to an attribute through the
# <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
# offending attribute.
class AttributeAssignmentError < ActiveRecordError
attr_reader :exception, :attribute
def initialize(message, exception, attribute)
@exception = exception
@attribute = attribute
@message = message
end
end
# Raised when there are multiple errors while doing a mass assignment through the +attributes+
# method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
# objects, each corresponding to the error while assigning to an attribute.
class MultiparameterAssignmentErrors < ActiveRecordError
attr_reader :errors
def initialize(errors)
@errors = errors
end
end
# Active Record objects don't specify their attributes directly, but rather infer them from the table definition with # Active Record objects don't specify their attributes directly, but rather infer them from the table definition with
# which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change # which they're linked. Adding, removing, and changing attributes and their type is done directly in the database. Any change
# is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain # is instantly reflected in the Active Record objects. The mapping that binds a given Active Record class to a certain
@@ -551,7 +389,7 @@ module ActiveRecord #:nodoc:
class << self # Class methods class << self # Class methods
def colorize_logging(*args) def colorize_logging(*args)
ActiveSupport::Deprecation.warn "ActiveRecord::Base.colorize_logging and " << ActiveSupport::Deprecation.warn "ActiveRecord::Base.colorize_logging and " <<
"config.active_record.colorize_logging are deprecated. Please use " << "config.active_record.colorize_logging are deprecated. Please use " <<
"Rails::LogSubscriber.colorize_logging or config.colorize_logging instead", caller "Rails::LogSubscriber.colorize_logging or config.colorize_logging instead", caller
end end
alias :colorize_logging= :colorize_logging alias :colorize_logging= :colorize_logging
@@ -2401,8 +2239,10 @@ module ActiveRecord #:nodoc:
include Aggregations, Transactions, Reflection, Serialization include Aggregations, Transactions, Reflection, Serialization
NilClass.add_whiner(self) if NilClass.respond_to?(:add_whiner)
end end
end end
# TODO: Remove this and make it work with LAZY flag # TODO: Remove this and make it work with LAZY flag
require 'active_record/connection_adapters/abstract_adapter' require 'active_record/connection_adapters/abstract_adapter'
ActiveRecord.run_base_hooks(ActiveRecord::Base)

View File

@@ -0,0 +1,165 @@
module ActiveRecord
# Generic Active Record exception class.
class ActiveRecordError < StandardError
end
# Raised when the single-table inheritance mechanism fails to locate the subclass
# (for example due to improper usage of column that +inheritance_column+ points to).
class SubclassNotFound < ActiveRecordError #:nodoc:
end
# Raised when an object assigned to an association has an incorrect type.
#
# class Ticket < ActiveRecord::Base
# has_many :patches
# end
#
# class Patch < ActiveRecord::Base
# belongs_to :ticket
# end
#
# # Comments are not patches, this assignment raises AssociationTypeMismatch.
# @ticket.patches << Comment.new(:content => "Please attach tests to your patch.")
class AssociationTypeMismatch < ActiveRecordError
end
# Raised when unserialized object's type mismatches one specified for serializable field.
class SerializationTypeMismatch < ActiveRecordError
end
# Raised when adapter not specified on connection (or configuration file <tt>config/database.yml</tt> misses adapter field).
class AdapterNotSpecified < ActiveRecordError
end
# Raised when Active Record cannot find database adapter specified in <tt>config/database.yml</tt> or programmatically.
class AdapterNotFound < ActiveRecordError
end
# Raised when connection to the database could not been established (for example when <tt>connection=</tt> is given a nil object).
class ConnectionNotEstablished < ActiveRecordError
end
# Raised when Active Record cannot find record by given id or set of ids.
class RecordNotFound < ActiveRecordError
end
# Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
# saved because record is invalid.
class RecordNotSaved < ActiveRecordError
end
# Raised when SQL statement cannot be executed by the database (for example, it's often the case for MySQL when Ruby driver used is too old).
class StatementInvalid < ActiveRecordError
end
# Raised when SQL statement is invalid and the application gets a blank result.
class ThrowResult < ActiveRecordError
end
# Parent class for all specific exceptions which wrap database driver exceptions
# provides access to the original exception also.
class WrappedDatabaseException < StatementInvalid
attr_reader :original_exception
def initialize(message, original_exception)
super(message)
@original_exception = original_exception
end
end
# Raised when a record cannot be inserted because it would violate a uniqueness constraint.
class RecordNotUnique < WrappedDatabaseException
end
# Raised when a record cannot be inserted or updated because it references a non-existent record.
class InvalidForeignKey < WrappedDatabaseException
end
# Raised when number of bind variables in statement given to <tt>:condition</tt> key (for example, when using +find+ method)
# does not match number of expected variables.
#
# For example, in
#
# Location.find :all, :conditions => ["lat = ? AND lng = ?", 53.7362]
#
# two placeholders are given but only one variable to fill them.
class PreparedStatementInvalid < ActiveRecordError
end
# Raised on attempt to save stale record. Record is stale when it's being saved in another query after
# instantiation, for example, when two users edit the same wiki page and one starts editing and saves
# the page before the other.
#
# Read more about optimistic locking in ActiveRecord::Locking module RDoc.
class StaleObjectError < ActiveRecordError
end
# Raised when association is being configured improperly or
# user tries to use offset and limit together with has_many or has_and_belongs_to_many associations.
class ConfigurationError < ActiveRecordError
end
# Raised on attempt to update record that is instantiated as read only.
class ReadOnlyRecord < ActiveRecordError
end
# ActiveRecord::Transactions::ClassMethods.transaction uses this exception
# to distinguish a deliberate rollback from other exceptional situations.
# Normally, raising an exception will cause the +transaction+ method to rollback
# the database transaction *and* pass on the exception. But if you raise an
# ActiveRecord::Rollback exception, then the database transaction will be rolled back,
# without passing on the exception.
#
# For example, you could do this in your controller to rollback a transaction:
#
# class BooksController < ActionController::Base
# def create
# Book.transaction do
# book = Book.new(params[:book])
# book.save!
# if today_is_friday?
# # The system must fail on Friday so that our support department
# # won't be out of job. We silently rollback this transaction
# # without telling the user.
# raise ActiveRecord::Rollback, "Call tech support!"
# end
# end
# # ActiveRecord::Rollback is the only exception that won't be passed on
# # by ActiveRecord::Base.transaction, so this line will still be reached
# # even on Friday.
# redirect_to root_url
# end
# end
class Rollback < ActiveRecordError
end
# Raised when attribute has a name reserved by Active Record (when attribute has name of one of Active Record instance methods).
class DangerousAttributeError < ActiveRecordError
end
# Raised when unknown attributes are supplied via mass assignment.
class UnknownAttributeError < NoMethodError
end
# Raised when an error occurred while doing a mass assignment to an attribute through the
# <tt>attributes=</tt> method. The exception has an +attribute+ property that is the name of the
# offending attribute.
class AttributeAssignmentError < ActiveRecordError
attr_reader :exception, :attribute
def initialize(message, exception, attribute)
@exception = exception
@attribute = attribute
@message = message
end
end
# Raised when there are multiple errors while doing a mass assignment through the +attributes+
# method. The exception has an +errors+ property that contains an array of AttributeAssignmentError
# objects, each corresponding to the error while assigning to an attribute.
class MultiparameterAssignmentErrors < ActiveRecordError
attr_reader :errors
def initialize(errors)
@errors = errors
end
end
end

View File

@@ -24,31 +24,39 @@ module ActiveRecord
log_subscriber ActiveRecord::Railties::LogSubscriber.new log_subscriber ActiveRecord::Railties::LogSubscriber.new
initializer "active_record.initialize_timezone" do initializer "active_record.initialize_timezone" do
ActiveRecord::Base.time_zone_aware_attributes = true ActiveRecord.base_hook do
ActiveRecord::Base.default_timezone = :utc self.time_zone_aware_attributes = true
self.default_timezone = :utc
end
end end
initializer "active_record.logger" do initializer "active_record.logger" do
ActiveRecord::Base.logger ||= ::Rails.logger ActiveRecord.base_hook { self.logger ||= ::Rails.logger }
end end
initializer "active_record.set_configs" do |app| initializer "active_record.set_configs" do |app|
app.config.active_record.each do |k,v| ActiveRecord.base_hook do
ActiveRecord::Base.send "#{k}=", v app.config.active_record.each do |k,v|
send "#{k}=", v
end
end end
end end
# This sets the database configuration from Configuration#database_configuration # This sets the database configuration from Configuration#database_configuration
# and then establishes the connection. # and then establishes the connection.
initializer "active_record.initialize_database" do |app| initializer "active_record.initialize_database" do |app|
ActiveRecord::Base.configurations = app.config.database_configuration ActiveRecord.base_hook do
ActiveRecord::Base.establish_connection self.configurations = app.config.database_configuration
establish_connection
end
end end
# Expose database runtime to controller for logging. # Expose database runtime to controller for logging.
initializer "active_record.log_runtime" do |app| initializer "active_record.log_runtime" do |app|
require "active_record/railties/controller_runtime" require "active_record/railties/controller_runtime"
ActionController::Base.send :include, ActiveRecord::Railties::ControllerRuntime ActionController.base_hook do
include ActiveRecord::Railties::ControllerRuntime
end
end end
# Setup database middleware after initializers have run # Setup database middleware after initializers have run
@@ -64,18 +72,22 @@ module ActiveRecord
end end
initializer "active_record.load_observers" do initializer "active_record.load_observers" do
ActiveRecord::Base.instantiate_observers ActiveRecord.base_hook { instantiate_observers }
ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do ActiveRecord.base_hook do
ActiveRecord::Base.instantiate_observers ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do
ActiveRecord::Base.instantiate_observers
end
end end
end end
initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app| initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
unless app.config.cache_classes ActiveRecord.base_hook do
ActionDispatch::Callbacks.after do unless app.config.cache_classes
ActiveRecord::Base.reset_subclasses ActionDispatch::Callbacks.after do
ActiveRecord::Base.clear_reloadable_connections! ActiveRecord::Base.reset_subclasses
ActiveRecord::Base.clear_reloadable_connections!
end
end end
end end
end end

View File

@@ -9,6 +9,7 @@ require 'test/unit'
require 'stringio' require 'stringio'
require 'active_record' require 'active_record'
require 'active_support/dependencies'
require 'connection' require 'connection'
begin begin

View File

@@ -53,6 +53,7 @@ module ActiveSupport
autoload :Deprecation autoload :Deprecation
autoload :Gzip autoload :Gzip
autoload :Inflector autoload :Inflector
autoload :JSON
autoload :Memoizable autoload :Memoizable
autoload :MessageEncryptor autoload :MessageEncryptor
autoload :MessageVerifier autoload :MessageVerifier
@@ -70,3 +71,5 @@ module ActiveSupport
autoload :SafeBuffer, "active_support/core_ext/string/output_safety" autoload :SafeBuffer, "active_support/core_ext/string/output_safety"
autoload :TestCase autoload :TestCase
end end
autoload :I18n, "active_support/i18n"

View File

@@ -1,4 +1,3 @@
require 'active_support' require 'active_support'
require 'active_support/i18n'
require 'active_support/time' require 'active_support/time'
require 'active_support/core_ext' require 'active_support/core_ext'

View File

@@ -1,7 +1,6 @@
require 'active_support/core_ext/hash/keys' require 'active_support/core_ext/hash/keys'
require 'active_support/core_ext/hash/reverse_merge' require 'active_support/core_ext/hash/reverse_merge'
require 'active_support/inflector' require 'active_support/inflector'
require 'active_support/i18n'
class Array class Array
# Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options: # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options:

View File

@@ -1,91 +1 @@
=begin require 'i18n/core_ext/string/interpolate'
heavily based on Masao Mutoh's gettext String interpolation extension
http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb
Copyright (C) 2005-2010 Masao Mutoh
You may redistribute it and/or modify it under the same license terms as Ruby.
=end
if RUBY_VERSION < '1.9' && !"".respond_to?(:interpolate_without_ruby_19_syntax)
# KeyError is raised by String#% when the string contains a named placeholder
# that is not contained in the given arguments hash. Ruby 1.9 includes and
# raises this exception natively. We define it to mimic Ruby 1.9's behaviour
# in Ruby 1.8.x
class KeyError < IndexError
def initialize(message = nil)
super(message || "key not found")
end
end unless defined?(KeyError)
# Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError.
#
# String#% method which accept "named argument". The translator can know
# the meaning of the msgids using "named argument" instead of %s/%d style.
class String
alias :interpolate_without_ruby_19_syntax :% # :nodoc:
INTERPOLATION_PATTERN = Regexp.union(
/%%/,
/%\{(\w+)\}/, # matches placeholders like "%{foo}"
/%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d"
)
# % uses self (i.e. the String) as a format specification and returns the
# result of applying it to the given arguments. In other words it interpolates
# the given arguments to the string according to the formats the string
# defines.
#
# There are three ways to use it:
#
# * Using a single argument or Array of arguments.
#
# This is the default behaviour of the String class. See Kernel#sprintf for
# more details about the format string.
#
# Example:
#
# "%d %s" % [1, "message"]
# # => "1 message"
#
# * Using a Hash as an argument and unformatted, named placeholders.
#
# When you pass a Hash as an argument and specify placeholders with %{foo}
# it will interpret the hash values as named arguments.
#
# Example:
#
# "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"}
# # => "Masao Mutoh"
#
# * Using a Hash as an argument and formatted, named placeholders.
#
# When you pass a Hash as an argument and specify placeholders with %<foo>d
# it will interpret the hash values as named arguments and format the value
# according to the formatting instruction appended to the closing >.
#
# Example:
#
# "%<integer>d, %<float>.1f" % { :integer => 10, :float => 43.4 }
# # => "10, 43.3"
def %(args)
if args.kind_of?(Hash)
dup.gsub(INTERPOLATION_PATTERN) do |match|
if match == '%%'
'%'
else
key = ($1 || $2).to_sym
raise KeyError unless args.has_key?(key)
$3 ? sprintf("%#{$3}", args[key]) : args[key]
end
end
elsif self =~ INTERPOLATION_PATTERN
raise ArgumentError.new('one hash required')
else
result = gsub(/%([{<])/, '%%\1')
result.send :'interpolate_without_ruby_19_syntax', args
end
end
end
end

View File

@@ -1,7 +1,12 @@
require "active_support/inflector/methods" require "active_support/inflector/methods"
require "active_support/lazy_load_hooks"
module ActiveSupport module ActiveSupport
module Autoload module Autoload
def self.extended(base)
base.extend(LazyLoadHooks)
end
@@autoloads = {} @@autoloads = {}
@@under_path = nil @@under_path = nil
@@at_path = nil @@at_path = nil

View File

@@ -1,2 +1,3 @@
require 'i18n' require 'i18n'
I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
ActiveSupport.run_base_hooks(:i18n)

View File

@@ -0,0 +1,25 @@
module ActiveSupport
module LazyLoadHooks
def _setup_base_hooks
@base_hooks ||= Hash.new {|h,k| h[k] = [] }
@base ||= {}
end
def base_hook(name = nil, &block)
_setup_base_hooks
if base = @base[name]
base.instance_eval(&block)
else
@base_hooks[name] << block
end
end
def run_base_hooks(base, name = nil)
_setup_base_hooks
@base_hooks[name].each { |hook| base.instance_eval(&hook) } if @base_hooks
@base[name] = base
end
end
end

View File

@@ -37,10 +37,12 @@ module I18n
config.i18n.load_path = [] config.i18n.load_path = []
initializer "i18n.initialize" do initializer "i18n.initialize" do
require 'active_support/i18n' ActiveSupport.base_hook(:i18n) do
ActionDispatch::Callbacks.to_prepare do
I18n.reload! I18n.reload!
ActionDispatch::Callbacks.to_prepare do
I18n.reload!
end
end end
end end

View File

@@ -25,17 +25,16 @@
# By default it is on in development and test modes, and it is off in production # By default it is on in development and test modes, and it is off in production
# mode. # mode.
class NilClass class NilClass
WHINERS = [::Array]
WHINERS << ::ActiveRecord::Base if defined? ::ActiveRecord::Base
METHOD_CLASS_MAP = Hash.new METHOD_CLASS_MAP = Hash.new
WHINERS.each do |klass| def self.add_whiner(klass)
methods = klass.public_instance_methods - public_instance_methods methods = klass.public_instance_methods - public_instance_methods
class_name = klass.name class_name = klass.name
methods.each { |method| METHOD_CLASS_MAP[method.to_sym] = class_name } methods.each { |method| METHOD_CLASS_MAP[method.to_sym] = class_name }
end end
add_whiner ::Array
# Raises a RuntimeError when you attempt to call +id+ on +nil+. # Raises a RuntimeError when you attempt to call +id+ on +nil+.
def id def id
raise RuntimeError, "Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id", caller raise RuntimeError, "Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id", caller

View File

@@ -9,6 +9,8 @@ end
require 'abstract_unit' require 'abstract_unit'
require 'active_support/whiny_nil' require 'active_support/whiny_nil'
NilClass.add_whiner ::ActiveRecord::Base
class WhinyNilTest < Test::Unit::TestCase class WhinyNilTest < Test::Unit::TestCase
def test_unchanged def test_unchanged
nil.method_thats_not_in_whiners nil.method_thats_not_in_whiners

View File

@@ -27,7 +27,7 @@ module Rails
routes.clear! routes.clear!
paths.each { |path| load(path) } paths.each { |path| load(path) }
routes.finalize! ActionController.base_hook { routes.finalize! }
nil nil
ensure ensure

View File

@@ -2,4 +2,6 @@ def helper
@helper ||= ApplicationController.helpers @helper ||= ApplicationController.helpers
end end
@controller = ApplicationController.new def controller
@controller ||= ApplicationController.new
end

View File

@@ -97,8 +97,8 @@ module Rails
initializer :add_view_paths do initializer :add_view_paths do
views = paths.app.views.to_a views = paths.app.views.to_a
ActionController::Base.prepend_view_path(views) if defined?(ActionController::Base) ActionController.base_hook { prepend_view_path(views) } if defined?(ActionController)
ActionMailer::Base.prepend_view_path(views) if defined?(ActionMailer::Base) ActionMailer.base_hook { prepend_view_path(views) } if defined?(ActionMailer)
end end
initializer :add_metals do |app| initializer :add_metals do |app|