class Module
Public Instance Methods
# File lib/utilrb/module/include.rb, line 22 def __include_single_module(mod) if mod.const_defined?(:ModuleExtension) if is_a?(Module) unless const_defined?(:ModuleExtension) const_set(:ModuleExtension, Module.new) end const_get(:ModuleExtension).class_eval do __instance_include__ mod.const_get(:ModuleExtension) end extend mod.const_get(:ModuleExtension) end # Do nothing on classes end if mod.const_defined?(:ClassExtension) if !is_a?(Class) unless const_defined?(:ClassExtension) const_set(:ClassExtension, Module.new) end const_get(:ClassExtension).class_eval do __instance_include__ mod.const_get(:ClassExtension) end else extend mod.const_get(:ClassExtension) end end __instance_include__ mod end
Support for attributes that are enumerables. This methods defines two methods:
obj.attr_name # => enumerable obj.each_name(key = nil) { |value| ... } # => obj
The first one returns the enumerable object itself. The second one iterates on the values in attr_name. If key
is not nil, then attr_name is supposed to be a hash of enumerables, and key
is used to select the enumerable to iterate on.
The following calls are equivalent
obj.attr_name.each { |value| ... } obj.each_name { |value| ... }
And these two are equivalent:
obj.attr_name[key].each { |value| ... } obj.each_name(key) { |value| ... }
enumerator
is the name of the enumeration method we should use. init_block
, if given, should return the value at which we should initialize attr_name.
# File lib/utilrb/module/attr_enumerable.rb, line 25 def attr_enumerable(name, attr_name = name, enumerator = :each, &init_block) class_eval do attribute(attr_name, &init_block) end class_eval <<-EOF, __FILE__, __LINE__+1 def each_#{name}(key = nil, &iterator) return unless #{attr_name} if key #{attr_name}[key].#{enumerator}(&iterator) else #{attr_name}.#{enumerator}(&iterator) end self end EOF end
Defines a name?
predicate, and if writable is true a name= method. Note that name
can end with '?', in which case the ending '?' is removed.
The methods use the @name instance variable internally
# File lib/utilrb/module/attr_predicate.rb, line 7 def attr_predicate(name, writable = false) attr_name = name.to_s.gsub(/\?$/, '') attr_reader attr_name alias_method "#{attr_name}?", attr_name remove_method attr_name if writable class_eval "def #{attr_name}=(value); @#{attr_name} = !!value end", __FILE__, __LINE__+1 end end
Creates enum_#{name}
method which returs an Enumerator object for the each_#{enum_name}
method. This enumerator is created once.
If with_arg
is true, it is supposed that the 'each_' method requires one argument, which is given in argument of the 'enum' method. In that case, an enumerator is created for each argument
# File lib/utilrb/module/cached_enum.rb, line 21 def cached_enum(enum_name, name, with_arg) include CachedValuesSupport if with_arg class_eval <<-EOD, __FILE__, __LINE__+1 def enum_#{name}(arg) @enum_#{name} ||= Hash.new cached_variables << :@enum_#{name} @enum_#{name}[arg] ||= enum_for(:each_#{enum_name}, arg) end EOD else class_eval <<-EOD, __FILE__, __LINE__+1 def enum_#{name} cached_variables << :@enum_#{name} @enum_#{name} ||= enum_for(:each_#{enum_name}) end EOD end end
# File lib/utilrb/module/const_defined_here_p.rb, line 3 def const_defined_here?(name) const_defined?(name, false) end
Emulate block-passing by converting the block into a Proc object and passing it to the given block as last argument dule)
For instance
define_method('my_method') do |a, &block| end
Is written as
define_method_with_block('my_method') do |block, a| end
The block is given first to allow the following construct:
define_method_with_block('my_method') do |block, *args| end
block
is nil
if no block is given during the method call
# File lib/utilrb/module/define_method.rb, line 21 def define_method_with_block(name, &mdef) class_eval <<-EOD, __FILE__, __LINE__+1 def #{name}(*args, &block) dmwb_#{name}_user_definition(block, *args) end EOD define_method("dmwb_#{name}_user_definition", &mdef) end
:call-seq
define_or_reuse(name, value) -> value define_or_reuse(name) { ... } -> value
Defines a new constant under a given module, or reuse the already-existing value if the constant is already defined.
In the first form, the method gets its value from its argument. In the second case, it calls the provided block
# File lib/utilrb/module/define_or_reuse.rb, line 12 def define_or_reuse(name, value = nil) if const_defined_here?(name) const_get(name) else module_eval do const_set(name, (value || yield)) end end end
This defines a name
instance method on the given class which accepts zero or one argument
Without any argument, it acts as a getter for the +@name+ attribute. With one argument, it acts instead as a setter for the same attribute and returns self. If a block has been given to dsl_attribute
, any new value is passed to the block, whose return value is actually saved in the instance variable. This block can therefore both filter the value (convert it to a desired form) and validate it.
The goal of this method is to have a nicer way to handle attribute in DSLs: instead of
model = create_model do self.my_model_attribute = 'bla' if (my_model_attribute) <do something> end end
(or worse, using set_ and get_ prefixes), we can do
model = create_model do my_model_attribute 'bla', arg0, arg1, ... if (my_model_attribute) <do something> end end
# File lib/utilrb/module/dsl_attribute.rb, line 37 def dsl_attribute(*names, &filter_block) if names.size > 1 && filter_block raise ArgumentError, "multiple names as argument are only supported if no block is given" end names.each do |name| class_eval do if filter_block define_method("__dsl_attribute__#{name}__filter__", &filter_block) end define_method(name) do |*value| if value.empty? instance_variable_get("@#{name}") elsif filter_block if filter_block.arity >= 0 && value.size != filter_block.arity raise ArgumentError, "too many arguments. Got #{value.size}, expected #{filter_block.arity}" end filtered_value = send("__dsl_attribute__#{name}__filter__", *value) instance_variable_set("@#{name}", filtered_value) self else if value.size == 1 instance_variable_set("@#{name}", value.first) else instance_variable_set("@#{name}", value) end self end end end end end
Includes a module in this one, with support for class extensions
If a module defines a ClassExtension submodule, then
-
if it is included in a module, the target's ClassExtension module includes the source ClassExtension (and if there is no ClassExtension in the target, it is created)
-
if it is included in a Class, the ClassExtension module extends the class.
# File lib/utilrb/module/include.rb, line 16 def include(*mods) mods.each do |mod| __include_single_module(mod) end end
It so happens that this method to determine whether a class is a singleton class is valid for ruby 2.0 and breaks on 2.1 … However (!) on 2.1 singleton_class? is defined
# File lib/utilrb/module/singleton_class_p.rb, line 6 def singleton_class? if instance_variable_defined?(:@__utilrb_singleton_class) @__utilrb_singleton_class else @__utilrb_singleton_class = (ancestors.first != self) end end