Расширение модуля Ruby в другом модуле, включая методы модуля
Всякий раз, когда я пытаюсь расширить модуль 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 не будет определен.
Есть ли способ сделать что-то вроде этого?
Ответы
Ответ 1
Если вы являетесь автором 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"
Ответ 2
Я не думаю, что есть простой способ сделать это.
Итак, сложный способ:
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
Ответ 3
Используйте 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"
Ответ 4
Johnathan,
Я не уверен, что вам все еще интересно об этом, но есть два разных способа использования модулей в рубине. A.) вы используете модули в их собственной форме Base:: Tree.entity(params) непосредственно в вашем коде или B.) вы используете модули как mixins или вспомогательные методы.
а. Позволит использовать модули как шаблон пространства имен. Это полезно для крупных проектов, где есть вероятность конфликтов имен методов.
module Base
module Tree
def self.entity(params={},&block)
# some great code goes here
end
end
end
Теперь вы можете использовать это для создания какой-либо структуры дерева в своем коде без необходимости создавать экземпляр нового класса для каждого вызова базы:: 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)
В. Mix-Ins
Это ваши друзья. Используйте их, когда вы думаете, что вам придется делать что-то более одного раза в своем коде.
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. Поэтому, если вы хотите использовать модуль в качестве микширования и не хотите объявлять что-либо singleton, не используйте ключевое слово self, однако, если вы хотите сохранить состояние внутри объекта, просто используйте класс и mix- в нужные модули.
Ответ 5
Мне не нравятся все, кто использует 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