TODO Is autoimport bets name for this?
extend | -> | _extend |
remove_method | -> | remove |
undef_method | -> | nodef |
=== | -> | class? |
Alias for #===. This provides a verbal method for inquery.
s = "HELLO" String.class?(s) #=> true |
||
append_features | -> | append_features_without_classmethods |
const_missing | -> | const_missing_before_autoimport |
$autoimport_activated = true |
# File lib/more/facets/class_extension.rb, line 85 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 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 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
This function provided a "shortcut" for creating the identity method based on given accessors and returns the Equatable module for inclusion.
include Equatable(: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/equatable.rb, line 115 def Equatable(*accessors) Equatable.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/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
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
Create aliases for flag reader.
CREDIT: Trans
# File lib/more/facets/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 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/more/facets/attr.rb, line 81 def alias_setter(*args) args = args - [orig] args.each do |name| alias_method(name, orig) end end
Create aliases for validators.
# File lib/more/facets/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
Create aliases for flag writer.
CREDIT: Trans
# File lib/more/facets/attr.rb, line 200 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
# File lib/more/facets/class_extension.rb, line 95 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
# File lib/more/facets/classmethods.rb, line 163 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
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/attr.rb, line 107 def attr_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_reader! :a
is equivalent to
def a? @a ? true : @a end
# File lib/more/facets/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
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/more/facets/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
Like attr_writer, but the writer method validates the setting against the given block.
CREDIT: ?
# File lib/more/facets/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
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/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
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.
CREDIT: WhyTheLuckyStiff
# File lib/core/facets/metaid.rb, line 82 def class_def name, &blk class_eval { define_method name, &blk } end
# File lib/more/facets/classmethods.rb, line 180 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
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
# File lib/more/facets/dependency.rb, line 120 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
# File lib/more/facets/dependency.rb, line 90 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
Compile list of all unique prerequisite calls.
# File lib/more/facets/dependency.rb, line 106 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
# File lib/core/facets/module/extend.rb, line 5 def extend(mod=nil, &blk) _extend mod if mod _extend Module.new(&blk) if blk 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 103 def include_as(h) h.each{ |name, mod| method_space(name, mod) } 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>
NOTE: This is limited to the scope of the current module/class.
# File lib/more/facets/1stclassmethod.rb, line 134 def instance_method!(s) #( @@__instance_methods__ ||= {} )[s] ||= instance_method(s) # TODO when fixed ( @__instance_methods__ ||= {} )[s] ||= instance_method(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
alias_method :is, :include
# File lib/core/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
# File lib/core/facets/module/is.rb, line 13 def is?(base) ancestors.slice(1..-1).include?(base) end
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
# File lib/more/facets/memoize.rb, line 64 def memoize(*meths) meths.each do |meth| old = instance_method(meth) new = proc do |*args| mc = ((@_MEMOIZE_CACHE ||= {})[meth] ||= {}) if mc.has_key? args mc[args] else mc[args] = old.bind(self).call(*args) end end send(:define_method, meth, &new) end 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.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
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
Store for parametric mixin parameters.
Returns a hash, the keys of which are the parametric mixin module and the values are the parameters associacted with this module/class.
class C include P(:x=>1) end C.mixin_parameters[P] #=> {:x=>1}
# File lib/more/facets/paramix.rb, line 196 def mixin_parameters @mixin_parameters ||= {} 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 def modspace space = name[ 0...(name.rindex( '::' ) || 0)] space.empty? ? Object : eval(space) end
Load file into module/class namespace.
CREDIT: Trans
# File lib/core/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
# File lib/core/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
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
# File lib/more/facets/overload.rb, line 45 def overload( name, *signiture, &block ) raise ArgumentError unless signiture.all?{|s| s.instance_of?(Class)} 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 if (0...cmp.size).all?{ |i| cmp[i] >= sig[i] } break hit = cmp end 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
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
Defines a configuration setting for the enclosing class.
class Compiler
setting :template_root, :default => 'src/template', :doc => 'The template root dir'
end
# File lib/more/facets/settings.rb, line 233 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
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 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