Class: Archipelago::Treasure::Dubloon

Inherits:
Object
  • Object
show all
Defined in:
lib/archipelago/treasure.rb

Overview

A proxy to something in the chest.

This will do a very efficient masquerade as the object it proxies. When asked for its class or any other attribute it will forward the query to the proxied object.

It can also be a part of a transaction, either because it was fetched from the chest within a transaction, or because it is the return value of a Dubloon#join call.

In this case all forwarded methods will also be within the same transaction, and any change to the proxied object will be inside that transaction.

If the proxied object itself needs to handle transaction semantics it can implement the with_transaction(transaction, &block) method, which will wrap the method call itself within the home Chest of the proxied object.

Constant Summary collapse

@@debug_callable =
nil

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key, chest, transaction, chest_id) ⇒ Dubloon

Initialize us with knowledge of our chest, the key to our target in the chest, the known public_methods of our target and any transaction we are associated with.



173
174
175
176
177
178
# File 'lib/archipelago/treasure.rb', line 173

def initialize(key, chest, transaction, chest_id)
  @key = key
  @chest = chest
  @transaction = transaction
  @chest_id = chest_id
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object

Call meth with args and block on our target if it responds to it.



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/archipelago/treasure.rb', line 238

def method_missing(meth, *args, &block)
  begin
    return @chest.call_instance_method(@key, meth, @transaction, *args, &block)
  rescue WrongChestException => e
    @@debug_callable.call("#{self.object_id} got #{e} when trying to call #{meth}.#{args}") if @@debug_callable
    #
    # We just have to find the new chest we belong at now...
    #
    new_chest_record = nil
    if defined?(Archipelago::Pirate::BLACKBEARD)
      Archipelago::Pirate::BLACKBEARD.update_services!
      new_chest_record = Archipelago::Pirate::BLACKBEARD.responsible_chest(@key)
      new_chest_record = nil unless new_chest_record[:service].include?(@key, @transaction)
    end
    raise e unless new_chest_record

    chest = new_chest_record

    retry
  rescue DRb::DRbConnError => e
    @@debug_callable.call("#{self.object_id} got #{e} when trying to call #{meth}.#{args}") if @@debug_callable
    #
    # Here is some fancy rescuing being done.
    #
    # First: If we have an MC we will try to reconnect to our actual chest.
    #
    # Second: If that failed, we will try to reconnect to the chest that (according to the BLACKBEARD)
    # is responsible for our key - if it actually HAS our key.
    #
    # If this fails, lets raise hell.
    #
    new_chest_record = nil
    if defined?(Archipelago::Disco::MC)
      new_chest_record = Archipelago::Disco::MC.lookup(Archipelago::Disco::Query.new({
                                                                                       :service_id => @chest_id
                                                                                     }))[@chest_id]
    end
    if new_chest_record.nil? && defined?(Archipelago::Pirate::BLACKBEARD)
      Archipelago::Pirate::BLACKBEARD.update_services!(:validate => true)
      new_chest_record = Archipelago::Pirate::BLACKBEARD.responsible_chest(@key)
      new_chest_record = nil unless new_chest_record[:service].include?(@key, @transaction)
    end
    raise e unless new_chest_record

    chest = new_chest_record

    retry
  end
end

Class Method Details

._load(s) ⇒ Object

Load some instance variables and replace @chest if we know that it is incorrect.



194
195
196
197
198
199
200
201
# File 'lib/archipelago/treasure.rb', line 194

def self._load(s)
  key, chest, transaction, chest_id = Marshal.load(s)
  if CHEST_BY_SERVICE_ID.include?(chest_id)
    chest = CHEST_BY_SERVICE_ID[chest_id]
  end

  return self.new(key, chest, transaction, chest_id)
end

.debug_callable=(c) ⇒ Object



122
123
124
# File 'lib/archipelago/treasure.rb', line 122

def self.debug_callable=(c)
  @@debug_callable = c
end

.dubloon?(o) ⇒ Boolean

Will return whether o is an instance of Dubloon or one of its subclasses.

Returns:

  • (Boolean)


129
130
131
# File 'lib/archipelago/treasure.rb', line 129

def self.dubloon?(o)
  return o.respond_to?(:dubloon?) && o.dubloon?
end

Instance Method Details

#==(o) ⇒ Object

If o is a Dubloon, will return true if it has the same Dubloon#object_id.

Otherwise will defer to Dubloon#method_missing.



162
163
164
165
166
167
# File 'lib/archipelago/treasure.rb', line 162

def ==(o)
  if Dubloon.dubloon?(o)
    return true if self.object_id == o.object_id
  end
  return self.method_missing(:==, o)
end

#_dump(dummy_levels) ⇒ Object

A more or less normal dump of all our instance variables.



182
183
184
185
186
187
188
189
# File 'lib/archipelago/treasure.rb', line 182

def _dump(dummy_levels)
  Marshal.dump([
                @key, 
                @chest, 
                @transaction, 
                @chest_id
               ])
end

#assert_transaction(transaction) ⇒ Object

Raises exception if the given transaction is not the same as our own.



214
215
216
# File 'lib/archipelago/treasure.rb', line 214

def assert_transaction(transaction)
  raise UnknownTransactionException.new(self, transaction) unless transaction == @transaction
end

#dubloon?Boolean

Will return that this is a Dubloon.

Returns:

  • (Boolean)


154
155
156
# File 'lib/archipelago/treasure.rb', line 154

def dubloon?
  true
end

#eql?(o) ⇒ Boolean

Defers to Dubloon#==.

Returns:

  • (Boolean)


148
149
150
# File 'lib/archipelago/treasure.rb', line 148

def eql?(o)
  self.==(o)
end

#hashObject

This Dubloon will always have the same hash, based on object_id.



142
143
144
# File 'lib/archipelago/treasure.rb', line 142

def hash
  self.object_id.hash
end

#join(transaction) ⇒ Object

Return a clone of myself that is joined to the transaction.



206
207
208
209
# File 'lib/archipelago/treasure.rb', line 206

def join(transaction)
  @chest.join!(transaction) if transaction
  return Dubloon.new(@key, @chest, transaction, @chest_id)
end

#object_idObject

This Dubloon will always have the same object_id, based on @chest_id, @key and possibly @transaction.



221
222
223
224
225
# File 'lib/archipelago/treasure.rb', line 221

def object_id
  id = "Archipelago::Treasure::Dubloon:#{@key}@#{@chest_id}"
  id << ":#{@transaction.transaction_id}" if @transaction
  return id
end

#respond_to?(meth) ⇒ Boolean

Does our target respond to meth?

Returns:

  • (Boolean)


229
230
231
232
233
# File 'lib/archipelago/treasure.rb', line 229

def respond_to?(meth)
  # This one will be called far too often, and it seems safe to ignore it.
  return false if meth == :marshal_dump
  return super(meth) || self.method_missing(:respond_to?, meth)
end