1require_relative '../../spec_helper'
2require_relative 'fixtures/classes'
3
4module ModuleSpecs
5  class Parent
6    def method_to_remove; 1; end
7  end
8
9  class First
10    def method_to_remove; 1; end
11  end
12
13  class Second < First
14    def method_to_remove; 2; end
15  end
16end
17
18describe "Module#remove_method" do
19  before :each do
20    @module = Module.new { def method_to_remove; end }
21  end
22
23  ruby_version_is ''...'2.5' do
24    it "is a private method" do
25      Module.should have_private_instance_method(:remove_method, false)
26    end
27  end
28  ruby_version_is '2.5' do
29    it "is a public method" do
30      Module.should have_public_instance_method(:remove_method, false)
31    end
32  end
33
34  it "removes the method from a class" do
35    klass = Class.new do
36      def method_to_remove; 1; end
37    end
38    x = klass.new
39    klass.send(:remove_method, :method_to_remove)
40    x.respond_to?(:method_to_remove).should == false
41  end
42
43  it "removes method from subclass, but not parent" do
44    child = Class.new(ModuleSpecs::Parent) do
45      def method_to_remove; 2; end
46      remove_method :method_to_remove
47    end
48    x = child.new
49    x.respond_to?(:method_to_remove).should == true
50    x.method_to_remove.should == 1
51  end
52
53  it "removes multiple methods with 1 call" do
54    klass = Class.new do
55      def method_to_remove_1; 1; end
56      def method_to_remove_2; 2; end
57      remove_method :method_to_remove_1, :method_to_remove_2
58    end
59    x = klass.new
60    x.respond_to?(:method_to_remove_1).should == false
61    x.respond_to?(:method_to_remove_2).should == false
62  end
63
64  it "accepts multiple arguments" do
65    Module.instance_method(:remove_method).arity.should < 0
66  end
67
68  it "does not remove any instance methods when argument not given" do
69    before = @module.instance_methods(true) + @module.private_instance_methods(true)
70    @module.send :remove_method
71    after = @module.instance_methods(true) + @module.private_instance_methods(true)
72    before.sort.should == after.sort
73  end
74
75  it "returns self" do
76    @module.send(:remove_method, :method_to_remove).should equal(@module)
77  end
78
79  it "raises a NameError when attempting to remove method further up the inheritance tree" do
80    lambda {
81      class Third < ModuleSpecs::Second
82        remove_method :method_to_remove
83      end
84    }.should raise_error(NameError)
85  end
86
87  it "raises a NameError when attempting to remove a missing method" do
88    lambda {
89      class Third < ModuleSpecs::Second
90        remove_method :blah
91      end
92    }.should raise_error(NameError)
93  end
94
95  describe "on frozen instance" do
96    before :each do
97      @frozen = @module.dup.freeze
98    end
99
100    it "raises a #{frozen_error_class} when passed a name" do
101      lambda { @frozen.send :remove_method, :method_to_remove }.should raise_error(frozen_error_class)
102    end
103
104    it "raises a #{frozen_error_class} when passed a missing name" do
105      lambda { @frozen.send :remove_method, :not_exist }.should raise_error(frozen_error_class)
106    end
107
108    it "raises a TypeError when passed a not name" do
109      lambda { @frozen.send :remove_method, Object.new }.should raise_error(TypeError)
110    end
111
112    it "does not raise exceptions when no arguments given" do
113      @frozen.send(:remove_method).should equal(@frozen)
114    end
115  end
116end
117