Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Support (un-)extending private / protected methods #1

Merged
merged 1 commit into from

2 participants

@stephankaag

No description provided.

@archan937 archan937 merged commit e73c5a3 into from
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Aug 15, 2012
  1. @stephankaag
This page is out of date. Refresh to see the latest.
View
6 lib/unextendable/module.rb
@@ -1,5 +1,11 @@
class Module
+ def my_methods(include_private = true)
+ public_instance_methods.tap do |methods|
+ return methods + private_instance_methods + protected_instance_methods if include_private
+ end
+ end
+
def unextendable
@unextendable = true
end
View
11 lib/unextendable/object.rb
@@ -43,7 +43,7 @@ def unextend?(mod, &block)
def wrap_unextendable_module(mod)
return unless (mod.class == Module) && mod.unextendable?
- mod.instance_methods.each do |method_name|
+ mod.my_methods.each do |method_name|
wrap_unextendable_method method_name
end
@@ -51,12 +51,12 @@ def wrap_unextendable_module(mod)
instance_eval <<-CODE
def respond_to?(symbol, include_private = false)
- meta_class.extended_modules.any?{|x| x.instance_methods.collect(&:to_s).include? symbol.to_s} ||
+ meta_class.extended_modules.any?{|x| x.my_methods(include_private).collect(&:to_s).include? symbol.to_s} ||
begin
if meta_class.method_procs.has_key? symbol.to_s
meta_class.method_procs[symbol.to_s].class == Proc
else
- self.class.instance_methods.collect(&:to_s).include? symbol.to_s
+ self.class.my_methods(include_private).collect(&:to_s).include? symbol.to_s
end
end
end
@@ -68,7 +68,7 @@ def respond_to?(symbol, include_private = false)
def wrap_unextendable_method(method_name)
return if meta_class.method_procs.key? method_name.to_s
- meta_class.method_procs[method_name.to_s] = respond_to?(method_name) ? method(method_name.to_s).to_proc : nil
+ meta_class.method_procs[method_name.to_s] = respond_to?(method_name, true) ? method(method_name.to_s).to_proc : nil
instance_eval <<-CODE
def #{method_name}(*args, &block)
@@ -86,12 +86,13 @@ def call_unextendable_method(method_name, *args, &block)
if method = method_for(method_name)
method.call(*args, &block)
else
+ binding.pry if method_name.to_s == "id"
raise NoMethodError, "undefined method `#{method_name}' for #{self.inspect}"
end
end
def method_for(method_name)
- mod = meta_class.extended_modules.detect{|x| x.instance_methods.collect(&:to_s).include? method_name.to_s}
+ mod = meta_class.extended_modules.detect{|x| x.my_methods.collect(&:to_s).include? method_name.to_s}
mod ? mod.instance_method(method_name).bind(self) : proc_for(method_name)
end
View
39 test/object_test.rb
@@ -17,6 +17,10 @@ def salutation
def name
"C"
end
+ private
+ def id
+ "C"
+ end
end
@c = C.new
@c.title = "Mr."
@@ -72,6 +76,14 @@ module B; end
context "which are unextendable" do
setup do
+ module B
+ unextendable
+ public
+ def id
+ "B"
+ end
+ end
+
module U
unextendable
def name
@@ -80,6 +92,12 @@ def name
def foo
"bar"
end
+
+ private
+
+ def id
+ "U"
+ end
end
end
@@ -95,14 +113,14 @@ def foo
end
should "call wrap_unextendable_method" do
- @c.expects(:wrap_unextendable_method).twice
+ @c.expects(:wrap_unextendable_method).times(3)
@c.extend U
end
should "add nil value as method proc when not responding to module method name" do
@c.extend U
assert_equal 1, @c.meta_class.method_procs.select{|k, v| v.nil?}.size
- assert_equal 1, @c.meta_class.method_procs.select{|k, v| v.class == Proc}.size
+ assert_equal 2, @c.meta_class.method_procs.select{|k, v| v.class == Proc}.size
end
should "add the module to extended_modules" do
@@ -114,7 +132,7 @@ def foo
should "add method proc to method_procs" do
assert @c.meta_class.send(:method_procs).empty?
@c.extend U
- assert_equal 2, @c.meta_class.send(:method_procs).size
+ assert_equal 3, @c.meta_class.send(:method_procs).size
end
context "when calling an unextendable method" do
@@ -151,11 +169,24 @@ def foo
context "when unextending the module afterwards" do
should "remove the module from extended_modules" do
+ exception = assert_raises(NoMethodError) do
+ @c.id
+ end
+ assert_equal "private method `id' called for", exception.message[0..29]
+ assert_equal "C", @c.send(:id)
+
@c.extend U
assert @c.meta_class.extended_modules.include?(U)
+ assert_equal "private method `id' called for", exception.message[0..29]
+ assert_equal "U", @c.send(:id)
+
+ @c.extend B
+ assert_equal "B", @c.send(:id)
- @c.unextend U
+ @c.unextend
assert !@c.meta_class.extended_modules.include?(U)
+ assert_equal "private method `id' called for", exception.message[0..29]
+ assert_equal "C", @c.send(:id)
end
should "remove the module but when passed a block only when it passes" do
Something went wrong with that request. Please try again.