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!TrueClass

Returns:

  • (TrueClass)


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

def _aquire_lock!
  true
end

#_release_lock!TrueClass

Returns:

  • (TrueClass)


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

def _release_lock!
  true
end

#_zoidberg_available?TrueClass, FalseClass

Returns currently unlocked.

Returns:

  • (TrueClass, FalseClass)

    currently unlocked



82
83
84
# File 'lib/zoidberg/proxy.rb', line 82

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)


182
183
184
185
186
187
188
189
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
# File 'lib/zoidberg/proxy.rb', line 182

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 })
  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)


154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/zoidberg/proxy.rb', line 154

def _zoidberg_handle_unexpected_error(error)
  unless(::Zoidberg.in_shutdown?)
    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
    inst = args.shift.unshelled_new(*args.first, &args.last)
    _zoidberg_set_instance(inst)
    if(_raw_instance.respond_to?(:restarted!))
      _raw_instance.restarted!
    end
    _release_lock!
  end
  true
end

Returns:

  • (Object, NilClass)


92
93
94
# File 'lib/zoidberg/proxy.rb', line 92

def _zoidberg_link
  @_zoidberg_link
end

#_zoidberg_link=(inst) ⇒ Object

Returns:

  • (Object)


87
88
89
# File 'lib/zoidberg/proxy.rb', line 87

def _zoidberg_link=(inst)
  @_zoidberg_link = inst
end

#_zoidberg_locked?TrueClass, FalseClass

Returns currently locked.

Returns:

  • (TrueClass, FalseClass)

    currently locked



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

def _zoidberg_locked?
  false
end

#_zoidberg_objectself

Returns:

  • (self)


218
219
220
# File 'lib/zoidberg/proxy.rb', line 218

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)


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

def _zoidberg_set_instance(inst)
  @_raw_instance = inst
  @_raw_instance._zoidberg_proxy(self)
  nil
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



108
109
110
111
112
113
114
115
# File 'lib/zoidberg/proxy.rb', line 108

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:



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

def _zoidberg_signal=(signal)
  @_zoidberg_signal = signal
end

#_zoidberg_unexpected_error(e) ⇒ Object

Properly handle an unexpected exception when encountered

Parameters:

  • e (Exception)


120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/zoidberg/proxy.rb', line 120

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