Module: NameMagic::Namespace
- Defined in:
- lib/y_support/name_magic/namespace.rb
Overview
Module methods for the modules serving as NameMagic
namespaces. What is a NameMagic
namespace? For a class that includes NameMagic
, namespace is the “civil registry” of all instances, both named and nameless. For this purpose, namespace has variable @instances. The registry of instances is a hash of pairs { instance => name }
. Nameless instances have nil value instead of name in the registry. In general Ruby, namespace would mean that the module holds the instances in constants looking like Namespace::Name
. In Ruby core, we can see such behavior with Struct class, which has native constant magic and stores its instances in constants using Struct as a namespace. NameMagic used to perform this constant assignment in the namespace prior to YSupport version ~2.0. This is one of the reasons why names of instances must start with a capital letter and be usable as constant names. Since YSupport version 2.0+, NameMagic does no constant assignment on its own – all constant assignments are left to the user. In this way, NameMagic now sees constant assignment purely as a way to learn instance names intended by the user.
The instance registry is accessible via #instances
method. Individual instances can be queried for by #instance
method, eg. by their names. Until Matz provides the possibility of constant magic in every class, which I requested some time ago, the registry of instances will remain the essential part, without which +NameMagic wouldn’t work.
Life cycle of instances of NameMagic
user classes
Let us consider for example Human class that uses NameMagic.
class Human
require 'y_support/name_magic'
include NameMagic
end
Life cycle of Human in instances of begins, unsurprisingly, by instantiation. All instances are created nameless:
newborn = Human.new newborn.name #=> nil
User has several ways of naming the instances. Typical for NameMagic
is naming by constant assignment:
Fred = newborn newborn.name #=> :Fred
+NameMagic makes it possible to supply :name parameter directly to the #new method. Such instances are named immediately:
newborn = Human.new name: “Joe” newborn.name #=> :Joe
Another way is to name the instances using #name= method.
newborn = Human.new newborn.name #=> nil newborn.name = “Mike” newborn.name #=> Mike
Just for the record, we have created three instances:
Human.instances #=> [Fred, Joe, Mike]
In other words, at some point in their life, instances may or may not undergo baptism, which involves a complicated procedure of searching all existing Ruby modules for constants to which nameless instances of the class in question are assigned. Baptized instances then know their names, and can be accessed by their names through the instance registry:
Human.instance( “Mike” ) #=> Mike
Namespace gives the user 2 hook methods, #instantiation_exec and #exec_when_naming. The first one is executed upon instantiation and passed one argument, the new instance. The second one is executed when the namespace baptizes a new instance, and passed three arguments: suggested name, instance, and previous name if any. (Renaming instances may require special care.) Consequantly, you should define unary block with #instantiation_exec and ternary one with #exec_when_naming. Example:
Human.instantiation_exec do |instance|
puts "Instance with object id #{instance.object_id} created!"
end newborn = Human.new #=> Instance with object id 75756140 created!
The naming hook can also be used to censor and modify the intended name. Consider the following censorship:
Human.exec_when_naming do |name, instance, old_name|
fail NameError, "#{name.capitalize} is not a saint in the " +
"Church of Emacs!" unless name.end_with? "gnucius"
"St_IGNUcius"
end
Now we can no longer use ordinary names, we have to use names of saints in the Church of Emacs!
newborn.name = “Dave” #=> NameError: Dave is not a saint in the Church of Emacs!
Name Ignucius is OK, but the censor corrects it to St_IGNUcius:
newborn.name = “Ignucius” #=> St_IGNUcius
Life cycle of an instance ends when it is deleted from the instance registry and garbage-collected (unless something else holds a reference to it). Example:
Human.instances #=> [Fred, Joe, Mike, St_IGNUcius] Human.forget “St_IGNUcius” Human.instances #=> [Fred, Joe, Mike]
St. IGNUcius has just been deleted from the registry.
Human.forget_all_instances Human.instances #=> []
All Human instances have now been deleted from the registry, but only Joe and Mike are garbage-collected, because Fred is still assigned to a constant. Fred still exists, but he and his name has been deleted from the instance registry.
Fred #=> #<Human:0x89449b0>
We could even re-register Fred by recreating his entry, although this is far from the way NameMagic
works in everyday life.
Human.__instances__.merge! Fred => :Fred Human.instances #=> [Fred]
Avidity of the instances
After the offered name is checked and modified by the name set hook closure, there is one more remaining problem to worry about: Whether the name is already used by another instance in the same namespace. If the name is taken, the ensuing action depends on whether the instance being named is avid. Avid instances are so eager to get a name, that they will steal the offered name for themselves even if other instances already use the name, making the conflicting instance nameless in the process. In NameMagic
, it turns out to be convenient to make the new instances avid by default, unless the name was explicitly supplied to the constructor by :name
argument, or avidity suppressed by setting +:name_avid option to false.
Techincally, avid instances are registered as an array kept by the namespace under the variable @avid_instances.
Forgetting instances
As mentioned earlier, namespace can de-register, or forget instances. For this purpose, see methods #forget
, +#__forget__, #forget_nameless_instances
, #forget_all_instances
.
Ersatz constant magic
To imitate built-in constant magic of some Ruby classes, NamespaceMethods
provides ersatz method #const_magic
, that searches all the modules in the object space for the pertinent instances newly assigned to constants. Method #const_magic
is called automatically before executing almost every public method of NameMagic
, thus keeping the “civil registry” up-to-date. While not exactly computationally efficient, it tends to make the user code more readable and pays off in most usecases. For efficiency, we are looking forward to the #const_assigned
hook promised by Ruby core team…
The namespace method versions that do not perform ersatz constant magic are generally denoted by underlines: Eg. methods #__instances__
and #__forget__
do not perform constant magic, while #instances
and #forget
do.
Instance Method Summary collapse
-
#__avid_instances__ ⇒ Object
Avid instances registered in this namespace.
-
#__forget__(instance) ⇒ Object
Removes the specified instance from the registry, without performing #const_magic first.
-
#__instances__ ⇒ Object
Presents namespace-owned @instances hash.
-
#const_magic ⇒ Object
Searches all the modules in the the object space for constants referring to receiver class objects, and names the found instances accordingly.
-
#exec_when_naming(&block) ⇒ Object
(also: #name_set_hook)
Registers a block to execute just prior to naming of an instance.
-
#exec_when_unnaming(&block) ⇒ Object
Registers a block to execute just prior to unnaming of an instance.
-
#forget(instance, *args) ⇒ Object
Removes the specified instance from the registry.
-
#forget_all_instances ⇒ Object
Clears references to all the instances.
-
#forget_nameless_instances ⇒ Object
Removes all anonymous instances from the registry.
-
#instance(arg) ⇒ Object
Returns the instance identified by the argument.
-
#instances(*args) ⇒ Object
Presents the instances registered in this namespace.
-
#instantiation_exec(&block) ⇒ Object
(also: #new_instance_hook)
Registers a block to execute when a new instance of the
NameMagic
user class is created. -
#nameless_instances(*args) ⇒ Object
Returns those instances, whose name is nil.
-
#permanent_names! ⇒ Object
Orders the namespace to disallow unnaming instances.
-
#permanent_names? ⇒ Boolean
Inquirer whether unnaming instances has been disallowed in the namespace.
-
#validate_name(name) ⇒ Object
Checks whether a name is acceptable as a constant name.
Instance Method Details
#__avid_instances__ ⇒ Object
Avid instances registered in this namespace. (“Avid” means that the instance will steal (overwrite) a name from another instance, should there be a conflict. The method does not trigger #const_magic
.
214 215 216 |
# File 'lib/y_support/name_magic/namespace.rb', line 214 def __avid_instances__ @avid_instances ||= [] end |
#__forget__(instance) ⇒ Object
Removes the specified instance from the registry, without performing #const_magic first. The argument should be a registered instance. Returns instance name for forgotten named instances, nil for forgotten nameless instances, and false if the argument was not a registered instance.
278 279 280 281 282 283 |
# File 'lib/y_support/name_magic/namespace.rb', line 278 def __forget__ instance return false unless __instances__.keys.include? instance # namespace.send :remove_const, instance.name if instance.name __avid_instances__.delete( instance ) __instances__.delete instance end |
#__instances__ ⇒ Object
Presents namespace-owned @instances hash. The hash consists of pairs { instance => instance_name }
. Unnamed instances have nil
value instead of their name. This method does not trigger #const_magic
.
205 206 207 |
# File 'lib/y_support/name_magic/namespace.rb', line 205 def __instances__ @instances ||= {} end |
#const_magic ⇒ Object
Searches all the modules in the the object space for constants referring to receiver class objects, and names the found instances accordingly. Internally, it works by invoking private procedure +#search_all_modules. The return value is the remaining number of nameless instances.
241 242 243 244 245 |
# File 'lib/y_support/name_magic/namespace.rb', line 241 def const_magic return 0 if nameless_instances.size == 0 search_all_modules return nameless_instances.size end |
#exec_when_naming(&block) ⇒ Object Also known as: name_set_hook
Registers a block to execute just prior to naming of an instance. (In other words, this method provides user class’es naming hook.) The block will be executed in the context of the user class and will be supplied three ordered arguments: suggested name, instance, and previous name. The block should thus be written as ternary, expecting these three arguments. The block can be used to validate / censor the suggested name and for this reason, it should return the censored name that will actually be requested for the instance. (Of course, just like there is no duty to use this hook, if you do use it, there is likewise no duty to censor the suggested name in it. You can just return the suggested name unchanged from the block.) The point is that the return value of the block will actually be used to name the instance. If no block is given, the method returns the previously defined block, if any, or a default block that does nothing and returns the suggested name without any changes.
338 339 340 341 342 |
# File 'lib/y_support/name_magic/namespace.rb', line 338 def exec_when_naming &block @exec_when_naming = block if block @exec_when_naming ||= -> name, instance, previous_name=nil { name } end |
#exec_when_unnaming(&block) ⇒ Object
Registers a block to execute just prior to unnaming of an instance. (In other words, this method provides user class’es unnaming hook.) The block will be executed in the context of the user class and will be supplied two ordered arguments: instance and its previous name. The block can thus be written as up to binary. Return value of the block is unimportant. If no block is given, the method returns the block defined earlier, if any, or a default block that does nothing.
355 356 357 358 |
# File 'lib/y_support/name_magic/namespace.rb', line 355 def exec_when_unnaming &block @exec_when_unnaming = block if block @exec_when_unnaming ||= -> instance, previous_name=nil { } end |
#forget(instance, *args) ⇒ Object
Removes the specified instance from the registry. Note that this is different from “unnaming” an instance by setting inst.name = nil
, which makes the instance anonymous, but still registered.
259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/y_support/name_magic/namespace.rb', line 259 def forget instance, *args instance = begin instance instance rescue ArgumentError return nil # nothing to forget end ɴ = instance.nil? ? nil : instance.name # namespace.send :remove_const, ɴ if ɴ __instances__.delete( instance ) __avid_instances__.delete( instance ) return instance end |
#forget_all_instances ⇒ Object
Clears references to all the instances.
297 298 299 300 301 302 |
# File 'lib/y_support/name_magic/namespace.rb', line 297 def forget_all_instances instances.map { |instance| __forget__ instance } # constants( false ).each { |sym| # namespace.send :remove_const, sym if # const_get( sym ).is_a? self } end |
#forget_nameless_instances ⇒ Object
Removes all anonymous instances from the registry.
287 288 289 290 291 292 293 |
# File 'lib/y_support/name_magic/namespace.rb', line 287 def forget_nameless_instances const_magic # #nameless_instances doesn't trigger it nameless_instances.each { |instance| __instances__.delete( instance ) __avid_instances__.delete( instance ) } end |
#instance(arg) ⇒ Object
Returns the instance identified by the argument.
220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/y_support/name_magic/namespace.rb', line 220 def instance arg # In @instances hash, nil value denotes nameless instances! fail TypeError, "Nil is not an instance identifier!" if arg.nil? # Get the list of all instances. ii = instances # If arg belongs to the list, just return it back. return arg if ii.any? { |i| arg.equal? i } # Assume that arg is an instance name. name = arg.to_sym registry = __instances__ ii.find { |i| registry[ i ] == name } or fail NameError, "No instance #{arg} in #{self}!" end |
#instances(*args) ⇒ Object
Presents the instances registered in this namespace.
195 196 197 198 |
# File 'lib/y_support/name_magic/namespace.rb', line 195 def instances *args const_magic __instances__.keys end |
#instantiation_exec(&block) ⇒ Object Also known as: new_instance_hook
Registers a block to execute when a new instance of the NameMagic
user class is created. (In other words, this method provides user class’es instantiation hook.) Expects a unary block, whose argument is the new instance. Return value of the block is unimportant. The block will be executed in the context of the user class. If no block is given, the method returns the previously defined block, if any, or a default block that does nothing.
313 314 315 316 |
# File 'lib/y_support/name_magic/namespace.rb', line 313 def instantiation_exec &block @instantiation_exec = block if block @instantiation_exec ||= -> instance { } end |
#nameless_instances(*args) ⇒ Object
Returns those instances, whose name is nil. This method does not trigger #const_magic.
250 251 252 |
# File 'lib/y_support/name_magic/namespace.rb', line 250 def nameless_instances *args __instances__.select { |key, val| val.nil? }.keys end |
#permanent_names! ⇒ Object
Orders the namespace to disallow unnaming instances. As a consequence, the instances’ names will now be permanent.
182 183 184 |
# File 'lib/y_support/name_magic/namespace.rb', line 182 def permanent_names! @permanent_names = true end |
#permanent_names? ⇒ Boolean
Inquirer whether unnaming instances has been disallowed in the namespace.
189 190 191 |
# File 'lib/y_support/name_magic/namespace.rb', line 189 def permanent_names? @permanent_names end |
#validate_name(name) ⇒ Object
Checks whether a name is acceptable as a constant name.
362 363 364 365 366 367 368 369 370 |
# File 'lib/y_support/name_magic/namespace.rb', line 362 def validate_name name n = name.to_s # rejecting non-capitalized names fail NameError unless ( ?A..?Z ) === n.chars.first # rejecting names with spaces fail NameError if n.chars.include? ' ' # Return value is the validated name. return name end |