Подтвердить что ты не робот

Расширение модуля 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 не будет определен.

Есть ли способ сделать что-то вроде этого?

4b9b3361

Ответ 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