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
- *
- +
- -
- Comparable
- abstract
- alias_accessor
- alias_method_chain
- alias_module_function
- alias_reader
- alias_writer
- all_instance_methods
- ancestor?
- basename
- class_def
- class_load
- class_require
- conflict?
- include_function_module
- instance_method_defined?
- integrate
- is
- is?
- modspace
- module_load
- module_method_defined?
- module_require
- nesting
- pathize
- prepend
- private_conflict?
- protected_conflict?
- public_conflict?
- redef
- redefine_method
- redirect
- redirect_method
- rename
- rename_method
- revisal
- revise
- singleton_method_defined?
- spacename
- wrap
- wrap_method
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
[ show source ]
# File lib/facets/module/op.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
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
[ show source ]
# File lib/facets/module/op.rb, line 36 def +(other) base = self Module.new do include base include other end end
Subtract modules.
TODO: Should this use all instance_methods, not just public? CREDIT: Thomas Sawyer CREDIT: Robert Dober
[ show source ]
# File lib/facets/module/op.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
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
[ show source ]
# File lib/facets/comparable/comparable.rb, line 28 def Comparable(*accessors) define_method(:comparability){ accessors } code = %{ def <=>(other) comparability.each do |a| cmp = (send(a) <=> other.send(a)); return cmp unless cmp == 0 end end } module_eval code return Comparable 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
[ show source ]
# File lib/facets/module/abstract.rb, line 15 def abstract( *sym ) sym.each { |s| define_method( s ) { raise TypeError, "undefined abstraction ##{s}" } } 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
[ show source ]
# File lib/facets/module/instance_methods.rb, line 13 def all_instance_methods(include_super=true) public_instance_methods(include_super) + protected_instance_methods(include_super) + private_instance_methods(include_super) end
Is a given class or module an ancestor of this class or module?
class X ; end class Y < X ; end X.ancestor?(Y)
[ show source ]
# File lib/facets/module/ancestor.rb, line 11 def ancestor?( mod ) ancestors.include? mod 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
[ show source ]
# File lib/facets/module/nesting.rb, line 54 def basename if name and not name.empty? name.gsub(/^.*::/, '') else nil #inspect.gsub('#<','').gsub('>','').sub(':', '_') end end
Defines an instance method within a class.
CREDIT: WhyTheLuckyStiff
[ show source ]
# File lib/facets/metaid.rb, line 109 def class_def name, &blk class_eval { define_method name, &blk } end
Alias for module_load
Alias for module_require
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
[ show source ]
# File lib/facets/module/conflict.rb, line 21 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
Query whether an instance method is defined for the module.
CREDIT: Gavin Sinclair CREDIT: Noah Gibbs
[ show source ]
# File lib/facets/module/instance_methods.rb, line 24 def instance_method_defined?(meth) instance_methods_all(true).find{ |m| m == meth.to_s } 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
[ show source ]
# File lib/facets/module/revise.rb, line 46 def integrate(mod, &block) #include mod.revisal( &blk ) m = Module.new{ include mod } m.class_eval(&block) include m end
alias_method :is, :include
[ show source ]
# File lib/facets/module/is.rb, line 27 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 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
[ show source ]
# File lib/facets/module/is.rb, line 13 def is?(base) ancestors.slice(1..-1).include?(base) end
Returns the module‘s container module.
module Example class Demo end end Example::Demo.modspace #=> Example
See also Module#basename.
CREDIT: Trans
[ show source ]
# File lib/facets/module/nesting.rb, line 30 def modspace space = name[ 0...(name.rindex( '::' ) || 0)] space.empty? ? Object : eval(space) end
Load file into module/class namespace.
CREDIT: Trans
[ show source ]
# File lib/facets/module/module_load.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 raise LoadError, "no such file to load -- #{path}" unless file module_eval(File.read(file)) end
Require file into module/class namespace.
CREDIT: Trans
[ show source ]
# File lib/facets/module/module_load.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 raise LoadError, "no such file to load -- #{path}" unless file @loaded ||= {} if @loaded.key?(file) false else @loaded[file] = true script = File.read(file) module_eval(script) true end end
Show a modules nesting in module namespaces.
A::B::C.nesting #=> [ A, A::B ] CREDIT: Trans
[ show source ]
# File lib/facets/module/nesting.rb, line 9 def nesting n = [] name.split(/::/).inject(self) do |mod, name| c = mod.const_get(name) ; n << c ; c end return n end
Converts a class name to a unix path
Examples
CoolClass.pathize #=> "cool_class" My::CoolClass.pathize #=> "my/cool_class"
[ show source ]
# File lib/facets/module/pathize.rb, line 9 def pathize to_s. gsub(/::/, '/'). gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). gsub(/([a-z\d])([A-Z])/,'\1_\2'). tr("-", "_"). downcase end
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}" CREDIT Trans
[ show source ]
# File lib/facets/module/prepend.rb, line 19 def prepend(aspect) aspect.__send__(:include, self) extend aspect end
Like conflict?, but checks only private methods.
[ show source ]
# File lib/facets/module/conflict.rb, line 43 def private_conflict?(other) c = private_instance_methods(true) & other.private_instance_methods(true) c.empty ? false : c end
Like conflict?, but checks only protected methods.
[ show source ]
# File lib/facets/module/conflict.rb, line 50 def protected_conflict?(other) c = protected_instance_methods(true) & other.protected_instance_methods(true) c.empty ? false : c end
Like conflict?, but checks only public methods.
[ show source ]
# File lib/facets/module/conflict.rb, line 36 def public_conflict?(other) c = public_instance_methods(true) & other.public_instance_methods(true) c.empty ? false : c end
Alias for revise
Return a new module based on another. This includes the original module into the new one.
CREDIT: Trans
[ show source ]
# File lib/facets/module/revise.rb, line 8 def revise(&blk) base = self nm = Module.new{ include base } nm.class_eval(&blk) nm end
Query whether a normal (singleton) method is defined for the module.
CREDIT: Gavin Sinclair CREDIT: Noah Gibbs
[ show source ]
# File lib/facets/module/instance_methods.rb, line 33 def singleton_method_defined?(meth) singleton_methods(true).find{ |m| m == meth.to_s } 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
[ show source ]
# File lib/facets/module/nesting.rb, line 78 def spacename name[0...(name.rindex('::') || 0)] #name.gsub(/::[^:]*$/, '') end
Alias for wrap_method
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
[ show source ]
# File lib/facets/module/revise.rb, line 160 def wrap_method( sym, &blk ) old = instance_method(sym) define_method(sym) { |*args| blk.call(old.bind(self), *args) } 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
[ show source ]
# File lib/facets/module/alias.rb, line 14 def alias_accessor(*args) orig = args.last args = args - [orig] args.each do |name| alias_method(name, orig) alias_method("#{name}=", "#{orig}=") end 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 CREDIT: Rails Team
[ show source ]
# File lib/facets/module/alias.rb, line 93 def alias_method_chain(target, feature) # 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 yield(aliased_target, punctuation) if block_given? with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}" alias_method without_method, target alias_method target, with_method case when public_method_defined?(without_method) public target when protected_method_defined?(without_method) protected target when private_method_defined?(without_method) private target end 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
[ show source ]
# File lib/facets/module/alias.rb, line 63 def alias_module_function(new, old) alias_method(new, old) module_function(new) end
As with alias_accessor, but just for the reader. This is basically the same as alias_method.
[ show source ]
# File lib/facets/module/alias.rb, line 26 def alias_reader(*args) orig = args.last args = args - [orig] args.each do |name| alias_method(name, orig) end end
As with alias_method but does the writer instead.
[ show source ]
# File lib/facets/module/alias.rb, line 36 def alias_writer(*args) orig = args.last args = args - [orig] args.each do |name| alias_method("#{name}=", "#{orig}=") end 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
[ show source ]
# File lib/facets/module/include_function_module.rb, line 19 def include_function_module *mod include(*mod) module_function(*mod.collect{|m| m.private_instance_methods & m.methods(false)}.flatten) end
Alias for redefine_method
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
[ show source ]
# File lib/facets/module/revise.rb, line 76 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
Alias for redirect_method
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
[ show source ]
# File lib/facets/module/revise.rb, line 115 def redirect_method( method_hash ) method_hash.each do |targ,adv| define_method(targ) { |*args| send(adv,*args) } end end
Alias for rename_method
Aliases a method and undefines the original.
rename_method( :to_method, :from_method ) CREDIT: Trans
[ show source ]
# File lib/facets/module/revise.rb, line 127 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