Class: Module

Inherits:
Object show all
Includes:
Glue::Validation::ClassMethods
Defined in:
lib/more/facets/capsule.rb,
lib/more/facets/let.rb,
lib/more/facets/memoize.rb,
lib/more/facets/paramix.rb,
lib/more/facets/prepend.rb,
lib/more/facets/overload.rb,
lib/more/facets/settings.rb,
lib/more/facets/interface.rb,
lib/more/facets/namespace.rb,
lib/more/facets/attributes.rb,
lib/more/facets/compare_on.rb,
lib/more/facets/dependency.rb,
lib/more/facets/validation.rb,
lib/core/facets/module/attr.rb,
lib/core/facets/module/name.rb,
lib/more/facets/annotations.rb,
lib/core/facets/module/alias.rb,
lib/core/facets/module/clone.rb,
lib/more/facets/classmethods.rb,
lib/more/facets/instantiable.rb,
lib/core/facets/kernel/metaid.rb,
lib/core/facets/module/modify.rb,
lib/core/facets/module/traits.rb,
lib/core/facets/1stclassmethod.rb,
lib/core/facets/module/include.rb,
lib/core/facets/module/methods.rb,
lib/core/facets/module/require.rb,
lib/core/facets/module/abstract.rb,
lib/more/facets/class_extension.rb

Overview

ClassMethods

Miniframework provides a very convenient way to have modules pass along class methods in the inheritance chain.

An oddity of Ruby, when including modules, class/module methods are not inherited. To achieve this behavior requires some clever Ruby Karate. Instead ClassMethods provides an easy to use and clean solution. Simply place the class inheritable methods in a block of the special module method #ClassMetods.

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!"

This is equivalent to the original (but still functional) techinique of putting the class/module methods in a nested ClassMethods module and extending the original module manually. Eg.

module Mix
  def inst_meth
    puts 'inst_meth'
  end

  module ClassMethods
    def class_meth
      "Class Method!"
    end
  end

  extend ClassMethods
end

class X
  include Mix
end

X.class_meth  #=> "Class Method!"

Also note that #class_inherit is an available alias for #class_methods for the sake of backward compatability. And #class_extension is alias (potentially) looking forward to a future version on Ruby.

Notes

Just a quick comment on the need for this behavior.

A module is an encapsulation of code, hence when a module is included (or extends), the module itself should have discretion over how it effects the receiving class/module. That is the very embodiment of encapsulation. Having it otherwise, as Ruby now does, stymies the practice –and we end up with “hacks”, like this and ClassMethods, to compensate.

Ruby would be much improved by making this bevaivor standard. And making non-inheritance the exception, which is alwasy easy enough to achieve: put the code in a separate (and thus uninherited) module.

Direct Known Subclasses

Aspect, Behavior, Capsule

Defined Under Namespace

Modules: Instantiable

Constant Summary collapse

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

Constants included from Glue::Validation::ClassMethods

Glue::Validation::ClassMethods::LENGTHS

Instance Method Summary collapse

Methods included from Glue::Validation::ClassMethods

#validate_format, #validate_inclusion, #validate_length, #validate_numeric, #validate_value

Instance Method Details

#*(rename_map) ⇒ Object

Rename methods.

module A
  def a; "a"; end
end

B = A * { :a => :b }

class X; include B; end

X.new.b    #=> "a"

CREDIT: Thomas Sawyer
CREDIT: Robert Dober


83
84
85
86
87
88
89
90
91
92
# File 'lib/core/facets/module/traits.rb', line 83

def *(rename_map)
  base = self
  Module.new do
    include base
    rename_map.each do |from, to|
      alias_method to, from
      undef_method from
    end
  end
end

#+(other) ⇒ Object

Combine modules.

module A
  def a; "a"; end
end

module B
  def b; "b"; end
end

C = A + B

class X; include C; end

X.new.a    #=> "a"
X.new.b    #=> "b"

Note that in the old version of traits.rb we cloned modules and altered their copies. Eg.

