A registry object.
Add the given object to the registry. You must specify the type of object, which is used to determine what tuples correspond to it.
# File lib/idregistry/registry.rb, line 260 def add(type_, object_) @config.lock # Some sanity checks of the arguments. if object_.nil? raise ObjectKeyError, "Attempt to add a nil object" end unless @types.has_key?(type_) raise ObjectKeyError, "Unrecognized type: #{type_}" end # Synchronize the actual add to protect against concurrent mutation. @mutex.synchronize do _internal_add(type_, object_, nil, nil) end self end
Return all the categories for the given object or tuple.
If you pass an Array, it is interpreted as a tuple. If you pass something
other than an Array or a Hash, it is interpreted as an object. Otherwise,
you can explicitly specify whether you are passing a tuple or object by
using hash named arguments, e.g. :tuple =>
, or
:object =>
.
The return value is a hash. The keys are the category types relevant to this object. The values are the value arrays indicating which category the object falls under for each type.
# File lib/idregistry/registry.rb, line 150 def categories(arg_) @config.lock objdata_ = _get_objdata(arg_) return nil unless objdata_ hash_ = {} objdata_[2].each do |tup_, tupcats_| tupcats_.each do |cat_| hash_[cat_] = @categories[cat_][1].map{ |elem_| tup_[elem_] } end end hash_ end
Clear out all cached objects from the registry.
# File lib/idregistry/registry.rb, line 389 def clear @mutex.synchronize do @tuples.clear @objects.clear @catdata.clear end self end
Get the configuration for this registry.
You may also configure this registry by providing a block. The configuration object will then be available as a DSL.
# File lib/idregistry/registry.rb, line 68 def config(&block_) ::Blockenspiel.invoke(block_, @config) if block_ @config end
Delete the given object.
If you pass an Array, it is interpreted as a tuple. If you pass something
other than an Array or a Hash, it is interpreted as an object. Otherwise,
you can explicitly specify whether you are passing a tuple or object by
using hash named arguments, e.g. :tuple =>
, or
:object =>
.
# File lib/idregistry/registry.rb, line 288 def delete(arg_) @config.lock @mutex.synchronize do if (objdata_ = _get_objdata(arg_)) @objects.delete(objdata_[0].object_id) objdata_[2].each_key{ |tup_| _remove_tuple(objdata_, tup_) } end end self end
Delete all objects in a given category, which is specified by the category type and the value array indicating which category of that type.
# File lib/idregistry/registry.rb, line 305 def delete_category(category_type_, *category_spec_) @config.lock if @categories.include?(category_type_) spec_ = category_spec_.size == 1 && category_spec_.first.is_a?(::Array) ? category_spec_.first : category_spec_ if (tuple_hash_ = (@catdata[category_type_] ||= {})[spec_]) @mutex.synchronize do tuple_hash_.values.each do |objdata_| @objects.delete(objdata_[0].object_id) objdata_[2].each_key{ |tup_| _remove_tuple(objdata_, tup_) } end end end end self end
Retrieve the cached object corresponding to the given tuple. Returns nil if the object is not currently cached. Does not attempt to generate the object for you.
# File lib/idregistry/registry.rb, line 100 def get(tuple_) objdata_ = @tuples[tuple_] objdata_ ? objdata_[0] : nil end
Returns true if the given object or tuple is present.
If you pass an Array, it is interpreted as a tuple. If you pass something
other than an Array or a Hash, it is interpreted as an object. Otherwise,
you can explicitly specify whether you are passing a tuple or object by
using hash named arguments, e.g. :tuple =>
, or
:object =>
.
# File lib/idregistry/registry.rb, line 132 def include?(arg_) _get_objdata(arg_) ? true : false end
Get the object corresponding to the given tuple. If the tuple is not present, the registry tries to generate the object for you. Returns nil if it is unable to do so.
You may pass the tuple as a single array argument, or as a set of arguments.
If the last argument is a hash, it is removed from the tuple and treated as an options hash that may be passed to an object generator block.
# File lib/idregistry/registry.rb, line 204 def lookup(*args_) opts_ = args_.last.is_a?(::Hash) ? args_.pop : {} tuple_ = args_.size == 1 && args_.first.is_a?(::Array) ? args_.first : args_ @config.lock # Fast-track lookup if it's already there if (objdata_ = @tuples[tuple_]) return objdata_[0] end # Not there for now. Try to create the object. # We want to do this before entering the synchronize block because # we don't want callbacks called within the synchronization. obj_ = nil type_ = nil pattern_ = nil @patterns.each do |pat_, patdata_| if Utils.matches?(pat_, tuple_) block_ = patdata_[1] obj_ = case block_.arity when 0 then block_.call when 1 then block_.call(tuple_) when 2 then block_.call(tuple_, self) else block_.call(tuple_, self, opts_) end unless obj_.nil? pattern_ = pat_ type_ = patdata_[0] break end end end if obj_ # Now attempt to insert the object. # This part is synchronized to protect against concurrent mutation. # Once in the synchronize block, we also double-check that no other # thread added the object in the meantime. If another thread did, # we throw away the object we just created, and return the other # thread's object instead. @mutex.synchronize do if (objdata_ = @tuples[tuple_]) obj_ = objdata_[0] else _internal_add(type_, obj_, tuple_, pattern_) end end end obj_ end
Return all objects in a given category, which is specified by the category type and the value array indicating which category of that type.
# File lib/idregistry/registry.rb, line 169 def objects_in_category(category_type_, *category_spec_) @config.lock return nil unless @categories.include?(category_type_) spec_ = category_spec_.size == 1 && category_spec_.first.is_a?(::Array) ? category_spec_.first : category_spec_ tuple_hash_ = (@catdata[category_type_] ||= {})[spec_] tuple_hash_ ? tuple_hash_.values.map{ |objdata_| objdata_[0] } : [] end
Recompute the tuples for the given object, which may be identified by object or tuple. Call this when the value of the object changes in such a way that the registry should identify it differently.
If you pass an Array, it is interpreted as a tuple. If you pass something
other than an Array or a Hash, it is interpreted as an object. Otherwise,
you can explicitly specify whether you are passing a tuple or object by
using hash named arguments, e.g. :tuple =>
, or
:object =>
.
# File lib/idregistry/registry.rb, line 334 def rekey(arg_) @config.lock # Resolve the object. if (objdata_ = _get_objdata(arg_)) # Look up tuple generators from the type, and determine the # new tuples for the object. # Do this before entering the synchronize block because we # don't want callbacks called within the synchronization. obj_ = objdata_[0] type_ = objdata_[1] new_tuple_list_ = [] @types[type_].each do |pat_| if (block_ = @patterns[pat_][2]) new_tuple_ = block_.call(obj_) new_tuple_list_ << new_tuple_ if new_tuple_ else raise ObjectKeyError, "Not all patterns for this type can generate tuples" end end # Synchronize to protect against concurrent mutation. @mutex.synchronize do # One last check to ensure the object is still present if @objects.has_key?(obj_.object_id) # Ensure none of the new tuples isn't pointed elsewhere already. # Tuples pointed at this object, ignore them. # Tuples pointed at another object, raise an error. tuple_hash_ = objdata_[2] new_tuple_list_.delete_if do |tup_| if tuple_hash_.has_key?(tup_) true elsif @tuples.has_key?(tup_) raise ObjectKeyError, "Could not rekey because one of the new tuples is already present" else false end end # Now go through and edit the tuples (tuple_hash_.keys - new_tuple_list_).each do |tup_| _remove_tuple(objdata_, tup_) end new_tuple_list_.each do |tup_| _add_tuple(objdata_, tup_) end end end end self end
Return the number of objects cached in the registry.
# File lib/idregistry/registry.rb, line 91 def size @objects.size end
Create a new empty registry, duplicating this registry’s configuration.
If the :unlocked
option is set to true, the new registry will
have an unlocked configuration that can be modified further. Otherwise, the
new registry’s configuration will be locked.
Spawning a locked registry from a locked configuration is very fast because it reuses the configuration objects.
# File lib/idregistry/registry.rb, line 84 def spawn_registry(opts_={}) config.spawn_registry(opts_) end
Returns an array of all tuples corresponding to the given object, or the object identified by the given tuple. Returns nil if the given object is not cached in the registry.
If you pass an Array, it is interpreted as a tuple. If you pass something
other than an Array or a Hash, it is interpreted as an object. Otherwise,
you can explicitly specify whether you are passing a tuple or object by
using hash named arguments, e.g. :tuple =>
, or
:object =>
.
# File lib/idregistry/registry.rb, line 117 def tuples_for(arg_) objdata_ = _get_objdata(arg_) objdata_ ? objdata_[2].keys : nil end
Return all tuples in a given category, which is specified by the category type and the value array indicating which category of that type.
# File lib/idregistry/registry.rb, line 183 def tuples_in_category(category_type_, *category_spec_) @config.lock return nil unless @categories.include?(category_type_) spec_ = category_spec_.size == 1 && category_spec_.first.is_a?(::Array) ? category_spec_.first : category_spec_ tuple_hash_ = (@catdata[category_type_] ||= {})[spec_] tuple_hash_ ? tuple_hash_.keys : [] end