extend | -> | can |
An alias for extend.
class X can Forwardable end BTW, why is Forwardable an -able? It‘s not a mixin! |
||
=== | -> | class? |
Alias for #===. This provides a verbal method for inquery.
s = "HELLO" String.class?(s) #=> true |
||
extend | -> | _extend |
remove_method | -> | remove |
undef_method | -> | nodef |
const_missing | -> | const_missing_before_autoimport |
$autoimport_activated = true | ||
attr_accessor! | -> | attr_toggler |
append_features | -> | append_features_without_class_extension |
# File lib/more/facets/module/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 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 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 attr_toggler.
CREDIT: Trans
# File lib/more/facets/module/attr_toggler.rb, line 49 def alias_toggler(*args) orig = args.last args = args - [orig] args.each do |name| alias_method("#{name}!", "#{orig}!") 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/module/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 << %{ 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 << %{ 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 << %{ 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 << %{ 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
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_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/module/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
# File lib/core/facets/module/extend.rb, line 5 def extend(*mod, &blk) _extend *mod unless mod.empty? _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
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 %{ 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
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
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
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 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
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
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