Class: IDRegistry::Registry
- Inherits:
-
Object
- Object
- IDRegistry::Registry
- Defined in:
- lib/idregistry/registry.rb
Overview
A registry object.
Instance Method Summary collapse
-
#add(type_, object_) ⇒ Object
Add the given object to the registry.
-
#categories(arg_) ⇒ Object
Return all the categories for the given object or tuple.
-
#clear ⇒ Object
Clear out all cached objects from the registry.
-
#config(&block_) ⇒ Object
Get the configuration for this registry.
-
#delete(arg_) ⇒ Object
Delete the given object.
-
#delete_category(category_type_, *category_spec_) ⇒ Object
Delete all objects in a given category, which is specified by the category type and the value array indicating which category of that type.
-
#get(tuple_) ⇒ Object
Retrieve the cached object corresponding to the given tuple.
-
#include?(arg_) ⇒ Boolean
Returns true if the given object or tuple is present.
-
#initialize(patterns_, types_, categories_, methods_) ⇒ Registry
constructor
:nodoc:.
-
#inspect ⇒ Object
:nodoc:.
-
#lookup(*args_) ⇒ Object
Get the object corresponding to the given tuple.
-
#method_missing(name_, *args_) ⇒ Object
Implement convenience methods.
-
#objects_in_category(category_type_, *category_spec_) ⇒ Object
Return all objects in a given category, which is specified by the category type and the value array indicating which category of that type.
-
#rekey(arg_) ⇒ Object
Recompute the tuples for the given object, which may be identified by object or tuple.
-
#respond_to?(name_) ⇒ Boolean
Make sure respond_to does the right thing for convenience methods.
-
#size ⇒ Object
Return the number of objects cached in the registry.
-
#spawn_registry(opts_ = {}) ⇒ Object
Create a new empty registry, duplicating this registry’s configuration.
-
#tuples_for(arg_) ⇒ Object
Returns an array of all tuples corresponding to the given object, or the object identified by the given tuple.
-
#tuples_in_category(category_type_, *category_spec_) ⇒ Object
Return all tuples in a given category, which is specified by the category type and the value array indicating which category of that type.
Constructor Details
#initialize(patterns_, types_, categories_, methods_) ⇒ Registry
:nodoc:
45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/idregistry/registry.rb', line 45 def initialize(patterns_, types_, categories_, methods_) # :nodoc: @patterns = patterns_ @types = types_ @categories = categories_ @methods = methods_ @tuples = {} @objects = {} @catdata = {} @config = Configuration._new(self, @patterns, @types, @categories, @methods) @mutex = ::Mutex.new end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(name_, *args_) ⇒ Object
Implement convenience methods.
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 |
# File 'lib/idregistry/registry.rb', line 401 def method_missing(name_, *args_) # :nodoc: if (method_info_ = @methods[name_]) tuple_ = method_info_[0].dup indexes_ = method_info_[1] case indexes_ when ::Array lookup_args_ = args_.size == indexes_.size + 1 ? args_.pop : {} if lookup_args_.is_a?(::Hash) && args_.size == indexes_.size args_.each_with_index do |a_, i_| if (j_ = indexes_[i_]) tuple_[j_] = a_ end end return lookup(tuple_, lookup_args_) end when ::Hash lookup_args_ = args_.size == 2 ? args_.pop : {} if lookup_args_.is_a?(::Hash) && args_.size == 1 arg_ = args_[0] if arg_.is_a?(::Hash) arg_.each do |k_, v_| if (j_ = indexes_[k_]) tuple_[j_] = v_ end end return lookup(tuple_, lookup_args_) end end end end super end |
Instance Method Details
#add(type_, object_) ⇒ 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.
260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# 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 |
#categories(arg_) ⇒ Object
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.
150 151 152 153 154 155 156 157 158 159 160 161 162 |
# 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 ⇒ Object
Clear out all cached objects from the registry.
389 390 391 392 393 394 395 396 |
# File 'lib/idregistry/registry.rb', line 389 def clear @mutex.synchronize do @tuples.clear @objects.clear @catdata.clear end self end |
#config(&block_) ⇒ Object
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.
68 69 70 71 |
# File 'lib/idregistry/registry.rb', line 68 def config(&block_) ::Blockenspiel.invoke(block_, @config) if block_ @config end |
#delete(arg_) ⇒ Object
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 =>
.
288 289 290 291 292 293 294 295 296 297 298 |
# 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_category(category_type_, *category_spec_) ⇒ Object
Delete all objects in a given category, which is specified by the category type and the value array indicating which category of that type.
305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# 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 |
#get(tuple_) ⇒ Object
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.
100 101 102 103 |
# File 'lib/idregistry/registry.rb', line 100 def get(tuple_) objdata_ = @tuples[tuple_] objdata_ ? objdata_[0] : nil end |
#include?(arg_) ⇒ Boolean
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 =>
.
132 133 134 |
# File 'lib/idregistry/registry.rb', line 132 def include?(arg_) _get_objdata(arg_) ? true : false end |
#inspect ⇒ Object
:nodoc:
58 59 60 |
# File 'lib/idregistry/registry.rb', line 58 def inspect # :nodoc: "#<#{self.class}:0x#{object_id.to_s(16)} size=#{size}>" end |
#lookup(*args_) ⇒ Object
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.
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 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 |
# 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 |
#objects_in_category(category_type_, *category_spec_) ⇒ Object
Return all objects in a given category, which is specified by the category type and the value array indicating which category of that type.
169 170 171 172 173 174 175 176 |
# 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 |
#rekey(arg_) ⇒ Object
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 =>
.
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 |
# 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 |
#respond_to?(name_) ⇒ Boolean
Make sure respond_to does the right thing for convenience methods
437 438 439 |
# File 'lib/idregistry/registry.rb', line 437 def respond_to?(name_) # :nodoc: super || @methods.include?(name_.to_sym) end |
#size ⇒ Object
Return the number of objects cached in the registry.
91 92 93 |
# File 'lib/idregistry/registry.rb', line 91 def size @objects.size end |
#spawn_registry(opts_ = {}) ⇒ Object
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.
84 85 86 |
# File 'lib/idregistry/registry.rb', line 84 def spawn_registry(opts_={}) config.spawn_registry(opts_) end |
#tuples_for(arg_) ⇒ Object
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 =>
.
117 118 119 120 |
# File 'lib/idregistry/registry.rb', line 117 def tuples_for(arg_) objdata_ = _get_objdata(arg_) objdata_ ? objdata_[2].keys : nil end |
#tuples_in_category(category_type_, *category_spec_) ⇒ Object
Return all tuples in a given category, which is specified by the category type and the value array indicating which category of that type.
183 184 185 186 187 188 189 190 |
# 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 |