class PEROBS::Hash

A Hash that is transparently persisted in the back-end storage. It is very similar to the Ruby built-in Hash class but has some additional limitations. The hash key must always be a String.

The implementation is largely a proxy around the standard Hash class. But all mutating methods must be re-implemented to convert PEROBS::Objects to POXReference objects and to register the object as modified with the cache. However, it is not designed for large data sets as it always reads and writes the full data set for every access (unless it is cached). For data sets that could have more than a few hundred entries BigHash is the recommended alternative.

We explicitely don't support Hash::store() as it conflicts with ObjectBase::store() method to access the store.

Public Class Methods

new(p, default = nil, &block) click to toggle source

New PEROBS objects must always be created by calling # Store.new(). PEROBS users should never call this method or equivalents of derived methods directly. @param p [PEROBS::Handle] PEROBS handle @param default [Object] The default value that is returned when no value

is stored for a specific key. The default must be of the
supported type.
Calls superclass method
# File lib/perobs/Hash.rb, line 112
def initialize(p, default = nil, &block)
  super(p)
  _check_assignment_value(default)
  if block_given?
    @data = ::Hash.new(&block)
  else
    @data = ::Hash.new(default)
  end

  # Ensure that the newly created object will be pushed into the database.
  @store.cache.cache_write(self)
end

Public Instance Methods

[]=(key, value) click to toggle source

Proxy for assignment method.

# File lib/perobs/Hash.rb, line 126
def []=(key, value)
  unless key.is_a?(String) || key.respond_to?(:is_poxreference?)
    raise ArgumentError, "PEROBS::Hash[] key must be a String or " +
      "a PEROBS object but is a #{key.class}"
  end
  _check_assignment_value(value)
  @store.cache.cache_write(self)
  @data[key] = value
end
_delete_reference_to_id(id) click to toggle source

This method should only be used during store repair operations. It will delete all referenced to the given object ID. @param id [Integer] targeted object ID

# File lib/perobs/Hash.rb, line 162
def _delete_reference_to_id(id)
  original_length = @data.length

  @data.delete_if do |k, v|
    (k && k.respond_to?(:is_poxreference?) && k.id == id) ||
    (v && v.respond_to?(:is_poxreference?) && v.id == id)
  end

  if @data.length != original_length
    @store.cache.cache_write(self)
  end
end
_deserialize(data) click to toggle source

Restore the persistent data from a single data structure. This is a library internal method. Do not use outside of this library. @param data [Hash] the actual Hash object @private

# File lib/perobs/Hash.rb, line 179
def _deserialize(data)
  @data = {}

  data.each do |k, v|
    # References to other PEROBS Objects are marshalled with our own
    # format. If we detect such a marshalled String we convert it into a
    # POXReference object.
    if (match = /^#<PEROBS::POReference id=([0-9]+)>$/.match(k))
      k = POXReference.new(@store, match[1].to_i)
    end
    dv = v.is_a?(POReference) ? POXReference.new(@store, v.id) : v
    @data[k] = dv
  end

  @data
end
_referenced_object_ids() click to toggle source

Return a list of all object IDs of all persistend objects that this Hash is referencing. @return [Array of Integer] IDs of referenced objects

# File lib/perobs/Hash.rb, line 145
def _referenced_object_ids
  ids = []
  @data.each do |k, v|
    if k && k.respond_to?(:is_poxreference?)
      ids << k.id
    end
    if v && v.respond_to?(:is_poxreference?)
      ids << v.id
    end
  end

  ids
end
default=(value) click to toggle source

Proxy for default= method.

# File lib/perobs/Hash.rb, line 137
def default=(value)
  _check_assignment_value(value)
  @data.default=(value)
end
inspect() click to toggle source

Textual dump for debugging purposes @return [String]

# File lib/perobs/Hash.rb, line 198
def inspect
  "<#{self.class}:#{@_id}>\n{\n" +
  @data.map do |k, v|
    "  #{k.inspect} => " + (v.respond_to?(:is_poxreference?) ?
            "<PEROBS::ObjectBase:#{v._id}>" : v.inspect)
  end.join(",\n") +
  "\n}\n"
end

Private Instance Methods

_serialize() click to toggle source
# File lib/perobs/Hash.rb, line 209
def _serialize
  data = {}

  @data.each do |k, v|
    if k.respond_to?(:is_poxreference?)
      # JSON only supports Strings as hash keys. Since JSON is the default
      # internal storage format in the database, we have to marshall
      # PEROBS::Object references ourselves.
      k = "#<PEROBS::POReference id=#{k.id}>"
    elsif k[0..24] == '#<PEROBS::POReference id='
      # This could obviously result in conflicts with 'normal' String hash
      # keys. This is extremely unlikely, but we better catch this case
      # before it causes hard to debug trouble.
      raise ArgumentError, "Hash key #{k} conflicts with PEROBS " +
        "internal representation of marshalled hash keys!"
    end
    data[k] = serialize_helper(v)
  end

  data
end
serialize_helper(v) click to toggle source
# File lib/perobs/Hash.rb, line 231
def serialize_helper(v)
  if v.respond_to?(:is_poxreference?)
    # References to other PEROBS objects (POXReference) are stored as
    # POReference in the database.
    return POReference.new(v.id)
  else
    # Outside of the PEROBS library all PEROBS::ObjectBase derived
    # objects should not be used directly. The library only exposes them
    # via POXReference proxy objects.
    if v.is_a?(ObjectBase)
      PEROBS.log.fatal 'A PEROBS::ObjectBase object escaped! ' +
        "It is stored in a PEROBS::Hash. " +
        'Have you used self() instead of myself() to ' +
        "get the reference of this PEROBS object?\n" +
        v.inspect
    end

    # All other objects are serialized by their native methods.
    return v
  end
end