Работа с анонимными модулями в Ruby - PullRequest
5 голосов
/ 21 мая 2010

Предположим, я делаю модуль следующим образом:

m = Module.new do
  class C
  end
end

Три вопроса:

  • Кроме ссылки на m, есть ли способ, которым я могудоступ C и другие вещи внутри m?

  • Могу ли я дать имя анонимному модулю после того, как я его создал (как если бы я набрал) модуль... ")?

  • Как мне удалить анонимный модуль, когда я закончу с ним, чтобы его константы больше не присутствовали?

Ответы [ 3 ]

7 голосов
/ 21 мая 2010

Три ответа:

  • Да, используя ObjectSpace. Этот код заставляет c ссылаться на ваш класс C без ссылки m:

    c = nil  
    ObjectSpace.each_object { |obj|  
      c = obj if (Class === obj and obj.name =~ /::C$/)  
    }
    

    Конечно, это зависит от того, что в программе нет других классов с именем C, но вы поняли.

  • Да, вроде. Если вы просто назначите ее константе, например, M = m, m.name вернет "M" вместо nil, и ссылки, подобные M::C, будут работать. На самом деле, когда я делаю это и набираю M::C в irb, я получаю #<Module:0x9ed509c>::C, но, возможно, это ошибка.

  • Я думаю, что его следует собирать, если на него нет ссылок, т. Е. Когда нет экземпляров или подтипов m или C, а m имеет другое значение или выходит за рамки , Если вы присвоили ее константе, как указано выше, вам также потребуется изменить ее на другое значение (хотя изменение констант обычно не рекомендуется).
1 голос
/ 04 апреля 2013

Определить именованный модуль

Один из способов справиться с этим - определить собственный тип модуля, который можно инициализировать именем.

class NamedModule < Module
  attr_accessor :name

  def initialize(name, &block)
    super(&block)
    self.name = name
  end

  def to_s
    [self.class.name, name, object_id].join(':')
  end
end

Тогда вы можете сделать это:

piracy = NamedModule.new("Piracy") do
  def berate
    puts "Yer a #{adjectives.sample} #{nouns.sample}!"
  end

  private

  def adjectives
    %w[yella-bellied landlubbing]
  end

  def nouns
    %w[scallywag bilge-drinker]
  end
end

Sailor = Class.new
Sailor.send(:include, piracy)
Sailor.new.berate #=> "Yer a yella-bellied scallywag!"

Определение to_s дает хороший вывод в ancestors:

Sailor.ancestors 
#=> [Sailor, NamedModule:Piracy:70169997844420, Object, Kernel, BasicObject]

Обновление - используйте Именованный камень

После того, как мы с коллегой поэкспериментировали с этим, он написал небольшую реализацию гема. Проверьте Именованный драгоценный камень - Rubygems и Github .

0 голосов
/ 19 января 2012

Я попробовал второй ответ wdebeaum в Ruby 1.9.3-p0, и он не сработал. M::C возвращается NameError: uninitialized constant M::C и M.constants возвращает []

Так что вам следует попробовать предложенный подход здесь

m = Module.new do
  class self::C
  end
end

И вы можете использовать m::C как обычно.

...