Class: Module

Inherits:
Object show all
Defined in:
lib/qualitysmith_extensions/module/alias_method_chain.rb,
lib/qualitysmith_extensions/module/join.rb,
lib/qualitysmith_extensions/module/split.rb,
lib/qualitysmith_extensions/module/create.rb,
lib/qualitysmith_extensions/module/parents.rb,
lib/qualitysmith_extensions/module/basename.rb,
lib/qualitysmith_extensions/module/namespace.rb,
lib/qualitysmith_extensions/kernel/remove_const.rb,
lib/qualitysmith_extensions/module/guard_method.rb,
lib/qualitysmith_extensions/module/alias_method_chain.rb,
lib/qualitysmith_extensions/module/bool_attr_accessor.rb,
lib/qualitysmith_extensions/module/bool_attr_accessor.rb,
lib/qualitysmith_extensions/module/attribute_accessors.rb,
lib/qualitysmith_extensions/module/malias_method_chain.rb,
lib/qualitysmith_extensions/module/ancestry_of_instance_method.rb

Overview

Author

Tyler Rick

Copyright

Copyright © 2007 QualitySmith, Inc.

License

Ruby License

Submit to Facets?

Yes.

  • May not have taken every single case into consideration. Needs a bit more testing.

    • public/private/protected?

  • Tests for this method can be found in ../object/ancestry_of_method.rb . It made more sense to test those two methods together; yet it’s still to use module/ancestry_of_instance_method.rb without using ../object/ancestry_of_method.rb.

++

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.basename(module_or_name) ⇒ Object

Gets the basename of a “module path” (the name of the module without any of the namespace modules that it is contained in), in the same sense that File.basename returns the basename of a filesystem path.

This is identical to Facets’ String#basename (‘facets/core/string/basename’) except that:

  • it is a class method instead of an instance method of String,

  • it accepts modules, strings, and symbols.

See also Module.dirname/Module.namespace_name_of.

These can be used together, such that the following is always true:

OuterModule::MiddleModule::InnerModule == Module.join(Module.dirname(some_module), Module.basename(some_module)).constantize


29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/qualitysmith_extensions/module/basename.rb', line 29

def self.basename(module_or_name)
  case module_or_name
    when Module
      module_or_name.basename
    when Symbol
      module_or_name.to_s.basename
    when String
      module_or_name.basename
    else
      raise ArgumentError
  end
end

.dirname(module_or_name) ⇒ Object Also known as: namespace_name_of

Gets the “dirname” of a “module path” (the string/symbol representing the namespace modules that it is contained in), in the same sense that File.dirname returns the dirname of a filesystem path.

Same as namespace_of, only this just returns the name of the namespace module (as a string), rather than returning the constant itself.

See also Module.basename



32
33
34
35
36
37
38
39
40
41
# File 'lib/qualitysmith_extensions/module/namespace.rb', line 32

def self.dirname(module_or_name)
  case module_or_name
    when Module
      module_or_name.namespace_module.name
    when Symbol
      Module.split_name(module_or_name)[0..-2].join('::')
    when String
      Module.split_name(module_or_name)[0..-2].join('::')
  end
end

.join(*path_parts) ⇒ Object

Joins pieces of a “module path” together in the same sense that File.join joins pieces of a filesystem path.

See also Module.dirname/Module.namespace_name_of and Module.basename.

These can be used together, such that the following is always true:

OuterModule::MiddleModule::InnerModule == Module.join(Module.dirname(some_module), Module.basename(some_module)).constantize


25
26
27
# File 'lib/qualitysmith_extensions/module/join.rb', line 25

def self.join(*path_parts)
  path_parts.map(&:to_s).join('::')
end

.namespace_of(module_or_name) ⇒ Object



46
47
48
# File 'lib/qualitysmith_extensions/module/namespace.rb', line 46

def self.namespace_of(module_or_name)
  namespace_name_of(module_or_name).constantize
end

.split_name(name) ⇒ Object

Like Module#split, only this operates on a string/symbol. Useful for when you don’t want to or can’t actually instantiate the module represented by the symbol.



24
25
26
# File 'lib/qualitysmith_extensions/module/split.rb', line 24

def self.split_name(name)
  name.to_s.split(/::/).map(&:to_sym)
end

Instance Method Details

#alias_method_chain(target, feature) {|aliased_target, punctuation| ... } ⇒ Object

Yields:

  • (aliased_target, punctuation)


18
19
20
21
22
23
24
25
# File 'lib/qualitysmith_extensions/module/alias_method_chain.rb', line 18

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?
  alias_method "#{aliased_target}_without_#{feature}#{punctuation}", target
  alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}"
