File: class_method.rb

package info (click to toggle)
ruby-mocha 1.1.0-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 1,400 kB
  • ctags: 2,016
  • sloc: ruby: 10,921; makefile: 12
file content (127 lines) | stat: -rw-r--r-- 3,439 bytes parent folder | download | duplicates (2)
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
require 'metaclass'

module Mocha

  class ClassMethod

    PrependedModule = Class.new(Module)

    attr_reader :stubbee, :method

    def initialize(stubbee, method)
      @stubbee = stubbee
      @original_method, @original_visibility = nil, nil
      @method = RUBY_VERSION < '1.9' ? method.to_s : method.to_sym
    end

    def stub
      hide_original_method
      define_new_method
    end

    def unstub
      remove_new_method
      restore_original_method
      mock.unstub(method.to_sym)
      unless mock.any_expectations?
        reset_mocha
      end
    end

    def mock
      stubbee.mocha
    end

    def reset_mocha
      stubbee.reset_mocha
    end

    def hide_original_method
      if method_exists?(method)
        begin
          @original_method = stubbee._method(method)
          @original_visibility = :public
          if stubbee.__metaclass__.protected_instance_methods.include?(method)
            @original_visibility = :protected
          elsif stubbee.__metaclass__.private_instance_methods.include?(method)
            @original_visibility = :private
          end
          if @original_method && @original_method.owner == stubbee.__metaclass__
            stubbee.__metaclass__.send(:remove_method, method)
          end

          include_prepended_module if RUBY_VERSION >= '2.0'
        rescue NameError
          # deal with nasties like ActiveRecord::Associations::AssociationProxy
        end
      end
    end

    def define_new_method
      definition_target.class_eval(<<-CODE, __FILE__, __LINE__ + 1)
        def #{method}(*args, &block)
          mocha.method_missing(:#{method}, *args, &block)
        end
      CODE
      if @original_visibility
        Module.instance_method(@original_visibility).bind(definition_target).call(method)
      end
    end

    def remove_new_method
      definition_target.send(:remove_method, method)
    end

    def restore_original_method
      if @original_method && @original_method.owner == stubbee.__metaclass__
        if RUBY_VERSION < '1.9'
          original_method = @original_method
          stubbee.__metaclass__.send(:define_method, method) do |*args, &block|
            original_method.call(*args, &block)
          end
        else
          stubbee.__metaclass__.send(:define_method, method, @original_method)
        end
      end
      if @original_visibility
        Module.instance_method(@original_visibility).bind(stubbee.__metaclass__).call(method)
      end
    end

    def matches?(other)
      return false unless (other.class == self.class)
      (stubbee.object_id == other.stubbee.object_id) and (method == other.method)
    end

    alias_method :==, :eql?

    def to_s
      "#{stubbee}.#{method}"
    end

    def method_exists?(method)
      symbol = method.to_sym
      __metaclass__ = stubbee.__metaclass__
      __metaclass__.public_method_defined?(symbol) || __metaclass__.protected_method_defined?(symbol) || __metaclass__.private_method_defined?(symbol)
    end

    private

    def include_prepended_module
      possible_prepended_modules = stubbee.__metaclass__.ancestors.take_while do |mod|
        !(Class === mod)
      end

      if possible_prepended_modules.any?
        @definition_target = PrependedModule.new
        stubbee.__metaclass__.__send__ :prepend, @definition_target
      end
    end

    def definition_target
      @definition_target ||= stubbee.__metaclass__
    end

  end

end