Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Sven Fuchs
committed
Mar 28, 2010
0 parents
commit f66f8d6
Showing
9 changed files
with
270 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
require 'active_support/inflector' | ||
require 'active_support/core_ext/module/delegation' | ||
|
||
class Command | ||
autoload :Base, 'command/base' | ||
autoload :Message, 'command/message' | ||
autoload :Poller, 'command/poller' | ||
|
||
class << self | ||
def queue(receiver, message) | ||
Message.if_unprocessed(message) do |message| | ||
message.commands.each { |type| new(type, message).dispatch } # we don't have a queue yet, so just dispatch | ||
end | ||
end | ||
end | ||
|
||
include Base | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
module Command::Base | ||
attr_reader :command, :message, :arguments | ||
delegate :receiver, :source, :to => :message | ||
|
||
def initialize(command, message) | ||
@command = command | ||
@message = message | ||
@arguments = message.arguments | ||
end | ||
|
||
def dispatch | ||
send(command) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
class Command::Message | ||
class << self | ||
def if_unprocessed(data) | ||
return if find_by_message_id(data[:message_id]) | ||
yield create(data.merge(:received_at => Time.now)) | ||
end | ||
|
||
def max_message_id # TODO how the fuck would i not jump through all of these hoops | ||
database = CouchPotato.database.instance_variable_get(:@database) | ||
spec = view_max_message_id | ||
query = CouchPotato::View::ViewQuery.new(database, spec.design_document, spec.view_name, spec.map_function, spec.reduce_function) | ||
result = query.query_view!(spec.view_parameters)['rows'].first | ||
result['value'] if result | ||
end | ||
end | ||
|
||
COMMAND_PATTERN = /(?:!|#)([\w]+)/ | ||
ARGUMENT_PATTERN = /[\S]+:[\S]+/ | ||
|
||
include SimplyStored::Couch | ||
|
||
property :message_id | ||
property :text | ||
property :sender | ||
property :receiver | ||
property :source | ||
property :received_at | ||
|
||
view :by_message_id, :key => :message_id | ||
view :view_max_message_id, :type => :custom, | ||
:map => "function(doc) { if(doc.ruby_class == 'Command::Message') { emit(null, doc['message_id']); } }", | ||
:reduce => "function(key, values, rereduce) { return Math.max.apply(Math, values); }" | ||
|
||
def initialize(data = {}) | ||
data.each { |key, value| self.send("#{key}=", value) } | ||
end | ||
|
||
def commands | ||
text.scan(COMMAND_PATTERN).flatten | ||
end | ||
|
||
def arguments | ||
text.scan(ARGUMENT_PATTERN) | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module Command::Poller | ||
autoload :Twitter, 'command/poller/twitter' | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# checks for new replies only once | ||
|
||
require 'twibot' | ||
|
||
class Command::Poller::Twitter < Twibot::Bot | ||
def initialize(type, login, password) | ||
super(Twibot::Config.default << { | ||
:login => login, | ||
:password => password, | ||
:process => Command::Message.max_message_id, | ||
:min_interval => 0, | ||
:max_interval => 0 | ||
}) | ||
|
||
add_handler(type, handler(login)) | ||
end | ||
|
||
def handler(receiver) | ||
Twibot::Handler.new(Command::Message::COMMAND_PATTERN) do |message, *args| | ||
Command.queue(receiver, | ||
:receiver => receiver, | ||
:message_id => message.id, | ||
:sender => message.user.screen_name, | ||
:text => message.text, | ||
:source => 'twitter' | ||
) | ||
end | ||
end | ||
|
||
def receive_messages | ||
super.tap { @abort = true } # we only poll once | ||
end | ||
|
||
def receive_replies | ||
super.tap { @abort = true } # we only poll once | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
require File.expand_path('../test_helper', __FILE__) | ||
|
||
class MessageTwitterTest < Test::Unit::TestCase | ||
test "max_message_id returns the latest message_id" do | ||
%w(12345 12346 12347 12348).each { |id| msg(id).save } | ||
assert_equal 12348, Command::Message.max_message_id | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
require File.expand_path('../test_helper', __FILE__) | ||
require 'stringio' | ||
|
||
class PollerTest < Test::Unit::TestCase | ||
def setup | ||
setup_stubs | ||
end | ||
|
||
def process!(from, message, id = '12345') | ||
status = twitter_status(from, message, id) | ||
bot = Command::Poller::Twitter.new(:reply, 'rugb_test', 'password') | ||
bot.handler('rugb_test').dispatch(status) | ||
end | ||
|
||
test "polls from twitter once and handles new replies by queueing commands" do | ||
Command::Message.stubs(:max_message_id).returns(12345) | ||
poller = Command::Poller::Twitter.new(:reply, 'rugb_test', 'password') | ||
|
||
replies = [twitter_status('svenfuchs', '@rugb_test !update')] | ||
poller.twitter.expects(:status).with(:replies, { :since_id => 12345 }).returns(replies) | ||
|
||
message = { :message_id => '12345', :receiver => 'rugb_test', :sender => 'svenfuchs', :text => '@rugb_test !update', :source => 'twitter' } | ||
Command.expects(:queue).with('rugb_test', message) | ||
log = capture_stdout { poller.run! } | ||
|
||
assert_match /imposing as @rugb_test/, log | ||
assert_match /Received 1 reply/, log | ||
end | ||
|
||
test 'updating w/ a me url and a github handle' do | ||
process!('svenfuchs', '!update json:http://tinyurl.com/yc7t8bv github:svenphoox') | ||
identity = Identity.find_by_handle('svenphoox') | ||
|
||
assert_equal 'svenphoox', identity.github['handle'] | ||
assert_equal 'Sven', identity.github['name'] | ||
end | ||
|
||
test 'updating an existing profile' do | ||
process!('svenfuchs', '!create', '12345') | ||
assert Identity.find_by_handle('svenfuchs') | ||
|
||
process!('svenfuchs', '!update json:http://tinyurl.com/yc7t8bv', '12346') | ||
identity = Identity.find_by_handle('svenfuchs') | ||
|
||
assert_equal 'svenfuchs', identity.twitter['handle'] | ||
assert_equal 'Sven Fuchs', identity.twitter['name'] | ||
assert_equal 'svenfuchs', identity.json['irc'] | ||
end | ||
|
||
test 'logs processed messages' do | ||
now = Time.now | ||
Time.stubs(:now).returns(now) | ||
|
||
process!('svenfuchs', '!update') | ||
Identity.find_by_handle('svenfuchs') | ||
message = Command::Message.find_by_message_id('12345') | ||
|
||
assert_equal 'rugb_test', message.receiver | ||
assert_equal 'svenfuchs', message.sender | ||
assert_equal '!update', message.text | ||
assert_equal '12345', message.message_id | ||
assert_equal now.to_s, Time.parse(message.received_at).to_s | ||
end | ||
|
||
test 'does not process an already processed message' do | ||
process!('svenfuchs', '!update') | ||
Identity.find_by_handle('svenfuchs') | ||
|
||
Command.expects(:new).never # TODO | ||
Identity.find_by_handle('svenfuchs') | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
module TestMethod | ||
def self.included(base) | ||
base.class_eval do | ||
def self.test(name, &block) | ||
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym | ||
defined = instance_method(test_name) rescue false | ||
raise "#{test_name} is already defined in #{self}" if defined | ||
if block_given? | ||
define_method(test_name, &block) | ||
else | ||
define_method(test_name) do | ||
flunk "No implementation provided for #{name}" | ||
end | ||
end | ||
end | ||
end | ||
end | ||
end | ||
|
||
class Module | ||
include TestMethod | ||
end | ||
|
||
class Test::Unit::TestCase | ||
include TestMethod | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
$: << File.expand_path('../..', __FILE__) | ||
$: << File.expand_path('../../lib', __FILE__) | ||
|
||
require 'test/unit' | ||
require 'rack/test' | ||
require 'mocha' | ||
require 'twibot' | ||
require File.expand_path('../test_declarative', __FILE__) | ||
|
||
require 'command' | ||
|
||
CouchPotato::Config.database_name = "http://localhost:5984/command" | ||
|
||
class Test::Unit::TestCase | ||
def teardown | ||
Command::Message.all.each { |message| message.delete } | ||
end | ||
|
||
def command(type, receiver, sender, text = '', source = 'twitter') | ||
Command.new(type, msg(12345, text, sender, receiver, source)) | ||
end | ||
|
||
def msg(id = 12345, text = 'text', sender = 'sender', receiver = 'receiver', source = 'twitter') | ||
Command::Message.create :message_id => id, | ||
:text => text, | ||
:sender => sender, | ||
:receiver => receiver, | ||
:source => source, | ||
:received_at => Time.now | ||
end | ||
|
||
def twitter_status(from, message, id = '12345') | ||
Twitter::Status.new(:id => id, :user => twitter_sender(from), :text => message, :created_at => Time.now) | ||
end | ||
|
||
def twitter_sender(name) | ||
Twitter::User.new(:screen_name => name) | ||
end | ||
|
||
def capture_stdout | ||
@stdout, $stdout = $stdout, (io = StringIO.new) | ||
yield | ||
$stdout = @stdout | ||
io.string | ||
end | ||
end |