end

#alias_method_chain_with_prevent_repeat_aliasing(target, feature, &block) ⇒ Object



31
32
33
34
35
36
37
38
39
40
# File 'lib/qualitysmith_extensions/module/alias_method_chain.rb', line 31

def alias_method_chain_with_prevent_repeat_aliasing(target, feature, &block)
  # 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
  target_without_feature = "#{aliased_target}_without_#{feature}#{punctuation}"

  #puts "#{target} is #{method_defined?(target)}"
  alias_method_chain_without_prevent_repeat_aliasing(target, feature, &block) unless method_defined?(target_without_feature)
end

#alias_method_chain_with_target_need_not_exist(target, feature, options = {}, &block) ⇒ Object

If you pass :create_target => true as one of the options:

  • Guarantees that alias_method_chain target, feature will work even if the target method has not been defined yet. If the target method doesn’t exist yet, an empty (no-op) target method will be created.

  • This could come in handy for callback methods (method_added, method_missing, etc.), for instane, when you don’t know if that particular callback method has been defined or not.

  • You want your alias_method_chain to wrap the existing method if there is an existing method, but to not break in the case that the method _doesn’t_ exist.



50
51
52
53
54
55
56
57
58
59
60
# File 'lib/qualitysmith_extensions/module/alias_method_chain.rb', line 50

def alias_method_chain_with_target_need_not_exist(target, feature, options = {}, &block)
  create_target = options.delete(:create_target)
  
  if create_target && true #!self.methods.include?(target)
    self.send :define_method, target do |*args|
      # Intentionally empty
    end
  end

  alias_method_chain_without_target_need_not_exist(target, feature, &block)
end

#ancestry_of_instance_method(method_name) ⇒ Object

Returns the module/class which defined the given instance method. If more than one module/class defined the method, returns the closest ancestor to have defined it (would be self if it is defined in self).

This looks at the results of instance_methods, which means that if you call this on a module/class, it will only look at instance methods. Thus, (unlike ancestry_of_method) it only makes sense to call this method on modules/classes, not instances of those modules/classes.

Example:

class Base
  def it; end
end
class SubWithIt < Base
  def it; end
end
class SubWithoutIt < Base
end
SubWithIt.ancestors # => [SubWithIt, Base, Object, Kernel]
SubWithIt.ancestry_of_instance_method(:it)    # => SubWithIt  # (Stops with self)
SubWithoutIt.ancestry_of_instance_method(:it) # => Base       # (Goes one step up the ancestry tree)

Returns nil if it cannot be found in self or in any ancestor.



36
37
38
39
40
41
# File 'lib/qualitysmith_extensions/module/ancestry_of_instance_method.rb', line 36

def ancestry_of_instance_method(method_name)
  method_name = method_name.to_s
  self.ancestors.find do |ancestor|
    ancestor.instance_methods(false).include? method_name
  end
end

#bool_attr_accessor(*args) ⇒ Object

This creates both a reader and a setter for a boolean (flag) attribute (instance variable).

bool_attr_accessor :a

is equivalent to

bool_attr_reader :a
bool_attr_setter :a

Examples:

x = Klass.new
x.a! true   # sets @a to true
x.a?        # => true
x.a!        # toggles @a, so that it ends up being false
x.a!        # toggles @a, so that it ends up being true
x.a! false  # sets @a to false


125
126
127
128
# File 'lib/qualitysmith_extensions/module/bool_attr_accessor.rb', line 125

def bool_attr_accessor(*args)
  bool_attr_reader *args
  bool_attr_setter *args
end

#bool_attr_reader(*args) ⇒ Object

This creates a reader method for a boolean (flag) attribute (instance variable).

bool_attr_reader :a

is equivalent to

def a?
  @a ? true : @a
end

Example:

class Foo
  def set_it
    @a = true
  end
end
x = Foo.new
x.a?        # => false
x.set_it
x.a?        # => true


39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/qualitysmith_extensions/module/bool_attr_accessor.rb', line 39

def bool_attr_reader(*args)
  options = (if args.last.is_a?(Hash) then args.pop else {} end)    # These options aren't used here, per se, but it allows us to have bool_attr_accessor pass along same args to both bool_attr_reader and bool_attr_setter.

  make = {}
  args.each { |a|
    make["#{a}?".to_sym] = %{
      def #{a}?(true_value=true)
        @#{a} ? true_value : @#{a}
      end
    }
  }
  module_eval make.values.join("\n"), __FILE__, __LINE__

  make.keys
