Class: Module
- Includes:
- Glue::Validation::ClassMethods
- Defined in:
- lib/more/facets/capsule.rb,
lib/more/facets/let.rb,
lib/more/facets/memoize.rb,
lib/more/facets/paramix.rb,
lib/more/facets/prepend.rb,
lib/more/facets/overload.rb,
lib/more/facets/settings.rb,
lib/more/facets/interface.rb,
lib/more/facets/namespace.rb,
lib/more/facets/attributes.rb,
lib/more/facets/compare_on.rb,
lib/more/facets/dependency.rb,
lib/more/facets/validation.rb,
lib/core/facets/module/attr.rb,
lib/core/facets/module/name.rb,
lib/more/facets/annotations.rb,
lib/core/facets/module/alias.rb,
lib/core/facets/module/clone.rb,
lib/more/facets/classmethods.rb,
lib/more/facets/instantiable.rb,
lib/core/facets/kernel/metaid.rb,
lib/core/facets/module/modify.rb,
lib/core/facets/module/traits.rb,
lib/core/facets/1stclassmethod.rb,
lib/core/facets/module/include.rb,
lib/core/facets/module/methods.rb,
lib/core/facets/module/require.rb,
lib/core/facets/module/abstract.rb,
lib/more/facets/class_extension.rb
Overview
ClassMethods
Miniframework provides a very convenient way to have modules pass along class methods in the inheritance chain.
An oddity of Ruby, when including modules, class/module methods are not inherited. To achieve this behavior requires some clever Ruby Karate. Instead ClassMethods provides an easy to use and clean solution. Simply place the class inheritable methods in a block of the special module method #ClassMetods.
module Mix
def inst_meth
puts 'inst_meth'
end
class_methods do
def class_meth
"Class Method!"
end
end
end
class X
include Mix
end
X.class_meth #=> "Class Method!"
This is equivalent to the original (but still functional) techinique of putting the class/module methods in a nested ClassMethods module and extending the original module manually. Eg.
module Mix
def inst_meth
puts 'inst_meth'
end
module ClassMethods
def class_meth
"Class Method!"
end
end
extend ClassMethods
end
class X
include Mix
end
X.class_meth #=> "Class Method!"
Also note that #class_inherit is an available alias for #class_methods for the sake of backward compatability. And #class_extension is alias (potentially) looking forward to a future version on Ruby.
Notes
Just a quick comment on the need for this behavior.
A module is an encapsulation of code, hence when a module is included (or extends), the module itself should have discretion over how it effects the receiving class/module. That is the very embodiment of encapsulation. Having it otherwise, as Ruby now does, stymies the practice –and we end up with “hacks”, like this and ClassMethods, to compensate.
Ruby would be much improved by making this bevaivor standard. And making non-inheritance the exception, which is alwasy easy enough to achieve: put the code in a separate (and thus uninherited) module.
Defined Under Namespace
Modules: Instantiable
Constant Summary collapse
- METHOD_TYPES =
[ :inherited, :ancestors, :local, :public, :private, :protected, :all ]
Constants included from Glue::Validation::ClassMethods
Glue::Validation::ClassMethods::LENGTHS
Instance Method Summary collapse
-
#*(rename_map) ⇒ Object
Rename methods.
-
#+(other) ⇒ Object
Combine modules.
-
#-(other) ⇒ Object
Subtract modules.
-
#abstract(*sym) ⇒ Object
Create an abstract method.
-
#alias_accessor!(*args) ⇒ Object
(also: #alias_switcher, #alias_toggler)
Create aliases for flag accessors.
-
#alias_reader!(*args) ⇒ Object
(also: #alias_reader?, #alias_tester)
Create aliases for flag reader.
-
#alias_setter(*args) ⇒ Object
Alias an accessor.
-
#alias_validator(*args) ⇒ Object
Create aliases for validators.
-
#alias_writer!(*args) ⇒ Object
Create aliases for flag writer.
-
#all_instance_methods(include_super = true) ⇒ Object
List all instance_methods, equivalent to.
-
#ancestor?(mod) ⇒ Boolean
Is a given class or module an ancestor of this class or module?.
-
#ann(ref, keys_or_class = nil, keys = nil) ⇒ Object
Set or read annotations.
-
#ann!(ref, keys_or_class = nil, keys = nil) ⇒ Object
To change an annotation’s value in place for a given class or module it first must be duplicated, otherwise the change may effect annotations in the class or module’s ancestors.
- #annotations ⇒ Object
- #append_features(mod) ⇒ Object
- #append_features_without_class_extension ⇒ Object
- #append_features_without_classmethods ⇒ Object
- #attr(*args) ⇒ Object
-
#attr_accessor!(*args) ⇒ Object
(also: #attr_switcher, #attr_toggler)
Create a toggle attribute.
-
#attr_reader!(*args) ⇒ Object
(also: #attr_reader?, #attr_tester)
Create an tester attribute.
-
#attr_setter(*args) ⇒ Object
Create an attribute method for both getting and setting an instance variable.
-
#attr_validator(*symbols, &validator) ⇒ Object
Like attr_writer, but the writer method validates the setting against the given block.
-
#attr_writer!(*args) ⇒ Object
Create a flaggable attribute.
-
#basename ⇒ Object
Returns the root name of the module/class.
-
#by_name(name) ⇒ Object
Note: the following documentation uses “class” because it’s more common, but it applies to modules as well..
-
#class_def(name, &blk) ⇒ Object
Defines an instance method within a class.
- #class_methods(&yld) ⇒ Object (also: #class_inherit)
-
#classified_attributes ⇒ Object
Return list of attributes that have a :class annotation.
-
#clone_removing(*meths) ⇒ Object
Returns an anonymous module with the specified methods of the receiving module removed.
-
#clone_renaming(pairs) ⇒ Object
Returns an anonymous module with the specified methods of the receiving module renamed.
-
#clone_using(*methods) ⇒ Object
Returns an anonymous module with only the specified methods of the receiving module intact.
-
#conflict?(other) ⇒ Boolean
Detect conflicts.
- #define_dependency(name, *deps) ⇒ Object
- #depend(name_and_deps = nil) ⇒ Object
-
#dependencies(name, build = []) ⇒ Object
Compile list of all unique prerequisite calls.
-
#dirname ⇒ Object
Returns the name of module’s container module.
-
#equate_on(*fields) ⇒ Object
(also: #key_attributes)
Generates identity/key methods based on specified attributes.
- #extend(*args) ⇒ Object (also: #can)
- #extend_without_parameters ⇒ Object
- #heritage(ref) ⇒ Object
- #include(*args) ⇒ Object
-
#include_as(h) ⇒ Object
Include a module via a specified namespace.
-
#include_function_module(*mod) ⇒ Object
Include module and apply module_fuction to the included methods.
- #include_without_parameters ⇒ Object
-
#instance_interface(*args) ⇒ Object
Provides an improved method lookup routnine.
-
#instance_method!(s) ⇒ Object
Easy access to method as objects, and they retain state!.
- #instance_method_defined?(meth) ⇒ Boolean
-
#integrate(mod, &block) ⇒ Object
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.
-
#is(*mods) ⇒ Object
alias_method :is, :include.
-
#is?(base) ⇒ Boolean
Is a given class or module an ancestor of this class or module?.
-
#let ⇒ Object
Use let to build a class like so:.
-
#memoize(*meths) ⇒ Object
Directive for making your functions faster by trading space for time.
- #method_overloads ⇒ Object
-
#method_space(name, mod = nil, &blk) ⇒ Object
Define a simple method namespace.
-
#mixin_parameters ⇒ Object
Store for module parameters.
-
#modspace ⇒ Object
Returns the module’s container module.
-
#module_load(path) ⇒ Object
Load file into module/class namespace.
-
#module_method_defined?(meth) ⇒ Boolean
Query whether a normal (singleton) method is defined for the module.
-
#module_require(path) ⇒ Object
Require file into module/class namespace.
-
#nesting ⇒ Object
Show a modules nesting in module namespaces.
-
#on_included(code) ⇒ Object
A macro for dynamic modules.
-
#overload(name, *signiture, &block) ⇒ Object
Overload methods.
-
#prepend(aspect) ⇒ Object
Prepend an
aspectmodule to a module. -
#private_conflict?(other) ⇒ Boolean
Like #conflict?, but checks only private methods.
-
#protected_conflict?(other) ⇒ Boolean
Like #conflict?, but checks only protected methods.
-
#public_conflict?(other) ⇒ Boolean
Like #conflict?, but checks only public methods.
-
#revisal(&blk) ⇒ Object
Return a new module based on another.
-
#setting(sym, options = {}) ⇒ Object
Defines a configuration setting for the enclosing class.
-
#sort_on(*fields) ⇒ Object
(also: #compare_on, #sort_attributes)
Automatically generate sorting defintions base on attribute fields.
-
#wrap_method(sym, &blk) ⇒ Object
(also: #wrap)
Creates a new method wrapping the previous of the same name.
Methods included from Glue::Validation::ClassMethods
#validate_format, #validate_inclusion, #validate_length, #validate_numeric, #validate_value
Instance Method Details
#*(rename_map) ⇒ Object
Rename methods.
module A
def a; "a"; end
end
B = A * { :a => :b }
class X; include B; end
X.new.b #=> "a"
CREDIT: Thomas Sawyer
CREDIT: Robert Dober
83 84 85 86 87 88 89 90 91 92 |
# File 'lib/core/facets/module/traits.rb', line 83 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 |
#+(other) ⇒ Object
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
CREDIT: Robert Dober
36 37 38 39 40 41 42 |
# File 'lib/core/facets/module/traits.rb', line 36 def +(other) base = self Module.new do include base include other end end |
#-(other) ⇒ Object
Subtract modules.
TODO: Should this use all instance_methods, not just public?
CREDIT: Thomas Sawyer
CREDIT: Robert Dober
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/core/facets/module/traits.rb', line 51 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 |
#abstract(*sym) ⇒ Object
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
15 16 17 18 19 |
# File 'lib/core/facets/module/abstract.rb', line 15 def abstract( *sym ) sym.each { |s| define_method( s ) { raise TypeError, "undefined abstraction ##{s}" } } end |
#alias_accessor!(*args) ⇒ Object Also known as: alias_switcher, alias_toggler
Create aliases for flag accessors.
CREDIT: Trans
117 118 119 120 121 122 123 124 |
# File 'lib/core/facets/module/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 |
#alias_reader!(*args) ⇒ Object Also known as: alias_reader?, alias_tester
Create aliases for flag reader.
CREDIT: Trans
159 160 161 162 163 164 165 |
# File 'lib/core/facets/module/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_setter(*args) ⇒ Object
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
81 82 83 84 85 86 |
# File 'lib/core/facets/module/attr.rb', line 81 def alias_setter(*args) args = args - [orig] args.each do |name| alias_method(name, orig) end end |
#alias_validator(*args) ⇒ Object
Create aliases for validators.
24 25 26 27 28 29 30 31 |
# File 'lib/core/facets/module/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 |
#alias_writer!(*args) ⇒ Object
Create aliases for flag writer.
CREDIT: Trans
200 201 202 203 204 205 206 |
# File 'lib/core/facets/module/attr.rb', line 200 def alias_writer!(*args) orig = args.last args = args - [orig] args.each do |name| alias_method("#{name}!", "#{orig}!") end end |
#all_instance_methods(include_super = true) ⇒ Object
List all instance_methods, equivalent to
public_instance_methods +
protected_instance_methods +
private_instance_methods
CREDIT: Trans
11 12 13 14 15 |
# File 'lib/core/facets/module/methods.rb', line 11 def all_instance_methods(include_super=true) public_instance_methods(include_super) + protected_instance_methods(include_super) + private_instance_methods(include_super) end |
#ancestor?(mod) ⇒ Boolean
Is a given class or module an ancestor of this class or module?
class X ; end
class Y < X ; end
X.ancestor?(Y)
74 75 76 |
# File 'lib/core/facets/module/include.rb', line 74 def ancestor?( mod ) ancestors.include? mod end |
#ann(ref, keys_or_class = nil, keys = nil) ⇒ Object
Set or read annotations.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/more/facets/annotations.rb', line 109 def ann( ref, keys_or_class=nil, keys=nil ) return heritage(ref) unless keys_or_class or keys if Class === keys_or_class keys ||= {} keys[:class] = keys_or_class else keys = keys_or_class end if Hash === keys ref = ref.to_sym annotations[ref] ||= {} annotations[ref].update(keys.rekey) else key = keys.to_sym heritage(ref)[key] end end |
#ann!(ref, keys_or_class = nil, keys = nil) ⇒ Object
To change an annotation’s value in place for a given class or module it first must be duplicated, otherwise the change may effect annotations in the class or module’s ancestors.
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/more/facets/annotations.rb', line 133 def ann!( ref, keys_or_class=nil, keys=nil ) #return heritage(ref) unless keys_or_class or keys return annotations[ref] unless keys_or_class or keys if Class === keys_or_class keys ||= {} keys[:class] = keys_or_class else keys = keys_or_class end if Hash === keys ref = ref.to_sym annotations[ref] ||= {} annotations[ref].update(keys.rekey) else key = keys.to_sym annotations[ref][key] = heritage(ref)[key].dup end end |
#annotations ⇒ Object
94 95 96 97 |
# File 'lib/more/facets/annotations.rb', line 94 def annotations #$annotations[self] @annotations ||= {} end |
#append_features(mod) ⇒ Object
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/more/facets/classmethods.rb', line 121 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 |
#append_features_without_class_extension ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# File 'lib/more/facets/class_extension.rb', line 34 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 |
#append_features_without_classmethods ⇒ Object
119 |
# File 'lib/more/facets/classmethods.rb', line 119 alias_method :append_features_without_classmethods, :append_features |
#attr(*args) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/more/facets/attributes.rb', line 53 def attr( *args ) args.flatten! case args.last when TrueClass args.pop attr_accessor( *args ) when FalseClass args.pop attr_reader( *args ) else attr_reader( *args ) end end |
#attr_accessor!(*args) ⇒ Object Also known as: attr_switcher, attr_toggler
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
107 108 109 |
# File 'lib/core/facets/module/attr.rb', line 107 def attr_accessor!(*args) attr_reader!(*args) + attr_writer!(*args) end |
#attr_reader!(*args) ⇒ Object Also known as: attr_reader?, attr_tester
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
139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/core/facets/module/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 |
#attr_setter(*args) ⇒ Object
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
53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'lib/core/facets/module/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 |
#attr_validator(*symbols, &validator) ⇒ Object
Like attr_writer, but the writer method validates the setting against the given block.
CREDIT: ?
8 9 10 11 12 13 14 15 16 17 18 19 20 |
# File 'lib/core/facets/module/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 |
#attr_writer!(*args) ⇒ Object
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
181 182 183 184 185 186 187 188 189 190 191 192 193 194 |
# File 'lib/core/facets/module/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 |
#basename ⇒ Object
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
63 64 65 66 67 68 69 |
# File 'lib/core/facets/module/name.rb', line 63 def basename if name and not name.empty? name.gsub(/^.*::/, '') else nil #inspect.gsub('#<','').gsub('>','').sub(':', '_') end end |
#by_name(name) ⇒ Object
Note: the following documentation uses “class” because it’s more common, but it applies to modules as well.
Given the name of a class, returns the class itself (i.e. instance of Class). The dereferencing starts at Object. That is,
Class.by_name("String")
is equivalent to
Object.const_get("String")
The parameter name is expected to be a Symbol or String, or at least to respond to to_str.
An ArgumentError is raised if name does not correspond to an existing class. If name is not even a valid class name, the error you’ll get is not defined.
Examples:
Class.by_name("String") # -> String
Class.by_name("::String") # -> String
Class.by_name("Process::Sys") # -> Process::Sys
Class.by_name("GorillaZ") # -> (ArgumentError)
Class.by_name("Enumerable") # -> Enumerable
Module.by_name("Enumerable") # -> Enumerable
CREDIT: Gavin Sinclair
33 34 35 36 37 38 39 40 41 42 |
# File 'lib/core/facets/module/name.rb', line 33 def by_name(name) #result = Object.constant(name) # TODO: Does self need to be Object in the following lines? const = name.to_s.dup base = const.sub!(/^::/, '') ? Object : ( self.kind_of?(Module) ? self : self.class ) result = const.split(/::/).inject(base){ |mod, subconst| mod.const_get(subconst) } return result if result.kind_of?( Module ) raise ArgumentError, "#{name} is not a module or class" end |
#class_def(name, &blk) ⇒ Object
Defines an instance method within a class.
CREDIT: WhyTheLuckyStiff
156 157 158 |
# File 'lib/core/facets/kernel/metaid.rb', line 156 def class_def name, &blk class_eval { define_method name, &blk } end |
#class_methods(&yld) ⇒ Object Also known as: class_inherit
138 139 140 141 142 143 144 145 146 |
# File 'lib/more/facets/classmethods.rb', line 138 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 |
#classified_attributes ⇒ Object
Return list of attributes that have a :class annotation.
class MyClass
attr_accessor :test
attr_accessor :name, String, :doc => 'Hello'
attr_accessor :age, Fixnum
end
MyClass.instance_attributes # => [:test, :name, :age, :body]
MyClass.classified_attributes # => [:name, :age]
126 127 128 129 130 |
# File 'lib/more/facets/attributes.rb', line 126 def classified_attributes instance_attributes.find_all do |a| self.ann(a, :class) end end |
#clone_removing(*meths) ⇒ Object
Returns an anonymous module with the specified methods of the receiving module removed.
NOTE: These is likely to be usurped by traits.rb.
CREDIT: Trans
28 29 30 31 32 33 |
# File 'lib/core/facets/module/clone.rb', line 28 def clone_removing( *meths ) mod = self.dup methods_to_remove = meths methods_to_remove.each{ |m| mod.class_eval{ remove_method m } } return mod end |
#clone_renaming(pairs) ⇒ Object
Returns an anonymous module with the specified methods of the receiving module renamed.
NOTE: These is likely to be usurped by traits.rb.
CREDIT: Trans
10 11 12 13 14 15 16 17 18 19 |
# File 'lib/core/facets/module/clone.rb', line 10 def clone_renaming( pairs ) mod = self.dup pairs.each_pair{ |to_sym, from_sym| mod.class_eval{ alias_method( to_sym, from_sym ) undef_method( from_sym ) } } return mod end |
#clone_using(*methods) ⇒ Object
Returns an anonymous module with only the specified methods of the receiving module intact.
NOTE: These is likely to be usurped by traits.rb.
CREDIT: Trans
42 43 44 45 46 47 48 |
# File 'lib/core/facets/module/clone.rb', line 42 def clone_using( *methods ) methods.collect!{ |m| m.to_s } methods_to_remove = instance_methods - methods mod = self.dup mod.class_eval{ methods_to_remove.each{ |m| undef_method m } } return mod end |
#conflict?(other) ⇒ Boolean
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
CREDIT: Robert Dober
112 113 114 115 116 117 118 |
# File 'lib/core/facets/module/traits.rb', line 112 def conflict?(other) 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.empty ? false : c end |
#define_dependency(name, *deps) ⇒ Object
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/more/facets/dependency.rb', line 104 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 |
#depend(name_and_deps = nil) ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/more/facets/dependency.rb', line 74 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 |
#dependencies(name, build = []) ⇒ Object
Compile list of all unique prerequisite calls.
90 91 92 93 94 95 96 97 98 99 100 |
# File 'lib/more/facets/dependency.rb', line 90 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 |
#dirname ⇒ Object
Returns the name of module’s container module.
module Example
class Demo
end
end
Demo.name #=> "Example::Demo"
Demo.dirname #=> "Example"
See also Module#basename.
CREDIT: Trans
85 86 87 88 |
# File 'lib/core/facets/module/name.rb', line 85 def dirname name[0...(name.rindex('::') || 0)] #name.gsub(/::[^:]*$/, '') end |
#equate_on(*fields) ⇒ Object Also known as: key_attributes
Generates identity/key methods based on specified attributes.
equate_on :a, :b
_is equivalent to_
def ==(o)
self.a == o.a && self.b == o.b
end
def eql?(o)
self.a.eql?(o.a) && self.b.eql?(o.b)
end
def hash()
self.a.hash ^ self.b.hash
end
79 80 81 82 83 84 85 86 |
# File 'lib/more/facets/compare_on.rb', line 79 def equate_on(*fields) code = "" code << "def ==(o) " << fields.map {|f| "self.#{f} == o.#{f}" }.join(" && ") << " end\n" code << "def eql?(o) " << fields.map {|f| "self.#{f}.eql?(o.#{f})" }.join(" && ") << " end\n" code << "def hash() " << fields.map {|f| "self.#{f}.hash" }.join(" ^ ") << " end\n" class_eval( code ) fields end |
#extend(*args) ⇒ Object Also known as: can
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 |
# File 'lib/more/facets/paramix.rb', line 123 def extend(*args) params = args.last.is_a?(Hash) ? args.pop : {} args.each do |mod| (class << self; self; end).class_eval do mixin_parameters[mod] = params if mod.basename define_method( mod.basename ) do |key| if params.key?(key) params[key] else super if defined?( super ) end end end end end r = extend_without_parameters(*args) for mod in args if mod.method_defined?(:extended_with_parameters) mod.extended_with_parameters( self, params ) end end r end |
#extend_without_parameters ⇒ Object
121 |
# File 'lib/more/facets/paramix.rb', line 121 alias_method :extend_without_parameters, :extend |
#heritage(ref) ⇒ Object
99 100 101 102 103 104 105 |
# File 'lib/more/facets/annotations.rb', line 99 def heritage(ref) ref = ref.to_sym ancestors.inject({}) { |memo, ancestor| ancestor.annotations[ref] ||= {} ancestor.annotations[ref] + memo } end |
#include(*args) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 |
# File 'lib/more/facets/paramix.rb', line 98 def include(*args) params = args.last.is_a?(Hash) ? args.pop : {} args.each do |mod| mixin_parameters[mod] = params if mod.basename define_method( mod.basename ) do |key| if params.key?(key) params[key] else super if defined?( super ) end end end end r = include_without_parameters(*args) for mod in args if mod.respond_to?(:included_with_parameters) mod.included_with_parameters( self, params ) end end r end |
#include_as(h) ⇒ Object
Include a module via a specified namespace.
module T
def t ; "HERE" ; end
end
class X
include_as :test => T
def t ; test.t ; end
end
X.new.t #=> "HERE"
53 54 55 |
# File 'lib/more/facets/namespace.rb', line 53 def include_as(h) h.each{ |name, mod| method_space(name, mod) } end |
#include_function_module(*mod) ⇒ Object
Include module and apply module_fuction to the included methods.
CREDIT: Trans
108 109 110 111 |
# File 'lib/core/facets/module/include.rb', line 108 def include_function_module *mod include(*mod) module_function(*mod.collect{|m| m.private_instance_methods & m.methods(false)}.flatten) end |
#include_without_parameters ⇒ Object
96 |
# File 'lib/more/facets/paramix.rb', line 96 alias_method :include_without_parameters, :include |
#instance_interface(*args) ⇒ Object
Provides an improved method lookup routnine. It returns a list of methods according to symbol(s) given.
Recognized symbols are:
-
:publicinclude public methods -
:privateinclude private methods -
:protectedinclude protected methods -
:ancestorsinclude all ancestor’s methods -
:inherited(same as above) -
<tt>:local</tti> include non-ancestor methods
-
:allinclude all of the above
This method also uses the symbol-not system. So you can specify the inverse of all of the above. For instance ~:public would mean :private, :protected (see facets/symbol/not).
If no symbol is given then :public, :local is assumed. Unrecognized symbols raise an error.
module Demo
def test
puts("Hello World!")
end
end
Demo.instance_methods(:local) #=> ['test']
To maintain backward compatibility true as an intitial argument is converted to :local, :ancestors (i.e. it includes both).
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 |
# File 'lib/more/facets/interface.rb', line 97 def instance_interface(*args) # for backward compatibility args << true if args.empty? case args[0] when TrueClass args.shift args << :ancestors args << :local when FalseClass args.shift args << :local end # default public, local args |= [:public] unless [:publix,:private,:protected,:all].any?{ |a| args.include?(a) } args |= [:ancestors,:local] unless [:ancestors,:inherited,:local,:all].any?{ |a| args.include?(a) } raise ArgumentError if args.any?{ |a| ! METHOD_TYPES.include?(a) } pos, neg = args.partition { |s| ! s.not? } m = [] pos.each do |n| case n when :inherited, :ancestors m |= ( public_instance_methods( true ) - public_instance_methods( false ) ) if pos.include?(:public) m |= ( private_instance_methods( true ) - private_instance_methods( false ) ) if pos.include?(:private) m |= ( protected_instance_methods( true ) - protected_instance_methods( false ) ) if pos.include?(:protected) when :local m |= public_instance_methods( false ) if pos.include?(:public) m |= private_instance_methods( false ) if pos.include?(:private) m |= protected_instance_methods( false ) if pos.include?(:protected) when :all m |= public_instance_methods( true ) m |= private_instance_methods( true ) m |= protected_instance_methods( true ) end end neg.each do |n| case n when :public m -= public_instance_methods( true ) when :private m -= private_instance_methods( true ) when :protected m -= protected_instance_methods( true ) when :inherited, :ancestors m -= ( public_instance_methods( true ) - public_instance_methods( false ) ) m -= ( private_instance_methods( true ) - private_instance_methods( false ) ) m -= ( protected_instance_methods( true ) - protected_instance_methods( false ) ) when :local m -= public_instance_methods( false ) m -= private_instance_methods( false ) m -= protected_instance_methods( false ) end end m.sort end |
#instance_method!(s) ⇒ Object
Easy access to method as objects, and they retain state!
module K
def hello
puts "Hello World!"
end
end
p K.instance_method!(:hello) #=> <UnboundMethod: #hello>
CAUTION! This it is currently limited to the scope of the current module/class.
112 113 114 115 |
# File 'lib/core/facets/1stclassmethod.rb', line 112 def instance_method!(s) #( @@__instance_methods__ ||= {} )[s] ||= instance_method(s) # TODO when fixed ( @__instance_methods__ ||= {} )[s] ||= instance_method(s) end |
#instance_method_defined?(meth) ⇒ Boolean
23 24 25 |
# File 'lib/core/facets/module/methods.rb', line 23 def instance_method_defined?(meth) instance_methods(true).find { |m| m == meth.to_s } end |
#integrate(mod, &block) ⇒ Object
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
42 43 44 45 46 47 |
# File 'lib/core/facets/module/modify.rb', line 42 def integrate(mod, &block) #include mod.revisal( &blk ) m = Module.new{ include mod } m.class_eval(&block) include m end |
#is(*mods) ⇒ Object
alias_method :is, :include
13 14 15 16 17 18 19 20 21 22 23 24 25 |
# File 'lib/core/facets/module/include.rb', line 13 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?(base) ⇒ Boolean
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
62 63 64 |
# File 'lib/core/facets/module/include.rb', line 62 def is?(base) ancestors.slice(1..-1).include?( base ) end |
#let ⇒ Object
Use let to build a class like so:
class X
let.y = "Hello"
let.up = lambda { puts @y.upcase }
end
X.new.up #=> HELLO
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/more/facets/let.rb', line 45 def let klass = self Functor.new do |op, *args| case op.to_s[-1,1] when '=' op = op.to_s.chomp('=') if Proc === args[0] define_method(op, &args[0]) else define_method( op ) do r = instance_variable_set( "@#{op}", args[0] ) klass.class.class_eval %{ def #{op}; @#{op}; end } r end end else klass.class_eval %{ def #{op}; @#{op}; end } end end end |
#memoize(*meths) ⇒ Object
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
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
# File 'lib/more/facets/memoize.rb', line 55 def memoize(*meths) @_MEMOIZE_CACHE ||= Hash.new meths.each do |meth| mc = @_MEMOIZE_CACHE[meth] = Hash.new old = instance_method(meth) new = proc do |*args| if mc.has_key? args mc[args] else mc[args] = old.bind(self).call(*args) end end send(:define_method, meth, &new) end end |
#method_overloads ⇒ Object
32 33 34 |
# File 'lib/more/facets/overload.rb', line 32 def method_overloads @method_overloads ||= {} end |
#method_space(name, mod = nil, &blk) ⇒ Object
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
71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/more/facets/namespace.rb', line 71 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 |
#mixin_parameters ⇒ Object
Store for module parameters. This is local per module and indexed on module/class included-into.
94 |
# File 'lib/more/facets/paramix.rb', line 94 def mixin_parameters ; @mixin_parameters ||= {} ; end |
#modspace ⇒ Object
Returns the module’s container module.
module Example
class Demo
end
end
Demo.modspace #=> Example
See also Module#basename.
CREDIT: Trans
103 104 105 106 |
# File 'lib/core/facets/module/name.rb', line 103 def modspace space = name[ 0...(name.rindex( '::' ) || 0)] space.empty? ? Object : eval(space) end |
#module_load(path) ⇒ Object
Load file into module/class namespace.
CREDIT: Trans
9 10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/core/facets/module/require.rb', line 9 def module_load( path ) if path =~ /^[\/~.]/ file = File.(path) else $LOAD_PATH.each do |lp| file = File.join(lp,path) break if File.exist?(file) file = nil end end module_eval(File.read(file)) end |
#module_method_defined?(meth) ⇒ Boolean
Query whether a normal (singleton) method is defined for the module.
CREDIT: Gavin Sinclair
CREDIT: Noah Gibbs
32 33 34 |
# File 'lib/core/facets/module/methods.rb', line 32 def module_method_defined?(meth) singleton_methods(true).find { |m| m == meth.to_s } end |
#module_require(path) ⇒ Object
Require file into module/class namespace.
CREDIT: Trans
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/core/facets/module/require.rb', line 27 def module_require( path ) if path =~ /^[\/~.]/ file = File.(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 @loaded ||= {} if @loaded.key?(file) false else @loaded[file] = true script = File.read(file) module_eval(script) true end end |
#nesting ⇒ Object
Show a modules nesting in module namespaces.
A::B::C.nesting #=> [ A, A::B ]
CREDIT: Trans
114 115 116 117 118 119 120 |
# File 'lib/core/facets/module/name.rb', line 114 def nesting n = [] name.split(/::/).inject(self) do |mod, name| c = mod.const_get(name) ; n << c ; c end return n end |
#on_included(code) ⇒ Object
A macro for dynamic modules.
TODO: This method ecourages a bad practice, and should not be used.
It's here because Nitro uses it, but eventually it will be deprecated.
CREDIT: George Moschovitis
122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/core/facets/module/include.rb', line 122 def on_included(code) tag = caller[0].split(' ').first.split(/\/|\\/).last.gsub(/:|\.|\(|\)/, '_') old = "__included_#{tag}" module_eval %{ class << self alias_method :#{old}, :included def included(base) #{old}(base) #{code} end end } end |
#overload(name, *signiture, &block) ⇒ Object
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/more/facets/overload.rb', line 52 def overload( name, *signiture, &block ) 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 cmp.size.times { |i| next unless cmp[i] < sig[i] } hit = cmp 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 |
#prepend(aspect) ⇒ Object
Prepend an aspect module to a module.
module X
def x; "x"; end
end
module U
def x; '{' + super + '}'; end
end
X.prepend U
X.x # => "{x}"
45 46 47 48 |
# File 'lib/more/facets/prepend.rb', line 45 def prepend( aspect ) aspect.send(:include, self) extend aspect end |
#private_conflict?(other) ⇒ Boolean
Like #conflict?, but checks only private methods.
134 135 136 137 |
# File 'lib/core/facets/module/traits.rb', line 134 def private_conflict?(other) c = private_instance_methods(true) & other.private_instance_methods(true) c.empty ? false : c end |
#protected_conflict?(other) ⇒ Boolean
Like #conflict?, but checks only protected methods.
141 142 143 144 |
# File 'lib/core/facets/module/traits.rb', line 141 def protected_conflict?(other) c = protected_instance_methods(true) & other.protected_instance_methods(true) c.empty ? false : c end |
#public_conflict?(other) ⇒ Boolean
Like #conflict?, but checks only public methods.
127 128 129 130 |
# File 'lib/core/facets/module/traits.rb', line 127 def public_conflict?(other) c = public_instance_methods(true) & other.public_instance_methods(true) c.empty ? false : c end |
#revisal(&blk) ⇒ Object
Return a new module based on another. This includes the original module into the new one.
CREDIT: Trans
54 55 56 57 58 59 |
# File 'lib/core/facets/module/modify.rb', line 54 def revisal(&blk) base = self nm = Module.new{ include base } nm.class_eval(&blk) nm end |
#setting(sym, options = {}) ⇒ Object
Defines a configuration setting for the enclosing class.
Example
class Compiler
setting :template_root, :default => 'src/template', :doc => 'The template root dir'
end
235 236 237 238 239 240 241 242 243 244 245 246 247 |
# File 'lib/more/facets/settings.rb', line 235 def setting(sym, = {}) Settings.add_setting(self, sym, ) module_eval %{ def self.#{sym} Settings[#{self}][:#{sym}].value end def self.#{sym}=(obj) Settings.setting #{self}, :#{sym}, :value => obj end } end |
#sort_on(*fields) ⇒ Object Also known as: compare_on, sort_attributes
Automatically generate sorting defintions base on attribute fields.
sort_on :a, :b
_is equivalent to_
def <=>(o)
cmp = self.a <=> o.a; return cmp unless cmp == 0
cmp = self.b <=> o.b; return cmp unless cmp == 0
0
end
44 45 46 47 48 49 50 51 52 53 |
# File 'lib/more/facets/compare_on.rb', line 44 def sort_on(*fields) code = %{def <=>(o)\n} fields.each { |f| code << %{cmp = ( @#{f} <=> o.instance_variable_get('@#{f}') ); return cmp unless cmp == 0\n} } code << %{0\nend; alias_method :cmp, :<=>;} class_eval( code ) fields end |
#wrap_method(sym, &blk) ⇒ Object Also known as: wrap
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
166 167 168 169 |
# File 'lib/core/facets/module/modify.rb', line 166 def wrap_method( sym, &blk ) old = instance_method(sym) define_method(sym) { |*args| blk.call(old.bind(self), *args) } end |