diff --git a/ChangeLog b/ChangeLog index 716bd41dc7..52d0862dc9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,11 @@ +Mon Jul 1 12:56:41 2013 Shugo Maeda + + * eval.c (rb_using_module): activate refinements in the ancestors of + the argument module to support refinement inheritance by + Module#include. [ruby-core:55671] [Feature #8571] + + * test/ruby/test_refinement.rb: related test. + Mon Jul 1 12:02:39 2013 Tanaka Akira * bignum.c (rb_cstr_to_inum): Skip leading zeros. diff --git a/NEWS b/NEWS index 0906cfcac4..a3906f26a3 100644 --- a/NEWS +++ b/NEWS @@ -46,6 +46,11 @@ with all sufficient information, see the ChangeLog file. * pack/unpack (Array/String) * Q! and q! directives for long long type if platform has the type. +* toplevel + * extended methods: + * main.using activates refinements in the ancestors of the argument + module to support refinement inheritance by Module#include. + === Core classes compatibility issues (excluding feature bug fixes) * IO diff --git a/eval.c b/eval.c index 00bddb51ac..0bf8337175 100644 --- a/eval.c +++ b/eval.c @@ -1118,19 +1118,43 @@ using_refinement(VALUE klass, VALUE module, VALUE arg) return ST_CONTINUE; } -void -rb_using_module(NODE *cref, VALUE module) +static void +using_module_recursive(NODE *cref, VALUE klass) { ID id_refinements; - VALUE refinements; + VALUE super, module, refinements; - Check_Type(module, T_MODULE); + super = RCLASS_SUPER(klass); + if (super) { + using_module_recursive(cref, super); + } + switch (BUILTIN_TYPE(klass)) { + case T_MODULE: + module = klass; + break; + + case T_ICLASS: + module = RBASIC(klass)->klass; + break; + + default: + rb_raise(rb_eTypeError, "wrong argument type %s (expected Module)", + rb_obj_classname(klass)); + break; + } CONST_ID(id_refinements, "__refinements__"); refinements = rb_attr_get(module, id_refinements); if (NIL_P(refinements)) return; rb_hash_foreach(refinements, using_refinement, (VALUE) cref); } +void +rb_using_module(NODE *cref, VALUE module) +{ + Check_Type(module, T_MODULE); + using_module_recursive(cref, module); +} + VALUE rb_refinement_module_get_refined_class(VALUE module) { ID id_refined_class; diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb index 5c4a99b121..e812e62b59 100644 --- a/test/ruby/test_refinement.rb +++ b/test/ruby/test_refinement.rb @@ -875,6 +875,60 @@ class TestRefinement < Test::Unit::TestCase end end + class Bar + end + + module BarExt + refine Bar do + def x + return "BarExt#x" + end + end + end + + module FooBarExt + include FooExt + include BarExt + end + + module FooBarExtClient + using FooBarExt + + def self.invoke_x_on(foo) + return foo.x + end + end + + def test_module_inclusion + foo = Foo.new + assert_equal("FooExt#x", FooBarExtClient.invoke_x_on(foo)) + bar = Bar.new + assert_equal("BarExt#x", FooBarExtClient.invoke_x_on(bar)) + end + + module FooFoo2Ext + include FooExt + include FooExt2 + end + + module FooFoo2ExtClient + using FooFoo2Ext + + def self.invoke_x_on(foo) + return foo.x + end + + def self.invoke_y_on(foo) + return foo.y + end + end + + def test_module_inclusion2 + foo = Foo.new + assert_equal("FooExt2#x", FooFoo2ExtClient.invoke_x_on(foo)) + assert_equal("FooExt2#y Foo#y", FooFoo2ExtClient.invoke_y_on(foo)) + end + private def eval_using(mod, s)