end

#bool_attr_setter(*args) ⇒ Object

This creates a setter method for a boolean (flag) attribute (instance variable).

bool_attr_setter :a

is equivalent to

def a!(switch=Exception)
  if switch == Exception
    @a = !@a
  else
    @a = switch ? true : false
    self
  end
end

This setter method can either be used to set it directly to true or false or to toggle it.

Examples:

x = Klass.new
x.a! true   # sets @a to true
x.a!        # toggles @a, so that it ends up being false
x.a!        # toggles @a, so that it ends up being true
x.a! false  # sets @a to false

Use :allow_nil if you want it to be a tri-state flag – that is, if you need to be able to set it to nil as well as true and false.

bool_attr_setter :a, :b, :allow_nil => true
x.a! nil  # sets @a to nil


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/qualitysmith_extensions/module/bool_attr_accessor.rb', line 83

def bool_attr_setter(*args)
  options = (if args.last.is_a?(Hash) then args.pop else {} end)

  make = {}
  args.each { |a|
    make["#{a}!".to_sym] = %{
      def #{a}!(switch=Exception)
        if switch == Exception
          @#{a} = !@#{a}
        else
          #{
            options[:allow_nil] ?
              "@#{a} = switch ? true : switch" :
              "@#{a} = switch ? true : false"
          }
          self
        end
      end
    }
  }
  module_eval make.values.join("\n"), __FILE__, __LINE__
  make.keys
end

#create(name, options = {}, &block) ⇒ Object

Creates a new module with the specified name. This is essentially the same as actually defining the module like so:

module NewModule
end

or as a class:

class NewClass < SuperKlass
end

Configuration options: superclass - The class to inherit from. This only applies when using Class#create. Default is Object. namespace/parent - The class/module namespace that contains this module. Default is Object.

You can also include the namespace in the name if you’d prefer. For instance, name = :'Foo::Bar' is the same as specifying name = :Bar, :namespace => Foo. (Note: The namespace module specified must already be defined, just like it would have to be defined if you used the :namespace option.)

Examples:

Module.create(:Foo)                                                      # => Foo
Module.create(:'Foo::Bar', :namespace => Foo)                            # => Foo::Bar
Module.create(:Bar, :namespace => Foo)                                   # => Foo::Bar

Class.create(:Base)                                                      # => Base
Class.create(:'Foo::Klass', :superclass => Base)                         # => Foo::Klass

Unlike the built-in Ruby module/class directive, this actually returns the newly created module/class as the return value. So, for example, you can do things like this:

klass = Class.create(:'Foo::Klass', :superclass => Class.create(:Base))  # => Foo::Klass
klass.name                                                               # => Foo::Klass
klass.superclass                                                         # => Base

You can also pass a block to create. This:

Class.create(:ClassWithBlock, :superclass => BaseClass) do
  def self.say_hello
    'hello'
  end
end

is equivalent to this:

class ClassWithBlock < BaseClass do
  def self.say_hello
    'hello'
  end
end

Raises:

  • (ArgumentError)


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
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/qualitysmith_extensions/module/create.rb', line 84

def create(name, options = {}, &block)
  # Validate arguments
  raise ArgumentError unless name.respond_to?(:to_s)
  options[:namespace] = options.delete(:parent) if options.has_key?(:parent)
  options.assert_has_only_keys(
    :superclass,
    :namespace
  )
  module_or_class = self.to_s.downcase
  raise ArgumentError, 'Modules cannot have superclasses' if options[:superclass] && module_or_class == 'module'

  # Set defaults
  namespace_module, superclass =
    options[:namespace]  || ::Object, 
    options[:superclass] || ::Object

  # Determine the namespace to create it in
  nesting = Module.split_name(name)
  if nesting.size > 1
    namespace_module = Module.namespace_of(name) # For example, would be A::B for A::B::C
    base_name = Module.basename(name)            # For example, would be :C   for A::B::C
  else
    base_name = name
  end
  
  # Actually create the new module
  if superclass != ::Object
    superclass = " < ::#{superclass}"
  else
    superclass = ''
  end
  namespace_module.class_eval <<-end_eval, __FILE__, __LINE__
    #{module_or_class} #{base_name}#{superclass}
      # Empty
    end
  end_eval
  our_new_module = namespace_module.const_get(base_name)
  
  our_new_module.class_eval(&block) if block_given?
  our_new_module
end

#guard_method(guard_method_name, guard_variable) ⇒ Object

A guard method (by this definition anyway) is a method that sets a flag, executes a block, and then returns the flag to its previous value. It ensures that the flag is set during the execution of the block.

