class Module
Public Instance Methods
Rename methods.
module AStar def a; "a"; end end BStar = AStar * { :a => :b } class XStar; include BStar; end XStar.new.b #=> "a"
CREDIT: Thomas Sawyer, Robert Dober
# File lib/core/facets/module/op.rb, line 95 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 APlus def a; "a"; end end module BPlus def b; "b"; end end CPlus = APlus + BPlus class XPlus; include CPlus; end XPlus.new.a #=> "a" XPlus.new.b #=> "b"
Note that in the old version of traits.rb we cloned modules and altered their copies…
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.
module AMinus def a; "a"; end def b; "b"; end end CMinus = AMinus - [:a] class XMinus; include CMinus; end expect NameError do XMinus.new.a #=> "a" end XMinus.new.b #=> "b"
TODO: Should this use all instance methods, not just public?
CREDIT: Thomas Sawyer, Robert Dober
# File lib/core/facets/module/op.rb, line 64 def -(other) instance_methods = instance_methods(true).map{|m| m.to_sym} case other when Array subtract = instance_methods & other.map{|m| m.to_sym} when Module subtract = instance_methods & other.instance_methods(true).map{|m| m.to_sym} # false? when String, Symbol subtract = instance_methods & [other.to_sym] end base = self Module.new do include base subtract.each{ |x| undef_method x } end end
Create an abstract method. If it is not overridden, it will raise a TypeError when called.
class AbstractExample abstract :a end c = AbstractExample.new expect TypeError do c.a end
CREDIT: Trans
# File lib/core/facets/module/abstract.rb, line 18 def abstract(*sym) sym.each do |s| define_method(s){ raise TypeError, "undefined abstraction ##{s}" } end end
Alias a class method.
# File lib/core/facets/module/alias_class_method.rb, line 5 def alias_class_method(name, target) class << self alias_method(name, target) 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
For example
class AliasMethodChainExample def foo "foo" end def foo_with_feature "foo!" end alias_method_chain :foo, :feature end
And both aliases are set up for you.
example = AliasMethodChainExample.new example.foo #=> "foo!" example.foo_without_feature #=> "foo"
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 45 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.
Note that this method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: Trans
@uncommon
require 'facets/module/attr_tester'
# File lib/core/facets/module/attr_tester.rb, line 48 def alias_tester(*args) orig = args.last args = args - [orig] args.each do |name| alias_method("#{name}?", "#{orig}?") end end
Create aliases for validator attribute methods.
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/attr_validator'
# File lib/core/facets/module/attr_validator.rb, line 36 def alias_validator(*args) orig = args.last args = args - [orig] args.each do |name| #alias_method(name, orig) 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/all_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
A module may or may not have a name.
module M; end M.name # => “M”
m = Module.new m.name # => “”
A module gets a name when it is first assigned to a constant. Either via
the module
or class
keyword or by an explicit
assignment:
m = Module.new # creates an anonymous module M = m # => m gets a name here as a side-effect m.name # => “M”
# File lib/core/facets/module/anonymous.rb, line 18 def anonymous? # The name of an anonymous class is an empty # string in 1.8, and nil in 1.9. name.nil? || name.empty? end
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/attr_class_accessor'
# File lib/core/facets/module/attr_class_accessor.rb, line 10 def attr_class_accessor(name) attr_class_reader(name) attr_class_writer(name) end
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/attr_class_reader'
# File lib/core/facets/module/attr_class_accessor.rb, line 22 def attr_class_reader(name) module_eval(" def self.#{name} @#{name} end def #{name} self.class.#{name} end ", __FILE__, __LINE__) end
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/attr_class_writer'
# File lib/core/facets/module/attr_class_accessor.rb, line 40 def attr_class_writer(name) module_eval(" def self.#{name}=(x) @#{name} = x end def #{name}=(x) self.class.#{name} = x end ", __FILE__, __LINE__) 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 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
Note that this method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: Trans
@todo This method will probably be deprecated.
@uncommon
require 'facets/module/attr_tester'
# File lib/core/facets/module/attr_tester.rb, line 24 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
Like attr_writer, but the writer method validates the setting against the given block.
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: ?
@uncommon
require 'facets/module/attr_validator'
# File lib/core/facets/module/attr_validator.rb, line 14 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
Original name, but conflicts with File.basename, so is mildly deprecated.
Creates a class-variable attribute that can be accessed both on an instance and class level.
class CARExample @@a = 10 cattr :a end CARExample.a #=> 10 CARExample.new.a #=> 10
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: David Heinemeier Hansson
@uncommon
require 'facets/module/cattr'
# File lib/core/facets/module/mattr.rb, line 22 def cattr(*syms) writers, readers = syms.flatten.partition{ |a| a.to_s =~ /=$/ } writers = writers.map{ |e| e.to_s.chomp('=').to_sym } ##readers.concat( writers ) # writers also get readers cattr_reader(*readers) cattr_writer(*writers) return readers + writers end
Creates a class-variable attr_accessor that can be accessed both on an instance and class level.
class CAAExample cattr_accessor :a end CAAExample.a = 10 CAAExample.a #=> 10 mc = CAAExample.new mc.a #=> 10
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: David Heinemeier Hansson
@uncommon
require 'facets/module/cattr'
# File lib/core/facets/module/mattr.rb, line 133 def cattr_accessor(*syms) cattr_reader(*syms) + cattr_writer(*syms) end
Creates a class-variable attr_reader that can be accessed both on an instance and class level.
class CARExample @@a = 10 cattr_reader :a end CARExample.a #=> 10 CARExample.new.a #=> 10
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: David Heinemeier Hansson
@uncommon
require 'facets/module/cattr'
# File lib/core/facets/module/mattr.rb, line 52 def cattr_reader(*syms) syms.flatten.each do |sym| module_eval(" unless defined? @@#{sym} @@#{sym} = nil end def self.#{sym} @@#{sym} end def #{sym} @@#{sym} end ", __FILE__, __LINE__) end return syms end
Creates a class-variable attr_writer that can be accessed both on an instance and class level.
class CAWExample cattr_writer :a def self.a @@a end end CAWExample.a = 10 CAWExample.a #=> 10 CAWExample.new.a = 29 CAWExample.a #=> 29
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: David Heinemeier Hansson
@uncommon
require 'facets/module/cattr'
# File lib/core/facets/module/mattr.rb, line 94 def cattr_writer(*syms) syms.flatten.each do |sym| module_eval(" unless defined? @@#{sym} @@#{sym} = nil end def self.#{sym}=(obj) @@#{sym} = obj end def #{sym}=(obj) @@#{sym}=(obj) end ", __FILE__, __LINE__) end return syms end
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/class_accessor'
# File lib/core/facets/module/class_accessor.rb, line 10 def class_accessor(name) class_reader(name) class_writer(name) 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.
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: Daniel Schierbeck, Thomas Sawyer
THANKS: Nobu Nakada, Ulysses
@uncommon
require 'facets/module/class_extend'
# File lib/core/facets/module/class_extend.rb, line 42 def class_extend(*mods, &block) class_extension = Module.new class_extension.__send__(:include, *mods) class_extension.module_eval(&block) if block_given? extend(class_extension) # extend this module too append_method = method(:append_features) (class << self; self; end).class_eval do define_method(:append_features) do |mod| append_method.call(mod) mod.extend(class_extension) if mod.instance_of?(Module) mod.__send__(:class_extend, class_extension) end end end class_extensions << class_extension end
# File lib/core/facets/module/class_extend.rb, line 68 def class_extensions @class_extensions ||= [] end
Class Inheritor¶ ↑
Create an dynamic class inheritable attribute.
Inheritor providse a means to store and inherit data via the class heirarchy. An inheritor creates two methods one named after the key that provides a reader. And one named after key! which provides the writer. (Because of the unique nature of inheritor the reader and writer can't be the same method.)
The first argument is the inheritor's name. The second argument is the archtype object. This object must be duplicable (via dup). The last argument is either the symbolic operator/method or a block that specifies how one hierarchical level “integrates” with the next.
class X class_inheritor :x, [], :+ end class Y < X end X.x! << :a X.x #=> [:a] Y.x #=> [:a] Y.x! << :b X.x #=> [:a] Y.x #=> [:a, :b]
NOTE: Adding an inheritor directly to Module or Class will probably not do what is expected. Thankfully that usecase is likely a YAGNI, but in anycase it is even more likely that it is not possible with this code.
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: Thomas Sawyer
@uncommon
require 'facets/module/class_inheritor'
# File lib/core/facets/module/class_inheritor.rb, line 48 def class_inheritor(key, obj, op=nil, &fop) raise ArgumentError if op && fop if !fop op = op ? op.to_sym : :+ fop = lambda{ |o, x| o.__send__(op, x) } end #(class << self; self; end).module_eval do class_extend do define_method(key) do ancestors.reverse.inject(obj.dup) do |o, a| if a.respond_to?("#{key}!") fop.call(o, a.__send__("#{key}!")) else o end end end define_method("#{key}!") do if instance_variable_defined?("@#{key}") instance_variable_get("@#{key}") else instance_variable_set("@#{key}", obj.dup) end end end end
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/class_reader'
# File lib/core/facets/module/class_accessor.rb, line 22 def class_reader(name) module_eval(" def self.#{name} @#{name} end ", __FILE__, __LINE__) end
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/class_writer'
# File lib/core/facets/module/class_accessor.rb, line 37 def class_writer(name) module_eval(" def self.#{name}=(x) @#{name} = x end ", __FILE__, __LINE__) end
Like class_inheritor but non-dynamic. The value of the inheritor is copied from the ancestor on first read.
c = Class.new do def self.x; ['x']; end end d = Class.new(c) do copy_inheritor :x end d.x #=> ['x']
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: Thomas Sawyer
@uncommon
require 'facets/module/copy_inheritor'
# File lib/core/facets/module/copy_inheritor.rb, line 26 def copy_inheritor(name, default={}) class_extend do define_method(name) do if instance_variable_defined?("@#{name}") instance_variable_get("@#{name}") else if anc = ancestors[1..-1].find{ |a| a.respond_to?(name) } value = anc.__send__(name) value = value.dup rescue value instance_variable_set("@#{name}", value) else instance_variable_set("@#{name}", default) end end end end end
Returns the module which contains this one according to its name.
module ::EncExample module M module N end end end EncExample::M::N.enclosure #=> EncExample::M
The enclosure of top-level and anonymous modules is Object.
EncExample.enclosure #=> Object Module.new.enclosure #=> Object
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/enclosure'
# File lib/core/facets/module/enclosure.rb, line 25 def enclosure name = /::[^:]+\Z/ =~ self.name ? $` : nil if name #base = name.sub!(/^::/, '') ? Object : self name.split(/::/).inject(self) do |mod, cref| if /\:(0x.*?)\>$/ =~ cref # TODO: does this ever happen? #p $1.to_i(16) ObjectSpace._idref($1.to_i(16)) else mod.const_get(cref) end end else Object end end
Returns all the namespaces of this module according ordered from nearest and moving outwards. The receiver is not contained within the result.
module ::EncExample module M module N end end end EncExample.enclosures #=> [Object] EncExample::M.enclosures #=> [EncExample, Object] EncExample::M::N.enclosures #=> [EncExample::M, EncExample, Object]
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/enclosures'
# File lib/core/facets/module/enclosure.rb, line 63 def enclosures n = [] name.split(/::/).inject(self) do |mod, cref| c = mod.const_get(cref) ; n.unshift(c) ; c end n << Object # ? n.shift # we really don't need +self+ too. n end
Enclosure name.
module ::EncExample module M module N end end end EncExample::M::N.encname #=> "EncExample::M"
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/enclosure'
# File lib/core/facets/module/enclosure.rb, line 90 def encname /::[^:]+\Z/ =~ self.name ? $` : nil end
Returns the module or class containing the receiver.
module ::HomeExample module M module N end end end HomeExample::M::N.home #=> HomeExample::M
The home of a top-level module/class is Object.
HomeExample.home #=> Object
This method is called home because techinally a module or class is just a constant reference, and as such can reside with multiple namespaces, like any variable. For example:
module OtherPlace M = ::HomeExample::M end
In this example, you might think that OtherPlace::M
's home
would be OtherPlace
, but OtherPlace::M
is the
same object as HomeExample::M
, and it can have only one “home”
–the original location of it's definition.
# File lib/core/facets/module/home.rb, line 29 def home #homename = /::[^:]+\Z/ =~ name ? $` : nil if homename homename.split(/::/).inject(self) do |mod, cref| if /\:(0x.*?)\>$/ =~ cref # TODO: does this ever happen? #p $1.to_i(16) ObjectSpace._idref($1.to_i(16)) else mod.const_get(cref) end end else Object end end
Returns the name of module or class containing the receiver.
module ::HomeExample module M module N end end end HomeExample::M::N.homename #=> "HomeExample::M"
See also #basename.
# File lib/core/facets/module/home.rb, line 57 def homename /::[^:]+\Z/ =~ name ? $` : nil end
Returns all the namespaces of this module according ordered from nearest and moving outwards. The receiver is not contained within the result.
module ::HouseExample module M module N end end end HouseExample.housing #=> [Object] HouseExample::M.housing #=> [HouseExample, Object] HouseExample::M::N.housing #=> [HouseExample::M, HouseExample, Object]
Compare this to Module.nesting
.
# File lib/core/facets/module/home.rb, line 80 def housing n = [] name.split(/::/).inject(self) do |mod, cref| c = mod.const_get(cref) ; n.unshift(c) ; c end n << Object # ? n.shift # we really don't need +self+ too. n 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"
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/method_space'
# File lib/core/facets/module/method_space.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 MyModule.jumble("Try", "Me") #=> "TryMe" s = "Try" s.extend MyModule s.jumble("Me") #=> "TryMe"
Note: This used to be a module called PromoteSelf and later Instantize, before becoming a method.
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
@uncommon
require 'facets/module/instance_function'
# File lib/core/facets/module/instance_function.rb, line 34 def instance_function(*meths) this = self if meths.empty? extend InstanceFunction else meths.each do |meth| module_eval do define_method(meth) do |*args| this.__send__(meth, self, *args) end end ##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 K.instance_method!(:hello).inspect #=> "#<UnboundMethod: K#hello>"
NOTE: This is limited to the scope of the current module/class.
# File lib/core/facets/module/instance_method.rb, line 18 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_method_defined.rb, line 7 def instance_method_defined?(meth) instance_methods(true).find{ |m| m.to_sym == meth.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 IntegrateExampleModule def q ; "q" ; end def y ; "y" ; end end class InegrateExampleClass integrate IntegrateExampleModule do undef_method :y end end x = InegrateExampleClass.new x.q #=> "q" expect NameError do x.y end
This is like revisal, but revisal only returns the reconstructred module. It does not include it.
CREDIT: Trans
# File lib/core/facets/module/integrate.rb, line 41 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 IsExample 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 IsX ; end class IsY < IsX ; end IsY.is?(IsX) #=> true
CREDIT: Trans
# File lib/core/facets/module/is.rb, line 13 def is?(base) Module===base && ancestors.slice(1..-1).include?(base) end
Returns the root name of the module/class.
module ::BaseName class Example end end BaseName::Example.name #=> "BaseName::Example" BaseName::Example.lastname #=> "Example"
Returns nil when module is anonymous.
CREDIT: Trans
# File lib/core/facets/module/lastname.rb, line 17 def lastname if name and not name.empty? name.gsub(/^.*::/, '') else nil #inspect.gsub('#<','').gsub('>','').sub(':', '_') end end
Create a memoized method. This method has been popularized by RSpec.
class LetExample let(:seed) { rand } end eg = LetExample.new eg.seed == eg.seed
CREDIT: Howard Yeh
@uncommon
require 'facets/module/let'
# File lib/core/facets/module/let.rb, line 17 def let(var,&block) name = "@#{var}" self.class_eval do define_method(var) do if instance_variable_defined?(name) instance_variable_get(name) else val = self.instance_eval(&block) instance_variable_set(name,val) end end end end
Creates a class-variable attribute that can be accessed both on an instance and class level.
c = Class.new do mattr :a def initialize @@a = 10 end end c.new.a #=> 10 c.a #=> 10
NOTE: The mattr methods may not be as useful for modules as the cattr methods are for classes, becuase class-level methods are not “inherited” across the metaclass for included modules.
NOTE: This methiod is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: David Heinemeier Hansson
@uncommon
require 'facets/module/mattr'
# File lib/core/facets/module/mattr.rb, line 162 def mattr(*syms) writers, readers = syms.flatten.partition{ |a| a.to_s =~ /=$/ } writers = writers.collect{ |e| e.to_s.chomp('=').to_sym } ##readers.concat( writers ) # writers also get readers mattr_writer( *writers ) mattr_reader( *readers ) return readers + writers end
Creates a class-variable attr_accessor that can be accessed both on an instance and class level.
c = Class.new do mattr_accessor :a end c.a = 10 c.a #=> 10 x = c.new x.a #=> 10
NOTE: This methiod is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: David Heinemeier Hansson
@uncommon
require 'facets/module/mattr'
# File lib/core/facets/module/mattr.rb, line 275 def mattr_accessor(*syms) mattr_reader(*syms) + mattr_writer(*syms) end
Creates a class-variable attr_reader that can be accessed both on an instance and class level.
c = Class.new do @@a = 10 mattr_reader :a end c.a #=> 10 c.new.a #=> 10
NOTE: This methiod is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: David Heinemeier Hansson
@uncommon
require 'facets/module/mattr'
# File lib/core/facets/module/mattr.rb, line 192 def mattr_reader( *syms ) syms.flatten.each do |sym| module_eval(" unless defined? @@#{sym} @@#{sym} = nil end def self.#{sym} @@#{sym} end def #{sym} @@#{sym} end ", __FILE__, __LINE__) end return syms end
Creates a class-variable attr_writer that can be accessed both on an instance and class level.
c = Class.new do mattr_writer :a def self.a @@a end end c.a = 10 c.a #=> 10 c.new.a = 29 c.a #=> 29
NOTE: This methiod is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: David Heinemeier Hansson
@uncommon
require 'facets/module/mattr'
# File lib/core/facets/module/mattr.rb, line 235 def mattr_writer(*syms) syms.flatten.each do |sym| module_eval(" unless defined? @@#{sym} @@#{sym} = nil end def self.#{sym}=(obj) @@#{sym} = obj end def #{sym}=(obj) @@#{sym}=(obj) end ", __FILE__, __LINE__) end return syms end
This is here for backward compatibility.
@uncommon
require 'facets/module/memoize'
# File lib/core/facets/module/memoize.rb, line 10 def memoize(*args) include Memoizable Memoizable.instance_method(:memoize).bind(self).call(*args) #super(*args) # TODO: why is super not working here? end
Detect method name clash between modules and/or classes, regardless of method visibility:
module MethodClashExample module A def c; end end module B private def c; end end A.method_clash(B) #=> [:c] end
CREDIT: Thomas Sawyer, Robert Dober
@uncommon
require 'facets/module/method_clash'
# File lib/core/facets/module/method_clash.rb, line 30 def method_clash(other) common_ancestor = (ancestors & other.ancestors).first s = [] s += public_instance_methods(true) s += private_instance_methods(true) s += protected_instance_methods(true) o = [] o += other.public_instance_methods(true) o += other.private_instance_methods(true) o += other.protected_instance_methods(true) c = s & o if common_ancestor c -= common_ancestor.public_instance_methods(true) c -= common_ancestor.private_instance_methods(true) c -= common_ancestor.protected_instance_methods(true) end return c end
Uses method_clash to return
true
or false
if there are method name clashes.
@uncommon
require 'facets/module/method_clash'
# File lib/core/facets/module/method_clash.rb, line 60 def method_clash?(other) c = method_clash(other) !c.empty? end
Create method namespaces, allowing for method chains but still accessing the object's instance.
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 expect NoMethodError do a.x end
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: Pit Captain
@uncommon
require 'facets/module/method_space'
# File lib/core/facets/module/method_space.rb, line 32 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| if meth = mtab[op.to_sym] meth.bind(self).call(*args) else #self.__send__(op, *args) raise NoMethodError, "undefined method `#{m}'" end end end end
Translate a module name to a suitable method name.
module ::EgMethodize module Eg end end EgMethodize.methodize #=> "eg_methodize" EgMethodize::Eg.methodize #=> "eg_methodize__eg"
# File lib/core/facets/module/methodize.rb, line 15 def methodize name.methodize 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.
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: Trans
@uncommon
require 'facets/module/module_load'
# File lib/core/facets/module/module_load.rb, line 18 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.
NOTE: This method is not a common core extension and is not loaded
automatically when using require 'facets'
.
CREDIT: Trans
@uncommon
require 'facets/module/module_load'
# File lib/core/facets/module/module_load.rb, line 53 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.
module ::ExamplePathize module Example end end ExamplePathize.pathize #=> "example_pathize" ExamplePathize::Example.pathize #=> "example_pathize/example"
# File lib/core/facets/module/pathize.rb, line 15 def pathize name.pathize end
Prepend an aspect
module to a module. This only works at the
module level.
module ::PreX def x; "x"; end end module ::PreU def x; '{' + super + '}'; end end PreX.preextend(PreU) PreX.x # => "{x}"
NOTE: This is not a common core extension and is not loaded automatically
when using require 'facets'
.
CREDIT Trans
@uncommon
require 'facets/module/preextend'
# File lib/core/facets/module/preextend.rb, line 26 def preextend(aspect) aspect.__send__(:include, self) extend aspect end
Query whether a normal (singleton) method is defined for the module.
CREDIT: Gavin Sinclair, Noah Gibbs
# File lib/core/facets/module/instance_method_defined.rb, line 15 def singleton_method_defined?(meth) singleton_methods(true).find{ |m| m.to_sym == meth.to_sym } end
Private Instance Methods
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/standard/facets/equitable.rb, line 71 def Equitable(*accessors) Equitable.identify(self, *accessors) 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 AliasExample module_function def hello "Hello" end end AliasExample.hello #=> 'Hello' module AliasExample alias_module_function( :hi , :hello ) end AliasExample.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) alias_method(aka, sym) if aka ## prevent warning about method overwrite begin remove_method(sym) rescue NameError end ## define replacelemt method define_method(sym, &blk) end # alias_method :redef, :redefine_method 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 RedirectExample redirect_method :hi => :hello, :hey => :hello def hello(name) "Hello, #{name}." end end e = RedirectExample.new e.hello("Bob") #=> "Hello, Bob." e.hi("Bob") #=> "Hello, Bob." e.hey("Bob") #=> "Hello, Bob."
The above class definition is equivalent to …
class RedirectExample 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.
class RenameExample def foo; "foo"; end rename_method(:bar, :foo) end example = RenameExample.new example.bar #=> 'foo' expect NoMethodError do example.foo end
CREDIT: Trans
# File lib/core/facets/module/rename_method.rb, line 21 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
Return a new module based on another. This includes the original module into the new revised module.
module ReviseExample def foo; "foo"; end end ReviseExample2 = ReviseExample.revise do alias_method :bar, :foo end object = Object.new object.extend ReviseExample2 object.bar #=> 'foo'
CREDIT: Trans
# File lib/core/facets/module/revise.rb, line 26 def revise(&blk) base = self nm = Module.new{ include base } nm.class_eval(&blk) nm end
Sets an option to the given value. If the value is a proc, the proc will be called every time the option is accessed.
CREDIT: Blake Mizerany (Sinatra)
# File lib/core/facets/module/set.rb, line 10 def set(option, value=self, &block) raise ArgumentError if block && value != self value = block if block if value.kind_of?(Proc) if value.arity == 1 yield self else (class << self; self; end).module_eval do define_method(option, &value) define_method("#{option}?"){ !!__send__(option) } define_method("#{option}="){ |val| set(option, Proc.new{val}) } end end elsif value == self option.each{ |k,v| set(k, v) } elsif respond_to?("#{option}=") __send__("#{option}=", value) else set(option, Proc.new{value}) end self end
Returns the name of module's container module.
module ::SpaceName class Example end end SpaceName::Example.name #=> "SpaceName::Example" SpaceName::Example.spacename #=> "SpaceName"
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
Create an instance of Object and extend it with
self
.
mod = Module.new do def foo; "foo"; end end obj = mod.to_obj obj.foo #=> "foo"
# File lib/core/facets/module/to_obj.rb, line 13 def to_obj o = Object.new o.extend self o 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.
class WrapExample def foo "foo" end wrap_method(:foo) do |old_meth, *args| old_meth.call + '!' end end example = WrapExample.new example.foo #=> 'foo!'
Keep in mind that this cannot be used to wrap methods that take a block.
CREDIT: Trans
# File lib/core/facets/module/wrap_method.rb, line 27 def wrap_method( sym, &blk ) old = instance_method(sym) define_method(sym) { |*args| blk.call(old.bind(self), *args) } end