Skip to content

Commit

Permalink
Faster tsort
Browse files Browse the repository at this point in the history
Use a hash to speed up tsorting children.
  • Loading branch information
gmcgibbon committed Nov 13, 2024
1 parent 5b50d30 commit ee0c696
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 19 deletions.
2 changes: 1 addition & 1 deletion railties/lib/rails/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
68 changes: 50 additions & 18 deletions railties/lib/rails/initializable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -34,24 +28,62 @@ 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
@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

Expand Down Expand Up @@ -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
Expand Down

0 comments on commit ee0c696

Please sign in to comment.