File: constant.rb

package info (click to toggle)
ruby-spy 1.0.1-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 360 kB
  • sloc: ruby: 3,101; makefile: 2
file content (125 lines) | stat: -rw-r--r-- 3,779 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
module Spy
  class Constant
    include Base
    # @!attribute [r] base_module
    #   @return [Module] the module that is being watched
    #
    # @!attribute [r] constant_name
    #   @return [Symbol] the name of the constant that is/will be stubbed
    #
    # @!attribute [r] original_value
    #   @return [Object] the original value that was set when it was hooked


    attr_reader :base_module, :constant_name, :original_value

    # @param base_module [Module] the module this spy should be on
    # @param constant_name [Symbol] the constant this spy is watching
    def initialize(base_module, constant_name)
      raise ArgumentError, "#{base_module.inspect} is not a kind of Module" unless base_module.is_a? Module
      raise ArgumentError, "#{constant_name.inspect} is not a kind of Symbol" unless constant_name.is_a? Symbol
      @base_module, @constant_name = base_module, constant_name.to_sym
      @original_value = @new_value = @previously_defined = nil
    end

    # full name of spied constant
    def name
      "#{base_module.name}::#{constant_name}"
    end

    # stashes the original constant then overwrites it with nil
    # @param opts [Hash{force => false}] set :force => true if you want it to ignore if the constant exists
    # @return [self]
    def hook(opts = {})
      opts[:force] ||= false
      Nest.fetch(base_module).add(self)
      Agency.instance.recruit(self)

      @previously_defined = currently_defined?
      if previously_defined? || !opts[:force]
        @original_value = base_module.const_get(constant_name, false)
      end
      and_return(@new_value)
      self
    end

    # restores the original value of the constant or unsets it if it was unset
    # @return [self]
    def unhook
      Nest.get(base_module).remove(self)
      Agency.instance.retire(self)

      and_return(@original_value) if previously_defined?

      @original_value = @previously_defined = nil
      self
    end

    # unsets the constant
    # @return [self]
    def and_hide
      base_module.send(:remove_const, constant_name) if currently_defined?
      self
    end

    # sets the constant to the requested value
    # @param value [Object]
    # @return [self]
    def and_return(value)
      @new_value = value
      and_hide
      base_module.const_set(constant_name, @new_value)
      self
    end

    # checks to see if this spy is hooked?
    # @return [Boolean]
    def hooked?
      self.class.get(base_module, constant_name) == self
    end

    # checks to see if the constant is hidden?
    # @return [Boolean]
    def hidden?
      hooked? && currently_defined?
    end

    # checks to see if the constant is currently defined?
    # @return [Boolean]
    def currently_defined?
      base_module.const_defined?(constant_name, false)
    end

    # checks to see if the constant is previously defined?
    # @return [Boolean]
    def previously_defined?
      @previously_defined
    end

    class << self
      # finds existing spy or creates a new constant spy and hooks the constant
      # @return [Constant]
      def on(base_module, constant_name)
        new(base_module, constant_name).hook
      end

      # retrieves the spy for given constant and module and unhooks the constant
      # from the module
      # @return [Constant]
      def off(base_module, constant_name)
        spy = get(base_module, constant_name)
        raise NoSpyError, "#{constant_name} was not spied on #{base_module}" unless spy
        spy.unhook
      end

      # retrieves the spy for given constnat and module or returns nil
      # @return [Nil, Constant]
      def get(base_module, constant_name)
        nest = Nest.get(base_module)
        if nest
          nest.get(constant_name)
        end
      end
    end
  end
end