Я пытаюсь выучить 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
.