Class: Zoidberg::Proxy

Inherits:
BasicObject
Defined in:
lib/zoidberg/proxy.rb,
lib/zoidberg/proxy.rb,
lib/zoidberg/proxy/confined.rb,
lib/zoidberg/proxy/liberated.rb

Overview

Instance proxy that filters requests to shelled instance

Direct Known Subclasses

Confined, Liberated

Defined Under Namespace

Classes: Confined, Liberated

Constant Summary collapse

@@__registry =
::Hash.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(*_) ⇒ Proxy

Abstract class gets no builder

Raises:

  • (NotImplementedError)


52
53
54
# File 'lib/zoidberg/proxy.rb', line 52

def initialize(*_)
  raise NotImplementedError
end

Class Method Details

.inherited(klass) ⇒ Object

Setup proxy for proper scrubbing support



42
43
44
45
46
47
48
49
# File 'lib/zoidberg/proxy.rb', line 42

def self.inherited(klass)
  klass.class_eval do
    # @return [Array] arguments used to build real instance
    attr_accessor :_build_args
    # @return [Object] wrapped instance
    attr_reader :_raw_instance
  end
end

.register(r_id, proxy) ⇒ Zoidberg::Proxy

Register the proxy a WeakRef is pointing to

Parameters:

  • r_id (Integer)

    object ID of WeakRef

  • proxy (Zoidberg::Proxy)

    actual proxy instance

Returns:



24
25
26
# File 'lib/zoidberg/proxy.rb', line 24

def register(r_id, proxy)
  @@__registry[r_id] = proxy
end

.registryHash

Returns WeakRef -> Proxy mapping.

Returns:

  • (Hash)

    WeakRef -> Proxy mapping



15
16
17
# File 'lib/zoidberg/proxy.rb', line 15

def registry
  @@__registry
end

.scrub!(o_id) ⇒ Truthy, Falsey

Destroy the proxy referenced by the WeakRef with the provided ID

Parameters:

  • o_id (Integer)

    Object ID

Returns:

  • (Truthy, Falsey)


33
34
35
36
37
38
# File 'lib/zoidberg/proxy.rb', line 33

def scrub!(o_id)
  proxy = @@__registry.delete(o_id)
  if(proxy)
    proxy._zoidberg_destroy!
  end
end

Instance Method Details

#_zoidberg_available?TrueClass, FalseClass

Returns currently unlocked.

Returns:

  • (TrueClass, FalseClass)

    currently unlocked



62
63
64
# File 'lib/zoidberg/proxy.rb', line 62

def _zoidberg_available?
  !_zoidberg_locked?
end

#_zoidberg_destroy!(error = nil, &block) ⇒ TrueClass Also known as: terminate

Destroy the real instance. Will update all methods on real instance to raise exceptions noting it as terminated rendering it unusable. This is generally used with the supervise module but can be used on its own if desired.

Returns:

  • (TrueClass)


162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/zoidberg/proxy.rb', line 162

def _zoidberg_destroy!(error=nil, &block)
  unless(_raw_instance.respond_to?(:_zoidberg_destroyed))
    if(_raw_instance.respond_to?(:terminate))
      if(_raw_instance.method(:terminate).arity == 0)
        _raw_instance.terminate
      else
        _raw_instance.terminate(error)
      end
    end
    death_from_above = ::Proc.new do
      ::Kernel.raise ::Zoidberg::DeadException.new('Instance in terminated state!')
    end
    death_from_above_display = ::Proc.new do
      "#<#{self.class.name}:TERMINATED>"
    end
    block.call if block
    _raw_instance.instance_variables.each do |i_var|
      _raw_instance.remove_instance_variable(i_var)
    end
    (
      _raw_instance.public_methods(false) +
      _raw_instance.protected_methods(false) +
      _raw_instance.private_methods(false)
    ).each do |m_name|
      next if m_name.to_sym == :alive?
      _raw_instance.send(:define_singleton_method, m_name, &death_from_above)
    end
    _raw_instance.send(:define_singleton_method, :to_s, &death_from_above_display)
    _raw_instance.send(:define_singleton_method, :inspect, &death_from_above_display)
    _raw_instance.send(:define_singleton_method, :_zoidberg_destroyed, ::Proc.new{ true })
    _zoidberg_signal(:destroyed)
  end
  true
