Помогите рубиновому нубу понять наследование классов - PullRequest
1 голос
/ 27 января 2011

Я пытаюсь выучить ruby, создав базового бота из Campfire, с которым можно связываться на работе. Я получил довольно далеко (это работает!) И многому научился (это работает!), Но теперь я пытаюсь сделать его немного более сложным, разделив действия, которые нужно выполнить, на их собственные классы, чтобы они может быть легче написать / исправить, когда сломан. Если вы заинтересованы в том, чтобы увидеть весь (возможно, дрянной) код, он все на GitHub . Но ради этого вопроса я немного сужу рамки.

В идеале я хотел бы иметь возможность легко создавать плагины, называть их так же, как имя класса, и помещать их в каталог "actions" в корне проекта, где они будут созданы во время выполнения. Я хочу, чтобы сами плагины были написаны как можно проще, поэтому я хочу, чтобы все они наследовали некоторые базовые методы и свойства класса action.

Вот action.rb, как оно существует в настоящее время:

module CampfireBot
  class Action
    @handlers = {}

    def initialize(room)
      @room = room
    end


    class << self
      attr_reader :handlers
      attr_reader :room

      def hear(pattern, &action)
        Action.handlers[pattern] = action
      end
    end
  end
end

Где @room - объект комнаты, а @handlers - хэш шаблонов и блоков. Я вроде не понимаю, почему я должен сделать этот вызов class << self, но это единственный способ, с помощью которого я могу заставить дочерние классы плагинов увидеть этот hear метод.

Затем я пытаюсь создать простой плагин, например, так называемый Foo.rb:

class Foo < CampfireBot::Action

    hear /foo/i do
        @room.speak "bar"
    end
end

Затем мои плагины создаются в bot.rb следующим образом:

def load_handlers(room)
  actions = Dir.entries("#{BOT_ROOT}/actions").delete_if {|action| /^\./.match(action)}
  action_classes = []
  # load the source
  actions.each do |action|
    load "#{BOT_ROOT}/actions/#{action}"
    action_classes.push(action.chomp(".rb"))
  end

  # and instantiate
  action_classes.each do |action_class|
    Kernel.const_get(action_class).new(room)
  end

  @handlers =  Action.handlers

end

Блоки затем вызываются внутри room.rb, когда шаблон соответствует следующему:

handlers.each do |pattern, action|
  if pattern.match(msg)
    action.call($~)
  end
end

Если я выполню puts @room внутри инициализации Action, я вижу объект комнаты, распечатанный на консоли. И если я сделаю puts "foo" внутри Foo.rb hear метода, я увижу foo, распечатанный на консоли (так что сопоставление с образцом работает). Но я не могу прочитать этот @room объект из родительского класса (он выглядит как нулевой объект). Очевидно, я что-то упускаю из-за того, как это должно работать.

Кроме того, если я сделаю что-нибудь, чтобы сделать плагин немного чище (для больших функций) и переписать его так:

class Foo < CampfireBot::Action

    hear /foo/i do
        say_bar
    end

    def say_bar
        @room.speak "bar"
    end
end

Я получаю NoMethodError: undefined method 'say_bar' for Foo:Class.

1 Ответ

1 голос
/ 28 января 2011

Определение hear может быть извлечено из блока class << self и изменено на:

 def self.hear(pattern, &action)
   Action.handlers[pattern] = action
 end

, чтобы получить точно такой же результат. Это также сразу объясняет проблему. hear Является ли метод класса. say_bar это метод экземпляра. Вы не можете вызвать метод экземпляра из метода класса, потому что просто не существует доступного экземпляра класса.

Чтобы понять бит class << self, вам придется самостоятельно читать и экспериментировать: я не буду пытаться улучшить то, что уже было сказано. Я только скажу, что в блоке class << self .. end, self относится к методу класса eigenclass или класса CampfireBot::Action Это экземпляр класса Class, который содержит определение класса CampfireBot :: Action.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...