Methods
Constants
METHOD_TYPES = [ :inherited, :ancestors, :local, :public, :private, :protected, :all ]
Public Class methods
append_features(mod)
# File lib/facets/core/module/class_extension.rb, line 64
      def self.append_features(mod)
        append_features_without_class_extension(mod)
      end
Public Instance methods
abstract( *sym )
# File lib/facets/core/module/abstract.rb, line 13
  def abstract( *sym )
    sym.each { |s|
      define_method( s ) { raise TypeError, "undefined abstraction ##{s}" }
    }
  end
alias_method_chain(target, feature=nil)

Encapsulates the common pattern of:

  alias_method :foo_without_feature, :foo
  alias_method :foo, :foo_with_feature

With this, you simply do:

  alias_method_chain :foo, :feature

And both aliases are set up for you.

Query and bang methods (foo?, foo!) keep the same punctuation:

  alias_method_chain :foo?, :feature

is equivalent to

  alias_method :foo_without_feature?, :foo?
  alias_method :foo?, :foo_without_feature?

so you can safely chain foo, foo?, and foo! with the same feature.

# File lib/facets/core/module/alias_method_chain.rb, line 29
  def alias_method_chain(target, feature=nil)
    # Strip out punctuation on predicates or bang methods since
    # e.g. target?_without_feature is not a valid method name.
    aliased_target, punctuation = target.to_s.sub(/([?!])$/, ''), $1
    alias_method "#{aliased_target}_without_#{feature}#{punctuation}", target
    alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}"
  end
ancestor?( mod )

Is a given class or module an ancestor of this class or module?

 class X ; end
 class Y < X ; end

  X.ancestor?(Y)
# File lib/facets/core/module/ancestor.rb, line 12
  def ancestor?( mod )
    ancestors.include? mod
  end
append_features(mod)
# File lib/facets/core/module/class_extension.rb, line 74
  def append_features(mod)
    append_features_without_class_extension(mod)
    mod.extend(class_extension)
    if mod.instance_of? Module
      mod.__send__(:class_extension).__send__(:include, class_extension)
    end
  end
attr(*args)

There’s a school of thought that says an attribute show always have a reader and a writer.

# File lib/facets/core/module/attr.rb, line 10
  def attr(*args)
    args.pop unless args.last.is_a?(Symbol) or args.last.is_a?(String)
    attr_accessor *args
    args.collect{ |a| [ "#{a}".to_sym, "#{a}=".to_sym ] }.flatten
  end
