Skip to content

Commit

Permalink
plugin management with a custom responder
Browse files Browse the repository at this point in the history
This is going to take some refactoring to make it less hackish.
Potential ideas include making all plugins inherit directly from Plugin
to remove the need for special casing between the two types.

But as a first pass this works.
  • Loading branch information
dougcole committed Sep 5, 2011
1 parent f0ad3a8 commit 4bca15e
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 25 deletions.
2 changes: 2 additions & 0 deletions lib/moneypenny.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
$LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)

require 'moneypenny/moneypenny'
require 'moneypenny/plugin'
require 'moneypenny/plugin_manager'
require 'moneypenny/responder'
require 'moneypenny/listener'
require 'moneypenny/connections/campfire'
Expand Down
36 changes: 21 additions & 15 deletions lib/moneypenny/moneypenny.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ module Moneypenny
VERSION = (git_sha = `cd #{ROOT_PATH} && git rev-parse HEAD`.strip) == '' ? "v#{File.read(File.join(ROOT_PATH, 'VERSION')).strip}" : git_sha

class Moneypenny
attr_accessor :logger
attr_accessor :logger, :listeners, :responders

def initialize(config, logger)
@config = config
@logger = logger
self.responders = Responder.new
self.listeners = Listener.new
responders.load_all!
listeners.load_all!
end

def connection
Expand Down Expand Up @@ -38,27 +42,29 @@ def matching_message(message)
message.gsub( /\A(mp|moneypenny)(\,|)/i, '' ).strip
end

def respond_with(plugins, message)
response = nil
plugins.each do |responder|
response = responder.respond message
if response
say response
end
end
response
end

def hear(message)
logger.debug "Heard: #{message}"
if matched_message = matching_message(message)
responded = false
Responder.all.each do |responder|
response = responder.respond matched_message
if response
say response
responded = true
end
end
unless responded
if response = PluginManager.new(responders, listeners).respond( message )
say response
elsif !respond_with(responders.loaded_plugins, matched_message)
apologize
end
else
Listener.all.each do |listener|
if response = listener.respond( message )
say response
end
end
respond_with(listeners.loaded_plugins, message)
end
end

end
end
36 changes: 36 additions & 0 deletions lib/moneypenny/plugin_manager.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
module Moneypenny
class PluginManager
def initialize(responders, listeners)
@responders = responders
@listeners = listeners
end

def respond( message )
case message
when /unload plugin (.*)/
begin
@responders.unload_plugin!($1) &&
"Unloaded #{$1}"
rescue ArgumentError
@listeners.unload_plugin!($1)
"Unloaded #{$1}"
end
when /load plugin (.*)/
begin
@responders.load_plugin!($1) &&
"Loaded #{$1}"
rescue ArgumentError
@listeners.load_plugin!($1)
"Loaded #{$1}"
end
when /load all plugins/
@responders.load_all! &&
@listeners.load_all!
else
false
end
rescue ArgumentError
"Can't find that plugin"
end
end
end
19 changes: 9 additions & 10 deletions spec/moneypenny_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,48 +18,47 @@

describe "hear" do
it "ignores messages that don't start with moneypenny or mp" do
Moneypenny::Responder.expects(:all).never
Moneypenny::Listener.stubs(:all).returns([])
@bot.responders = mock('responders').expects(:loaded_plugins).never
@connection.expects(:say).never
@bot.hear('foo')
end

it "passes all messages to listeners" do
Moneypenny::Listener.expects(:all).returns([])
@bot.listeners.expects(:loaded_plugins).returns([])
@bot.hear('foo')
end

it "calls responders on all messages that match the bot's name" do
message = mock('message')
@bot.stubs(:matching_message).with(message).returns(true)
Moneypenny::Responder.expects(:all).returns([])
@bot.stubs(:matching_message).with(message).returns('true')
@bot.responders.expects(:loaded_plugins).returns([])
@bot.stubs(:apologize)
@bot.hear( message )
end

it "appologizes if the message matches but no responders respond to it" do
message = mock('message')
@bot.stubs(:matching_message).with(message).returns(true)
Moneypenny::Responder.stubs(:all).returns([])
@bot.stubs(:matching_message).with(message).returns('true')
@bot.expects(:apologize)
@bot.hear( message )
end

it 'says any message returned by a listener' do
@bot.stubs(:matching_message).returns(false)
listener = mock('listener', :respond => 'msg')
Moneypenny::Listener.expects(:all).returns( [listener] )
@bot.listeners.expects(:loaded_plugins).returns( [listener] )
@bot.expects(:say).with('msg')
@bot.hear('foo')
end

it 'says any message returned by a responder' do
@bot.stubs(:matching_message).returns('matching message')
responder = mock('responder', :respond => 'msg')
Moneypenny::Responder.stubs(:all).returns( [responder] )
Moneypenny::Listener.stubs(:all).returns( [] )
@bot.responders.stubs(:loaded_plugins).returns( [responder] )
@bot.listeners.stubs(:loaded_plugins).returns( [] )
@bot.expects(:say).with('msg')
@bot.hear('foo')
end
end

end
72 changes: 72 additions & 0 deletions spec/plugin_manager_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')

class TestResponder < Moneypenny::Responder
def self.respond(message)
end
end

class TestListener < Moneypenny::Listener
def self.respond(message)
end
end

describe Moneypenny::PluginManager do

before(:each) do
@responder = Moneypenny::Responder.new
@listener = Moneypenny::Listener.new
@plugin_manager = Moneypenny::PluginManager.new(@responder, @listener)
end

describe "respond" do
it "returns true for a message 'unload plugin (.*)'" do
@plugin_manager.respond("unload plugin bar").should be_true
end

it "it removes the requested responder plugin" do
@responder.load_plugin!(TestResponder.name)
@responder.loaded_plugins.should include(TestResponder)
@plugin_manager.respond("unload plugin TestResponder")
@responder.loaded_plugins.should_not include(TestResponder)
end

it "it removes the requested listener plugin" do
@listener.load_plugin!(TestListener.name)
@listener.loaded_plugins.should include(TestListener)
@plugin_manager.respond("unload plugin TestListener")
@listener.loaded_plugins.should_not include(TestListener)
end

it "returns true for a message 'load plugin (.*)'" do
@plugin_manager.respond("load plugin bar").should be_true
end

it "it loads the requested responder plugin" do
@responder.loaded_plugins.should_not include(TestResponder)
@plugin_manager.respond("load plugin TestResponder")
@responder.loaded_plugins.should include(TestResponder)
end

it "it loads the requested listener plugin" do
@listener.loaded_plugins.should_not include(TestListener)
@plugin_manager.respond("load plugin TestListener")
@listener.loaded_plugins.should include(TestListener)
end

it "returns true for a message 'load all plugins'" do
@plugin_manager.respond("load all plugins").should be_true
end

it "loads all plugins when asked to" do
@listener.loaded_plugins.should_not include(TestListener)
@responder.loaded_plugins.should_not include(TestResponder)
@plugin_manager.respond("load all plugins")
@responder.loaded_plugins.should include(TestResponder)
@listener.loaded_plugins.should include(TestListener)
end

it "returns false for any non-matching message" do
@plugin_manager.respond("piss off").should be_false
end
end
end

0 comments on commit 4bca15e

Please sign in to comment.