Class: Module

Inherits:
Object show all
Defined in:
lib/nano/module/sort_on.rb,
lib/nano/module/wrap.rb,
lib/nano/module/redef.rb,
lib/nano/module/sattr.rb,
lib/nano/module/undef.rb,
lib/nano/module/remove.rb,
lib/nano/module/rename.rb,
lib/nano/module/by_name.rb,
lib/nano/module/dirname.rb,
lib/nano/module/memoize.rb,
lib/nano/module/abstract.rb,
lib/nano/module/basename.rb,
lib/nano/module/modspace.rb,
lib/nano/module/redirect.rb,
lib/nano/module/equate_on.rb,
lib/nano/module/integrate.rb,
lib/nano/module/namespace.rb,
lib/nano/module/include_as.rb,
lib/nano/module/attr_setter.rb,
lib/nano/module/attr_tester.rb,
lib/nano/module/clone_using.rb,
lib/nano/module/initializer.rb,
lib/nano/module/wrap_method.rb,
lib/nano/module/cattr_reader.rb,
lib/nano/module/cattr_writer.rb,
lib/nano/module/rename_method.rb,
lib/nano/module/cattr_accessor.rb,
lib/nano/module/clone_removing.rb,
lib/nano/module/clone_renaming.rb,
lib/nano/module/redefine_method.rb,
lib/nano/module/redirect_method.rb,
lib/nano/module/instance_methods.rb,
lib/nano/module/alias_module_function.rb,
lib/nano/module/generate_instance_method_name.rb,
lib/nanosys.rb

Overview

– :TODO: Perhaps need to make a check against overriding Megas annotated version. ++

Constant Summary collapse

METHOD_TYPES =
[ :inherited, :ancestors, :local,
:public, :private, :protected,
:all ]

Instance Method Summary collapse

Instance Method Details

#abstract(*sym) ⇒ Object



13
14
15
16
17
# File 'lib/nano/module/abstract.rb', line 13

def abstract( *sym )
  sym.each { |s|
    define_method( s ) { raise TypeError, "undefined abstraction ##{s}" }
  }
end

#attr_setter(*args) ⇒ Object

Create an attribute method for both getting and setting an instance variable.

attr_setter :a

_is equivalent to_

def a(*args)
  if args.size > 0
    @a = args[0]
    self
  else
    @a
  end
end


23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/nano/module/attr_setter.rb', line 23

