Created
June 8, 2012 18:33
-
-
Save eagletmt/2897460 to your computer and use it in GitHub Desktop.
exclude/unextend
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include <ruby.h> | |
| #include <ruby/backward/classext.h> | |
| static void exclude(VALUE prev, VALUE mod) | |
| { | |
| Check_Type(mod, T_MODULE); | |
| VALUE klass = RCLASS_SUPER(prev); | |
| for (; klass; prev = klass, klass = RCLASS_SUPER(klass)) { | |
| if (RCLASS(klass)->m_tbl == RCLASS(mod)->m_tbl) { | |
| RCLASS_SUPER(prev) = RCLASS_SUPER(klass); | |
| rb_clear_cache(); | |
| break; | |
| } | |
| } | |
| } | |
| static VALUE m_unextend(VALUE self, VALUE mod) | |
| { | |
| exclude(rb_singleton_class(self), mod); | |
| return self; | |
| } | |
| static VALUE m_exclude(VALUE self, VALUE mod) | |
| { | |
| exclude(self, mod); | |
| return self; | |
| } | |
| void Init_exclude(void) | |
| { | |
| rb_define_method(rb_cObject, "unextend", RUBY_METHOD_FUNC(m_unextend), 1); | |
| rb_define_private_method(rb_cModule, "exclude", RUBY_METHOD_FUNC(m_exclude), 1); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| require 'mkmf' | |
| create_makefile 'exclude' |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/ruby | |
| require 'minitest/unit' | |
| require 'minitest/autorun' | |
| require './exclude' | |
| module M | |
| def m | |
| end | |
| end | |
| class TestUnextend < MiniTest::Unit::TestCase | |
| def test_unextend | |
| obj = Object.new | |
| assert !obj.respond_to?(:m) | |
| assert !obj.kind_of?(M) | |
| obj.extend M | |
| assert obj.respond_to?(:m) | |
| assert obj.kind_of?(M) | |
| obj.unextend M | |
| assert !obj.respond_to?(:m) | |
| assert !obj.kind_of?(M) | |
| end | |
| def test_unextend_no_effect | |
| obj = Object.new | |
| obj.unextend M | |
| pass | |
| end | |
| end | |
| class TestExclude < MiniTest::Unit::TestCase | |
| class C | |
| include M | |
| end | |
| def test_exclude | |
| obj = C.new | |
| assert obj.respond_to?(:m) | |
| assert obj.kind_of?(M) | |
| C.class_eval do | |
| exclude M | |
| end | |
| assert !obj.respond_to?(:m) | |
| assert !obj.kind_of?(M) | |
| end | |
| class D | |
| end | |
| def test_exclude_no_effect | |
| D.class_eval do | |
| exclude M | |
| end | |
| pass | |
| end | |
| end | |
| class TestSuper < MiniTest::Unit::TestCase | |
| class C1 | |
| def foo | |
| puts :C1 | |
| end | |
| end | |
| module M1 | |
| def foo | |
| puts :M1 | |
| end | |
| end | |
| class C2 < C1 | |
| include M1 | |
| def foo | |
| super | |
| puts :C2 | |
| end | |
| end | |
| def test_class | |
| obj = C2.new | |
| out1, _ = capture_io { obj.foo } | |
| assert_equal out1.lines.map(&:chomp), ['M1', 'C2'] | |
| obj.unextend M1 | |
| out2, _ = capture_io { obj.foo } | |
| assert_equal out2.lines.map(&:chomp), ['C1', 'C2'] | |
| end | |
| module M2 | |
| def foo | |
| puts :M2 | |
| end | |
| end | |
| class C3 | |
| include M1 | |
| include M2 | |
| def foo | |
| super | |
| puts :C3 | |
| end | |
| end | |
| def test_module | |
| obj = C3.new | |
| out1, _ = capture_io { obj.foo } | |
| assert_equal out1.lines.map(&:chomp), ['M2', 'C3'] | |
| obj.unextend M2 | |
| out2, _ = capture_io { obj.foo } | |
| assert_equal out2.lines.map(&:chomp), ['M1', 'C3'] | |
| end | |
| module M3 | |
| def foo | |
| end | |
| end | |
| class C4 | |
| include M3 | |
| def foo | |
| super | |
| end | |
| end | |
| def test_no_super | |
| obj = C4.new | |
| assert obj.respond_to?(:foo) | |
| obj.unextend M3 | |
| assert obj.respond_to?(:foo) | |
| e = assert_raises(NoMethodError) { obj.foo } | |
| assert_match e.message, /^super: no superclass method/ | |
| end | |
| end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment