Skip to content

Commit

Permalink
guard extension methods from being redefined later
Browse files Browse the repository at this point in the history
  • Loading branch information
cldwalker committed Apr 16, 2011
1 parent bfcdf8c commit 01f2166
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 24 deletions.
15 changes: 15 additions & 0 deletions lib/watchdog.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,21 @@ def initialize(meth, from, to)
class ExtendError < Error; end
class IncludeError < Error; end

def self.guard(mod, guarded)
guard_mod = Module.new { class << self; attr_accessor :existing; end }
guard_mod.existing = mod
guard_meth = guarded.is_a?(Module) ? :method_added : :singleton_method_added
guard_mod.send(:define_method, guard_meth) do |meth|
if guard_mod.existing.instance_methods.include?(meth)
raise Watchdog::Error.new(meth, self, mod)
end
super
end
guarded.extend guard_mod
end

def append_features(mod)
Watchdog.guard(self, mod)
existing = mod.private_instance_methods + mod.instance_methods
(existing & self.instance_methods).each do |m|
raise IncludeError.new(m, self, mod)
Expand All @@ -16,6 +30,7 @@ def append_features(mod)
end

def extend_object(obj)
Watchdog.guard(self, obj)
self.instance_methods.each do |m|
raise ExtendError.new(m, self, obj) if obj.respond_to?(m, true)
end
Expand Down
80 changes: 56 additions & 24 deletions spec/watchdog_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,42 +8,74 @@ def create_methods(obj, *meths)
end

context "when extended" do
let(:safe_module) { create_methods Module.new.extend(Watchdog), :blah }
let(:extensions) { create_methods Module.new.extend(Watchdog), :blah }

it "doesn't raise error if no methods conflict" do
existing = Object.new
lambda { existing.extend safe_module }.should_not raise_error
end
context "new extension method" do
it "doesn't raise error if no existing methods conflict" do
existing = Object.new
lambda { existing.extend extensions }.should_not raise_error
end

it "raises error if existing public methods conflict" do
existing = create_methods Object.new, :blah
lambda { existing.extend extensions }.should raise_error(Watchdog::ExtendError)
end

it "raises error if public methods conflict" do
existing = create_methods Object.new, :blah
lambda { existing.extend safe_module }.should raise_error(Watchdog::ExtendError)
it "raises error if existing private methods conflict" do
existing = create_methods Object.new, :blah
class <<existing; self.send :private, :blah; end
lambda { existing.extend extensions }.should raise_error(Watchdog::ExtendError)
end
end

it "raises error if private methods conflict" do
existing = create_methods Object.new, :blah
class <<existing; self.send :private, :blah; end
lambda { existing.extend safe_module }.should raise_error(Watchdog::ExtendError)
context "new method" do
it "doesn't raise error if it doesn't redefine extension methods" do
existing = Object.new.extend extensions
lambda { def existing.bling; end }.should_not raise_error(Watchdog::Error)
end

it "raises error if it redefines extension methods" do
existing = Object.new.extend extensions
lambda { def existing.blah; end }.should raise_error(Watchdog::Error)
end
end
end

context "when included" do
let(:safe_module) { create_methods Module.new.extend(Watchdog), :blah }
let(:extensions) { create_methods Module.new.extend(Watchdog), :blah }

it "doesn't raise error if no methods conflict" do
existing = Module.new
lambda { existing.send :include, safe_module }.should_not raise_error
end
context "new extension method" do
it "doesn't raise error if no existing methods conflict" do
existing = Module.new
lambda { existing.send :include, extensions }.should_not raise_error
end

it "raises error if existing public methods conflict" do
existing = create_methods Module.new, :blah
lambda { existing.send :include, extensions }.should raise_error(Watchdog::IncludeError)
end

it "raises error if public methods conflict" do
existing = create_methods Module.new, :blah
lambda { existing.send :include, safe_module }.should raise_error(Watchdog::IncludeError)
it "raises error if existing private methods conflict" do
existing = create_methods Module.new, :blah
existing.send :private, :blah
lambda { existing.send :include, extensions }.should raise_error(Watchdog::IncludeError)
end
end

it "raises error if private methods conflict" do
existing = create_methods Module.new, :blah
existing.send :private, :blah
lambda { existing.send :include, safe_module }.should raise_error(Watchdog::IncludeError)
context "new method" do
it "doesn't raise error if it doesn't redefine extension methods" do
existing = Module.new.send :include, extensions
lambda {
existing.send(:define_method, :bling) { }
}.should_not raise_error(Watchdog::Error)
end

it "raises error if it redefines extension methods" do
existing = Module.new.send :include, extensions
lambda {
existing.send(:define_method, :blah) { }
}.should raise_error(Watchdog::Error)
end
end
end
end

0 comments on commit 01f2166

Please sign in to comment.