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.

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.



156
157
158
159
160
161
# File 'lib/archipelago/treasure.rb', line 156

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.



221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
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
# File 'lib/archipelago/treasure.rb', line 221

def method_missing(meth, *args, &block)
  begin
    return @chest.call_instance_method(@key, meth, @transaction, *args, &block)
  rescue WrongChestException => e
    #
    # 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
    #
    # 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.



177
178
179
180
181
182
183
184
# File 'lib/archipelago/treasure.rb', line 177

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

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.



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

def ==(o)
  if 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.



165
166
167
168
169
170
171
172
# File 'lib/archipelago/treasure.rb', line 165

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.



197
198
199
# File 'lib/archipelago/treasure.rb', line 197

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

#eql?(o) ⇒ Boolean

Defers to Dubloon#==.

Returns:

  • (Boolean)


137
138
139
# File 'lib/archipelago/treasure.rb', line 137

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

#hashObject

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



131
132
133
# File 'lib/archipelago/treasure.rb', line 131

def hash
  self.object_id.hash
end

#join(transaction) ⇒ Object

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



189
190
191
192
# File 'lib/archipelago/treasure.rb', line 189

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.



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

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

#respond_to?(meth) ⇒ Boolean

Does our target respond to meth?

Returns:

  • (Boolean)


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

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