def +(other)
  mod1 = other.clone
  mod2 = clone
  mod1.module_eval{ include mod2 }
end

Later it was realized that this thwarted the main benefit that Ruby’s concept of modules has over traditional traits, inheritance.

CREDIT: Thomas Sawyer
CREDIT: Robert Dober


36
37
38
39
40
41
42
# File 'lib/core/facets/module/traits.rb', line 36

def +(other)
  base = self
  Module.new do
    include base
    include other
  end
end

#-(other) ⇒ Object

Subtract modules.

TODO: Should this use all instance_methods, not just public?

CREDIT: Thomas Sawyer
CREDIT: Robert Dober


51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/core/facets/module/traits.rb', line 51

def -(other)
  case other
  when Array
    subtract = instance_methods(true) & other.collect{|m| m.to_s}
  when Module
    subtract = instance_methods(true) & other.instance_methods(true)  # false?
  when String, Symbol
    subtract = instance_methods(true) & [other.to_s]
  end
  base = self
  Module.new do
    include base
    subtract.each{ |x| undef_method x }
  end
end

#abstract(*sym) ⇒ Object

Create an abstract method. If it is not overridden, it will raise a TypeError when called.

class C
  abstract :a
end

c = C.new
c.a  #=> Error: undefined abstraction #a

CREDIT: Trans


15
16
17
18
19
# File 'lib/core/facets/module/abstract.rb', line 15

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

#alias_accessor!(*args) ⇒ Object Also known as: alias_switcher, alias_toggler

Create aliases for flag accessors.

CREDIT: Trans


117
118
119
120
121
122
123
124
# File 'lib/core/facets/module/attr.rb', line 117

def alias_accessor!(*args)
  orig = args.last
  args = args - [orig]
  args.each do |name|
    alias_method("#{name}?", "#{orig}?")
    alias_method("#{name}!", "#{orig}!")
  end
end

#alias_reader!(*args) ⇒ Object Also known as: alias_reader?, alias_tester

Create aliases for flag reader.

CREDIT: Trans


159
160
161
162
163
164
165
# File 'lib/core/facets/module/attr.rb', line 159

def alias_reader!(*args)
  orig = args.last
  args = args - [orig]
  args.each do |name|
    alias_method("#{name}?", "#{orig}?")
  end
end

#alias_setter(*args) ⇒ Object

Alias an accessor. This create an alias for both a reader and a writer.

 class X
   attr_accessor :a
   alias_accessor :b, :a
 end

 x = X.new
 x.b = 1
 x.a        #=> 1

CREDIT: Trans


81
82
83
84
85
86
# File 'lib/core/facets/module/attr.rb', line 81

def alias_setter(*args)
  args = args - [orig]
  args.each do |name|
    alias_method(name, orig)
  end
end

#alias_validator(*args) ⇒ Object

Create aliases for validators.



24
25
26
27
28
29
30
31
# File 'lib/core/facets/module/attr.rb', line 24

def alias_validator(*args)
  orig = args.last
  args = args - [orig]
  args.each do |name|
    #alias_method(name, orig)
    alias_method("#{name}=", "#{orig}=")
  end
end

#alias_writer!(*args) ⇒ Object

Create aliases for flag writer.

CREDIT: Trans


200
201
202
203
204
205
206
# File 'lib/core/facets/module/attr.rb', line 200

def alias_writer!(*args)
  orig = args.last
  args = args - [orig]
  args.each do |name|
    alias_method("#{name}!", "#{orig}!")
  end
end

#all_instance_methods(include_super = true) ⇒ Object

List all instance_methods, equivalent to

public_instance_methods +
protected_instance_methods +
private_instance_methods

CREDIT: Trans


11
12
13
14
15
# File 'lib/core/facets/module/methods.rb', line 11

def all_instance_methods(include_super=true)
  public_instance_methods(include_super) +
  protected_instance_methods(include_super) +
  private_instance_methods(include_super)
end

#ancestor?(mod) ⇒ Boolean

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

class X ; end
class Y < X ; end

 X.ancestor?(Y)

