From 39d6f9e112f2320d8c2006ee3bcc160cfa761d0a Mon Sep 17 00:00:00 2001 From: wycats Date: Sun, 7 Mar 2010 06:24:30 -0800 Subject: 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 --- activesupport/lib/active_support.rb | 3 + activesupport/lib/active_support/all.rb | 1 - .../active_support/core_ext/array/conversions.rb | 1 - .../core_ext/string/interpolation.rb | 92 +--------------------- .../lib/active_support/dependencies/autoload.rb | 5 ++ activesupport/lib/active_support/i18n.rb | 3 +- .../lib/active_support/lazy_load_hooks.rb | 25 ++++++ activesupport/lib/active_support/railtie.rb | 8 +- activesupport/lib/active_support/whiny_nil.rb | 7 +- activesupport/test/whiny_nil_test.rb | 2 + 10 files changed, 46 insertions(+), 101 deletions(-) create mode 100644 activesupport/lib/active_support/lazy_load_hooks.rb (limited to 'activesupport') diff --git a/activesupport/lib/active_support.rb b/activesupport/lib/active_support.rb index ae31d191c0..e34e46b4cf 100644 --- a/activesupport/lib/active_support.rb +++ b/activesupport/lib/active_support.rb @@ -53,6 +53,7 @@ module ActiveSupport autoload :Deprecation autoload :Gzip autoload :Inflector + autoload :JSON autoload :Memoizable autoload :MessageEncryptor autoload :MessageVerifier @@ -70,3 +71,5 @@ module ActiveSupport autoload :SafeBuffer, "active_support/core_ext/string/output_safety" autoload :TestCase end + +autoload :I18n, "active_support/i18n" diff --git a/activesupport/lib/active_support/all.rb b/activesupport/lib/active_support/all.rb index 64600575d9..f537818300 100644 --- a/activesupport/lib/active_support/all.rb +++ b/activesupport/lib/active_support/all.rb @@ -1,4 +1,3 @@ require 'active_support' -require 'active_support/i18n' require 'active_support/time' require 'active_support/core_ext' diff --git a/activesupport/lib/active_support/core_ext/array/conversions.rb b/activesupport/lib/active_support/core_ext/array/conversions.rb index 814567a5a6..2119322bfe 100644 --- a/activesupport/lib/active_support/core_ext/array/conversions.rb +++ b/activesupport/lib/active_support/core_ext/array/conversions.rb @@ -1,7 +1,6 @@ require 'active_support/core_ext/hash/keys' require 'active_support/core_ext/hash/reverse_merge' require 'active_support/inflector' -require 'active_support/i18n' class Array # Converts the array to a comma-separated sentence where the last element is joined by the connector word. Options: diff --git a/activesupport/lib/active_support/core_ext/string/interpolation.rb b/activesupport/lib/active_support/core_ext/string/interpolation.rb index 06d3505c60..932117cc10 100644 --- a/activesupport/lib/active_support/core_ext/string/interpolation.rb +++ b/activesupport/lib/active_support/core_ext/string/interpolation.rb @@ -1,91 +1 @@ -=begin - 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 "%.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 %d - # it will interpret the hash values as named arguments and format the value - # according to the formatting instruction appended to the closing >. - # - # Example: - # - # "%d, %.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 +require 'i18n/core_ext/string/interpolate' diff --git a/activesupport/lib/active_support/dependencies/autoload.rb b/activesupport/lib/active_support/dependencies/autoload.rb index 44edb89ad5..f669f4a77e 100644 --- a/activesupport/lib/active_support/dependencies/autoload.rb +++ b/activesupport/lib/active_support/dependencies/autoload.rb @@ -1,7 +1,12 @@ require "active_support/inflector/methods" +require "active_support/lazy_load_hooks" module ActiveSupport module Autoload + def self.extended(base) + base.extend(LazyLoadHooks) + end + @@autoloads = {} @@under_path = nil @@at_path = nil diff --git a/activesupport/lib/active_support/i18n.rb b/activesupport/lib/active_support/i18n.rb index 854c60eec1..034d7d8ddc 100644 --- a/activesupport/lib/active_support/i18n.rb +++ b/activesupport/lib/active_support/i18n.rb @@ -1,2 +1,3 @@ require 'i18n' -I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" \ No newline at end of file +I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml" +ActiveSupport.run_base_hooks(:i18n) \ No newline at end of file diff --git a/activesupport/lib/active_support/lazy_load_hooks.rb b/activesupport/lib/active_support/lazy_load_hooks.rb new file mode 100644 index 0000000000..36acfda524 --- /dev/null +++ b/activesupport/lib/active_support/lazy_load_hooks.rb @@ -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 \ No newline at end of file diff --git a/activesupport/lib/active_support/railtie.rb b/activesupport/lib/active_support/railtie.rb index 58d11585ba..d2c13e030d 100644 --- a/activesupport/lib/active_support/railtie.rb +++ b/activesupport/lib/active_support/railtie.rb @@ -37,10 +37,12 @@ module I18n config.i18n.load_path = [] initializer "i18n.initialize" do - require 'active_support/i18n' - - ActionDispatch::Callbacks.to_prepare do + ActiveSupport.base_hook(:i18n) do I18n.reload! + + ActionDispatch::Callbacks.to_prepare do + I18n.reload! + end end end diff --git a/activesupport/lib/active_support/whiny_nil.rb b/activesupport/lib/active_support/whiny_nil.rb index 0b68e936f6..91ddef2619 100644 --- a/activesupport/lib/active_support/whiny_nil.rb +++ b/activesupport/lib/active_support/whiny_nil.rb @@ -25,17 +25,16 @@ # By default it is on in development and test modes, and it is off in production # mode. class NilClass - WHINERS = [::Array] - WHINERS << ::ActiveRecord::Base if defined? ::ActiveRecord::Base - METHOD_CLASS_MAP = Hash.new - WHINERS.each do |klass| + def self.add_whiner(klass) methods = klass.public_instance_methods - public_instance_methods class_name = klass.name methods.each { |method| METHOD_CLASS_MAP[method.to_sym] = class_name } end + add_whiner ::Array + # Raises a RuntimeError when you attempt to call +id+ on +nil+. 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 diff --git a/activesupport/test/whiny_nil_test.rb b/activesupport/test/whiny_nil_test.rb index 1e4f8d854a..4b9f06dead 100644 --- a/activesupport/test/whiny_nil_test.rb +++ b/activesupport/test/whiny_nil_test.rb @@ -9,6 +9,8 @@ end require 'abstract_unit' require 'active_support/whiny_nil' +NilClass.add_whiner ::ActiveRecord::Base + class WhinyNilTest < Test::Unit::TestCase def test_unchanged nil.method_thats_not_in_whiners -- cgit v1.2.3