Whenever I try to extend a ruby module, I lose the module methods. Neither include nor extend will do this. Consider the snippet:
module A
def self.say_
I don't think there's any simple way to do it.
So here is a complex way:
module B
class << self
A.singleton_methods.each do |m|
define_method m, A.method(m).to_proc
end
end
end
You can put it in a helper method like this:
class Module
def include_module_methods(mod)
mod.singleton_methods.each do |m|
(class << self; self; end).send :define_method, m, mod.method(m).to_proc
end
end
end
module B
include_module_methods A
end
I don't like everyone using self.included
. I have simpler solution:
module A
module ClassMethods
def a
'a1'
end
end
def a
'a2'
end
end
module B
include A
module ClassMethods
include A::ClassMethods
def b
'b1'
end
end
def b
'b2'
end
end
class C
include B
extend B::ClassMethods
end
class D < C; end
puts D.a
puts D.b
puts D.new.a
puts D.new.b
Johnathan, I am not sure if you're still wondering about this, but there are two different ways to use modules in ruby. A.) you use modules in their self contained form Base::Tree.entity(params) directly in your code, or B.) you use modules as mixins or helper methods.
A. Will allow you to use modules as a Namespace pattern. This is good for larger projects where there is the chance for method name conflicts
module Base
module Tree
def self.entity(params={},&block)
# some great code goes here
end
end
end
Now you can use this to create some sort of Tree structure in your code, without having to instantiate a new class for every call to Base::Tree.entity.
Another way to do Namespace-ing is on a class by class basis.
module Session
module Live
class Actor
attr_accessor :type, :uuid, :name, :status
def initialize(params={},&block)
# check params, insert init values for vars..etc
# save your callback as a class variable, and use it sometime later
@block = block
end
def hit_rock_bottom
end
def has_hit_rock_bottom?
end
...
end
end
class Actor
attr_accessor :id,:scope,:callback
def initialize(params={},&block)
self.callback = block if block_given?
end
def respond
if self.callback.is_a? Proc
# do some real crazy things...
end
end
end
end
Now we have the potential for overlap in our classes. We want to know that when we make a create an Actor class that it is the correct class, so this is where namespaces come in handy.
Session::Live::Actor.new(params) do |res|...
Session::Actor.new(params)
B. Mix-Ins These are your friends. Use them whenever you think you will have to do something more than once in your code.
module Friendly
module Formatter
def to_hash(xmlstring)
#parsing methods
return hash
end
def remove_trailing_whitespace(string,&block)
# remove trailing white space from that idiot who pasted from textmate
end
end
end
Now whenever you need to format an xmlstring as a hash, or remove trailing whitespace in any of your future code, just mix it in.
module Fun
class Ruby
include Friendly::Formatter
attr_accessor :string
def initialize(params={})
end
end
end
Now you can format the string in your class.
fun_ruby = Fun::Ruby.new(params)
fun_ruby.string = "<xml><why><do>most</do><people></people><use>this</use><it>sucks</it></why></xml>"
fun_ruby_hash = fun_ruby.to_hash(fun_ruby.string)
Hope this is a good enough explanation. The points raised above are good examples of ways to extend classes, but with modules, the hard part is when to use the self keyword. It refers to the scope of the object within ruby's object hierarchy. So if you want to use a module as a mix-in, and don't want to declare anything singleton, don't use the self keyword, however if you want to keep state within the object, just use a class and mix-in the modules you want.
If you are the author of module A
and will frequently need this, you can re-author A like so:
module A
module ClassMethods
def say_hi
puts "hi"
end
end
extend ClassMethods
def self.included( other )
other.extend( ClassMethods )
end
end
module B
include A
end
A.say_hi #=> "hi"
B.say_hi #=> "hi"
Use include_complete
gem install include_complete
module A
def self.say_hi
puts "hi"
end
end
module B
include_complete A
end
B.say_hi #=> "hi"