Module: NameMagic

Defined in:
lib/y_support/name_magic.rb,
lib/y_support/name_magic/namespace_methods.rb

Overview

This mixin imitates Ruby constant magic and automates the named argument :name (alias :ɴ). One can write:

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

and the resulting object will know its #name:

Bar.name #=> :Bar
Foo::Bar #=> <Foo:0x.....>

This is done by searching whole Ruby namespace for constants, triggered by the method #const_magic defined in the namespace mixin. (Once the object is named, subsequent constant assignments have no effects.) By default, the namespace is the class, in which NameMagic is included, but it is possible to prescribe another module as a namespace:

Quux = Module.new
class FooBar
  include NameMagic
  self.namespace = Quux
end
FooBar.new name: "Baz"
FooBar::Baz #=> NameError
Quux::Baz #=> <FooBar:0x.....>

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.instance_names #=> []
Animal.instance_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.instance_names #=> [:Spot, :Rover]
Animal.instance_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(modul) ⇒ Object



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
# File 'lib/y_support/name_magic.rb', line 71

def self.included modul
  if modul.is_a? Class then # decorate #new
    class << modul
      alias :new_before_name_magic :new
    end
    modul.extend NameMagic::NamespaceMethods
    modul.extend NameMagic::ClassMethods
    # Attach namespace methods also to the namespace, if given
    begin
      if modul.namespace == modul then
        modul.define_singleton_method :namespace do modul end
      else
        modul.namespace.extend NameMagic::NamespaceMethods
      end
    rescue NoMethodError
    end
  else # it is a Module -- infect it with this #include
    orig, this = modul.method( :included ), method( :included )
    modul.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.



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

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

#nameObject Also known as: ɴ

Retrieves an instance name.



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

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

#name!(ɴ) ⇒ Object

Names an instance, aggresively (overwrites existing names).



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

def name!( ɴ )
  old_ɴ = namespace.__instances__[ self ]   # previous name
  if ɴ then puts "NameMagic: Rudely naming with #{ɴ}." if DEBUG
    ɴ = namespace.send( :validate_name,     # honor the hook
                        namespace.name_set_closure.( ɴ, self, old_ɴ ) ).to_sym
    puts "NameMagic: Name adjusted to #{ɴ}." if DEBUG
    return false if old_ɴ == ɴ # already named as required
    pair = namespace.__instances__.rassoc( ɴ )
    namespace.__forget__( pair[0] ) if pair # rudely forget the collider
    namespace.const_set ɴ, self             # write a constant
    namespace.__instances__[ self ] = ɴ     # write to @instances
    namespace.__forget__ old_ɴ              # forget the old name of self
  else
    self.name = nil # unnaming, no collider issues
  end
end

#name=(ɴ) ⇒ Object

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



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/y_support/name_magic.rb', line 116

def name=( ɴ )
  old_ɴ = namespace.__instances__[ self ]    # previous name
  if ɴ then puts "NameMagic: Naming with argument #{ɴ}." if DEBUG
    ɴ = namespace.send( :validate_name,     # honor the hook
                        namespace.name_set_closure.( ɴ, self, old_ɴ ) ).to_sym
    puts "NameMagic: Name adjusted to #{ɴ}." if DEBUG
    return if old_ɴ == ɴ                     # already named as required
    fail NameError, "Name '#{ɴ}' already exists in #{namespace} namespace!" if
      self.class.__instances__.rassoc( ɴ )
    namespace.const_set ɴ, self           # write a constant
    namespace.__instances__[ self ] = ɴ   # write to @instances
    namespace.__forget__ old_ɴ            # forget the old name of self
  else puts "NameMagic: Unnaming #{old_ɴ || self}" if DEBUG
    namespace.__instances__.update( self => nil ) # unname in @instances
    namespace.send :remove_const, old_ɴ if old_ɴ  # remove namespace const.
  end
end

#namespaceObject

The namespace of the instance’s class.



95
96
97
# File 'lib/y_support/name_magic.rb', line 95

def namespace
  self.class.namespace
end