Create aliases for flag reader.
NOTE: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: Trans
# File lib/facets/core-uncommon/facets/module/attr_tester.rb, line 40 def alias_tester(*args) orig = args.last args = args - [orig] args.each do |name| alias_method("#{name}?", "#{orig}?") end end
Create aliases for validators.
NOTE: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/attr_validator.rb, line 30 def alias_validator(*args) orig = args.last args = args - [orig] args.each do |name| #alias_method(name, orig) alias_method("#{name}=", "#{orig}=") end end
NOTE: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/attr_class_accessor.rb, line 6 def attr_class_accessor(name) attr_class_reader(name) attr_class_writer(name) end
NOTE: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/attr_class_accessor.rb, line 14 def attr_class_reader(name) module_eval("def self.\#{name}\n@\#{name}\nend\ndef \#{name}\nself.class.\#{name}\nend\n", __FILE__, __LINE__) end
NOTE: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/attr_class_accessor.rb, line 29 def attr_class_writer(name) module_eval("def self.\#{name}=(x)\n@\#{name} = x\nend\ndef \#{name}=(x)\nself.class.\#{name} = x\nend\n", __FILE__, __LINE__) end
NOTE: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/attr_inheritor.rb, line 6 def attr_inheritable_reader(name, default) copy_inheritor(name, default) module_eval("def \#{name}\nself.class.\#{name}\nend\n", __FILE__, __LINE__) 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: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: Trans
# File lib/facets/core-uncommon/facets/module/attr_tester.rb, line 19 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
Like attr_writer, but the writer method validates the setting against the given block.
NOTE: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: ?
# File lib/facets/core-uncommon/facets/module/attr_validator.rb, line 11 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
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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: David Heinemeier Hansson
# File lib/facets/core-uncommon/facets/module/cattr.rb, line 18 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: David Heinemeier Hansson
# File lib/facets/core-uncommon/facets/module/cattr.rb, line 119 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: David Heinemeier Hansson
# File lib/facets/core-uncommon/facets/module/cattr.rb, line 44 def cattr_reader(*syms) syms.flatten.each do |sym| module_eval("unless defined? @@\#{sym}\n@@\#{sym} = nil\nend\n\ndef self.\#{sym}\n@@\#{sym}\nend\n\ndef \#{sym}\n@@\#{sym}\nend\n", __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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: David Heinemeier Hansson
# File lib/facets/core-uncommon/facets/module/cattr.rb, line 83 def cattr_writer(*syms) syms.flatten.each do |sym| module_eval("unless defined? @@\#{sym}\n@@\#{sym} = nil\nend\n\ndef self.\#{sym}=(obj)\n@@\#{sym} = obj\nend\n\ndef \#{sym}=(obj)\n@@\#{sym}=(obj)\nend\n", __FILE__, __LINE__) end return syms end
NOTE: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/class_accessor.rb, line 6 def class_accessor(name) class_reader(name) class_writer(name) 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: Daniel Schierbeck, Thomas Sawyer
THANKS: Nobu Nakada, Ulysses
# File lib/facets/core-uncommon/facets/module/class_extend.rb, line 39 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/facets/core-uncommon/facets/module/class_extend.rb, line 65 def class_extensions @class_extensions ||= [] end
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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: Thomas Sawyer
# File lib/facets/core-uncommon/facets/module/class_inheritor.rb, line 45 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/class_accessor.rb, line 14 def class_reader(name) module_eval("def self.\#{name}\n@\#{name}\nend\n", __FILE__, __LINE__) end
NOTE: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/class_accessor.rb, line 26 def class_writer(name) module_eval("def self.\#{name}=(x)\n@\#{name} = x\nend\n", __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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: Thomas Sawyer
# File lib/facets/core-uncommon/facets/module/copy_inheritor.rb, line 23 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/enclosure.rb, line 21 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/enclosure.rb, line 55 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
Eclosure name.
module ::EncExample module M module N end end end EncExample::M::N.encname #=> "EncExample::M"
NOTE: This is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/enclosure.rb, line 78 def encname /::[^:]+\Z/ =~ self.name ? $` : nil 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/method_space.rb, line 95 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/instance_function.rb, line 30 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
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
# File lib/facets/core-uncommon/facets/module/let.rb, line 13 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: David Heinemeier Hansson
# File lib/facets/core-uncommon/facets/module/cattr.rb, line 144 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: David Heinemeier Hansson
# File lib/facets/core-uncommon/facets/module/cattr.rb, line 247 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: David Heinemeier Hansson
# File lib/facets/core-uncommon/facets/module/cattr.rb, line 170 def mattr_reader( *syms ) syms.flatten.each do |sym| module_eval("unless defined? @@\#{sym}\n@@\#{sym} = nil\nend\n\ndef self.\#{sym}\n@@\#{sym}\nend\n\ndef \#{sym}\n@@\#{sym}\nend\n", __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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: David Heinemeier Hansson
# File lib/facets/core-uncommon/facets/module/cattr.rb, line 210 def mattr_writer(*syms) syms.flatten.each do |sym| module_eval("unless defined? @@\#{sym}\n@@\#{sym} = nil\nend\n\ndef self.\#{sym}=(obj)\n@@\#{sym} = obj\nend\n\ndef \#{sym}=(obj)\n@@\#{sym}=(obj)\nend\n", __FILE__, __LINE__) end return syms end
This is here for backward compatibility.
# File lib/facets/core-uncommon/facets/module/memoize.rb, line 6 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
# File lib/facets/core-uncommon/facets/module/method_clash.rb, line 27 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.
# File lib/facets/core-uncommon/facets/module/method_clash.rb, line 53 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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: Pit Captain
# File lib/facets/core-uncommon/facets/module/method_space.rb, line 28 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 %{ 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
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 is not (presently) a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: Trans
# File lib/facets/core-uncommon/facets/module/module_load.rb, line 15 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 is not a common core extension and is not loaded automatically when using require ‘facets‘.
CREDIT: Trans
# File lib/facets/core-uncommon/facets/module/module_load.rb, line 47 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
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
# File lib/facets/core-uncommon/facets/module/preextend.rb, line 23 def preextend(aspect) aspect.__send__(:include, self) extend aspect end
Prepend module.
class X def a; "Xa"; end end module M def a; "M" + super ; end end class X prepend M end X.new.a #=> MXa
IMPORTANT! prepend is not dynamic, rather it copies all methods when included on a class or module. For this reason one must be careful to invoke prepend AFTER any method definitions that are to be effected. Ideally this would not be necessary, but it would require support in Ruby‘s C+ source to make it possible.
NOTE: This is not a common core extension and is not loaded automatically when using require ‘facets‘.
# File lib/facets/core-uncommon/facets/module/prepend.rb, line 55 def prepend(mod) include Prependable include mod end