In the simplest case, you’d use it like this:

class A
  guard_method :disable_stupid_stuff!, :@stupid_stuff_disabled
end
a = A.new
a.disable_stupid_stuff! do   # Causes @stupid_stuff_disabled to be set to true
  # Section of code during which you don't want any stupid stuff to happen
end                          # Causes @stupid_stuff_disabled to be set back to false
# Okay, a, you can resume doing stupid stuff again...

If you want your guard method to disable the flag rather than enable it, simply pass false to the guard method.

These calls can be nested however you wish:

a.guard_the_fruit! do
  a.guard_the_fruit!(false) do
    assert_equal false, a.guarding_the_fruit?
  end
  assert_equal true, a.guarding_the_fruit?
end

You can also use the guard methods as normal flag setter/clearer methods by simply not passing a block to it. Hence

a.guard_the_fruit!

will simply set @guarding_the_fruit to true, and

a.guard_the_fruit!(false)

will set @guarding_the_fruit to false.

Raises:

  • (ArgumentError)


52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/qualitysmith_extensions/module/guard_method.rb', line 52

def guard_method(guard_method_name, guard_variable)
  raise ArgumentError.new("Expected an instance variable name but got #{guard_variable}") if guard_variable !~ /^@([\w_]+)$/
  guard_variable.to_s =~ /^@([\w_]+)$/    # Why didn't the regexp above set $1 ??
  class_eval do
    bool_attr_accessor $1.to_sym
  end
  module_eval <<-End, __FILE__, __LINE__+1
    def #{guard_method_name}(new_value = true, &block)
      old_guard_state, #{guard_variable} = #{guard_variable}, new_value
      if block_given?
        begin
          returning = yield
        ensure
          #{guard_variable} = old_guard_state
          returning
        end
      end
    end
  End
end

#malias_method_chain(target, feature, *args) ⇒ Object

Same as Module#alias_method_chain, only it works for modules/classes

class X
  def self.foo
    'foo'
  end
  malias_method_chain :foo, :feature
end

Note: You could always do the same thing with Module#alias_method_chain by simply doing this:

class << self
  alias_method_chain :foo, :feature
end


32
33
34
35
36
37
38
39
# File 'lib/qualitysmith_extensions/module/malias_method_chain.rb', line 32

def malias_method_chain(target, feature, *args)
  # Strip out punctuation on predicates or bang methods since
  # e.g. target?_without_feature is not a valid method name.

  singleton_class.instance_eval do
    alias_method_chain target, feature, *args
  end
end

#mattr_accessor(*syms) ⇒ Object Also known as: cattr_accessor



44
45
46
47
# File 'lib/qualitysmith_extensions/module/attribute_accessors.rb', line 44

def mattr_accessor(*syms)
  mattr_reader(*syms)
  mattr_writer(*syms)
end

#mattr_reader(*syms) ⇒ Object Also known as: cattr_reader

:nodoc:



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# File 'lib/qualitysmith_extensions/module/attribute_accessors.rb', line 6

def mattr_reader(*syms)
  syms.each do |sym|
    class_eval(<<-EOS, __FILE__, __LINE__+1)
      unless defined? @@#{sym}
        @@#{sym} = nil
      end
      
      def self.#{sym}
        @@#{sym}
      end

      def #{sym}
        @@#{sym}
      end
    EOS
  end
end

#mattr_writer(*syms) ⇒ Object Also known as: cattr_writer



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/qualitysmith_extensions/module/attribute_accessors.rb', line 25

def mattr_writer(*syms)
  syms.each do |sym|
    class_eval(<<-EOS, __FILE__, __LINE__+1)
      unless defined? @@#{sym}
        @@#{sym} = nil
      end
      
      def self.#{sym}=(obj)
        @@#{sym} = obj
      end

      def #{sym}=(obj)
        @@#{sym} = obj
      end
    EOS
  end
end

#mbool_attr_accessor(*args) ⇒ Object

This creates both a reader and a setter for a boolean (flag) class/module variable.

mbool_attr_accessor :a

is equivalent to

mbool_attr_reader :a
mbool_attr_setter :a

Works for both classes and modules.



221
222
223
224
# File 'lib/qualitysmith_extensions/module/bool_attr_accessor.rb', line 221

def mbool_attr_accessor(*args)
  mbool_attr_reader *args
  mbool_attr_setter *args
end

#mbool_attr_reader(*args) ⇒ Object

This creates a reader method for a boolean (flag) class/module variable.

mbool_attr_reader :a

is equivalent to

