Module: NameMagic

Defined in:
lib/y_support/name_magic.rb

Overview

This mixin imitates Ruby constant magic and automates the named argument :name, alias :ɴ (Character “ɴ”, Unicode small capital N, generally stands for “name” ins YSupport). One can write:

require 'y_support/name_magic'
class Foo;
  include NameMagic
end
Bar = Foo.new

The resulting object will know its #name:

Bar.name #=> "Bar"

This is done by searching whole Ruby namespace for constants, via #const_magic defined in the namespace mixin. (Once the object is named, its assignments to other constants have no further effects.) By default, the class, in which NameMagic is included acts as a namespace: holds a list of instances and their names, which is often useful.

Foo.instances #=> [Bar]

It is possible to set another module as namespace:

Quux = Module.new
class FooBar
  include NameMagic
end
FooBar.namespace = Quux
FooBar.new name: "Baz"
Quux.instances #=> [Baz]

When subclassing the classes with NameMagic included, namespace setting does not change:

class Animal; include NameMagic end
class Dog < Animal; end
class Cat < Animal; end
Dog.namespace #=> Animal
Cat.namespace #=> Animal
Livia = Cat.new
Cat.instances._names_ #=> []
Animal.instances._names_ #=> [:Livia]

To make the subclasses use each their own namespace, use #namespace! method:

Dog.namespace!

NameMagic also provides an alternative way to create named objects by taking care of :name (alias :ɴ) named argument of the constructor:

Dog.new name: "Spot"
Dog.new ɴ: :Rover
Dog.instances._names_ #=> [:Spot, :Rover]
Animal.instances._names_ #=> []

Lastly, a name can be assigned by #name= accssor, as in

o = SomeClass.new o.name = "SomeName"

Hook is provided for when the name magic is performed, as well as when the name is retrieved.

Defined Under Namespace

Modules: ClassMethods, NamespaceMethods

Constant Summary collapse

DEBUG =
false

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.included(target) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/y_support/name_magic.rb', line 79

def self.included target
  if target.is_a? Class then # decorate #new
    target.singleton_class.class_exec do
      # Primer that sets the namespace of the class to self if the user has
      # not defined otherwise when this method is first called.
      # 
      define_method :namespace do
        target.extend ::NameMagic::NamespaceMethods
        define_singleton_method :namespace do target end # redefines itself
        namespace
      end
    end
    target.singleton_class.class_exec { prepend ::NameMagic::ClassMethods }
  else # it is a Module -- infect it with this #include
    orig, this = target.method( :included ), method( :included )
    target.define_singleton_method :included do |m| this.( m ); orig.( m ) end
  end
end

Instance Method Details

#__name__Object

Retrieves the instance name. Does not trigger #const_magic before doing so.



127
128
129
130
# File 'lib/y_support/name_magic.rb', line 127

def __name__
  ɴ = self.class.__instances__[ self ]
  namespace.name_get_hook.( ɴ ) if ɴ
end

#_name_Object Also known as: ɴ, name

Retrieves the instance’s name not prefixed by the namespace as a symbol. Underlines (#name) distinguish this method from #name method, which returns full name string for compatibility with vanilla Ruby Module#name.



108
109
110
111
# File 'lib/y_support/name_magic.rb', line 108

def _name_
  self.class.const_magic
  __name__ or ( yield self if block_given? )
end

#avid?Boolean

Is the instance avid for a name? (Will it overwrite other instance names?)

Returns:

  • (Boolean)


166
167
168
# File 'lib/y_support/name_magic.rb', line 166

def avid?
  namespace.__avid_instances__.any? &method( :equal? )
end

#full_nameObject

Returns the instance’s full name, a string in the style of those returned by Module#name method, eg. “Namespace::Name”.



119
120
121
# File 'lib/y_support/name_magic.rb', line 119

def full_name
  "#{namespace.name || namespace.inspect}::#{namespace.instances[ self ]}"
end

#inspectObject

Default #inspect method for NameMagic includers.



195
196
197
# File 'lib/y_support/name_magic.rb', line 195

def inspect
  to_s
end

#make_not_avid!Object

Make the instance not avid.



172
173
174
# File 'lib/y_support/name_magic.rb', line 172

def make_not_avid!
  namespace.__avid_instances__.delete_if { |i| i.object_id == object_id }
end

#name!(name) ⇒ Object

Names an instance, aggresively (overwrites existing names).



152
153
154
155
156
157
158
159
160
161
162
# File 'lib/y_support/name_magic.rb', line 152

def name!( name )
  old_ɴ = namespace.__instances__[ self ]   # previous name
  return self.name = nil if name.nil?       # no collider concerns
  ɴ = honor_name_set_hooks( name, old_ɴ )
  return false if old_ɴ == ɴ                # already named as required
  pair = namespace.__instances__.rassoc( ɴ )
  namespace.__forget__( pair[0] ) if pair   # rudely forget the collider
  namespace.__forget__ old_ɴ                # forget the old name of self
  namespace.const_set ɴ, self               # write a constant
  namespace.__instances__[ self ] = ɴ       # write to @instances
end

#name=(name) ⇒ Object

Names an instance, cautiously (ie. no overwriting of existing names).



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/y_support/name_magic.rb', line 134

def name=( name )
  old_ɴ = namespace.__instances__[ self ]         # previous name
  if name.nil? then
    namespace.__instances__.update( self => nil ) # unname in @instances
    namespace.send :remove_const, old_ɴ if old_ɴ  # remove namespace const.
  else
    ɴ = honor_name_set_hooks( name, old_ɴ )
    return if old_ɴ == ɴ                  # already named as required
    fail NameError, "Name '#{ɴ}' already exists in #{namespace} namespace!" if
      self.class.__instances__.rassoc( ɴ )
    namespace.__forget__ old_ɴ            # forget the old name of self
    namespace.const_set ɴ, self           # write a constant
    namespace.__instances__[ self ] = ɴ   # write to @instances
  end
end

#name_set_hook(&block) ⇒ Object

Registers a hook to execute upon instance naming. Instance’s ‘#name_set_hook` Behaves analogically as namespace’s ‘#name_set_hook`, and is executed right after the namespace’s hook. Expects a block with a single argument, name of the instance. The return value of the block is not used and should be nil. Without a block, this method acts as a getter.



182
183
184
185
# File 'lib/y_support/name_magic.rb', line 182

def name_set_hook &block
  tap { @name_set_hook = block } if block
  @name_set_hook ||= -> name { nil }
end

#namespaceObject

The namespace of the instance’s class.



100
101
102
# File 'lib/y_support/name_magic.rb', line 100

def namespace
  self.class.namespace
end

#to_sObject

Default #to_s method for NameMagic includers, returning the name.



189
190
191
# File 'lib/y_support/name_magic.rb', line 189

def to_s
  name ? name.to_s : super
end