Skip to content

Instantly share code, notes, and snippets.

@kaspth
Created December 27, 2022 14:31
Show Gist options
  • Save kaspth/6f74f670d689896c696743a69bca1719 to your computer and use it in GitHub Desktop.
Save kaspth/6f74f670d689896c696743a69bca1719 to your computer and use it in GitHub Desktop.
require "tsort"
module Rails
module Initializable
extend ActiveSupport::Concern
def run_initializers(group = :default, *arguments)
@ran ||= true.tap do
Initializer.tsort(initializers).each { instance_exec(*arguments, &_1.block) if _1.in?(group) }
end
end
def initializers
self.class.ancestors.reverse.flat_map { _1.initializers if _1.respond_to?(:initializers) }
end
class_methods do
def initializer(name, group: :default, before: nil, after: nil, &block)
raise ArgumentError, "A block must be passed when defining an initializer" unless block
after ||= initializers.last&.name unless initializers.any? { _1.name == before }
initializers << Initializer.new(name:, block:, group:, before:, after:)
end
def initializers = @initializers ||= []
end
class Initializer < Struct.new(:name, :block, :group, :before, :after, keyword_init: true)
def initialize(name:, block:, group: :default, before: nil, after: nil) = super
def self.tsort(initializers)
TSort.tsort initializers.to_enum,
->(initializer, &block) { initializer.pluck(initializers).each(&block) }
end
def pluck(initializers)
initializers.select { _1.before == name || after == _1.name }
end
# Alternative to pluck above to try to name, but the original inner select makes no sense to me.
def around?(other) = self < other || self > other
def >(other) = other.before == name
def <(other) = after == other.name
def in?(group)
@group == group || @group == :all
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment