From 4174485317944b9d2716a4a67ebdb617a9392a0c Mon Sep 17 00:00:00 2001 From: Gannon McGibbon Date: Thu, 14 Nov 2024 12:46:05 -0600 Subject: [PATCH] Delegate missing Rails::Initializable::Collection methods to inner array Previously, Rails::Initializable::Collection extended Array, but now it doesn't. The inner structure has changed enough so that we need to implement addition methods, but we can keep every other subtractive method as-is. --- railties/lib/rails/initializable.rb | 15 +++++++- railties/test/initializable_test.rb | 59 +++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/initializable.rb b/railties/lib/rails/initializable.rb index 59270cdfb41f8..fdf6b18505317 100644 --- a/railties/lib/rails/initializable.rb +++ b/railties/lib/rails/initializable.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "tsort" +require "active_support/core_ext/module/delegation" module Rails module Initializable @@ -38,6 +39,8 @@ class Collection include Enumerable include TSort + delegate_missing_to :@collection + def initialize(initializers = nil) @order = Hash.new { |hash, key| hash[key] = Set.new } @resolve = Hash.new { |hash, key| hash[key] = Set.new } @@ -73,13 +76,23 @@ def <<(initializer) @order[initializer.before] << initializer.name if initializer.before @order[initializer.name] << initializer.after if initializer.after @resolve[initializer.name] << initializer + self end - def concat(initializers) + def push(*initializers) initializers.each(&method(:<<)) self end + alias_method(:append, :push) + + def concat(*initializer_collections) + initializer_collections.each do |initializers| + initializers.each(&method(:<<)) + end + self + end + def has?(name) @resolve.key?(name) end diff --git a/railties/test/initializable_test.rb b/railties/test/initializable_test.rb index b32124bccc665..048414bb4ebe9 100644 --- a/railties/test/initializable_test.rb +++ b/railties/test/initializable_test.rb @@ -277,4 +277,63 @@ class OverriddenInitializerTest < ActiveSupport::TestCase assert_equal [1, 2, 3, 4], $arr end end + + class CollectionTest < ActiveSupport::TestCase + test "delegates missing to collection array" do + initializable = Class.new do + include Rails::Initializable + end + + Array.public_instance_methods.each do |method_name| + assert( + initializable.initializers.respond_to?(method_name), + "Expected Initializable::Collection to respond to #{method_name}, but does not.", + ) + end + end + + test "concat" do + one = collection(:a, :b) + two = collection(:c, :d) + initializers = one.initializers.concat(two.initializers) + initializer_names = initializers.tsort_each.map(&:name) + + assert_equal [:a, :b, :c, :d], initializer_names + end + + test "push" do + one = collection(:a, :b, :c) + two = collection(:d) + initializers = one.initializers.push(two.initializers.first) + initializer_names = initializers.tsort_each.map(&:name) + + assert_equal [:a, :b, :c, :d], initializer_names + end + + test "append" do + one = collection(:a) + two = collection(:b, :c) + initializers = one.initializers.append(two.initializers.first) + initializer_names = initializers.tsort_each.map(&:name) + + assert_equal [:a, :b], initializer_names + end + + test "<<" do + one = collection(:a, :b) + two = collection(:c) + initializers = (one.initializers << two.initializers.first) + initializer_names = initializers.tsort_each.map(&:name) + + assert_equal [:a, :b, :c], initializer_names + end + + private + def collection(*names) + Class.new do + include Rails::Initializable + names.each { |name| initializer(name) { } } + end + end + end end