summaryrefslogtreecommitdiff
path: root/spec/ruby/core/module/remove_method_spec.rb
blob: 94b255df6208562112e15ea76ab22ada8c3a0916 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
require_relative '../../spec_helper'
require_relative 'fixtures/classes'

module ModuleSpecs
  class Parent
    def method_to_remove; 1; end
  end

  class First
    def method_to_remove; 1; end
  end

  class Second < First
    def method_to_remove; 2; end
  end
end

describe "Module#remove_method" do
  before :each do
    @module = Module.new { def method_to_remove; end }
  end

  it "is a public method" do
    Module.should have_public_instance_method(:remove_method, false)
  end

  it "removes the method from a class" do
    klass = Class.new do
      def method_to_remove; 1; end
    end
    x = klass.new
    klass.send(:remove_method, :method_to_remove)
    x.respond_to?(:method_to_remove).should == false
  end

  it "removes method from subclass, but not parent" do
    child = Class.new(ModuleSpecs::Parent) do
      def method_to_remove; 2; end
      remove_method :method_to_remove
    end
    x = child.new
    x.respond_to?(:method_to_remove).should == true
    x.method_to_remove.should == 1
  end

  it "updates the method implementation" do
    m_module = Module.new do
      def foo
        'm'
      end
    end

    a_class = Class.new do
      include m_module

      def foo
        'a'
      end
    end

    a = a_class.new
    foo = -> { a.foo }
    foo.call.should == 'a'
    a_class.remove_method(:foo)
    foo.call.should == 'm'
  end

  it "removes multiple methods with 1 call" do
    klass = Class.new do
      def method_to_remove_1; 1; end
      def method_to_remove_2; 2; end
      remove_method :method_to_remove_1, :method_to_remove_2
    end
    x = klass.new
    x.respond_to?(:method_to_remove_1).should == false
    x.respond_to?(:method_to_remove_2).should == false
  end

  it "accepts multiple arguments" do
    Module.instance_method(:remove_method).arity.should < 0
  end

  it "does not remove any instance methods when argument not given" do
    before = @module.instance_methods(true) + @module.private_instance_methods(true)
    @module.send :remove_method
    after = @module.instance_methods(true) + @module.private_instance_methods(true)
    before.sort.should == after.sort
  end

  it "returns self" do
    @module.send(:remove_method, :method_to_remove).should equal(@module)
  end

  it "raises a NameError when attempting to remove method further up the inheritance tree" do
    Class.new(ModuleSpecs::Second) do
      -> {
        remove_method :method_to_remove
      }.should raise_error(NameError)
    end
  end

  it "raises a NameError when attempting to remove a missing method" do
    Class.new(ModuleSpecs::Second) do
      -> {
        remove_method :blah
      }.should raise_error(NameError)
    end
  end

  describe "on frozen instance" do
    before :each do
      @frozen = @module.dup.freeze
    end

    it "raises a FrozenError when passed a name" do
      -> { @frozen.send :remove_method, :method_to_remove }.should raise_error(FrozenError)
    end

    it "raises a FrozenError when passed a missing name" do
      -> { @frozen.send :remove_method, :not_exist }.should raise_error(FrozenError)
    end

    it "raises a TypeError when passed a not name" do
      -> { @frozen.send :remove_method, Object.new }.should raise_error(TypeError)
    end

    it "does not raise exceptions when no arguments given" do
      @frozen.send(:remove_method).should equal(@frozen)
    end
  end
end