def attr_setter(*args)

  make = {}
  args.each { |a|
    make[:"#{a}"] = %{
      def #{a}(*args)
        args.size > 0 ? ( @#{a}=args[0] ; self ) : @#{a}
      end
    }
  }
  module_eval( make.values.join("\n") )

  return make.keys
end

#attr_tester(*args) ⇒ Object

Create an tester attribute. This creates two methods for each given variable name. One is used to test the attribute and the other is used to set or toggle it.

attr_switch :a

is equivalent to

def a?
  @a ? true : @a
end

def a!(switch=nack)
  if switch == nack
    @a = ! @a
  else
    @a = @a ? switch : @a
    self
  end
end


26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/nano/module/attr_tester.rb', line 26

def attr_tester(*args)
  make = {}
  args.each { |a|
    make[:"#{a}?"] = %{ def #{a}?; @#{a} ? true : @#{a}; end }
    make[:"#{a}!"] = %{
      def #{a}!(switch=nack)
        if switch==nack
          @#{a} = !@#{a}
        else
          @#{a} = @#{a} ? switch : @#{a}
          self
        end
      end
    }
  }
  module_eval make.values.join("\n")

  return make.keys
end

#basenameObject

Returns the root name of the module/class.

module Example
  class Demo
  end
end

Demo.name       #=> Example::Demo
Demo.basename   #=> Demo


13
14
15
# File 'lib/nano/module/basename.rb', line 13

def basename
  self.name.gsub(/^.*::/, '')
end

#by_name(name) ⇒ Object

Note: the following documentation uses “class” because it’s more common, but it applies to modules as well.

Given the name of a class, returns the class itself (i.e. instance of Class). The dereferencing starts at Object. That is,

Class.by_name("String")

is equivalent to

Object.const_get("String")

The parameter name is expected to be a Symbol or String, or at least to respond to to_str.

An ArgumentError is raised if name does not correspond to an existing class. If name is not even a valid class name, the error you’ll get is not defined.

Examples:

Class.by_name("String")             # -> String
Class.by_name("::String")           # -> String
Class.by_name("Process::Sys")       # -> Process::Sys
Class.by_name("GorillaZ")           # -> (ArgumentError)

Class.by_name("Enumerable")         # -> Enumerable
Module.by_name("Enumerable")        # -> Enumerable

– Credit for this goes to Gavin Sinclair ++

Raises:

  • (ArgumentError)


37
38
39
40
41
# File 'lib/nano/module/by_name.rb', line 37

def by_name(name)
  result = Object.constant(name)
  return result if result.kind_of?( Module )
  raise ArgumentError, "#{name} is not a module or class"
end

#cattr_accessor(*syms) ⇒ Object

Creates a class-variable attr_accessor that can be accessed both on an instance and class level.

class MyClass
  cattr_accessor :a
end

MyClass.a = 10
MyClass.a           #=> 10
mc = MyClass.new
mc.a                #=> 10


18
19
20
21
22
23
# File 'lib/nano/module/cattr_accessor.rb', line 18

def cattr_accessor(*syms)
  m = []
  m.concat( cattr_reader(*syms) )
  m.concat( cattr_writer(*syms) )
  m
end

#cattr_reader(*syms) ⇒ Object

Creates a class-variable attr_reader that can be accessed both on an instance and class level.

class MyClass
  @@a = 10
  cattr_reader :a
end

MyClass.a           #=> 10
MyClass.new.a       #=> 10


21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/nano/module/cattr_reader.rb', line 21

def cattr_reader(*syms)
  hsyms = ( syms.last.is_a?(Hash) ? syms.pop : {} )
  hsyms.update( Hash[*(syms.zip([nil]*syms.size).flatten)] )
  hsyms.each_pair do |sym, val|
    class_eval "      if ! defined? @@\#{sym}\n        @@\#{sym} = val\n      end\n      def self.\#{sym}\n        @@\#{sym}\n      end\n      def \#{sym}\n        self.class.\#{sym}\n      end\n    EOS\n  end\n  return hsyms.keys\nend\n"

#cattr_writer(*syms) ⇒ Object

Creates a class-variable attr_writer that can be accessed both on an instance and class level.

class MyClass
  cattr_writer :a
  def a
    @@a
  end
end

MyClass.a = 10
MyClass.a            #=> 10
MyClass.new.a = 29
MyClass.a            #=> 29


25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/nano/module/cattr_writer.rb', line 25

def cattr_writer(*syms)
  hsyms = ( syms.last.is_a?(Hash) ? syms.pop : {} )
  hsyms.update( Hash[*(syms.zip([nil]*syms.size).flatten)] )
  hsyms.each_pair do |sym, val|
    class_eval "      if ! defined? @@\#{sym}\n        @@\#{sym} = val\n      end\n      def self.\#{sym}=(obj)\n        @@\#{sym} = obj\n      end\n      def \#{sym}=(obj)\n        self.class.\#{sym}=(obj)\n      end\n    EOS\n  end\n  return hsyms.keys\nend\n"

#clone_removing(*meths) ⇒ Object

Returns an anonymous module with the specified methods of the receiving module removed.



4
5
6
7
8
9
# File 'lib/nano/module/clone_removing.rb', line 4

def clone_removing( *meths )
  mod = self.dup
  methods_to_remove = meths
  methods_to_remove.each { |m|  mod.class_eval { remove_method m }  }
  return mod
end

#clone_renaming(pairs) ⇒ Object

Returns an anonymous module with the specified methods of the receiving module renamed.



4
5
6
7
8
9
10
11
12
13
# File 'lib/nano/module/clone_renaming.rb', line 4

def clone_renaming( pairs )
  mod = self.dup
  pairs.each_pair { |to_sym, from_sym|
    mod.class_eval {
      alias_method( to_sym, from_sym )
      undef_method( from_sym )
    }
  }
  return mod
end

#clone_using(*meths) ⇒ Object

Returns an anonymous module with only the specified methods of the receiving module intact.



6
7
8
9
10
11
12
# File 'lib/nano/module/clone_using.rb', line 6

def clone_using( *meths )
  meths = meths.collect { |m| m.to_s }
  methods_to_remove = (self.instance_methods - meths)
  mod = self.dup
  mod.class_eval { methods_to_remove.each { |m| undef_method m } }
  return mod
end

#dirnameObject

Returns the name of module’s container module.

module Example
  class Demo
  end
end

Demo.name       #=> "Example::Demo"
Demo.dirname    #=> "Example"

See also Module#basename.



15
16
17
# File 'lib/nano/module/dirname.rb', line 15

def dirname
  name.gsub(/::[^:]*$/, '')
end

#equate_on(*fields) ⇒ Object Also known as: key_attributes

Generates identity/key methods based on specified attributes.

equate_on :a, :b

_is equivalent to_

def ==(o)
  self.a == o.a && self.b == o.b
end

def eql?(o)
  self.a.eql?(o.a) && self.b.eql?(o.b)
end

def hash()
  self.a.hash ^ self.b.hash
end


25
26
27
28
29
30
31
32
# File 'lib/nano/module/equate_on.rb', line 25

def equate_on(*fields)
  code = ""
  code << "def ==(o) "   << fields.map {|f| "self.#{f} == o.#{f}" }.join(" && ")    << " end\n"
  code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?(o.#{f})" }.join(" && ") << " end\n"
  code << "def hash() "  << fields.map {|f| "self.#{f}.hash" }.join(" ^ ")          << " end\n"
  class_eval( code )
  fields
end

#generate_instance_method_name(name = 'a') ⇒ Object

Generates a new symbol that is unique among the inctance methods of the class/module. If a name argument is given, it will generate a similar name.

Class.generate_instance_method_name( :class ) => :_clast_


10
11
12
13
14
15
16
# File 'lib/nano/module/generate_instance_method_name.rb', line 10

def generate_instance_method_name( name='a' )
  s = name.to_s
  while self.method_defined?( "_#{s}_" )
    s = s.succ
  end
  return :"_#{s}_"
end

#include_as(h) ⇒ Object

Include a module via a specified namespace.

module T
  def t ; "HERE" ; end
end

class X
  include_as :test => T
  def t ; test.t ; end
end

X.new.t  #=> "HERE"


19
20
21
22
23
24
25
26
27
28
29
30
31
32
# File 'lib/nano/module/include_as.rb', line 19

def include_as( h )
  h.each{ |name, mod|
    s = self
    c = Class.new(SimpleDelegator) {
      include mod
      define_method(:initialize) { |s| super(s) }
    }
    class_eval {
      define_method( name ) {
        instance_variable_set( "@#{name}",  instance_variable_get( "@#{name}" ) || c.new(s) )
      }
    }
  }
end

#initializer(*attributes, &block) ⇒ Object

Automatically create an initializer assigning the given arguments.

class MyClass
  initializer(:a, "b", :c)
end

_is equivalent to_

class MyClass
  def initialize(a, b, c)
    @a,@b,@c = a,b,c
  end
end

Downside: Initializers defined like this can’t take blocks. This can be fixed when Ruby 1.9 is out.

The initializer will not raise an Exception when the user does not supply a value for each instance variable. In that case it will just set the instance variable to nil. You can assign default values or raise an Exception in the block.



25
26
27
28
29
30
31
32
33
# File 'lib/nano/module/initializer.rb', line 25

def initializer(*attributes, &block)
  define_method(:initialize) do |*args|
    attributes.zip(args) do |sym, value|
      instance_variable_set(:"@#{sym}", value)
    end

    instance_eval(&block) if block
  end
end

#instance_methods(*args) ⇒ Object

Provides an improved method lookup routnine. It returns a list of methods according to symbol(s) given.

Recognized symbols are:

  • :public include public methods

  • :private include private methods

  • :protected include protected methods

  • :ancestors include all ancestor’s methods

  • :inherited (same as above)

  • <tt>:local</tti> include non-ancestor methods

  • :all include all of the above

This method also uses the symbol-not system. So you can specify the inverse of all of the above. For instance ~:public would mean :private, :protected (see nano/symbol/not).

If no symbol is given then :public, :local is assumed. Unrecognized symbols raise an error.

module Demo
  def test
    puts("Hello World!")
  end
end

Demo.instance_methods(:local)    #=> ['test']

To maintain backward compatibility true as an intitial argument is converted to :local, :ancestors (i.e. it includes both).

Raises:

  • (ArgumentError)


40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/nano/module/instance_methods.rb', line 40

def instance_methods(*args)

  # for backward compatibility
  args << true if args.empty?
  case args[0]
  when TrueClass
    args.shift
    args << :ancestors
    args << :local
  when FalseClass
    args.shift
    args << :local
  end

  # default public, local
  args |= [:public] unless [:publix,:private,:protected,:all].any?{ |a| args.include?(a) }
  args |= [:ancestors,:local] unless [:ancestors,:inherited,:local,:all].any?{ |a| args.include?(a) }

  raise ArgumentError if args.any?{ |a| ! METHOD_TYPES.include?(a) }

  pos, neg = args.partition { |s| ! s.not? }

  m = []

  pos.each do |n|
    case n
    when :inherited, :ancestors
      m |= ( public_instance_methods( true ) - public_instance_methods( false ) )       if pos.include?(:public)
      m |= ( private_instance_methods( true ) - private_instance_methods( false ) )     if pos.include?(:private)
      m |= ( protected_instance_methods( true ) - protected_instance_methods( false ) ) if pos.include?(:protected)
    when :local
      m |= public_instance_methods( false )    if pos.include?(:public)
      m |= private_instance_methods( false )   if pos.include?(:private)
      m |= protected_instance_methods( false ) if pos.include?(:protected)
    when :all
      m |= public_instance_methods( true )
      m |= private_instance_methods( true )
      m |= protected_instance_methods( true )
    end
  end

  neg.each do |n|
    case n
    when :public
      m -= public_instance_methods( true )
    when :private
      m -= private_instance_methods( true )
    when :protected
      m -= protected_instance_methods( true )
    when :inherited, :ancestors
      m -= ( public_instance_methods( true ) - public_instance_methods( false ) )
      m -= ( private_instance_methods( true ) - private_instance_methods( false ) )
      m -= ( protected_instance_methods( true ) - protected_instance_methods( false ) )
    when :local
      m -= public_instance_methods( false )
      m -= private_instance_methods( false )
      m -= protected_instance_methods( false )
    end
  end

  m.sort
end

#integrate(mod, &blk) ⇒ Object



10
11
12
13
14
15
# File 'lib/nano/module/integrate.rb', line 10

def integrate mod, &blk
  newmod = mod.dup
  newmod.class_eval &blk
  class_eval { include newmod }
  newmod
end

#memoize(*meths) ⇒ Object

Directive for making your functions faster by trading space for time. When you “memoize” a method/function its results are cached so that later calls with the same arguments returns results in the cache instead of recalculating them.

class T
  def initialize(a)
    @a = a
  end
  def a
    "#{@a ^ 3 + 4}"
  end
  memoize :a
end

t = T.new
t.a.__id__ == t.a.__id__  #=> true


27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/nano/module/memoize.rb', line 27

def memoize(*meths)
  meths.each do |meth|
    mc = $MEMOIZE_CACHE[meth] = Hash.new
    old = instance_method(meth)
    new = proc do |*args|
      if mc.has_key? args
        mc[args]
      else
        mc[args] = old.bind(self).call(*args)
      end
    end
    send(:define_method, meth, &new)
  end
end

#modspaceObject

Returns the module’s container module.

module Example
  class Demo
  end
end

Demo.modspace    #=> Example

See also Module#basename.



17
18
19
# File 'lib/nano/module/modspace.rb', line 17

def modspace
  eval self.name[ 0...self.name.rindex( '::' ) ]
end

#namespace(name, &blk) ⇒ Object

Create a seperated method namespace.

module T
  def t ; "HERE" ; end
end

class X
  namespace :test { include T }
  def t ; test.t ; end
end

X.new.t  #=> "HERE"

NOTE: This is not as functional as it ought be in that the instance variables of the object are not accessible within the namespace.



22
23
24
25
26
27
28
29
30
31
# File 'lib/nano/module/namespace.rb', line 22

def namespace( name, &blk )
  s = self
  c = Class.new(SimpleDelegator, &blk)
  c.class_eval { define_method(:initialize) { |s| super(s) } }
  self.class_eval {
    define_method( name ) {
      instance_variable_set( "@#{name}",  instance_variable_get( "@#{name}" ) || c.new(s) )
    }
  }
end

#redirect_method(method_hash) ⇒ Object

Redirect methods to other methods. This simply defines methods by the name of a hash key which calls the method with the name of the hash’s value.

class Example
  redirect_method :hi => :hello, :hey => :hello
  def hello(name)
    puts "Hello, #{name}."
  end
end

e = Example.new
e.hello("Bob")    #=> "Hello, Bob."
e.hi("Bob")       #=> "Hello, Bob."
e.hey("Bob")      #=> "Hello, Bob."

The above class definition is equivalent to:

class Example
  def hi(*args)
    hello(*args)
  end
  def hey(*args)
    hello(*args)
  end
  def hello
    puts "Hello"
  end
end


33
34
35
36
37
# File 'lib/nano/module/redirect_method.rb', line 33

def redirect_method( method_hash )
  method_hash.each do |targ,adv|
    define_method(targ) { |*args| send(adv,*args) }
  end
end

#sattr(*args) ⇒ Object

Shortcuts for creating class instance attributes.

Concerning the naming of these emthods, since the letter ‘c’ has become the convention for class variable attributes (ie. @@v), the letter ‘s’ was chosen and stands for ‘self’ or ‘singleton’.



20
21
22
23
24
# File 'lib/nano/module/sattr.rb', line 20

def sattr(*args)
  class << self
    attr(*args)
  end
end

#sattr_accessor(*args) ⇒ Object



38
39
40
41
42
# File 'lib/nano/module/sattr.rb', line 38

def sattr_accessor(*args)
  class << self
    attr_accessor(*args)
  end
end

#sattr_reader(*args) ⇒ Object



26
27
28
29
30
# File 'lib/nano/module/sattr.rb', line 26

def sattr_reader(*args)
  class << self
    attr_reader(*args)
  end
end

#sattr_setter(*args) ⇒ Object



44
45
46
47
48
# File 'lib/nano/module/sattr.rb', line 44

def sattr_setter(*args)
  class << self
    attr_setter(*args)
  end
end

#sattr_tester(*args) ⇒ Object



50
51
52
53
54
# File 'lib/nano/module/sattr.rb', line 50

def sattr_tester(*args)
  class << self
    attr_tester(*args)
  end
end

#sattr_writer(*args) ⇒ Object



32
33
34
35
36
# File 'lib/nano/module/sattr.rb', line 32

def sattr_writer(*args)
  class << self
    attr_writer(*args)
  end
end

#sort_on(*fields) ⇒ Object Also known as: compare_on, sort_attributes

Automatically generate sorting defintions base on attribute fields.

sort_on :a, :b

_is equivalent to_

def <=>(o)
  cmp = self.a <=> o.a; return cmp unless cmp == 0
  cmp = self.b <=> o.b; return cmp unless cmp == 0
  0
end


19
20
21
22
23
24
25
26
27
# File 'lib/nano/module/sort_on.rb', line 19

def sort_on(*fields)
  code = %{def <=>(o)\n}
  fields.each { |f|
    code << %{cmp = ( @#{f} <=> o.instance_variable_get('@#{f}') ); return cmp unless cmp == 0\n}
  }
  code << %{0\nend; alias_method :cmp, :<=>;}
  class_eval( code )
  fields
end

#use(*meths) ⇒ Object

Require method nano(s) for the class/module.

String.use :humanize, last=


199
200
201
# File 'lib/nanosys.rb', line 199

def use( *meths )
  NanoSystem.require( self, *meths )
end