summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog8
-rw-r--r--eval.c32
-rw-r--r--test/ruby/test_refinement.rb65
3 files changed, 96 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index 310c52734f..008afd53b9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+Wed Jun 12 23:27:03 2013 Shugo Maeda <shugo@ruby-lang.org>
+
+ * eval.c (mod_using): new method Module#using, which activates
+ refinements of the specified module only in the current class or
+ module definition. [ruby-core:55273] [Feature #8481]
+
+ * test/ruby/test_refinement.rb: related test.
+
Wed Jun 12 22:58:48 2013 Shugo Maeda <shugo@ruby-lang.org>
* safe.c (rb_set_safe_level, safe_setter): raise an ArgumentError
diff --git a/eval.c b/eval.c
index c0d64e8717..f38f1c250e 100644
--- a/eval.c
+++ b/eval.c
@@ -1226,6 +1226,34 @@ rb_mod_refine(VALUE module, VALUE klass)
return refinement;
}
+/*
+ * call-seq:
+ * using(module) -> self
+ *
+ * Import class refinements from <i>module</i> into the current class or
+ * module definition.
+ */
+
+static VALUE
+mod_using(VALUE self, VALUE module)
+{
+ NODE *cref = rb_vm_cref();
+ rb_control_frame_t *prev_cfp = previous_frame(GET_THREAD());
+
+ warn_refinements_once();
+ if (prev_frame_func()) {
+ rb_raise(rb_eRuntimeError,
+ "Module#using is not permitted in methods");
+ }
+ if (prev_cfp && prev_cfp->self != self) {
+ rb_raise(rb_eRuntimeError, "Module#using is not called on self");
+ }
+ Check_Type(module, T_MODULE);
+ rb_using_module(cref, module);
+ rb_clear_cache();
+ return self;
+}
+
void
rb_obj_call_init(VALUE obj, int argc, VALUE *argv)
{
@@ -1354,7 +1382,8 @@ top_using(VALUE self, VALUE module)
warn_refinements_once();
if (cref->nd_next || (prev_cfp && prev_cfp->me)) {
- rb_raise(rb_eRuntimeError, "using is permitted only at toplevel");
+ rb_raise(rb_eRuntimeError,
+ "main.using is permitted only at toplevel");
}
Check_Type(module, T_MODULE);
rb_using_module(cref, module);
@@ -1558,6 +1587,7 @@ Init_eval(void)
rb_define_private_method(rb_cModule, "prepend_features", rb_mod_prepend_features, 1);
rb_define_private_method(rb_cModule, "prepend", rb_mod_prepend, -1);
rb_define_private_method(rb_cModule, "refine", rb_mod_refine, 1);
+ rb_define_private_method(rb_cModule, "using", mod_using, 1);
rb_undef_method(rb_cClass, "refine");
rb_undef_method(rb_cClass, "module_function");
diff --git a/test/ruby/test_refinement.rb b/test/ruby/test_refinement.rb
index 23b73667a7..5c4a99b121 100644
--- a/test/ruby/test_refinement.rb
+++ b/test/ruby/test_refinement.rb
@@ -429,14 +429,6 @@ class TestRefinement < Test::Unit::TestCase
end
end
- def test_no_module_using
- assert_raise(NoMethodError) do
- Module.new {
- using Module.new
- }
- end
- end
-
class UsingClass
end
@@ -826,6 +818,63 @@ class TestRefinement < Test::Unit::TestCase
assert_equal([:foo, :ref, bug7925], x, bug7925)
end
+ module ModuleUsing
+ using FooExt
+
+ def self.invoke_x_on(foo)
+ return foo.x
+ end
+
+ def self.invoke_y_on(foo)
+ return foo.y
+ end
+
+ def self.invoke_z_on(foo)
+ return foo.z
+ end
+
+ def self.send_z_on(foo)
+ return foo.send(:z)
+ end
+
+ def self.method_z(foo)
+ return foo.method(:z)
+ end
+
+ def self.invoke_call_x_on(foo)
+ return foo.call_x
+ end
+ end
+
+ def test_module_using
+ foo = Foo.new
+ assert_equal("Foo#x", foo.x)
+ assert_equal("Foo#y", foo.y)
+ assert_raise(NoMethodError) { foo.z }
+ assert_equal("FooExt#x", ModuleUsing.invoke_x_on(foo))
+ assert_equal("FooExt#y Foo#y", ModuleUsing.invoke_y_on(foo))
+ assert_equal("FooExt#z", ModuleUsing.invoke_z_on(foo))
+ assert_equal("Foo#x", foo.x)
+ assert_equal("Foo#y", foo.y)
+ assert_raise(NoMethodError) { foo.z }
+ end
+
+ def test_module_using_in_method
+ assert_raise(RuntimeError) do
+ Module.new.send(:using, FooExt)
+ end
+ end
+
+ def test_module_using_invalid_self
+ assert_raise(RuntimeError) do
+ eval <<-EOF, TOPLEVEL_BINDING
+ module TestRefinement::TestModuleUsingInvalidSelf
+ Module.new.send(:using, TestRefinement::FooExt)
+ end
+ EOF
+ end
+ end
+
private
def eval_using(mod, s)