Skip to content

Instantly share code, notes, and snippets.

@eagletmt
Created June 8, 2012 18:33
Show Gist options
  • Select an option

  • Save eagletmt/2897460 to your computer and use it in GitHub Desktop.

Select an option

Save eagletmt/2897460 to your computer and use it in GitHub Desktop.
exclude/unextend
#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);
}
require 'mkmf'
create_makefile 'exclude'
#!/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