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
# File lib/more/facets/class_extend.rb, line 87 def self.append_features(mod) append_features_without_class_extension(mod) 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 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, Robert Dober
# File lib/core/facets/module/op.rb, line 35 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, Robert Dober
# File lib/core/facets/module/op.rb, line 49 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 definitions based on attribute fields.
include Comparable(: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 def Comparable(*accessors) define_method(:comparability){ accessors } code = %Q{ 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
This function provided a “shortcut” for creating the identity method based on given accessors and returns the Equitable module for inclusion.
include Equitable(:a, :b)
is equivalent to including a module containing:
def ==(other) self.a == other.a && self.b == other.b end def eql?(other) self.a.eql?(other.a) && self.b.eql?(other.b) end def hash() self.a.hash ^ self.b.hash end
# File lib/more/facets/equitable.rb, line 115 def Equitable(*accessors) Equitable.identify(self, *accessors) 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 def abstract( *sym ) sym.each { |s| define_method( s ) { raise TypeError, "undefined abstraction ##{s}" } } end
Create aliases for flag accessors.
CREDIT: Trans
# File lib/more/facets/module/attr.rb, line 32 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, Rails Team
# File lib/core/facets/module/alias_method_chain.rb, line 27 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 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 def alias_setter(*args) args = args - [orig] args.each do |name| alias_method(name, orig) end end
Create aliases for flag reader.
CREDIT: Trans
# File lib/more/facets/module/attr_tester.rb, line 34 def alias_tester(*args) orig = args.last args = args - [orig] args.each do |name| alias_method("#{name}?", "#{orig}?") end end
Create aliases for validators.
# File lib/more/facets/module/attr_validator.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
Create aliases for flag writer.
CREDIT: Trans
# File lib/more/facets/module/attr.rb, line 74 def alias_writer!(*args) orig = args.last args = args - [orig] args.each do |name| alias_method("#{name}!", "#{orig}!") end 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 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)
# File lib/core/facets/module/ancestor.rb, line 11 def ancestor?( mod ) ancestors.include? mod end
Override append_features
to handle class-inheritable
extensions.
# File lib/more/facets/class_extend.rb, line 103 def append_features(mod) append_features_without_class_extension(mod) mod.extend(class_extend) if mod.instance_of? Module mod.__send__(:class_extend).__send__(:include, class_extend) end 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 def attr_setter(*args) code, made = '', [] args.each do |a| code << %Q{ def #{a}(*args) args.size > 0 ? ( @#{a}=args[0] ; self ) : @#{a} end } made << "#{a}".to_sym end module_eval code made end
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
# File lib/more/facets/module/attr.rb, line 22 def attr_switch_accessor(*args) attr_reader!(*args) + attr_writer!(*args) end
Create an tester attribute. This creates a single method used to test the attribute for truth.
attr_tester :a
is equivalent to
def a? @a ? true : @a end
# File lib/more/facets/module/attr_tester.rb, line 14 def attr_tester(*args) code, made = '', [] args.each do |a| code << %Q{ def #{a}?(truth=nil) @#{a} ? truth || @#{a} : @#{a} end } made << "#{a}?".to_sym end module_eval code made end
Create a flaggable attribute. This creates a single methods used to set an attribute to “true”.
attr_toggler :a
is equivalent to
def a? @a ? true : @a end def a!(value=Exception) if Exception @a = @a ? false : true else @a = value end self end
# File lib/more/facets/module/attr_toggler.rb, line 25 def attr_toggler(*args) code, made = '', [] args.each do |a| code << %Q{ def #{a}!(value=Excception) if Exception @a = @a ? false : true else @a = value end self end } made << "#{a}!".to_sym end module_eval code made.concat(attr_tester(*args)) made end
Like attr_writer, but the writer method validates the setting against the given block.
CREDIT: ?
# File lib/more/facets/module/attr_validator.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
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
# File lib/more/facets/module/attr.rb, line 55 def attr_writer!(*args) code, made = '', [] args.each do |a| code << %Q{ def #{a}!(value=true) @#{a} = value self end } made << "#{a}!".to_sym end module_eval code made 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 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/module.
CREDIT: WhyTheLuckyStiff
# File lib/core/facets/module/module_def.rb, line 7 def class_def name, &blk class_eval { define_method name, &blk } end
Normally when including modules, class/module methods are not extended. To achieve this behavior requires some clever Ruby Karate. Instead class_extend provides an easy to use and clean solution. Simply place the extending class methods in a block of the special module method class_extend.
module Mix def inst_meth puts 'inst_meth' end class_extend do def class_meth "Class Method!" end end end class X include Mix end X.class_meth #=> "Class Method!"
NOTE: This old class_extension version of this method did not extend the containing class automatically –it had to be done by hand. With class_extend, that is no longer the case.
# File lib/more/facets/class_extend.rb, line 85 def class_extend(*mods, &block) @class_extension ||= Module.new do def self.append_features(mod) append_features_without_class_extension(mod) end end @class_extension.__send__(:include, *mods) @class_extension.module_eval(&block) if block_given? extend(@class_extension) # extend this module too @class_extension 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 def conflict?(other) common_ancestor = (ancestors & other.ancestors).first 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 -= common_ancestor.public_instance_methods(true) c -= common_ancestor.private_instance_methods(true) c -= common_ancestor.protected_instance_methods(true) c.empty? ? false : c end
Include a module via a specified space.
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/more/facets/methodspace.rb, line 108 def include_as(h) h.each{ |name, mod| method_space(name, mod) } end
Converts module methods into instance methods such that the first parameter
is passed self
. This promotes DRY programming when wishing to
offer both inheritable and module callable procedures.
This method is modeled after module_function
which essentially
has the the opposite effect. Due to implementation limitations, this must
use the callback singleton_method_added to emulate
module_function
when no method names are given.
module MyModule instance_function def self.jumble( obj, arg ) obj + arg end end class String include MyModule end MyModule.jumble( "Try", "Me" ) #=> "TryMe" "Try".jumble( "Me" ) #=> 'TryMe'
Note: This used to be a module called PromoteSelf and later Instantize, before becoming a method.
# File lib/more/facets/instance_function.rb, line 30 def instance_function(*meths) if meths.empty? extend InstanceFunction else meths.each do |meth| class_eval %Q{ def #{meth}(*args) #{self.name}.#{meth}(self,*args) end } end end 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 def instance_method!(s) #( @@__instance_methods__ ||= {} )[s] ||= instance_method(s) # TODO: use class vars for 1.9+ ? #( @__instance_methods__ ||= {} )[s.to_sym] ||= instance_method(s.to_sym) $FIRST_CLASS_INSTANCE_METHODS[self][s.to_sym] ||= instance_method(s.to_sym) end
Query whether a public instance method is defined for the module.
CREDIT: Gavin Sinclair, Noah Gibbs
# File lib/core/facets/module/instance_methods.rb, line 23 def instance_method_defined?(meth) instance_methods(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
# File lib/core/facets/module/revise.rb, line 51 def integrate(mod, &block) #include mod.revisal( &blk ) m = Module.new{ include mod } m.class_eval(&block) include m end
An alias for include.
class X is Enumerable end
CREDIT: Trans
# File lib/core/facets/module/is.rb, line 25 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
# File lib/core/facets/module/is.rb, line 13 def is?(base) Module===base && ancestors.slice(1..-1).include?(base) end
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
# File lib/more/facets/methodspace.rb, line 48 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.module_eval %Q{ def #{m}(*a,&b) super(*a,&b) end } #mod.instance_eval do #define_method(m) # 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
Translate a module name to a suitable method name.
My::CoolClass.methodize => "my__cool_class"
# File lib/core/facets/module/methodize.rb, line 9 def methodize name.methodize end
Returns the module's container module.
module Example class Demo end end Example::Demo.modspace #=> Example
See also #basename.
CREDIT: Trans
# File lib/core/facets/module/modspace.rb, line 16 def modspace space = name[ 0...(name.rindex( '::' ) || 0)] space.empty? ? Object : eval(space) end
Defines an instance method within a class/module.
CREDIT: WhyTheLuckyStiff
# File lib/core/facets/module/module_def.rb, line 15 def module_def name, &blk module_eval { define_method name, &blk } 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 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.
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 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 # per-module load cache $module_require ||= {} $module_require[self] ||= {} loaded = $module_require[self] 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
# File lib/core/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"
# File lib/core/facets/module/pathize.rb, line 11 def pathize name.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. 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 def prepend(aspect) aspect.__send__(:include, self) extend aspect end
Like conflict?, but checks only private methods.
# File lib/core/facets/module/conflict.rb, line 46 def private_conflict?(other) common_ancestor = (ancestors & other.ancestors).first c = private_instance_methods(true) & other.private_instance_methods(true) c -= common_ancestor.private_instance_methods(true) c.empty? ? false : c end
Like conflict?, but checks only protected methods.
# File lib/core/facets/module/conflict.rb, line 54 def protected_conflict?(other) common_ancestor = (ancestors & other.ancestors).first c = protected_instance_methods(true) & other.protected_instance_methods(true) c -= common_ancestor.protected_instance_methods(true) c.empty? ? false : c end
Like conflict?, but checks only public methods.
# File lib/core/facets/module/conflict.rb, line 38 def public_conflict?(other) common_ancestor = (ancestors & other.ancestors).first c = public_instance_methods(true) & other.public_instance_methods(true) c -= common_ancestor.public_instance_methods(true) c.empty? ? false : c 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 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, Noah Gibbs
# File lib/core/facets/module/instance_methods.rb, line 31 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 #basename.
CREDIT: Trans
# File lib/core/facets/module/spacename.rb, line 19 def spacename name[0...(name.rindex('::') || 0)] #name.gsub(/::[^:]*$/, '') 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 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
# File lib/core/facets/module/alias_accessor.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
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 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.
# File lib/core/facets/module/alias_accessor.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.
# File lib/core/facets/module/alias_accessor.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
# File lib/core/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
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 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 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 def redirect_method( method_hash ) method_hash.each do |targ,adv| define_method(targ) { |*args| send(adv,*args) } end 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 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