summaryrefslogtreecommitdiff
path: root/spec/ruby/core/basicobject/singleton_method_added_spec.rb
blob: fc65a091aaade2f325880ee2bf24fa1328bf5a3f (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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
require_relative '../../spec_helper'
require_relative 'fixtures/singleton_method'

describe "BasicObject#singleton_method_added" do
  before :each do
    ScratchPad.clear
  end

  it "is a private method" do
    BasicObject.should have_private_instance_method(:singleton_method_added)
  end

  it "is called when a singleton method is defined on an object" do
    obj = BasicObject.new

    def obj.singleton_method_added(name)
      ScratchPad.record [:singleton_method_added, name]
    end

    def obj.new_singleton_method
    end

    ScratchPad.recorded.should == [:singleton_method_added, :new_singleton_method]
  end

  it "is not called for instance methods" do
    ScratchPad.record []

    Module.new do
      def self.singleton_method_added(name)
        ScratchPad << name
      end

      def new_instance_method
      end
    end

    ScratchPad.recorded.should_not include(:new_instance_method)
  end

  it "is called when a singleton method is defined on a module" do
    class BasicObjectSpecs::SingletonMethod
      def self.new_method_on_self
      end
    end
    ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_self]
  end

  it "is called when a method is defined in the singleton class" do
    class BasicObjectSpecs::SingletonMethod
      class << self
        def new_method_on_singleton
        end
      end
    end
    ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_singleton]
  end

  it "is called when a method is defined with alias_method in the singleton class" do
    class BasicObjectSpecs::SingletonMethod
      class << self
        alias_method :new_method_on_singleton_with_alias_method, :singleton_method_to_alias
      end
    end
    ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_singleton_with_alias_method]
  end

  it "is called when a method is defined with syntax alias in the singleton class" do
    class BasicObjectSpecs::SingletonMethod
      class << self
        alias new_method_on_singleton_with_syntax_alias singleton_method_to_alias
      end
    end
    ScratchPad.recorded.should == [:singleton_method_added, :new_method_on_singleton_with_syntax_alias]
  end

  it "is called when define_method is used in the singleton class" do
    class BasicObjectSpecs::SingletonMethod
      class << self
        define_method :new_method_with_define_method do
        end
      end
    end
    ScratchPad.recorded.should == [:singleton_method_added, :new_method_with_define_method]
  end

  describe "when singleton_method_added is undefined" do
    it "raises NoMethodError for a metaclass" do
      class BasicObjectSpecs::NoSingletonMethodAdded
        class << self
          undef_method :singleton_method_added
        end

        -> {
          def self.foo
          end
        }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for/)
      end
    end

    it "raises NoMethodError for a singleton instance" do
      object = Object.new
      class << object
        undef_method :singleton_method_added

        -> {
          def foo
          end
        }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/)

        -> {
          define_method(:bar) {}
        }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/)
      end

      -> {
        object.define_singleton_method(:baz) {}
      }.should raise_error(NoMethodError, /undefined method [`']singleton_method_added' for #<Object:/)
    end

    it "calls #method_missing" do
      ScratchPad.record []
      object = Object.new
      class << object
        def method_missing(*args)
          ScratchPad << args
        end

        undef_method :singleton_method_added

        def foo
        end

        define_method(:bar) {}
      end
      object.define_singleton_method(:baz) {}

      ScratchPad.recorded.should == [
        [:singleton_method_added, :foo],
        [:singleton_method_added, :bar],
        [:singleton_method_added, :baz],
      ]
    end
  end
end