Расширение модуля Ruby в другом модуле, включая методы модуля - PullRequest
32 голосов
/ 12 января 2011

Всякий раз, когда я пытаюсь расширить модуль 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 определяться не будет.

Есть ли способ сделать что-то подобное?

Ответы [ 5 ]

26 голосов
/ 12 января 2011

Если вы являетесь автором 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" 
9 голосов
/ 12 января 2011

Я не думаю, что есть какой-то простой способ сделать это.

Итак, вот сложный способ:

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 голосов
/ 12 января 2011

Использование 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"
2 голосов
/ 14 сентября 2011

Johnathan, Я не уверен, если вы все еще задаетесь вопросом об этом, но есть два разных способа использования модулей в ruby. A.) вы используете модули в их автономной форме Base :: Tree.entity (params) непосредственно в вашем коде, или B.) вы используете модули в качестве миксинов или вспомогательных методов.

A. Позволит вам использовать модули в качестве шаблона пространства имен. Это хорошо для более крупных проектов, где есть вероятность конфликта имен методов

module Base
  module Tree
    def self.entity(params={},&block)
      # some great code goes here
    end
  end
end

Теперь вы можете использовать это для создания некоторой древовидной структуры в вашем коде, без необходимости создавать новый класс для каждого вызова Base :: Tree.entity.

Другой способ сделать Namespace-ing - это класс за классом.

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. 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

Теперь, когда вам нужно отформатировать строку xml в виде хэша или удалить конечные пробелы в любом будущем коде, просто смешайте его.

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, однако, если вы хотите сохранить состояние внутри объекта, просто используйте класс и mix- в модулях, которые вы хотите.

0 голосов
/ 14 октября 2016

Мне не нравится, когда все используют 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...