diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 06ec110af86f8..195af88cf583c 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -609,7 +609,7 @@ def ordered_railties # :nodoc: end def railties_initializers(current) # :nodoc: - initializers = [] + initializers = Initializable::Collection.new ordered_railties.reverse.flatten.each do |r| if r == self initializers += current diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index 45547540f1e2b..ec8f540cfaf56 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -9,23 +9,17 @@ def self.included(base) # :nodoc: end class Initializer - attr_reader :name, :block + attr_reader :name, :block, :before, :after - def initialize(name, context, options, &block) - options[:group] ||= :default - @name, @context, @options, @block = name, context, options, block - end - - def before - @options[:before] - end - - def after - @options[:after] + def initialize(name, context, before:, after:, group: nil, &block) + @group = group || :default + @before = before + @after = after + @name, @context, @block = name, context, block end def belongs_to?(group) - @options[:group] == group || @options[:group] == :all + @group == group || @group == :all end def run(*args) @@ -34,7 +28,7 @@ def run(*args) def bind(context) return self if @context - Initializer.new(@name, context, @options, &block) + Initializer.new(@name, context, before:, after:, group: @group, &block) end def context_class @@ -42,16 +36,54 @@ def context_class end end - class Collection < Array + class Collection + include Enumerable include TSort + def initialize(initializers = nil) + @order = Hash.new { |hash, key| hash[key] = Set.new } + @resolve = Hash.new { |hash, key| hash[key] = Set.new } + @collection = [] + concat(initializers) if initializers + end + + def empty? + @collection.empty? + end + + def to_a + @collection + end + + def last + @collection.last + end + + def each(&block) + @collection.each(&block) + end + alias :tsort_each_node :each def tsort_each_child(initializer, &block) - select { |i| i.before == initializer.name || i.name == initializer.after }.each(&block) + @order[initializer.name].each do |name| + @resolve[name].each(&block) + end end def +(other) - Collection.new(to_a + other.to_a) + dup.concat(other.to_a) + end + + def <<(initializer) + @collection << initializer + @order[initializer.before] << initializer.name if initializer.before + @order[initializer.name] << initializer.after if initializer.after + @resolve[initializer.name] << initializer + end + + def concat(initializers) + initializers.each(&method(:<<)) + self end end @@ -88,7 +120,7 @@ def initializers_for(binding) 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) + initializers << Initializer.new(name, nil, before: opts[:before], after: opts[:after], group: opts[:group], &blk) end end end