summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--class.c29
-rw-r--r--test/ruby/test_refinement.rb45
3 files changed, 82 insertions, 0 deletions
diff --git a/ChangeLog b/ChangeLog
index 55104e3595..51af4f651e 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Wed Dec 12 18:30:29 2012 Shugo Maeda <shugo@ruby-lang.org>
+
+ * class.c (rb_prepend_module): move refined methods from the origin
+ of a class to the class, because refinements should have priority
+ over prepended modules.
+
+ * test/ruby/test_refinement.rb: related test.
+
Wed Dec 12 18:27:09 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
* time.c (zone_str): lookup or insert by using st_update() at once.
diff --git a/class.c b/class.c
index ca9bdef41e..cb85580d92 100644
--- a/class.c
+++ b/class.c
@@ -731,6 +731,33 @@ include_modules_at(VALUE klass, VALUE c, VALUE module)
return changed;
}
+static int
+move_refined_method(st_data_t key, st_data_t value, st_data_t data)
+{
+ rb_method_entry_t *me = (rb_method_entry_t *) value;
+ st_table *tbl = (st_table *) data;
+
+ if (me->def->type == VM_METHOD_TYPE_REFINED) {
+ if (me->def->body.orig_me) {
+ rb_method_entry_t *orig_me = me->def->body.orig_me, *new_me;
+ me->def->body.orig_me = NULL;
+ new_me = ALLOC(rb_method_entry_t);
+ *new_me = *me;
+ st_add_direct(tbl, key, (st_data_t) new_me);
+ *me = *orig_me;
+ xfree(orig_me);
+ return ST_CONTINUE;
+ }
+ else {
+ st_add_direct(tbl, key, (st_data_t) me);
+ return ST_DELETE;
+ }
+ }
+ else {
+ return ST_CONTINUE;
+ }
+}
+
void
rb_prepend_module(VALUE klass, VALUE module)
{
@@ -754,6 +781,8 @@ rb_prepend_module(VALUE klass, VALUE module)
RCLASS_ORIGIN(klass) = origin;
RCLASS_M_TBL(origin) = RCLASS_M_TBL(klass);
RCLASS_M_TBL(klass) = st_init_numtable();
+ st_foreach(RCLASS_M_TBL(origin), move_refined_method,
+ (st_data_t) RCLASS_M_TBL(klass));
}
changed = include_modules_at(klass, klass, module);
if (changed < 0)
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index 8b4a4fc4e9..fdaf7e8c46 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -755,6 +755,51 @@ class TestRefinement < Test::Unit::TestCase
PrependIntoRefinement::User.invoke_baz_on(x))
end
+ module PrependAfterRefine
+ class C
+ def foo
+ "original"
+ end
+ end
+
+ module M
+ refine C do
+ def foo
+ "refined"
+ end
+
+ def bar
+ "refined"
+ end
+ end
+ end
+
+ module Mixin
+ def foo
+ "mixin"
+ end
+
+ def bar
+ "mixin"
+ end
+ end
+
+ class C
+ prepend Mixin
+ end
+ end
+
+ def test_prepend_after_refine
+ x = eval_using(PrependAfterRefine::M,
+ "TestRefinement::PrependAfterRefine::C.new.foo")
+ assert_equal("refined", x)
+ assert_equal("mixin", TestRefinement::PrependAfterRefine::C.new.foo)
+ y = eval_using(PrependAfterRefine::M,
+ "TestRefinement::PrependAfterRefine::C.new.bar")
+ assert_equal("refined", y)
+ assert_equal("mixin", TestRefinement::PrependAfterRefine::C.new.bar)
+ end
+
private
def eval_using(mod, s)