Module: NameMagic::NamespaceMethods
- Defined in:
- lib/y_support/name_magic/namespace_methods.rb
Overview
Module methods for the modules serving as NameMagic
namespaces. A namespace for a certain class featuring NameMagic
holds “civil registry” of all its instances, be they named or nameless. For this purpose, the namespace owns @instances hash of pairs { instance => name }
, with nil values denoting nameless instances. For named instances, the namespace also holds references to them in constants in the style Namespace::Name
. This is one of the reasons why instance names in NameMagic
must start with a capital letter and generally must be usable as constant names. The list of instances is accessible via #instances
method. Individual instances can be queried by #instance
method, eg. by their names.
Life cycle of instances of classes featuring NameMagic
NameMagic
offers 3 hooks for the instances of its user classes. These hooks are closures invoked at the relevant points of the instances’ life cycle:
-
new instance hook – when the instance is created
-
name set hook – when the instance is offered a name
-
name get hook – when the instance’s name is queried
These three hooks are stored in instance variables owned by the namespace, accesible by methods +#new_instance_hook, #name_set_hook
and #name_get_hook
. If called with a block, these methods also serve to set their respective hook closures.
When an instance is first created, unary new_instance_hook
is called. When an instance is offered a name, name_set_hook
is called. It is a ternary closure with 3 ordered arguments name
, instance
and old_name
, receiving respectively the name offered, the instance, and the previous name of the instance (if any). The return value of the closure will be used to actually name the instance. This closure can thus be used to check and modify the names before they are actually used. Finally, when the instances’ name is queried, third closure, unary name_get_hook
is applied to modify the name output. The purpose of the name get hook is not to really change the name upon reading, but mainly to tweak the preferred form or spelling where multiple forms of are possible for the same name. (For example, the standard form in the @instances hash could be in camel case, such as “AcinonyxJubatus”, while preferred querying output would be a binomial name with whitespaces, “Acinonyx jubatus”.)
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 even if it is already in use, 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
A 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 then called 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, *args) ⇒ Object
Clears namespace-owned references to an instance, 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.
-
#forget(instance_identifier, *args) ⇒ Object
Clears namespace-owned references to a specified instance.
-
#forget_all_instances ⇒ Object
Clears namespace-owned references to all the instances.
-
#forget_nameless_instances ⇒ Object
Clears namespace-owned references to all the anonymous instances.
-
#instance(id, *args) ⇒ Object
Returns the instance identified by the argument, which can be typically a name (string/symbol).
-
#instance_names ⇒ Object
Deprecated method to get full names of the named instances.
-
#instances(*args) ⇒ Object
Presents the instances registered in this namespace.
-
#name_get_hook(&block) ⇒ Object
Registers a hook to execute whenever the instance is asked its name.
-
#name_set_hook(&block) ⇒ Object
Registers a hook to execute upon instance naming.
-
#nameless_instances(*args) ⇒ Object
Returns those instances, which are nameless (whose name is set to nil).
-
#new_instance_hook(&block) ⇒ Object
Registers a hook to execute upon instantiation.
-
#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 is able to steal (overwrite) a name from another registered instance. (The method does not trigger #const_magic
.)
109 110 111 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 109 def __avid_instances__ @avid_instances ||= [] end |
#__forget__(instance, *args) ⇒ Object
Clears namespace-owned references to an instance, without performing #const_magic first. The argument should be a registered instance. Returns the instance name, or false, if there was no such registered instance.
168 169 170 171 172 173 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 168 def __forget__( instance, *args ) 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
assigned to them as their name. (The method does not trigger #const_magic
.)
101 102 103 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 101 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.
136 137 138 139 140 141 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 136 def const_magic puts "#{self}#const_magic invoked!" if ::NameMagic::DEBUG return 0 if nameless_instances.size == 0 search_all_modules return nameless_instances.size end |
#forget(instance_identifier, *args) ⇒ Object
Clears namespace-owned references to a specified instance. (This is different from “unnaming” an instance by setting inst.name = nil
, which makes the instance anonymous, but still registered.)
153 154 155 156 157 158 159 160 161 162 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 153 def forget instance_identifier, *args inst = begin; instance( instance_identifier ); rescue ArgumentError return nil # nothing to forget end ɴ = inst.nil? ? nil : inst.name namespace.send :remove_const, ɴ if ɴ # clear constant assignment __instances__.delete( inst ) # remove @instances entry __avid_instances__.delete( inst ) # remove if any return inst # return the forgotten instance end |
#forget_all_instances ⇒ Object
Clears namespace-owned references to all the instances.
186 187 188 189 190 191 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 186 def forget_all_instances __instances__.clear constants( false ).each { |sym| namespace.send :remove_const, sym if const_get( sym ).is_a? self } end |
#forget_nameless_instances ⇒ Object
Clears namespace-owned references to all the anonymous instances.
177 178 179 180 181 182 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 177 def forget_nameless_instances nameless_instances.each { |inst| __instances__.delete( inst ) __avid_instances__.delete( inst ) # also from avid instances } end |
#instance(id, *args) ⇒ Object
Returns the instance identified by the argument, which can be typically a name (string/symbol). If a registered instance is supplied, it will be returned unchanged.
117 118 119 120 121 122 123 124 125 126 127 128 129 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 117 def instance id, *args # puts "#instance( #{identifier} )" if DEBUG # In @instances hash, value 'nil' indicates a nameless instance! fail TypeError, "'nil' is not an instance identifier!" if id.nil? ii = instances return id if ii.include? id # return the instance back begin # identifier not a registered instance -- treat it as a name ary = [id, id.to_sym] ihsh = __instances__ ii.find { |inst| ary.include? ihsh[ inst ] or ary.include? inst.name } rescue NoMethodError end or fail NameError, "No instance #{id} in #{self}." end |
#instance_names ⇒ Object
Deprecated method to get full names of the named instances.
91 92 93 94 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 91 def instance_names warn "Method #instance_names is deprecated. Use 'instances._names_' or 'instances.names' instead!" instances.names false end |
#instances(*args) ⇒ Object
Presents the instances registered in this namespace.
84 85 86 87 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 84 def instances *args const_magic __instances__.keys end |
#name_get_hook(&block) ⇒ Object
Registers a hook to execute whenever the instance is asked its name. The instance names are objects that are kept in a hash referred to by @instances variable owned by the namespace. Normally, NameMagic#name simply returns the name of the instance, as found in the @instances hash. When name_get_hook
is defined, this name is transformed by it before being returned. Without a block, this method acts as a getter.
223 224 225 226 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 223 def name_get_hook &block @name_get_hook = block if block @name_get_hook ||= -> name { name } end |
#name_set_hook(&block) ⇒ Object
Registers a hook to execute upon instance naming. Expects a ternary block, with arguments name, instance and old_name, representing respectively the the requested name, the instance to be named, and the previous name of that instance (if any). The output of the block should be the name to actually be used. In other words, the hook can be used (among other things) to check and/or modify the requested name when christening the instance. It is the responsibility of this block to output a symbol that can be used as a Ruby constant name. Without a block, this method acts as a getter.
211 212 213 214 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 211 def name_set_hook &block @name_set_hook = block if block @name_set_hook ||= -> name, instance, old_name=nil { name } end |
#nameless_instances(*args) ⇒ Object
Returns those instances, which are nameless (whose name is set to nil).
145 146 147 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 145 def nameless_instances *args __instances__.select { |key, val| val.nil? }.keys end |
#new_instance_hook(&block) ⇒ Object
Registers a hook to execute upon instantiation. Expects a unary block, whose argument represents the new instance. It is called right after instantiation, but before naming the instance. Without a block, it acts as a getter.
197 198 199 200 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 197 def new_instance_hook &block @new_instance_hook = block if block @new_instance_hook ||= -> instance { instance } end |
#validate_name(name) ⇒ Object
Checks whether a name is acceptable as a constant name.
230 231 232 233 234 235 |
# File 'lib/y_support/name_magic/namespace_methods.rb', line 230 def validate_name name name.to_s.tap do |ɴ| # check if the name starts with 'A'..'Z' fail NameError, "#{self}-registered name must start with a capital " + " letter 'A'..'Z' ('#{ɴ}' given)!" unless ( ?A..?Z ) === ɴ[0] end end |