From 897164ddb70ed6f51b026e5c91f2bf3f7aa46ba6 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Mon, 2 Nov 2009 17:19:03 -0800 Subject: Conceptually unify instance & global initializers --- railties/lib/rails/application.rb | 45 ++++++++++----- railties/lib/rails/initializable.rb | 111 ++++++++++++++++-------------------- railties/lib/rails/plugin.rb | 99 ++++++++++++++++---------------- railties/test/initializable_test.rb | 72 +++++++++++++++++++---- 4 files changed, 192 insertions(+), 135 deletions(-) (limited to 'railties') diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 242aea1842..b43dcb0041 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -1,6 +1,6 @@ module Rails class Application - extend Initializable + include Initializable class << self # Stub out App initialize @@ -8,6 +8,10 @@ module Rails new end + def new + @instance ||= super + end + def config @config ||= Configuration.new end @@ -27,23 +31,36 @@ module Rails config.root end - def routes - ActionController::Routing::Routes + def call(env) + new.call(env) end + end - def middleware - config.middleware - end + def initialize + run_initializers + end - def call(env) - @app ||= middleware.build(routes) - @app.call(env) - end + def config + self.class.config + end - def new - run_initializers - self - end + alias configuration config + + def plugin_loader + self.class.plugin_loader + end + + def middleware + config.middleware + end + + def routes + ActionController::Routing::Routes + end + + def call(env) + @app ||= middleware.build(routes) + @app.call(env) end initializer :initialize_rails do diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index c491d5e012..93349454dd 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -1,93 +1,82 @@ module Rails module Initializable - def self.included(klass) - klass.instance_eval do - extend Rails::Initializable - extend Rails::Initializable::ClassMethodsWhenIncluded - include Rails::Initializable::InstanceMethodsWhenIncluded - end + def self.included(base) + base.extend ClassMethods end - def self.extended(klass) - klass.extend Initializer + Initializer = Struct.new(:name, :before, :after, :global, :block) do + alias global? global end class Collection < Array - def initialize(klasses) - klasses.each do |klass| - (klass.added_initializers || []).each do |initializer| - index = if initializer.before - index_for(initializer.before) - elsif initializer.after - index_for(initializer.after) + 1 - else - length - end - - insert(index, initializer) + def initialize(initializers = []) + super() + initializers.each do |initializer| + if initializer.before + index = index_for(initializer.before) + elsif initializer.after + index = index_for(initializer.after) + 1 + else + index = length end + insert(index || -1, initializer) end end - def index_for(name) - inst = find {|i| i.name == name } - inst && index(inst) + def +(other) + Collection.new(to_a + other.to_a) end + def index_for(name) + initializer = find { |i| i.name == name } + initializer && index(initializer) + end end - attr_reader :added_initializers - - # When you include Rails::Initializable, this method will be on instances - # of the class included into. When you extend it, it will be on the - # class or module itself. - # - # The #initializers method is set up to return the right list of - # initializers for the context in question. - def run_initializers - return if @_initialized - - initializers.each {|initializer| instance_eval(&initializer.block) } - - @_initialized = true + def run_initializers(*args) + return if @ran + self.class.initializers_for(:instance).each do |initializer| + instance_exec(*args, &initializer.block) + end + @ran = true end - module Initializer - Initializer = Struct.new(:name, :before, :after, :block, :global) - - def all_initializers - klasses = ancestors.select {|klass| klass.is_a?(Initializable) }.reverse - initializers = Collection.new(klasses) + module ClassMethods + def initializers + @initializers ||= [] end - alias initializers all_initializers - - def initializer(name, options = {}, &block) - @added_initializers ||= [] - @added_initializers << - Initializer.new(name, options[:before], options[:after], block, options[:global]) + def initializers_for(scope = :global) + initializers = Collection.new + ancestors.reverse_each do |klass| + next unless klass.respond_to?(:initializers) + initializers = initializers + klass.initializers.select { |i| + (scope == :global) == !!i.global? + } + end + initializers end - end - module ClassMethodsWhenIncluded - def initializers - all_initializers.select {|i| i.global == true } + def initializer(name, opts = {}, &blk) + @initializers ||= [] + @initializers << Initializer.new(name, opts[:before], opts[:after], opts[:global], blk) end - end - - module InstanceMethodsWhenIncluded - def initializers - self.class.all_initializers.reject {|i| i.global == true } + def run_initializers(*args) + return if @ran + initializers_for(:global).each do |initializer| + instance_exec(*args, &initializer.block) + end + @ran = true end end end - extend Initializable + include Initializable # Check for valid Ruby version (1.8.2 or 1.8.4 or higher). This is done in an # external file, so we can use it from the `rails` program as well without duplication. - initializer :check_ruby_version do + initializer :check_ruby_version, :global => true do require 'rails/ruby_version_check' end @@ -97,7 +86,7 @@ module Rails # on ActionController::Base. # # For Ruby 1.9, UTF-8 is the default internal and external encoding. - initializer :initialize_encoding do + initializer :initialize_encoding, :global => true do if RUBY_VERSION < '1.9' $KCODE='u' else diff --git a/railties/lib/rails/plugin.rb b/railties/lib/rails/plugin.rb index 1c0af6411a..090ec6e4cb 100644 --- a/railties/lib/rails/plugin.rb +++ b/railties/lib/rails/plugin.rb @@ -18,6 +18,7 @@ module Rails # plugin.about["url"] # => "http://interblah.net" class Plugin include Comparable + include Initializable attr_reader :directory, :name @@ -99,67 +100,69 @@ module Rails def locale_files Dir[ File.join(locale_path, '*.{rb,yml}') ] end - - private - def load_about_information - about_yml_path = File.join(@directory, "about.yml") - parsed_yml = File.exist?(about_yml_path) ? YAML.load(File.read(about_yml_path)) : {} - parsed_yml || {} - rescue Exception - {} - end + private + def load_about_information + about_yml_path = File.join(@directory, "about.yml") + parsed_yml = File.exist?(about_yml_path) ? YAML.load(File.read(about_yml_path)) : {} + parsed_yml || {} + rescue Exception + {} + end - def report_nonexistant_or_empty_plugin! - raise LoadError, "Can not find the plugin named: #{name}" - end + def report_nonexistant_or_empty_plugin! + raise LoadError, "Can not find the plugin named: #{name}" + end - - def app_paths - [ File.join(directory, 'app', 'models'), File.join(directory, 'app', 'helpers'), controller_path, metal_path ] - end - - def lib_path - File.join(directory, 'lib') - end + def app_paths + [ File.join(directory, 'app', 'models'), File.join(directory, 'app', 'helpers'), controller_path, metal_path ] + end - def classic_init_path - File.join(directory, 'init.rb') - end + def lib_path + File.join(directory, 'lib') + end - def gem_init_path - File.join(directory, 'rails', 'init.rb') - end + def classic_init_path + File.join(directory, 'init.rb') + end - def init_path - File.file?(gem_init_path) ? gem_init_path : classic_init_path - end + def gem_init_path + File.join(directory, 'rails', 'init.rb') + end + def init_path + File.file?(gem_init_path) ? gem_init_path : classic_init_path + end - def has_app_directory? - File.directory?(File.join(directory, 'app')) - end + def has_app_directory? + File.directory?(File.join(directory, 'app')) + end - def has_lib_directory? - File.directory?(lib_path) - end + def has_lib_directory? + File.directory?(lib_path) + end - def has_init_file? - File.file?(init_path) - end + def has_init_file? + File.file?(init_path) + end + def evaluate_init_rb(initializer) + if has_init_file? + require 'active_support/core_ext/kernel/reporting' + silence_warnings do + # Allow plugins to reference the current configuration object + config = initializer.configuration - def evaluate_init_rb(initializer) - if has_init_file? - require 'active_support/core_ext/kernel/reporting' - silence_warnings do - # Allow plugins to reference the current configuration object - config = initializer.configuration - - eval(IO.read(init_path), binding, init_path) - end + eval(IO.read(init_path), binding, init_path) end - end + end + end + + class Vendored < Plugin + initializer :init_rb do |application| + evaluate_init_rb(application) + end + end end # This Plugin subclass represents a Gem plugin. Although RubyGems has already diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb index f7237e69cc..463fdc03c0 100644 --- a/railties/test/initializable_test.rb +++ b/railties/test/initializable_test.rb @@ -4,59 +4,59 @@ require 'rails/initializable' module InitializableTests class Foo - extend Rails::Initializable + include Rails::Initializable class << self attr_accessor :foo, :bar end - initializer :omg do + initializer :omg, :global => true do @foo ||= 0 @foo += 1 end end class Bar < Foo - initializer :bar do + initializer :bar, :global => true do @bar ||= 0 @bar += 1 end end module Word - extend Rails::Initializable + include Rails::Initializable - initializer :word do + initializer :word, :global => true do $word = "bird" end end class Parent - extend Rails::Initializable + include Rails::Initializable - initializer :one do + initializer :one, :global => true do $arr << 1 end - initializer :two do + initializer :two, :global => true do $arr << 2 end end class Child < Parent - extend Rails::Initializable + include Rails::Initializable - initializer :three, :before => :one do + initializer :three, :before => :one, :global => true do $arr << 3 end - initializer :four, :after => :one do + initializer :four, :after => :one, :global => true do $arr << 4 end end class Parent - initializer :five, :before => :one do + initializer :five, :before => :one, :global => true do $arr << 5 end end @@ -81,6 +81,38 @@ module InitializableTests end end + class WithArgs + include Rails::Initializable + + initializer :foo do |arg| + $with_arg = arg + end + end + + class OverriddenInitializer + class MoreInitializers + include Rails::Initializable + + initializer :startup, :before => :last do + $arr << 2 + end + end + + include Rails::Initializable + + initializer :first do + $arr << 1 + end + + initializer :last do + $arr << 3 + end + + def self.initializers + super + MoreInitializers.initializers + end + end + class Basic < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation @@ -140,4 +172,20 @@ module InitializableTests assert_equal [3, 4], $arr end end + + class WithArgsTest < ActiveSupport::TestCase + test "running initializers with args" do + $with_arg = nil + WithArgs.new.run_initializers('foo') + assert_equal 'foo', $with_arg + end + end + + class OverriddenInitializerTest < ActiveSupport::TestCase + test "merges in the initializers from the parent in the right order" do + $arr = [] + OverriddenInitializer.new.run_initializers + assert_equal [1, 2, 3], $arr + end + end end \ No newline at end of file -- cgit v1.2.3