From 6f9810e779b6fcb5d931665539fc95a5a29ebe93 Mon Sep 17 00:00:00 2001 From: Paul Rosania Date: Thu, 28 Jan 2010 16:50:24 -0500 Subject: [PATCH] tsort-based dependency resolution for initializers --- activerecord/lib/active_record/railtie.rb | 2 +- railties/lib/rails/initializable.rb | 29 +++++-------- .../application/initializers/frameworks_test.rb | 2 +- railties/test/initializable_test.rb | 43 +++++++++++++++++++- 4 files changed, 55 insertions(+), 21 deletions(-) diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 5e4ce34..e061ca5 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -47,7 +47,7 @@ module ActiveRecord end # Setup database middleware after initializers have run - initializer "active_record.initialize_database_middleware" do |app| + initializer "active_record.initialize_database_middleware", :after => "action_controller.set_configs" do |app| middleware = app.config.middleware if middleware.include?("ActiveRecord::SessionStore") middleware.insert_before "ActiveRecord::SessionStore", ActiveRecord::ConnectionAdapters::ConnectionManagement diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index d91f678..730fd0f 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -1,3 +1,5 @@ +require 'tsort' + module Rails module Initializable def self.included(base) @@ -30,29 +32,21 @@ module Rails end class Collection < Array + include TSort + + alias :tsort_each_node :each + def tsort_each_child(initializer, &block) + select { |i| i.before == initializer.name || i.name == initializer.after }.each(&block) + end + def initialize(initializers = []) - super() - initializers.each do |initializer| - if initializer.before - index = index_for(initializer.before) - elsif initializer.after - index = index_for(initializer.after) - index += 1 if index - else - index = length - end - insert(index || -1, initializer) - end + super(initializers) + replace(tsort) end 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 def run_initializers(*args) @@ -87,6 +81,7 @@ module Rails def initializer(name, opts = {}, &blk) raise ArgumentError, "A block must be passed when defining an initializer" unless blk + opts[:after] ||= initializers.last.name unless initializers.empty? || initializers.find { |i| i.name == opts[:before] } initializers << Initializer.new(name, nil, opts, &blk) end diff --git a/railties/test/application/initializers/frameworks_test.rb b/railties/test/application/initializers/frameworks_test.rb index ea05232..a84b882 100644 --- a/railties/test/application/initializers/frameworks_test.rb +++ b/railties/test/application/initializers/frameworks_test.rb @@ -1,7 +1,7 @@ require "isolation/abstract_unit" module ApplicationTests - class FrameworlsTest < Test::Unit::TestCase + class FrameworksTest < Test::Unit::TestCase include ActiveSupport::Testing::Isolation def setup diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb index 0c7378c..3bce63f 100644 --- a/railties/test/initializable_test.rb +++ b/railties/test/initializable_test.rb @@ -50,7 +50,7 @@ module InitializableTests $arr << 3 end - initializer :four, :after => :one do + initializer :four, :after => :one, :before => :two do $arr << 4 end end @@ -97,7 +97,7 @@ module InitializableTests $arr << 3 end - initializer :terminate, :after => :first do + initializer :terminate, :after => :first, :before => :startup do $arr << two end @@ -120,6 +120,39 @@ module InitializableTests super + MoreInitializers.new.initializers end end + + module Interdependent + class PluginA + include Rails::Initializable + + initializer "plugin_a.startup" do + $arr << 1 + end + + initializer "plugin_a.terminate" do + $arr << 4 + end + end + + class PluginB + include Rails::Initializable + + initializer "plugin_b.startup", :after => "plugin_a.startup" do + $arr << 2 + end + + initializer "plugin_b.terminate", :before => "plugin_a.terminate" do + $arr << 3 + end + end + + class Application + include Rails::Initializable + def self.initializers + PluginB.initializers + PluginA.initializers + end + end + end class Basic < ActiveSupport::TestCase include ActiveSupport::Testing::Isolation @@ -174,6 +207,12 @@ module InitializableTests Child.run_initializers assert_equal [5, 3, 1, 4, 2], $arr end + + test "handles dependencies introduced before all initializers are loaded" do + $arr = [] + Interdependent::Application.run_initializers + assert_equal [1, 2, 3, 4], $arr + end end class InstanceTest < ActiveSupport::TestCase -- 1.6.4.2