* 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.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@38344 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
shugo 2012-12-12 09:35:50 +00:00
Родитель a773539d3a
Коммит 75bed271f0
3 изменённых файлов: 82 добавлений и 0 удалений

Просмотреть файл

@ -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.

29
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)

Просмотреть файл

@ -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)