Returns:

  • (Boolean)


74
75
76
# File 'lib/core/facets/module/include.rb', line 74

def ancestor?( mod )
  ancestors.include? mod
end

#ann(ref, keys_or_class = nil, keys = nil) ⇒ Object

Set or read annotations.



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/more/facets/annotations.rb', line 109

def ann( ref, keys_or_class=nil, keys=nil )
  return heritage(ref) unless keys_or_class or keys

  if Class === keys_or_class
    keys ||= {}
    keys[:class] = keys_or_class
  else
    keys = keys_or_class
  end

  if Hash === keys
    ref = ref.to_sym
    annotations[ref] ||= {}
    annotations[ref].update(keys.rekey)
  else
    key = keys.to_sym
    heritage(ref)[key]
  end
end

#ann!(ref, keys_or_class = nil, keys = nil) ⇒ Object

To change an annotation’s value in place for a given class or module it first must be duplicated, otherwise the change may effect annotations in the class or module’s ancestors.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/more/facets/annotations.rb', line 133

def ann!( ref, keys_or_class=nil, keys=nil )
  #return heritage(ref) unless keys_or_class or keys
  return annotations[ref] unless keys_or_class or keys

  if Class === keys_or_class
    keys ||= {}
    keys[:class] = keys_or_class
  else
    keys = keys_or_class
  end

  if Hash === keys
    ref = ref.to_sym
    annotations[ref] ||= {}
    annotations[ref].update(keys.rekey)
  else
    key = keys.to_sym
    annotations[ref][key] = heritage(ref)[key].dup
  end
end

#annotationsObject



94
95
96
97
# File 'lib/more/facets/annotations.rb', line 94

def annotations
  #$annotations[self]
  @annotations ||= {}
end

#append_features(mod) ⇒ Object



121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/more/facets/classmethods.rb', line 121

def append_features( base )
  result = append_features_without_classmethods( base )
  if const_defined?( :ClassMethods )
    base.extend( self::ClassMethods )
    unless base.is_a?( Class )
      unless base.const_defined?( :ClassMethods )
        base.const_set( :ClassMethods, Module.new )
      end
      my = self
      base::ClassMethods.class_eval do
        include my::ClassMethods
      end
    end
  end
  result
end

#append_features_without_class_extensionObject



34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/more/facets/class_extension.rb', line 34

def append_features( base )
  result = append_features_without_classmethods( base )
  if const_defined?( :ClassMethods )
    base.extend( self::ClassMethods )
    unless base.is_a?( Class )
      unless base.const_defined?( :ClassMethods )
        base.const_set( :ClassMethods, Module.new )
      end
      my = self
      base::ClassMethods.class_eval do
        include my::ClassMethods
      end
    end
  end
  result
end

#append_features_without_classmethodsObject



119
# File 'lib/more/facets/classmethods.rb', line 119

alias_method :append_features_without_classmethods, :append_features

#attr(*args) ⇒ Object



53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/more/facets/attributes.rb', line 53

def attr( *args )
  args.flatten!
  case args.last
  when TrueClass
    args.pop
    attr_accessor( *args )
  when FalseClass
    args.pop
    attr_reader( *args )
  else
    attr_reader( *args )
  end
end

#attr_accessor!(*args) ⇒ Object Also known as: attr_switcher, attr_toggler

Create a toggle attribute. This creates two methods for each given name. One is a form of tester and the other is used to toggle the value.

attr_accessor! :a

is equivalent to

 def a?
   @a
 end

 def a!(value=true)
   @a = value
   self
 end

CREDIT: Trans


107
108
109
# File 'lib/core/facets/module/attr.rb', line 107

def attr_accessor!(*args)
  attr_reader!(*args) + attr_writer!(*args)
end

#attr_reader!(*args) ⇒ Object Also known as: attr_reader?, attr_tester

Create an tester attribute. This creates a single method used to test the attribute for truth.

attr_reader! :a

is equivalent to

def a?
  @a ? true : @a
end


