It would nice if we could the following. Then the middle portion of the Comparable method would not be needed. But I fear it might break others code.
module Comparable
def <=>(other)
comparability.each do |field|
cmp = send(field) <=> other.send(field); return cmp unless cmp == 0
end
end
end
Rename methods.
module A
def a; "a"; end
end
B = A * { :a => :b }
class X; include B; end
X.new.b #=> "a"
Thomas Sawyer, Robert Dober
# File lib/core/facets/module/op.rb, line 80
80: def *(rename_map)
81: base = self
82: Module.new do
83: include base
84: rename_map.each do |from, to|
85: alias_method to, from
86: undef_method from
87: end
88: end
89: end
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, Robert Dober
# File lib/core/facets/module/op.rb, line 35
35: def +(other)
36: base = self
37: Module.new do
38: include base
39: include other
40: end
41: end
Subtract modules.
TODO: Should this use all instance_methods, not just public?
CREDIT: Thomas Sawyer, Robert Dober
# File lib/core/facets/module/op.rb, line 49
49: def -(other)
50: case other
51: when Array
52: subtract = instance_methods(true) & other.collect{|m| m.to_s}
53: when Module
54: subtract = instance_methods(true) & other.instance_methods(true) # false?
55: when String, Symbol
56: subtract = instance_methods(true) & [other.to_s]
57: end
58: base = self
59: Module.new do
60: include base
61: subtract.each{ |x| undef_method x }
62: end
63: end
Automatically generate sorting defintions base on attribute fields.
include SortOn(:a, :b)
is equivalent to including a module containing:
def <=>(other)
cmp = self.a <=> other.a; return cmp unless cmp == 0
cmp = self.b <=> other.b; return cmp unless cmp == 0
0
end
# File lib/core/facets/comparable/comparable.rb, line 28
28: def Comparable(*accessors)
29: define_method(:comparability){ accessors }
30: code = %{
31: def <=>(other)
32: comparability.each do |a|
33: cmp = (send(a) <=> other.send(a)); return cmp unless cmp == 0
34: end
35: end
36: }
37: module_eval code
38: return Comparable
39: end
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
# File lib/core/facets/module/abstract.rb, line 15
15: def abstract( *sym )
16: sym.each { |s|
17: define_method( s ) { raise TypeError, "undefined abstraction ##{s}" }
18: }
19: end
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_with_feature?
so you can safely chain foo, foo?, and foo! with the same feature.
CREDIT: Bitsweat, Rails Team
# File lib/core/facets/module/alias_method_chain.rb, line 27
27: def alias_method_chain(target, feature)
28: # Strip out punctuation on predicates or bang methods since
29: # e.g. target?_without_feature is not a valid method name.
30: aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
31: yield(aliased_target, punctuation) if block_given?
32:
33: with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
34:
35: alias_method without_method, target
36: alias_method target, with_method
37:
38: case
39: when public_method_defined?(without_method)
40: public target
41: when protected_method_defined?(without_method)
42: protected target
43: when private_method_defined?(without_method)
44: private target
45: end
46: end
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
# File lib/core/facets/module/attr_setter.rb, line 49
49: def alias_setter(*args)
50: args = args - [orig]
51: args.each do |name|
52: alias_method(name, orig)
53: end
54: end
List all instance methods, equivalent to
public_instance_methods + protected_instance_methods + private_instance_methods
TODO: Better name for all_instance_methods?
CREDIT: Trans
# File lib/core/facets/module/instance_methods.rb, line 13
13: def all_instance_methods(include_super=true)
14: public_instance_methods(include_super) +
15: protected_instance_methods(include_super) +
16: private_instance_methods(include_super)
17: end
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/core/facets/module/ancestor.rb, line 11
11: def ancestor?( mod )
12: ancestors.include? mod
13: end
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
# File lib/core/facets/module/attr_setter.rb, line 21
21: def attr_setter(*args)
22: code, made = '', []
23: args.each do |a|
24: code << %{
25: def #{a}(*args)
26: args.size > 0 ? ( @#{a}=args[0] ; self ) : @#{a}
27: end
28: }
29: made << "#{a}".to_sym
30: end
31: module_eval code
32: made
33: end
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
# File lib/core/facets/module/basename.rb, line 22
22: def basename
23: if name and not name.empty?
24: name.gsub(/^.*::/, '')
25: else
26: nil #inspect.gsub('#<','').gsub('>','').sub(':', '_')
27: end
28: end
Defines an instance method within a class.
CREDIT: WhyTheLuckyStiff
# File lib/core/facets/metaid.rb, line 82
82: def class_def name, &blk
83: class_eval { define_method name, &blk }
84: end
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, Robert Dober
# File lib/core/facets/module/conflict.rb, line 20
20: def conflict?(other)
21: common_ancestor = (ancestors & other.ancestors).first
22: c = []
23: c += (public_instance_methods(true) & other.public_instance_methods(true))
24: c += (private_instance_methods(true) & other.private_instance_methods(true))
25: c += (protected_instance_methods(true) & other.protected_instance_methods(true))
26: c -= common_ancestor.public_instance_methods(true)
27: c -= common_ancestor.private_instance_methods(true)
28: c -= common_ancestor.protected_instance_methods(true)
29: c.empty? ? false : c
30: end
# File lib/core/facets/module/extend.rb, line 5 5: def extend(*mod, &blk) 6: _extend *mod unless mod.empty? 7: _extend Module.new(&blk) if blk 8: end
Access method as a singleton object and retain state.
module K
def hello
puts "Hello World!"
end
end
p K.instance_method!(:hello) #=> <UnboundMethod: #hello>
NOTE: This is limited to the scope of the current module/class.
# File lib/core/facets/module/instance_method.rb, line 17
17: def instance_method!(s)
18: #( @@__instance_methods__ ||= {} )[s] ||= instance_method(s) # TODO: use class vars for 1.9+ ?
19: #( @__instance_methods__ ||= {} )[s.to_sym] ||= instance_method(s.to_sym)
20: $FIRST_CLASS_INSTANCE_METHODS[self][s.to_sym] ||= instance_method(s.to_sym)
21: end
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
# File lib/core/facets/module/revise.rb, line 51
51: def integrate(mod, &block)
52: #include mod.revisal( &blk )
53: m = Module.new{ include mod }
54: m.class_eval(&block)
55: include m
56: end
alias_method :is, :include
# File lib/core/facets/module/is.rb, line 27
27: def is(*mods)
28: mods.each do |mod|
29: if mod.const_defined?(:Self)
30: extend mod::Self
31: # pass it along if module
32: if instance_of?(Module)
33: const_set(:Self, Module.new) unless const_defined?(:Self)
34: const_get(:Self).send(:include, mod::Self)
35: end
36: end
37: end
38: include(*mods)
39: end
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
# File lib/core/facets/module/is.rb, line 13
13: def is?(base)
14: ancestors.slice(1..-1).include?(base)
15: end
Translate a module name to a suitable method name.
My::CoolClass.methodize => "my__cool_class"
# File lib/core/facets/module/methodize.rb, line 9
9: def methodize
10: name.methodize
11: end
Returns the module‘s container module.
module Example
class Demo
end
end
Example::Demo.modspace #=> Example
See also Module#basename.
CREDIT: Trans
# File lib/core/facets/module/modspace.rb, line 16
16: def modspace
17: space = name[ 0...(name.rindex( '::' ) || 0)]
18: space.empty? ? Object : eval(space)
19: end
Load file directly into module/class namespace.
Please use this with careful consideration. It is best suited to loading plugin-type scripts, and should generally not be used as a substitue for Ruby‘s standard load system.
CREDIT: Trans
# File lib/core/facets/module/module_load.rb, line 12
12: def module_load( path )
13: if path =~ /^[\/~.]/
14: file = File.expand_path(path)
15: else
16: $LOAD_PATH.each do |lp|
17: file = File.join(lp,path)
18: break if File.exist?(file)
19: file = nil
20: end
21: end
22: raise LoadError, "no such file to load -- #{path}" unless file
23: module_eval(File.read(file))
24: end
Require file into module/class namespace.
Unlike load this keeps a per-module cache and will not load the same file into the same module more than once despite repeated attempts.
The cache is kept in a global var called +$module_require+.
Please use this with careful consideration. It is best suited to loading plugin-type scripts, and should generally not be used as a substitue for Ruby‘s standard load system.
CREDIT: Trans
# File lib/core/facets/module/module_load.rb, line 41
41: def module_require( path )
42: if path =~ /^[\/~.]/
43: file = File.expand_path(path)
44: else
45: $LOAD_PATH.each do |lp|
46: file = File.join(lp,path)
47: break if File.exist?(file)
48: file += '.rb'
49: break if File.exist?(file)
50: file = nil
51: end
52: end
53: raise LoadError, "no such file to load -- #{path}" unless file
54: # per-module load cache
55: $module_require ||= {}
56: $module_require[self] ||= {}
57: loaded = $module_require[self]
58: if loaded.key?(file)
59: false
60: else
61: loaded[file] = true
62: script = File.read(file)
63: module_eval(script)
64: true
65: end
66: end
Converts a class name to a unix path
Examples
CoolClass.pathize #=> "cool_class" My::CoolClass.pathize #=> "my/cool_class"
# File lib/core/facets/module/pathize.rb, line 11
11: def pathize
12: name.pathize
13: #to_s.
14: # gsub(/::/, '/').
15: # gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
16: # gsub(/([a-z\d])([A-Z])/,'\1_\2').
17: # tr("-", "_").
18: # downcase
19: end
Prepend an aspect module to a module. This only works at the module level.
module X
def x; "x"; end
end
module U
def x; '{' + super + '}'; end
end
X.prepend U
X.x # => "{x}"
CREDIT Trans
# File lib/core/facets/module/prepend.rb, line 20
20: def prepend(aspect)
21: aspect.__send__(:include, self)
22: extend aspect
23: end
Like conflict?, but checks only private methods.
# File lib/core/facets/module/conflict.rb, line 46
46: def private_conflict?(other)
47: common_ancestor = (ancestors & other.ancestors).first
48: c = private_instance_methods(true) & other.private_instance_methods(true)
49: c -= common_ancestor.private_instance_methods(true)
50: c.empty? ? false : c
51: end
Like conflict?, but checks only protected methods.
# File lib/core/facets/module/conflict.rb, line 54
54: def protected_conflict?(other)
55: common_ancestor = (ancestors & other.ancestors).first
56: c = protected_instance_methods(true) & other.protected_instance_methods(true)
57: c -= common_ancestor.protected_instance_methods(true)
58: c.empty? ? false : c
59: end
Like conflict?, but checks only public methods.
# File lib/core/facets/module/conflict.rb, line 38
38: def public_conflict?(other)
39: common_ancestor = (ancestors & other.ancestors).first
40: c = public_instance_methods(true) & other.public_instance_methods(true)
41: c -= common_ancestor.public_instance_methods(true)
42: c.empty? ? false : c
43: end
Return a new module based on another. This includes the original module into the new one.
CREDIT: Trans
# File lib/core/facets/module/revise.rb, line 13
13: def revise(&blk)
14: base = self
15: nm = Module.new{ include base }
16: nm.class_eval(&blk)
17: nm
18: end
Returns the name of module‘s container module.
module Example
class Demo
end
end
Demo.name #=> "Example::Demo"
Demo.spacename #=> "Example"
This used to be called dirname.
See also Module#basename.
CREDIT: Trans
# File lib/core/facets/module/spacename.rb, line 19
19: def spacename
20: name[0...(name.rindex('::') || 0)]
21: #name.gsub(/::[^:]*$/, '')
22: end
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
# File lib/core/facets/module/wrap_method.rb, line 20
20: def wrap_method( sym, &blk )
21: old = instance_method(sym)
22: define_method(sym) { |*args| blk.call(old.bind(self), *args) }
23: end
As with alias_method, but alias both reader and writer.
attr_accessor :x self.x = 1 alias_accessor :y, :x y #=> 1 self.y = 2 x #=> 2
# File lib/core/facets/module/alias_accessor.rb, line 14
14: def alias_accessor(*args)
15: orig = args.last
16: args = args - [orig]
17: args.each do |name|
18: alias_method(name, orig)
19: alias_method("#{name}=", "#{orig}=")
20: end
21: end
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/core/facets/module/alias_module_function.rb, line 24
24: def alias_module_function(new, old)
25: alias_method(new, old)
26: module_function(new)
27: end
As with alias_accessor, but just for the reader. This is basically the same as alias_method.
# File lib/core/facets/module/alias_accessor.rb, line 26
26: def alias_reader(*args)
27: orig = args.last
28: args = args - [orig]
29: args.each do |name|
30: alias_method(name, orig)
31: end
32: end
As with alias_method but does the writer instead.
# File lib/core/facets/module/alias_accessor.rb, line 36
36: def alias_writer(*args)
37: orig = args.last
38: args = args - [orig]
39: args.each do |name|
40: alias_method("#{name}=", "#{orig}=")
41: end
42: end
Include module and apply module_fuction to the included methods.
module Utils
module_function
def foo; "foo"; end
end
module UtilsPlus
include_function_module Utils
end
CREDIT: Trans
# File lib/core/facets/module/include_function_module.rb, line 19
19: def include_function_module *mod
20: include(*mod)
21: module_function(*mod.collect{|m| m.private_instance_methods & m.methods(false)}.flatten)
22: end
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!"
CREDIT: Trans
# File lib/core/facets/module/redefine_method.rb, line 26
26: def redefine_method(sym, aka=nil, &blk)
27: raise ArgumentError, "method does not exist" unless method_defined?( sym )
28: alias_method( aka, sym ) if aka
29: undef_method( sym )
30: define_method( sym, &blk )
31: end
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
CREDIT: Trans
# File lib/core/facets/module/redirect_method.rb, line 37
37: def redirect_method( method_hash )
38: method_hash.each do |targ,adv|
39: define_method(targ) { |*args| send(adv,*args) }
40: end
41: end
Aliases a method and undefines the original.
rename_method( :to_method, :from_method )
CREDIT: Trans
# File lib/core/facets/module/rename_method.rb, line 11
11: def rename_method( to_sym, from_sym )
12: raise ArgumentError, "method #{from_sym} does not exist" unless method_defined?( from_sym )
13: alias_method( to_sym, from_sym )
14: undef_method( from_sym )
15: end