[前][次][番号順一覧][スレッド一覧]

ruby-changes:60217

From: Jeremy <ko1@a...>
Date: Fri, 28 Feb 2020 04:03:29 +0900 (JST)
Subject: [ruby-changes:60217] 3556a834a2 (master): Make Module#include affect the iclasses of the module

https://git.ruby-lang.org/ruby.git/commit/?id=3556a834a2

From 3556a834a2847e52162d1d3302d4c64390df1694 Mon Sep 17 00:00:00 2001
From: Jeremy Evans <code@j...>
Date: Mon, 6 Jan 2020 16:41:03 -0800
Subject: Make Module#include affect the iclasses of the module

When calling Module#include, if the receiver is a module,
walk the subclasses list and include the argument module in each
iclass.

This does not affect Module#prepend, as fixing that is significantly
more involved.

Fixes [Bug #9573]

diff --git a/class.c b/class.c
index 98b2a1d..ae1a9c0 100644
--- a/class.c
+++ b/class.c
@@ -883,6 +883,26 @@ rb_include_module(VALUE klass, VALUE module) https://github.com/ruby/ruby/blob/trunk/class.c#L883
     changed = include_modules_at(klass, RCLASS_ORIGIN(klass), module, TRUE);
     if (changed < 0)
 	rb_raise(rb_eArgError, "cyclic include detected");
+
+    if (RB_TYPE_P(klass, T_MODULE)) {
+        rb_subclass_entry_t *iclass = RCLASS_EXT(klass)->subclasses;
+        int do_include = 1;
+        while (iclass) {
+            VALUE check_class = iclass->klass;
+            while (check_class) {
+                if (RB_TYPE_P(check_class, T_ICLASS) &&
+                        (RBASIC(check_class)->klass == module)) {
+                    do_include = 0;
+                }
+                check_class = RCLASS_SUPER(check_class);
+            }
+
+            if (do_include) {
+                include_modules_at(iclass->klass, RCLASS_ORIGIN(iclass->klass), module, TRUE);
+            }
+            iclass = iclass->next;
+        }
+    }
 }
 
 static enum rb_id_table_iterator_result
diff --git a/test/ruby/test_module.rb b/test/ruby/test_module.rb
index 5194a56..cafb0cd 100644
--- a/test/ruby/test_module.rb
+++ b/test/ruby/test_module.rb
@@ -473,6 +473,57 @@ class TestModule < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_module.rb#L473
     assert_raise(ArgumentError) { Module.new { include } }
   end
 
+  def test_include_into_module_already_included
+    c = Class.new{def foo; [:c] end}
+    modules = lambda do ||
+      sub = Class.new(c){def foo; [:sc] + super end}
+      [
+        Module.new{def foo; [:m1] + super end},
+        Module.new{def foo; [:m2] + super end},
+        Module.new{def foo; [:m3] + super end},
+        sub,
+        sub.new
+      ]
+    end
+
+    m1, m2, m3, sc, o = modules.call
+    assert_equal([:sc, :c], o.foo)
+    sc.include m1
+    assert_equal([:sc, :m1, :c], o.foo)
+    m1.include m2
+    assert_equal([:sc, :m1, :m2, :c], o.foo)
+    m2.include m3
+    assert_equal([:sc, :m1, :m2, :m3, :c], o.foo)
+
+    m1, m2, m3, sc, o = modules.call
+    sc.prepend m1
+    assert_equal([:m1, :sc, :c], o.foo)
+    m1.include m2
+    assert_equal([:m1, :m2, :sc, :c], o.foo)
+    m2.include m3
+    assert_equal([:m1, :m2, :m3, :sc, :c], o.foo)
+
+    m1, m2, m3, sc, o = modules.call
+    sc.include m2
+    assert_equal([:sc, :m2, :c], o.foo)
+    sc.prepend m1
+    assert_equal([:m1, :sc, :m2, :c], o.foo)
+    m1.include m2
+    assert_equal([:m1, :sc, :m2, :c], o.foo)
+    m1.include m3
+    assert_equal([:m1, :m3, :sc, :m2, :c], o.foo)
+
+    m1, m2, m3, sc, o = modules.call
+    sc.include m3
+    sc.include m2
+    assert_equal([:sc, :m2, :m3, :c], o.foo)
+    sc.prepend m1
+    assert_equal([:m1, :sc, :m2, :m3, :c], o.foo)
+    m1.include m2
+    m1.include m3
+    assert_equal([:m1, :sc, :m2, :m3, :c], o.foo)
+  end
+
   def test_included_modules
     assert_equal([], Mixin.included_modules)
     assert_equal([Mixin], User.included_modules)
-- 
cgit v0.10.2


--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]