end

#_zoidberg_handle_unexpected_error(error) ⇒ TrueClass

When real instance is being supervised, unexpected exceptions will force the real instance to be terminated and replaced with a fresh instance.

If the real instance provides a #restart method that will be called instead of forcibly terminating the current real instance and rebuild a new instance.

If the real instance provides a #restarted! method, that method will be called on the newly created instance on replacement

Parameters:

  • error (Exception)

    exception that was caught

Returns:

  • (TrueClass)


133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'lib/zoidberg/proxy.rb', line 133

def _zoidberg_handle_unexpected_error(error)
  if(_raw_instance.respond_to?(:restart))
    begin
      _raw_instance.restart(error)
      return # short circuit
    rescue => e
    end
  end
  _zoidberg_destroy!
  _aquire_lock!
  args = _build_args.dup
  @_raw_instance = args.shift.unshelled_new(
    *args.first,
    &args.last
  )
  _raw_instance._zoidberg_proxy(self)
  if(_raw_instance.respond_to?(:restarted!))
    _raw_instance.restarted!
  end
  _release_lock!
  true
end

Returns:

  • (Object, NilClass)


72
73
74
# File 'lib/zoidberg/proxy.rb', line 72

def _zoidberg_link
  @_zoidberg_link
end

#_zoidberg_link=(inst) ⇒ Object

Returns:

  • (Object)


67
68
69
# File 'lib/zoidberg/proxy.rb', line 67

def _zoidberg_link=(inst)
  @_zoidberg_link = inst
end

#_zoidberg_locked?TrueClass, FalseClass

Returns currently locked.

Returns:

  • (TrueClass, FalseClass)

    currently locked



57
58
59
# File 'lib/zoidberg/proxy.rb', line 57

def _zoidberg_locked?
  false
end

#_zoidberg_objectself

Returns:

  • (self)


199
200
201
# File 'lib/zoidberg/proxy.rb', line 199

def _zoidberg_object
  self
end

#_zoidberg_signal(sig) ⇒ TrueClass, FalseClass

Send a signal if the optional signal instance has been set

Parameters:

  • sig (Symbol)

Returns:

  • (TrueClass, FalseClass)

    signal was sent



88
89
90
91
92
93
94
95
# File 'lib/zoidberg/proxy.rb', line 88

def _zoidberg_signal(sig)
  if(@_zoidberg_signal)
    @_zoidberg_signal.signal(sig)
    true
  else
    false
  end
end

#_zoidberg_signal=(signal) ⇒ Signal

Set an optional state signal instance

Parameters:

Returns:



80
81
82
# File 'lib/zoidberg/proxy.rb', line 80

def _zoidberg_signal=(signal)
  @_zoidberg_signal = signal
end

#_zoidberg_unexpected_error(e) ⇒ Object

Properly handle an unexpected exception when encountered

Parameters:

  • e (Exception)


100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/zoidberg/proxy.rb', line 100

def _zoidberg_unexpected_error(e)
  ::Zoidberg.logger.error "Unexpected exception: #{e.class} - #{e}"
  unless((defined?(Timeout) && e.is_a?(Timeout::Error)) || e.is_a?(::Zoidberg::DeadException))
    if(_zoidberg_link)
      if(_zoidberg_link.class.trap_exit)
        ::Zoidberg.logger.warn "Calling linked exit trapper #{@_raw_instance.class.name} -> #{_zoidberg_link.class}: #{e.class} - #{e}"
        _zoidberg_link.async.send(
          _zoidberg_link.class.trap_exit, @_raw_instance, e
        )
      end
    else
      if(@_supervised)
        ::Zoidberg.logger.warn "Unexpected error for supervised class `#{@_raw_instance.class.name}`. Handling error (#{e.class} - #{e})"
        ::Zoidberg.logger.debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
        _zoidberg_handle_unexpected_error(e)
      end
    end
  end
end