139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/core/facets/module/attr.rb', line 139

def attr_reader!(*args)
  code, made = '', []
  args.each do |a|
    code << %{
      def #{a}?(truth=nil)
        @#{a} ? truth || @#{a} : @#{a}
      end
    }
    made << "#{a}?".to_sym
  end
  module_eval code
  made
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

CREDIT: Trans


53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/core/facets/module/attr.rb', line 53

def attr_setter(*args)
  code, made = '', []
  args.each do |a|
    code << %{
      def #{a}(*args)
        args.size > 0 ? ( @#{a}=args[0] ; self ) : @#{a}
      end
    }
    made << "#{a}".to_sym
  end
  module_eval code
  made
end

#attr_validator(*symbols, &validator) ⇒ Object

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

CREDIT: ?


8
9
10
11
12
13
14
15
16
17
18
19
20
# File 'lib/core/facets/module/attr.rb', line 8

def attr_validator(*symbols, &validator)
  made = []
  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
    made << "#{symbol}=".to_sym
  end
  made
end

#attr_writer!(*args) ⇒ Object

Create a flaggable attribute. This creates a single methods used to set an attribute to “true”.

attr_writer! :a

is equivalent to

def a!(value=true)
  @a = value
  self
end


181
182
183
184
185
186
187
188
189
190
191
192
193
194
# File 'lib/core/facets/module/attr.rb', line 181

def attr_writer!(*args)
  code, made = '', []
  args.each do |a|
    code << %{
      def #{a}!(value=true)
        @#{a} = value
        self
      end
    }
    made << "#{a}!".to_sym
  end
  module_eval code
  made
end

#basenameObject

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"

CREDIT: Trans


63
64
65
66
67
68
69
# File 'lib/core/facets/module/name.rb', line 63

def basename
  if name and not name.empty?
    name.gsub(/^.*::/, '')
  else
    nil #inspect.gsub('#<','').gsub('>','').sub(':', '_')
  end
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: Gavin Sinclair

Raises:

  • (ArgumentError)


33
34
35
36
37
38
39
40
41
42
# File 'lib/core/facets/module/name.rb', line 33

def by_name(name)
  #result = Object.constant(name)
  # TODO: Does self need to be Object in the following lines?
  const  = name.to_s.dup
  base   = const.sub!(/^::/, '') ? Object : ( self.kind_of?(Module) ? self : self.class )
  result = const.split(/::/).inject(base){ |mod, subconst| mod.const_get(subconst) }

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

#class_def(name, &blk) ⇒ Object

Defines an instance method within a class.

CREDIT: WhyTheLuckyStiff


156
157
158
# File 'lib/core/facets/kernel/metaid.rb', line 156

def class_def name, &blk
  class_eval { define_method name, &blk }
end

#class_methods(&yld) ⇒ Object Also known as: class_inherit



138
139
140
141
142
143
144
145
146
# File 'lib/more/facets/classmethods.rb', line 138

def class_methods( &yld )
  if const_defined?( :ClassMethods )
    self::ClassMethods.class_eval( &yld )
  else
    self.const_set( :ClassMethods, Module.new( &yld ) )
  end
  extend( self::ClassMethods )
  self::ClassMethods
end

#classified_attributesObject

Return list of attributes that have a :class annotation.

class MyClass
  attr_accessor :test
  attr_accessor :name, String, :doc => 'Hello'
  attr_accessor :age, Fixnum
end

MyClass.instance_attributes # => [:test, :name, :age, :body]
MyClass.classified_attributes # => [:name, :age]


126
127
128
129
130
# File 'lib/more/facets/attributes.rb', line 126

def classified_attributes
  instance_attributes.find_all do |a|
    self.ann(a, :class)
  end
end

#clone_removing(*meths) ⇒ Object

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

NOTE: These is likely to be usurped by traits.rb.

CREDIT: Trans


28
29
30
31
32
33
# File 'lib/core/facets/module/clone.rb', line 28

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.

NOTE: These is likely to be usurped by traits.rb.

CREDIT: Trans


10
11
12
13
14
15
16
17
18
19
# File 'lib/core/facets/module/clone.rb', line 10

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(*methods) ⇒ Object

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

NOTE: These is likely to be usurped by traits.rb.

CREDIT: Trans


42
43
44
45
46
47
48
# File 'lib/core/facets/module/clone.rb', line 42

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

#conflict?(other) ⇒ Boolean

Detect conflicts.

module A
  def c; end
end

module B
  def c; end
end

A.conflict?(B)  #=> ["c"]

TODO: All instance methods, or just public?

CREDIT: Thomas Sawyer
CREDIT: Robert Dober

Returns:

  • (Boolean)


112
113
114
115
116
117
118
# File 'lib/core/facets/module/traits.rb', line 112

def conflict?(other)
  c = []
  c += (public_instance_methods(true) & other.public_instance_methods(true))
  c += (private_instance_methods(true) & other.private_instance_methods(true))
  c += (protected_instance_methods(true) & other.protected_instance_methods(true))
  c.empty ? false : c
end

#define_dependency(name, *deps) ⇒ Object



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/more/facets/dependency.rb', line 104

def define_dependency( name, *deps )
  @dependency ||= {}
  if @dependency[name.to_sym]
    @dependency[name.to_sym] = deps
  else
    @dependency[name.to_sym] = deps
    deplist = lambda{ dependencies(name) }
    alias_method("#{name}:execute",name)
    define_method(name) do |*a|
      # run dependencies
      deplist.call.each do |d|
        if respond_to?("#{d}:execute")
          send("#{d}:execute",*a) #,&b)
        else
          send(d,*a) #,&b)
        end
      end
      # run core method
      send("#{name}:execute",*a) #,&b)
    end
  end
end

#depend(name_and_deps = nil) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/more/facets/dependency.rb', line 74

def depend( name_and_deps=nil )
  if Hash === name_and_deps
    name_and_deps.to_h.each do |name, deps|
      deps = [deps].flatten
      define_dependency(name, *deps)
    end
  elsif name_and_deps
    @dependency ||= {}
    @dependency[name_and_deps.to_sym]
  else
    @dependency ||= {}
  end
end

#dependencies(name, build = []) ⇒ Object

Compile list of all unique prerequisite calls.



90
91
92
93
94
95
96
97
98
99
100
# File 'lib/more/facets/dependency.rb', line 90

def dependencies(name, build=[])
  @dependency ||= {}
  deps = @dependency[name.to_sym]
  return build unless deps
  deps.each do |dep|
    build.unshift(dep)
    dependencies(dep,build)
  end
  build.uniq!
  build
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.

CREDIT: Trans


85
86
87
88
# File 'lib/core/facets/module/name.rb', line 85

def dirname
  name[0...(name.rindex('::') || 0)]
  #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


79
80
81
82
83
84
85
86
# File 'lib/more/facets/compare_on.rb', line 79

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

#extend(*args) ⇒ Object Also known as: can



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
# File 'lib/more/facets/paramix.rb', line 123

def extend(*args)
  params = args.last.is_a?(Hash) ? args.pop : {}
  args.each do |mod|
    (class << self; self; end).class_eval do
      mixin_parameters[mod] = params
      if mod.basename
        define_method( mod.basename ) do |key|
          if params.key?(key)
            params[key]
          else
            super if defined?( super )
          end
        end
      end
    end
  end
  r = extend_without_parameters(*args)
  for mod in args
    if mod.method_defined?(:extended_with_parameters)
      mod.extended_with_parameters( self, params )
    end
  end
  r
end

#extend_without_parametersObject



121
# File 'lib/more/facets/paramix.rb', line 121

alias_method :extend_without_parameters, :extend

#heritage(ref) ⇒ Object



99
100
101
102
103
104
105
# File 'lib/more/facets/annotations.rb', line 99

def heritage(ref)
  ref = ref.to_sym
  ancestors.inject({}) { |memo, ancestor|
    ancestor.annotations[ref] ||= {}
    ancestor.annotations[ref] + memo
  }
end

#include(*args) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/more/facets/paramix.rb', line 98

def include(*args)
  params = args.last.is_a?(Hash) ? args.pop : {}
  args.each do |mod|
    mixin_parameters[mod] = params
    if mod.basename
      define_method( mod.basename ) do |key|
        if params.key?(key)
          params[key]
        else
          super if defined?( super )
        end
      end
    end
  end
  r = include_without_parameters(*args)
  for mod in args
    if mod.respond_to?(:included_with_parameters)
      mod.included_with_parameters( self, params )
    end
  end
  r
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"


53
54
55
# File 'lib/more/facets/namespace.rb', line 53

def include_as(h)
  h.each{ |name, mod| method_space(name, mod) }
end

#include_function_module(*mod) ⇒ Object

Include module and apply module_fuction to the included methods.

CREDIT: Trans


108
109
110
111
# File 'lib/core/facets/module/include.rb', line 108

def include_function_module *mod
  include(*mod)
  module_function(*mod.collect{|m| m.private_instance_methods & m.methods(false)}.flatten)
end

#include_without_parametersObject



96
# File 'lib/more/facets/paramix.rb', line 96

alias_method :include_without_parameters, :include

#instance_interface(*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 facets/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)


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/more/facets/interface.rb', line 97

def instance_interface(*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

#instance_method!(s) ⇒ Object

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 it is currently limited to the scope of the current module/class.



112
113
114
115
# File 'lib/core/facets/1stclassmethod.rb', line 112

def instance_method!(s)
  #( @@__instance_methods__ ||= {} )[s] ||= instance_method(s)  # TODO when fixed
  ( @__instance_methods__ ||= {} )[s] ||= instance_method(s)
end

#instance_method_defined?(meth) ⇒ Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/core/facets/module/methods.rb', line 23

def instance_method_defined?(meth)
  instance_methods(true).find { |m| m == meth.to_s }
end

#integrate(mod, &block) ⇒ Object

Using integrate is just like using include except the module included is a reconstruction of the one given altered by the commands given in the block.

Convenient commands available are: #rename, #redef, #remove, #nodef and #wrap. But any module method can be used.

module W
  def q ; "q" ; end
  def y ; "y" ; end
end

class X
  integrate W do
    nodef :y
  end
end

x = X.new
x.q  #=> "q"
x.y  #=> missing method error

This is like #revisal, but #revisal only returns the reconstructred module. It does not include it.

CREDIT: Trans


42
43
44
45
46
47
# File 'lib/core/facets/module/modify.rb', line 42

def integrate(mod, &block)
  #include mod.revisal( &blk )
  m = Module.new{ include mod }
  m.class_eval(&block)
  include m
end

#is(*mods) ⇒ Object

alias_method :is, :include



13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/core/facets/module/include.rb', line 13

def is(*mods)
  mods.each do |mod|
    if mod.const_defined?(:Self)
      extend mod::Self
      # pass it along if module
      if instance_of?(Module)
        const_set(:Self, Module.new) unless const_defined?(:Self)
        const_get(:Self).send(:include, mod::Self)
      end
    end
  end
  include(*mods)
end

#is?(base) ⇒ Boolean

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

class X ; end
class Y < X ; end

 Y.is?(X)  #=> true

 CREDIT: Trans

Returns:

  • (Boolean)


62
63
64
# File 'lib/core/facets/module/include.rb', line 62

def is?(base)
  ancestors.slice(1..-1).include?( base )
end

#letObject

Use let to build a class like so:

class X

  let.y = "Hello"

  let.up = lambda { puts @y.upcase }

end

X.new.up  #=> HELLO


45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/more/facets/let.rb', line 45

def let
  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

#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


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/more/facets/memoize.rb', line 55

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

#method_overloadsObject



32
33
34
# File 'lib/more/facets/overload.rb', line 32

def method_overloads
  @method_overloads ||= {}
end

#method_space(name, mod = nil, &blk) ⇒ Object

Define a simple method namespace.

class A
  attr_writer :x
  method_space :inside do
    def x; @x; end
  end
end

a = A.new
a.x = 10
a.inside.x #=> 10
a.x  # no method error


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
102
103
104
105
106
107
108
109
110
111
# File 'lib/more/facets/namespace.rb', line 71

def method_space(name, mod=nil, &blk)

  # If block is given then create a module, otherwise
  # get the name of the module.
  if block_given?
    name = name.to_s
    raise ArgumentError if mod
    mod  = Module.new(&blk)
  else
    if Module === name
      mod = name
      name = mod.basename.downcase
    end
    mod  = mod.dup
  end

  # Include the module. This is neccessary, otherwise
  # Ruby won't let us bind the instance methods.
  include mod

  # Save the instance methods of the module and
  # replace them with a "transparent" version.
  methods = {}
  mod.instance_methods(false).each do |m|
    methods[m.to_sym] = mod.instance_method(m)
    mod.instance_eval do
      define_method(m) do
        super
      end
    end
  end

  # Add a method for the namespace that delegates
  # via the Functor to the saved instance methods.
  define_method(name) do
    mtab = methods
    Functor.new do |op, *args|
      mtab[op].bind(self).call(*args)
    end
  end
end

#mixin_parametersObject

Store for module parameters. This is local per module and indexed on module/class included-into.



94
# File 'lib/more/facets/paramix.rb', line 94

def mixin_parameters ; @mixin_parameters ||= {} ; end

#modspaceObject

Returns the module’s container module.

module Example
  class Demo
  end
end

Demo.modspace    #=> Example

See also Module#basename.

CREDIT: Trans


103
104
105
106
# File 'lib/core/facets/module/name.rb', line 103

def modspace
  space = name[ 0...(name.rindex( '::' ) || 0)]
  space.empty? ? Object : eval(space)
end

#module_load(path) ⇒ Object

Load file into module/class namespace.

CREDIT: Trans


9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/core/facets/module/require.rb', line 9

def module_load( path )
  if path =~ /^[\/~.]/
    file = File.expand_path(path)
  else
    $LOAD_PATH.each do |lp|
      file = File.join(lp,path)
      break if File.exist?(file)
      file = nil
    end
  end

  module_eval(File.read(file))
end

#module_method_defined?(meth) ⇒ Boolean

Query whether a normal (singleton) method is defined for the module.

CREDIT: Gavin Sinclair
CREDIT: Noah Gibbs

Returns:

  • (Boolean)


32
33
34
# File 'lib/core/facets/module/methods.rb', line 32

def module_method_defined?(meth)
  singleton_methods(true).find { |m| m == meth.to_s }
end

#module_require(path) ⇒ Object

Require file into module/class namespace.

CREDIT: Trans


27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/core/facets/module/require.rb', line 27

def module_require( path )
  if path =~ /^[\/~.]/
    file = File.expand_path(path)
  else
    $LOAD_PATH.each do |lp|
      file = File.join(lp,path)
      break if File.exist?(file)
      file += '.rb'
      break if File.exist?(file)
      file = nil
    end
  end
  @loaded ||= {}
  if @loaded.key?(file)
    false
  else
    @loaded[file] = true
    script = File.read(file)
    module_eval(script)
    true
  end
end

#nestingObject

Show a modules nesting in module namespaces.

A::B::C.nesting  #=> [ A, A::B ]

CREDIT: Trans


114
115
116
117
118
119
120
# File 'lib/core/facets/module/name.rb', line 114

def nesting
  n = []
  name.split(/::/).inject(self) do |mod, name|
    c = mod.const_get(name) ; n << c ; c
  end
  return n
end

#on_included(code) ⇒ Object

A macro for dynamic modules.

  TODO: This method ecourages a bad practice, and should not be used.
        It's here because Nitro uses it, but eventually it will be deprecated.

CREDIT: George Moschovitis


122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/core/facets/module/include.rb', line 122

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

#overload(name, *signiture, &block) ⇒ Object

Overload methods.

class X
  def x
    "hello"
  end

  overload :x, Integer do |i|
    i
  end

  overload :x, String, String do |s1, s2|
    [s1, s2]
  end
end


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
# File 'lib/more/facets/overload.rb', line 52

def overload( name, *signiture, &block )
  name = name.to_sym

  if method_overloads.key?( name )
    method_overloads[name][signiture] = block

  else
    method_overloads[name] = {}
    method_overloads[name][signiture] = block

    if method_defined?( name )
      #method_overloads[name][nil] = instance_method( name ) #true
      alias_method( "#{name}Generic", name )
      has_generic = true
    else
      has_generic = false
    end

    define_method( name ) do |*args|
      ovr = self.class.method_overloads["#{name}".to_sym]
      sig = args.collect{ |a| a.class }
      hit = nil
      faces = ovr.keys.sort { |a,b| b.size <=> a.size }
      faces.each do |cmp|
        next unless cmp.size == sig.size
        cmp.size.times { |i|
          next unless cmp[i] < sig[i]
        }
        hit = cmp
      end

      if hit
        ovr[hit].call(*args)
      else
        if has_generic #ovr[nil]
          send( "#{name}Generic", *args )
          #ovr[nil].bind(self).call(*args)
        else
          raise NoMethodError
        end
      end

    end

  end

end

#prepend(aspect) ⇒ Object

Prepend an aspect module to a module.

module X
  def x; "x"; end
end

module U
  def x; '{' + super + '}'; end
end

X.prepend U

X.x  # => "{x}"


45
46
47
48
# File 'lib/more/facets/prepend.rb', line 45

def prepend( aspect )
  aspect.send(:include, self)
  extend aspect
end

#private_conflict?(other) ⇒ Boolean

Like #conflict?, but checks only private methods.

Returns:

  • (Boolean)


134
135
136
137
# File 'lib/core/facets/module/traits.rb', line 134

def private_conflict?(other)
  c = private_instance_methods(true) & other.private_instance_methods(true)
  c.empty ? false : c
end

#protected_conflict?(other) ⇒ Boolean

Like #conflict?, but checks only protected methods.

Returns:

  • (Boolean)


141
142
143
144
# File 'lib/core/facets/module/traits.rb', line 141

def protected_conflict?(other)
  c = protected_instance_methods(true) & other.protected_instance_methods(true)
  c.empty ? false : c
end

#public_conflict?(other) ⇒ Boolean

Like #conflict?, but checks only public methods.

Returns:

  • (Boolean)


127
128
129
130
# File 'lib/core/facets/module/traits.rb', line 127

def public_conflict?(other)
  c = public_instance_methods(true) & other.public_instance_methods(true)
  c.empty ? false : c
end

#revisal(&blk) ⇒ Object

Return a new module based on another. This includes the original module into the new one.

CREDIT: Trans


54
55
56
57
58
59
# File 'lib/core/facets/module/modify.rb', line 54

def revisal(&blk)
  base = self
  nm = Module.new{ include base }
  nm.class_eval(&blk)
  nm
end

#setting(sym, options = {}) ⇒ Object

Defines a configuration setting for the enclosing class.

Example

class Compiler

setting :template_root, :default => 'src/template', :doc => 'The template root dir'

end



235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/more/facets/settings.rb', line 235

def setting(sym, options = {})
  Settings.add_setting(self, sym, options)

  module_eval %{
    def self.#{sym}
      Settings[#{self}][:#{sym}].value
    end

    def self.#{sym}=(obj)
      Settings.setting #{self}, :#{sym}, :value => obj
    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


44
45
46
47
48
49
50
51
52
53
# File 'lib/more/facets/compare_on.rb', line 44

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

#wrap_method(sym, &blk) ⇒ Object Also known as: wrap

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.

CREDIT: Trans


166
167
168
169
# File 'lib/core/facets/module/modify.rb', line 166

def wrap_method( sym, &blk )
  old = instance_method(sym)
  define_method(sym) { |*args| blk.call(old.bind(self), *args) }
end