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

#_aquire_lock!(&block) ⇒ TrueClass

Returns:

  • (TrueClass)


76
77
78
79
80
# File 'lib/zoidberg/proxy.rb', line 76

def _aquire_lock!(&block)
  result = block ? block.call : true
  _zoidberg_signal(:locked)
  result
end

#_release_lock!(&block) ⇒ TrueClass

Returns:

  • (TrueClass)


83
84
85
86
87
# File 'lib/zoidberg/proxy.rb', line 83

def _release_lock!(&block)
  result = block ? block.call : true
  _zoidberg_signal(:unlocked, self) if _zoidberg_available?
  result
end

#_zoidberg_available?TrueClass, FalseClass

Returns currently unlocked.

Returns:

  • (TrueClass, FalseClass)

    currently unlocked



90
91
92
# File 'lib/zoidberg/proxy.rb', line 90

def _zoidberg_available?
  !_zoidberg_locked?
end

#_zoidberg_destroy!(error = nil, &block) ⇒ TrueClass

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)


190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/zoidberg/proxy.rb', line 190

def _zoidberg_destroy!(error=nil, &block)
  unless(_raw_instance.respond_to?(:_zoidberg_destroyed))
    if(::Zoidberg.in_shutdown?)
      @_zoidberg_timer.terminate if @_zoidberg_timer
      @_zoidberg_signal.terminate if @_zoidberg_signal
    end
    if(_raw_instance.respond_to?(:terminate))
      begin
        if(_raw_instance.method(:terminate).arity == 0)
          _raw_instance.terminate
        else
          _raw_instance.terminate(error)
        end
      rescue => e
        ::Zoidberg.logger.error "Unexpected exception caught during terminatation of #{self}: #{e}"
        ::Zoidberg.logger.debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
      end
    end
    block.call if block
    oid = _raw_instance.object_id
    death_from_above = ::Proc.new do |*_|
      ::Kernel.raise ::Zoidberg::DeadException.new('Instance in terminated state!', oid)
    end
    death_from_above_display = ::Proc.new do
      "#<#{self.class.name}:TERMINATED>"
    end
    _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 })
  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)


156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'lib/zoidberg/proxy.rb', line 156

def _zoidberg_handle_unexpected_error(error)
  if(_raw_instance.respond_to?(:restart))
    unless(::Zoidberg.in_shutdown?)
      begin
        _raw_instance.restart(error)
        return # short circuit
      rescue => e
      end
    end
  end
  _zoidberg_destroy!
  if(@_supervised && !::Zoidberg.in_shutdown?)
    _aquire_lock!
    begin
      args = _build_args.dup
      inst = args.shift.unshelled_new(*args.first, &args.last)
      _zoidberg_set_instance(inst)
      ::Zoidberg.logger.debug "Supervised instance has been rebuilt: #{inst}"
      if(_raw_instance.respond_to?(:restarted!))
        _raw_instance.restarted!
      end
    ensure
      _release_lock!
    end
  end
  true
end

Returns:

  • (Object, NilClass)


100
101
102
# File 'lib/zoidberg/proxy.rb', line 100

def _zoidberg_link
  @_zoidberg_link
end

#_zoidberg_link=(inst) ⇒ Object

Returns:

  • (Object)


95
96
97
# File 'lib/zoidberg/proxy.rb', line 95

def _zoidberg_link=(inst)
  @_zoidberg_link = inst
end

#_zoidberg_locked?TrueClass, FalseClass

Returns currently locked.

Returns:

  • (TrueClass, FalseClass)

    currently locked



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

def _zoidberg_locked?
  false
end

#_zoidberg_objectself

Returns:

  • (self)


241
242
243
# File 'lib/zoidberg/proxy.rb', line 241

def _zoidberg_object
  self
end

#_zoidberg_set_instance(inst) ⇒ NilClass

Set the raw instance into the proxy and link proxy to instance

Parameters:

  • inst (Object)

    raw instance being wrapped

Returns:

  • (NilClass)


64
65
66
67
68
# File 'lib/zoidberg/proxy.rb', line 64

def _zoidberg_set_instance(inst)
  @_raw_instance = inst
  @_raw_instance._zoidberg_proxy(self)
  nil
end

#_zoidberg_signal(*args) ⇒ TrueClass, FalseClass

Send a signal if the optional signal instance has been set

Parameters:

  • sig (Symbol)

Returns:

  • (TrueClass, FalseClass)

    signal was sent



116
117
118
119
120
121
122
123
# File 'lib/zoidberg/proxy.rb', line 116

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

#_zoidberg_signal=(signal) ⇒ Signal

Set an optional state signal instance

Parameters:

Returns:



108
109
110
# File 'lib/zoidberg/proxy.rb', line 108

def _zoidberg_signal=(signal)
  @_zoidberg_signal = signal
end

#_zoidberg_signal_interfaceObject

Initialize the signal instance if not



264
265
266
267
268
269
# File 'lib/zoidberg/proxy.rb', line 264

def _zoidberg_signal_interface
  unless(@_zoidberg_signal)
    @_zoidberg_signal = ::Zoidberg::Signal.new(:cache_signals => self.class.option?(:cache_signals))
  end
  @_zoidberg_signal
end

#_zoidberg_timerTimer

Returns:



272
273
274
275
276
277
# File 'lib/zoidberg/proxy.rb', line 272

def _zoidberg_timer
  unless(@_zoidberg_timer)
    @_zoidberg_timer = Timer.new
  end
  @_zoidberg_timer
end

#_zoidberg_unexpected_error(e) ⇒ Object

Properly handle an unexpected exception when encountered

Parameters:

  • e (Exception)


128
129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'lib/zoidberg/proxy.rb', line 128

def _zoidberg_unexpected_error(e)
  ::Zoidberg.logger.error "Unexpected exception: #{e.class} - #{e}"
  ::Zoidberg.logger.debug "#{e.class}: #{e}\n#{e.backtrace.join("\n")}"
  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
    _zoidberg_handle_unexpected_error(e)
  end
end

#_zoidberg_unsuperviseObject



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

def _zoidberg_unsupervise
  @_supervised = false
end

#async(*args, &block) ⇒ Object



259
260
261
# File 'lib/zoidberg/proxy.rb', line 259

def async(*args, &block)
  _raw_instance.async(*args, &block)
end

#inspectObject

Override to directly output object inspection



251
252
253
# File 'lib/zoidberg/proxy.rb', line 251

def inspect
  _raw_instance.inspect
end

#signal(*args) ⇒ Object



255
256
257
# File 'lib/zoidberg/proxy.rb', line 255

def signal(*args)
  _raw_instance.signal(*args)
end

#terminateObject



234
235
236
237
# File 'lib/zoidberg/proxy.rb', line 234

def terminate
  _zoidberg_unsupervise
  _zoidberg_destroy!
end

#to_sObject

Override to directly output object stringification



246
247
248
# File 'lib/zoidberg/proxy.rb', line 246

def to_s
  _raw_instance.to_s
end