- abstract
- alias_method_chain
- alias_module_function
- ancestor?
- append_features
- append_features
- attr
- attr_setter
- attr_tester
- attr_tester
- attr_validator
- basename
- by_name
- class_extension
- clone_removing
- clone_renaming
- clone_using
- compare_on
- dirname
- equate_on
- generate_instance_method_name
- include_as
- initializer
- instance_method!
- instance_methods
- integrate
- is?
- key_attributes
- memoize
- modspace
- namespace
- nesting
- on_included
- redef
- redefine_method
- redirect
- redirect_method
- rename
- rename_method
- revisal
- shadow_all
- shadow_method
- sort_attributes
- sort_on
- this
- wrap
- wrap_method
METHOD_TYPES | = | [ :inherited, :ancestors, :local, :public, :private, :protected, :all ] |
[ show source ]
# File lib/facets/core/module/class_extension.rb, line 64 def self.append_features(mod) append_features_without_class_extension(mod) end
[ show source ]
# File lib/facets/core/module/abstract.rb, line 13 def abstract( *sym ) sym.each { |s| define_method( s ) { raise TypeError, "undefined abstraction ##{s}" } } 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_without_feature?
so you can safely chain foo, foo?, and foo! with the same feature.
[ show source ]
# File lib/facets/core/module/alias_method_chain.rb, line 29 def alias_method_chain(target, feature=nil) # Strip out punctuation on predicates or bang methods since # e.g. target?_without_feature is not a valid method name. aliased_target, punctuation = target.to_s.sub(/([?!])$/, ''), $1 alias_method "#{aliased_target}_without_#{feature}#{punctuation}", target alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}" end
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/core/module/ancestor.rb, line 12 def ancestor?( mod ) ancestors.include? mod end
[ show source ]
# File lib/facets/core/module/class_extension.rb, line 74 def append_features(mod) append_features_without_class_extension(mod) mod.extend(class_extension) if mod.instance_of? Module mod.__send__(:class_extension).__send__(:include, class_extension) end end
There’s a school of thought that says an attribute show always have a reader and a writer.
[ show source ]
# File lib/facets/core/module/attr.rb, line 10 def attr(*args) args.pop unless args.last.is_a?(Symbol) or args.last.is_a?(String) attr_accessor *args args.collect{ |a| [ "#{a}".to_sym, "#{a}=".to_sym ] }.flatten end
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
[ show source ]
# File lib/facets/core/module/attr_setter.rb, line 23 def attr_setter(*args) make = {} args.each { |a| make["#{a}".to_sym] = %{ def #{a}(*args) args.size > 0 ? ( @#{a}=args[0] ; self ) : @#{a} end } } module_eval( make.values.join("\n") ) return make.keys end
Create an tester attribute. This creates two methods for each given variable name. One is used to test the attribute and the other is used to set or toggle it.
attr_switch :a
is equivalent to
def a? @a ? true : @a end def a!(switch=nack) if switch == nack @a = ! @a else @a = @a ? switch : @a self end end
[ show source ]
# File lib/facets/core/module/attr_tester.rb, line 26 def attr_tester(*args) make = {} args.each { |a| make["#{a}?".to_sym] = %{ def #{a}?; @#{a} ? true : @#{a}; end } make["#{a}!".to_sym] = %{ def #{a}!(switch=nack) if switch==nack @#{a} = !@#{a} else @#{a} = @#{a} ? switch : @#{a} self end end } } module_eval make.values.join("\n") return make.keys end
Create an tester attribute. This creates two methods for each given variable name. One is used to test the attribute and the other is used to set or toggle it.
attr_switch :a
is equivalent to
def a? @a ? true : @a end def a!(switch=nack) if switch == nack @a = ! @a else @a = @a ? switch : @a self end end
[ show source ]
# File lib/facets/core/module/attr_query.rb, line 26 def attr_tester(*args) make = {} args.each { |a| make["#{a}?".to_sym] = %{ def #{a}?; @#{a} ? true : @#{a}; end } make["#{a}!".to_sym] = %{ def #{a}!(switch=nack) if switch==nack @#{a} = !@#{a} else @#{a} = @#{a} ? switch : @#{a} self end end } } module_eval make.values.join("\n") return make.keys end
Like attr_writer, but the writer method validates the setting against the given block.
[ show source ]
# File lib/facets/core/module/attr_validator.rb, line 7 def attr_validator(*symbols, &validator) symbols.each do |symbol| define_method "#{symbol}=" do |val| unless validator.call(val) raise ArgumentError, "Invalid value provided for #{symbol}" end instance_variable_set("@#{symbol}", val) end end end
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"
[ show source ]
# File lib/facets/core/module/basename.rb, line 20 def basename if name and not name.empty? name.gsub(/^.*::/, '') else nil #inspect.gsub('#<','').gsub('>','').sub(':', '_') end end
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
[ show source ]
# File lib/facets/core/module/by_name.rb, line 37 def by_name(name) result = Object.constant(name) return result if result.kind_of?( Module ) raise ArgumentError, "#{name} is not a module or class" end
Returns an anonymous module with the specified methods of the receiving module removed.
[ show source ]
# File lib/facets/core/module/clone_using.rb, line 19 def clone_removing( *meths ) mod = self.dup methods_to_remove = meths methods_to_remove.each { |m| mod.class_eval { remove_method m } } return mod end
Returns an anonymous module with the specified methods of the receiving module renamed.
[ show source ]
# File lib/facets/core/module/clone_using.rb, line 6 def clone_renaming( pairs ) mod = self.dup pairs.each_pair { |to_sym, from_sym| mod.class_eval { alias_method( to_sym, from_sym ) undef_method( from_sym ) } } return mod end
Returns an anonymous module with only the specified methods of the receiving module intact.
[ show source ]
# File lib/facets/core/module/clone_using.rb, line 28 def clone_using( *meths ) meths = meths.collect { |m| m.to_s } methods_to_remove = (self.instance_methods - meths) mod = self.dup mod.class_eval { methods_to_remove.each { |m| undef_method m } } return mod end
Alias for sort_on
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.
[ show source ]
# File lib/facets/core/module/dirname.rb, line 15 def dirname name.gsub(/::[^:]*$/, '') end
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
[ show source ]
# File lib/facets/core/module/equate_on.rb, line 25 def equate_on(*fields) code = "" code << "def ==(o) " << fields.map {|f| "self.#{f} == o.#{f}" }.join(" && ") << " end\n" code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?(o.#{f})" }.join(" && ") << " end\n" code << "def hash() " << fields.map {|f| "self.#{f}.hash" }.join(" ^ ") << " end\n" class_eval( code ) fields end
Generates a new symbol that is unique among the inctance methods of the class/module. If a name argument is given, it will generate a similar name.
Class.generate_instance_method_name( :class ) => :_clast_
[ show source ]
# File lib/facets/core/module/generate_instance_method_name.rb, line 10 def generate_instance_method_name( name='a' ) s = name.to_s while self.method_defined?( "_#{s}_" ) s = s.succ end return "_#{s}_".to_sym end
Include 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"
[ show source ]
# File lib/facets/core/module/include_as.rb, line 19 def include_as( h ) h.each{ |name, mod| s = self c = Class.new(SimpleDelegator) { include mod define_method(:initialize) { |s| super(s) } } class_eval { define_method( name ) { instance_variable_set( "@#{name}", instance_variable_get( "@#{name}" ) || c.new(s) ) } } } end
Automatically create an initializer assigning the given arguments.
class MyClass initializer(:a, "b", :c) end
_is equivalent to_
class MyClass def initialize(a, b, c) @a,@b,@c = a,b,c end end
Downside: Initializers defined like this can’t take blocks. This can be fixed when Ruby 1.9 is out.
The initializer will not raise an Exception when the user does not supply a value for each instance variable. In that case it will just set the instance variable to nil. You can assign default values or raise an Exception in the block.
[ show source ]
# File lib/facets/core/module/initializer.rb, line 27 def initializer(*attributes, &block) define_method(:initialize) do |*args| attributes.zip(args) do |sym, value| instance_variable_set("@#{sym}", value) end instance_eval(&block) if block end end
Easy access to method as objects, and they retain state!
module K def hello puts "Hello World!" end end p K.instance_method!(:hello) #=> <UnboundMethod: #hello>
CAUTION! This will not work exactly right until class variable lookup is fixed. Presently it is limited to the scope of the current module/class.
[ show source ]
# File lib/facets/core/kernel/method.rb, line 61 def instance_method!(s) #( @@__instance_methods__ ||= {} )[s] ||= instance_method(s) # TODO when fixed ( @__instance_methods__ ||= {} )[s] ||= instance_method(s) end
Provides an improved method lookup routnine. It returns a list of methods according to symbol(s) given.
Recognized symbols are:
- :public include public methods
- :private include private methods
- :protected include protected methods
- :ancestors include all ancestor’s methods
- :inherited (same as above)
- <tt>:local</tti> include non-ancestor methods
- :all include all of the above
This method also uses the symbol-not system. So you can specify the inverse of all of the above. For instance ~:public would mean :private, :protected (see facet/symbol/not).
If no symbol is given then :public, :local is assumed. Unrecognized symbols raise an error.
module Demo def test puts("Hello World!") end end Demo.instance_methods(:local) #=> ['test']
To maintain backward compatibility true as an intitial argument is converted to :local, :ancestors (i.e. it includes both).
[ show source ]
# File lib/facets/core/module/instance_methods.rb, line 40 def instance_methods(*args) # for backward compatibility args << true if args.empty? case args[0] when TrueClass args.shift args << :ancestors args << :local when FalseClass args.shift args << :local end # default public, local args |= [:public] unless [:publix,:private,:protected,:all].any?{ |a| args.include?(a) } args |= [:ancestors,:local] unless [:ancestors,:inherited,:local,:all].any?{ |a| args.include?(a) } raise ArgumentError if args.any?{ |a| ! METHOD_TYPES.include?(a) } pos, neg = args.partition { |s| ! s.not? } m = [] pos.each do |n| case n when :inherited, :ancestors m |= ( public_instance_methods( true ) - public_instance_methods( false ) ) if pos.include?(:public) m |= ( private_instance_methods( true ) - private_instance_methods( false ) ) if pos.include?(:private) m |= ( protected_instance_methods( true ) - protected_instance_methods( false ) ) if pos.include?(:protected) when :local m |= public_instance_methods( false ) if pos.include?(:public) m |= private_instance_methods( false ) if pos.include?(:private) m |= protected_instance_methods( false ) if pos.include?(:protected) when :all m |= public_instance_methods( true ) m |= private_instance_methods( true ) m |= protected_instance_methods( true ) end end neg.each do |n| case n when :public m -= public_instance_methods( true ) when :private m -= private_instance_methods( true ) when :protected m -= protected_instance_methods( true ) when :inherited, :ancestors m -= ( public_instance_methods( true ) - public_instance_methods( false ) ) m -= ( private_instance_methods( true ) - private_instance_methods( false ) ) m -= ( protected_instance_methods( true ) - protected_instance_methods( false ) ) when :local m -= public_instance_methods( false ) m -= private_instance_methods( false ) m -= protected_instance_methods( false ) end end m.sort end
[ show source ]
# File lib/facets/core/module/integrate.rb, line 39 def integrate( mod, &block ) #include mod.revisal( &blk ) m = Module.new { include mod } m.class_eval &block include m end
Is a given class or module an ancestor of this class or module?
class X ; end class Y < X ; end Y.is?(X) #=> true
[ show source ]
# File lib/facets/core/module/is.rb, line 22 def is?( base ) ancestors.slice(1..-1).include?( base ) end
Alias for equate_on
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
[ show source ]
# File lib/facets/core/module/memoize.rb, line 26 def memoize(*meths) @_MEMOIZE_CACHE ||= Hash.new meths.each do |meth| mc = @_MEMOIZE_CACHE[meth] = Hash.new old = instance_method(meth) new = proc do |*args| if mc.has_key? args mc[args] else mc[args] = old.bind(self).call(*args) end end send(:define_method, meth, &new) end end
Returns the module’s container module.
module Example class Demo end end Demo.modspace #=> Example
See also Module#basename.
[ show source ]
# File lib/facets/core/module/modspace.rb, line 17 def modspace eval self.name[ 0...self.name.rindex( '::' ) ] end
Create a seperated method namespace.
module T def t ; "HERE" ; end end class X namespace :test { include T } def t ; test.t ; end end X.new.t #=> "HERE"
NOTE: This is not as functional as it ought be in that the instance variables of the object are not accessible within the namespace.
[ show source ]
# File lib/facets/core/module/namespace.rb, line 22 def namespace( name, &blk ) s = self c = Class.new(SimpleDelegator, &blk) c.class_eval { define_method(:initialize) { |s| super(s) } } self.class_eval { define_method( name ) { instance_variable_set( "@#{name}", instance_variable_get( "@#{name}" ) || c.new(s) ) } } end
[ show source ]
# File lib/facets/core/module/nesting.rb, line 8 def nesting n = [] name.split(/::/).inject(self){ |mod, name| c = mod.const_get(name) ; n << c ; c } return n end
A useful macro for dynamic modules.
[ show source ]
# File lib/facets/core/module/on_included.rb, line 13 def on_included(code) tag = caller[0].split(' ').first.split(/\/|\\/).last.gsub(/:|\.|\(|\)/, '_') old = "__included_#{tag}" module_eval %{ class << self alias_method :#{old}, :included def included(base) #{old}(base) #{code} end end } end
Redirect 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
[ show source ]
# File lib/facets/core/module/redirect_method.rb, line 33 def redirect_method( method_hash ) method_hash.each do |targ,adv| define_method(targ) { |*args| send(adv,*args) } end end
[ show source ]
# File lib/facets/core/module/revisal.rb, line 10 def revisal( &blk ) base = self nm = Module.new { include base } nm.class_eval &blk nm end
Creates a shadow method for every currently defined method.
[ show source ]
# File lib/facets/core/module/shadow_all.rb, line 5 def shadow_all( include_ancestors=true ) instance_methods(include_ancestors).each { |m| shadow_method( m ) } end
Define a shadow alias for a method.
class X shadow_method( :class ) end X.new.__class__ #=> X
[ show source ]
# File lib/facets/core/module/shadow_method.rb, line 11 def shadow_method( name, old_name=name ) name, old_name = name.to_s, old_name.to_s shadow_name = '__' << name.gsub(/([=?!])$/, '') << '__' << $1.to_s alias_method( shadow_name, old_name ) end
Alias for sort_on
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
[ show source ]
# File lib/facets/core/module/sort_on.rb, line 19 def sort_on(*fields) code = %{def <=>(o)\n} fields.each { |f| code << %{cmp = ( @#{f} <=> o.instance_variable_get('@#{f}') ); return cmp unless cmp == 0\n} } code << %{0\nend; alias_method :cmp, :<=>;} class_eval( code ) fields end
[ show source ]
# File lib/facets/core/module/this.rb, line 18 def this klass = self Functor.new do |op, *args| case op.to_s[-1,1] when '=' op = op.to_s.chomp('=') if Proc === args[0] define_method( op, &args[0] ) else define_method( op ) do r = instance_variable_set( "@#{op}", args[0] ) klass.class.class_eval %{ def #{op}; @#{op}; end } r end end else klass.class_eval %{ def #{op}; @#{op}; end } end end end
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.
[ show source ]
# File lib/facets/core/module/wrap_method.rb, line 20 def wrap_method( sym, &blk ) raise ArgumentError, "method does not exist" unless method_defined?( sym ) old = instance_method(sym) undef_method(sym) define_method(sym) { |*args| blk.call(old.bind(self), *args) } end
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/core/module/alias_module_function.rb, line 23 def alias_module_function( new, old ) alias_method( new, old ) module_function( new ) end
class_extension
Normally when including modules, class/module methods are not extended. To achieve this behavior requires some clever Ruby Karate. Instead class_extension provides an easy to use and clean solution. Simply place the extending class methods in a block of the special module method class_extension.
module Mix def inst_meth puts 'inst_meth' end class_methods do def class_meth "Class Method!" end end end class X include Mix end X.class_meth #=> "Class Method!"
[ show source ]
# File lib/facets/core/module/class_extension.rb, line 62 def class_extension(&block) @class_extension ||= Module.new do def self.append_features(mod) append_features_without_class_extension(mod) end end @class_extension.module_eval(&block) if block_given? @class_extension end
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!"
[ show source ]
# File lib/facets/core/module/redefine_method.rb, line 25 def redefine_method( sym, aka=nil, &blk ) raise ArgumentError, "method does not exist" unless method_defined?( sym ) alias_method( aka, sym ) if aka undef_method( sym ) define_method( sym, &blk ) end
Alias for redirect_method
Alias for rename_method
Aliases a method and undefines the original.
rename_method( :to_method, :from_method )
[ show source ]
# File lib/facets/core/module/rename_method.rb, line 7 def rename_method( to_sym, from_sym ) raise ArgumentError, "method #{from_sym} does not exist" unless method_defined?( from_sym ) alias_method( to_sym, from_sym ) undef_method( from_sym ) end
Alias for wrap_method