attr_setter(*args)

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
# File lib/facets/core/module/attr_setter.rb, line 23
  def attr_setter(*args)

    make = {}
    args.each { |a|
      make["#{a}".to_sym] = %{
        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)

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
# File lib/facets/core/module/attr_tester.rb, line 26
  def attr_tester(*args)
    make = {}
    args.each { |a|
      make["#{a}?".to_sym] = %{ def #{a}?; @#{a} ? true : @#{a}; end }
      make["#{a}!".to_sym] = %{
        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
attr_tester(*args)

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
# File lib/facets/core/module/attr_query.rb, line 26
  def attr_tester(*args)
    make = {}
    args.each { |a|
      make["#{a}?".to_sym] = %{ def #{a}?; @#{a} ? true : @#{a}; end }
      make["#{a}!".to_sym] = %{
        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
attr_validator(*symbols, &validator)

Like attr_writer, but the writer method validates the setting against the given block.

# File lib/facets/core/module/attr_validator.rb, line 7
  def attr_validator(*symbols, &validator)
    symbols.each do |symbol|
      define_method "#{symbol}=" do |val|
        unless validator.call(val)
          raise ArgumentError, "Invalid value provided for #{symbol}"
        end
        instance_variable_set("@#{symbol}", val)
      end
    end
  end
basename()

Returns the root name of the module/class.

  module Example
    class Demo
    end
  end

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

For anonymous modules this will provide a basename based on Module#inspect.

  m = Module.new
  m.inspect       #=> "#<Module:0xb7bb0434>"
  m.basename      #=> "Module_0xb7bb0434"
# File lib/facets/core/module/basename.rb, line 20
  def basename
    if name and not name.empty?
      name.gsub(/^.*::/, '')
    else
      nil #inspect.gsub('#<','').gsub('>','').sub(':', '_')
    end
  end
by_name(name)

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
# File lib/facets/core/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
clone_removing( *meths )

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

# File lib/facets/core/module/clone_using.rb, line 19
  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 )

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

# File lib/facets/core/module/clone_using.rb, line 6
  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 )

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

# File lib/facets/core/module/clone_using.rb, line 28
  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
compare_on(*fields)

Alias for sort_on

dirname()

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.

# File lib/facets/core/module/dirname.rb, line 15
  def dirname
    name.gsub(/::[^:]*$/, '')
  end
equate_on(*fields)

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
This method is also aliased as key_attributes
# File lib/facets/core/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' )

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_
# File lib/facets/core/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}_".to_sym
  end
include_as( h )

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"
# File lib/facets/core/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)

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.

# File lib/facets/core/module/initializer.rb, line 27
  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_method!(s)

Easy access to method as objects, and they retain state!

  module K
    def hello
      puts "Hello World!"
    end
  end
  p K.instance_method!(:hello)   #=> <UnboundMethod: #hello>

CAUTION! This will not work exactly right until class variable lookup is fixed. Presently it is limited to the scope of the current module/class.

# File lib/facets/core/kernel/method.rb, line 61
  def instance_method!(s)
    #( @@__instance_methods__ ||= {} )[s] ||= instance_method(s)  # TODO when fixed
    ( @__instance_methods__ ||= {} )[s] ||= instance_method(s)
  end
instance_methods(*args)

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 facet/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).

# File lib/facets/core/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, &block )
# File lib/facets/core/module/integrate.rb, line 39
  def integrate( mod, &block )
    #include mod.revisal( &blk )
    m = Module.new { include mod }
    m.class_eval &block
    include m
  end
is?( base )

Is a given class or module an ancestor of this class or module?

 class X ; end
 class Y < X ; end

  Y.is?(X)  #=> true
# File lib/facets/core/module/is.rb, line 22
  def is?( base )
    ancestors.slice(1..-1).include?( base )
  end
key_attributes(*fields)

Alias for equate_on

memoize(*meths)

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
# File lib/facets/core/module/memoize.rb, line 26
  def memoize(*meths)
    @_MEMOIZE_CACHE ||= Hash.new
    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
modspace()

Returns the module’s container module.

  module Example
    class Demo
    end
  end

  Demo.modspace    #=> Example

See also Module#basename.

# File lib/facets/core/module/modspace.rb, line 17
  def modspace
    eval self.name[ 0...self.name.rindex( '::' ) ]
  end
namespace( name, &blk )

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.

# File lib/facets/core/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
nesting()
# File lib/facets/core/module/nesting.rb, line 8
  def nesting
    n = []
    name.split(/::/).inject(self){ |mod, name| c = mod.const_get(name) ; n << c ; c }
    return n
  end
on_included(code)

A useful macro for dynamic modules.

# File lib/facets/core/module/on_included.rb, line 13
  def on_included(code)
    tag = caller[0].split(' ').first.split(/\/|\\/).last.gsub(/:|\.|\(|\)/, '_')
    old = "__included_#{tag}"
    module_eval %{
      class << self
        alias_method :#{old}, :included
        def included(base)
          #{old}(base)
          #{code}
        end
      end
    }
  end
redirect_method( method_hash )

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
This method is also aliased as redirect
# File lib/facets/core/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
revisal( &blk )
# File lib/facets/core/module/revisal.rb, line 10
  def revisal( &blk )
    base = self
    nm = Module.new { include base }
    nm.class_eval &blk
    nm
  end
shadow_all( include_ancestors=true )

Creates a shadow method for every currently defined method.

# File lib/facets/core/module/shadow_all.rb, line 5
  def shadow_all( include_ancestors=true )
    instance_methods(include_ancestors).each { |m| shadow_method( m ) }
  end
shadow_method( name, old_name=name )

Define a shadow alias for a method.

  class X
    shadow_method( :class )
  end

  X.new.__class__  #=> X
# File lib/facets/core/module/shadow_method.rb, line 11
  def shadow_method( name, old_name=name )
    name, old_name = name.to_s, old_name.to_s
    shadow_name = '__' << name.gsub(/([=?!])$/, '') << '__' << $1.to_s
    alias_method( shadow_name, old_name )
  end
sort_attributes(*fields)

Alias for sort_on

sort_on(*fields)

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
This method is also aliased as compare_on sort_attributes
# File lib/facets/core/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
this()
# File lib/facets/core/module/this.rb, line 18
  def this
    klass = self
    Functor.new do |op, *args|
      case op.to_s[-1,1]
      when '='
        op = op.to_s.chomp('=')
        if Proc === args[0]
          define_method( op, &args[0] )
        else
          define_method( op ) do
            r = instance_variable_set( "@#{op}", args[0] )
            klass.class.class_eval %{
              def #{op}; @#{op}; end
            }
            r
          end
        end
      else
        klass.class_eval %{
          def #{op}; @#{op}; end
        }
      end
    end
  end
wrap_method( sym, &blk )

Creates a new method wrapping the previous of the same name. Reference to the old method is passed into the new definition block as the first parameter.

  wrap_method( sym ) { |old_meth, *args|
    old_meth.call
    ...
  }

Keep in mind that this can not be used to wrap methods that take a block.

This method is also aliased as wrap
# File lib/facets/core/module/wrap_method.rb, line 20
  def wrap_method( sym, &blk )
    raise ArgumentError, "method does not exist" unless method_defined?( sym )
    old = instance_method(sym)
    undef_method(sym)
    define_method(sym) { |*args| blk.call(old.bind(self), *args) }
  end
Private Instance methods
alias_module_function( new, old )

Alias a module function so that the alias is also a module function. The typical alias_method does not do this.

  module Demo
    module_function
    def hello
      "Hello"
    end
  end

  Demo.hello    #=> Hello

  module Demo
    alias_module_function( :hi , :hello )
  end

  Demo.hi       #=> Hello
# File lib/facets/core/module/alias_module_function.rb, line 23
  def alias_module_function( new, old )
    alias_method( new, old )
    module_function( new )
  end
class_extension(&block)

class_extension

Normally when including modules, class/module methods are not extended. To achieve this behavior requires some clever Ruby Karate. Instead class_extension provides an easy to use and clean solution. Simply place the extending class methods in a block of the special module method class_extension.

  module Mix
    def inst_meth
      puts 'inst_meth'
    end

    class_methods do
      def class_meth
        "Class Method!"
      end
    end
  end

  class X
    include Mix
  end

  X.class_meth  #=> "Class Method!"
# File lib/facets/core/module/class_extension.rb, line 62
  def class_extension(&block)
    @class_extension ||= Module.new do
      def self.append_features(mod)
        append_features_without_class_extension(mod)
      end
    end
    @class_extension.module_eval(&block) if block_given?
    @class_extension
  end
redef( sym, aka=nil, &blk )

Alias for redefine_method

redefine_method( sym, aka=nil, &blk )

Creates a new method for a pre-existing method.

If aka is given, then the method being redefined will first be aliased to this name.

  class Greeter
    def hello ; "Hello" ; end
  end

  Greeter.new.hello   #=> "Hello"

  class Greeter
    redefine_method( :hello, :hi ) do
      hi + ", friend!"
    end
  end

  Greeter.new.hello   #=> "Hello, friend!"
This method is also aliased as redef
# File lib/facets/core/module/redefine_method.rb, line 25
  def redefine_method( sym, aka=nil, &blk )
    raise ArgumentError, "method does not exist" unless method_defined?( sym )
    alias_method( aka, sym ) if aka
    undef_method( sym )
    define_method( sym, &blk )
  end
redirect( method_hash )

Alias for redirect_method

rename( to_sym, from_sym )

Alias for rename_method

rename_method( to_sym, from_sym )

Aliases a method and undefines the original.

  rename_method( :to_method, :from_method  )
This method is also aliased as rename
# File lib/facets/core/module/rename_method.rb, line 7
  def rename_method( to_sym, from_sym )
    raise ArgumentError, "method #{from_sym} does not exist" unless method_defined?( from_sym )
    alias_method( to_sym, from_sym )
    undef_method( from_sym )
  end
wrap( sym, &blk )

Alias for wrap_method