def self.a?
  @@a ? true : @@a
end

Works for both classes and modules.



147
148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/qualitysmith_extensions/module/bool_attr_accessor.rb', line 147

def mbool_attr_reader(*args)
  options = (if args.last.is_a?(Hash) then args.pop else {} end)    # These options aren't used here, per se, but it allows us to have bool_attr_accessor pass along same args to both bool_attr_reader and bool_attr_setter.

  make = {}
  args.each { |a|
    make["#{a}?".to_sym] = %{
      def self.#{a}?(true_value=true)
        @@#{a} ? true_value : @@#{a}
      end
    }
  }
  module_eval make.values.join("\n"), __FILE__, __LINE__
  make.keys
end

#mbool_attr_setter(*args) ⇒ Object

This creates a setter method for a boolean (flag) class/module variable.

mbool_attr_setter :a

is equivalent to

def self.a!(switch=Exception)
  if switch == Exception
    @@a = !@@a
  else
    @@a = switch ? true : false
    self
  end
end

Works for both classes and modules.

Use :allow_nil if you want it to be a tri-state flag – that is, if you need to be able to set it to nil as well as true and false.

mbool_attr_setter :a, :b, :allow_nil => true
C.a! nil  # sets @@a to nil


183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# File 'lib/qualitysmith_extensions/module/bool_attr_accessor.rb', line 183

def mbool_attr_setter(*args)
  options = (if args.last.is_a?(Hash) then args.pop else {} end)

  make = {}
  args.each { |a|
    # Initialize it first so that we won't have any NameErrors.
    module_eval %{ @@#{a} = nil if !defined?(@@#{a}) }, __FILE__, __LINE__

    make["#{a}!".to_sym] = %{
      def self.#{a}!(switch=Exception)
        if switch == Exception
          @@#{a} = !@@#{a}
        else
          #{
            options[:allow_nil] ?
              "@@#{a} = switch ? true : switch" :
              "@@#{a} = switch ? true : false"
          }
          self
        end
      end
    }
  }
  module_eval make.values.join("\n"), __FILE__, __LINE__
  make.keys
end

#mguard_method(guard_method_name, guard_variable) ⇒ Object

See the documentation for guard_method. mguard_method does the same thing, only it creates a class (or module) method rather than an instance method and it uses a class (or module) variable rather than an instance variable to store the guard state.

Example:

mguard_method :guard_the_fruit!, :@@guarding_the_fruit
mguard_method :use_assert_equal_with_highlight!, :@@always_use_assert_equal_with_highlight

Raises:

  • (ArgumentError)


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/qualitysmith_extensions/module/guard_method.rb', line 80

def mguard_method(guard_method_name, guard_variable)
  raise ArgumentError.new("Expected a class variable name but got #{guard_variable}") if guard_variable !~ /^@@[\w_]+$/
  guard_variable.to_s =~ /^@@([\w_]+)$/
  class_eval do
    mbool_attr_accessor $1.to_sym
  end
  module_eval <<-End, __FILE__, __LINE__+1
    class << self
      def #{guard_method_name}(new_value = true, &block)
        old_guard_state, #{guard_variable} = #{guard_variable}, new_value
        if block_given?
          begin
            returning = yield
          ensure
            #{guard_variable} = old_guard_state
            returning
          end
        end
      end
    end
  End
end

#namespace_moduleObject

Return the module which contains this one; if this is a root module, such as ::MyModule, then Object is returned.



20
21
22
23
# File 'lib/qualitysmith_extensions/module/namespace.rb', line 20

def namespace_module
  namespace_name = name.split('::')[0..-2].join('::')
  namespace_name.empty? ? Object : namespace_name.constantize
end

#parentsObject

Return all the parents of this module, ordered from nested outwards. The receiver is not contained within the result.



20
21
22
23
24
25
26
27
28
29
30
# File 'lib/qualitysmith_extensions/module/parents.rb', line 20

def parents
  parents = []
  parts = name.split('::')[0..-2]
  until parts.empty?
    #parents << (parts * '::').constantize
    parents << constant(parts * '::')
    parts.pop
  end
  parents << Object unless parents.include? Object
  parents
end

#splitObject

Very similar to Facets’ Module#nesting, but, whereas nesting will break A::B into an array of constants represting nesting ([A, A::B]), this method will split it into an array of symbols: [:A, :B].

Note that the second element in this array, :B, is not fully qualified, so you could not do a const_get on that symbol.



18
19
20
# File 'lib/qualitysmith_extensions/module/split.rb', line 18

def split
  name.split(/::/).map(&:to_sym)
end