每当我尝试扩展ruby模块时,我都会丢失模块方法。既不包含也不延伸都不会这样做。考虑一下代码段:
module A
def self.say_hi
puts "hi"
end
end
module B
include A
end
B.say_hi #undefined_method
是否包含B或扩展A,say_hi将不会被定义。
有没有办法完成这样的事情?
答案 0 :(得分:26)
如果您是module A
的作者并且经常需要此作品,您可以像这样重新创作A:
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"
答案 1 :(得分:9)
我认为没有任何简单的方法可以做到。
所以这是一个复杂的方式:
module B
class << self
A.singleton_methods.each do |m|
define_method m, A.method(m).to_proc
end
end
end
你可以把它放在这样的辅助方法中:
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
答案 2 :(得分:3)
gem install include_complete
module A
def self.say_hi
puts "hi"
end
end
module B
include_complete A
end
B.say_hi #=> "hi"
答案 3 :(得分:2)
乔纳森, 我不确定你是否还在想这个,但有两种不同的方法可以在ruby中使用模块。 A.)您在代码中直接使用自包含形式的Base :: Tree.entity(params)模块,或B.)您将模块用作mixins或helper方法。
一个。将允许您将模块用作命名空间模式。这对于可能存在方法名称冲突的大型项目非常有用
module Base
module Tree
def self.entity(params={},&block)
# some great code goes here
end
end
end
现在您可以使用它在代码中创建某种树结构,而不必为每次调用Base :: Tree.entity实例化一个新类。
另一种进行命名空间的方法是逐类进行。
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
现在我们的班级有可能重叠。我们想要知道,当我们创建一个Actor类,它是正确的类时,所以这就是命名空间派上用场的地方。
Session::Live::Actor.new(params) do |res|...
Session::Actor.new(params)
B中。混合宏 这些是你的朋友。只要您认为必须在代码中多次执行某些操作,请使用它们。
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
现在,只要您需要将xmlstring格式化为哈希值,或者在以后的任何代码中删除尾随空格,只需将其混合即可。
module Fun
class Ruby
include Friendly::Formatter
attr_accessor :string
def initialize(params={})
end
end
end
现在您可以格式化类中的字符串。
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)
希望这是一个很好的解释。上面提到的几点是扩展类的方法的很好的例子,但是对于模块,困难的部分是何时使用 self 关键字。它指的是ruby对象层次结构中对象的范围。因此,如果您想将模块用作混合,并且不想声明任何单例,请不要使用self关键字,但是如果您想在对象中保持状态,只需使用类并混合 - 在你想要的模块中。
答案 4 :(得分:0)
我不喜欢所有人使用self.included
。我有更简单的解决方案:
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