Skip to content

Commit

Permalink
Updated message handler to add by label rather than by state.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lucas Hansen committed Jul 27, 2012
1 parent 0ad4f5e commit 33ceac9
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 40 deletions.
53 changes: 41 additions & 12 deletions lib/cartan/message-handler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,19 @@ module Cartan
# A helper for performing stateful message routing.
class MessageHandler

Handler = Struct.new(:pattern, :hook)

# Initializes a MessageHandler instance.
#
# @param [Cartan::Node] node The node that this message handler is attached to.
# @param [Callable] node A callable which returns the current state.
# @yield [] (see Cartan::MessageHandler::handle) A block of code to be
# evaluated in the context of the handler.
def initialize(node, state = proc{ "none" }, &block)
def initialize(node, state = proc{ none }, &block)
@node = node
@state = state

handle(&block) if block_given?
bind(&block) if block_given?
end

# The message handler hook. Decides which handler(s) to use based on the
Expand All @@ -23,24 +25,37 @@ def initialize(node, state = proc{ "none" }, &block)
def receive(uuid, label, message)
current_state = @state.call

states[current_state].call(uuid, label, message) if states.has_key? current_state
states[all].call(uuid, label, message) if states.has_key? all
find_matches(label, states[current_state]).each do |h|
h.hook.call(uuid, label, message)
end

find_matches(label, states[all]).each do |h|
h.hook.call(uuid, label, message)
end
end

# Evaluates the block in the context of the handler. This is where messages
# are handled
def handle(&block)
def bind(&block)
instance_eval(&block) if block_given?
end

# Defines a new message handler, dependent on the current state.
def state(which_state, &block)
unless which_state.is_a?(Symbol) or which_state == all
raise Cartan::Exception::InvalidState,
"The state provided was not a symbol or all!"
end
# Defines a new message handler, dependent on the given states.
#
# @param [String Regexp] pattern The message label to handle
# @param [Symbol Array] which_states The states to handle
# @yield The block to be executed on any handled message.
def handle(pattern, which_states = all, &block)
handler = Handler.new(Regexp.new(pattern), block)
[*which_states].each do |s|
unless valid_state?(s)
raise Cartan::Exception::InvalidState,
"The state '#{s}'' was not a symbol or all!"
end

states[which_state] = block
states[s] ||= []
states[s] << handler
end
end

def states
Expand All @@ -51,6 +66,20 @@ def all
"all"
end

def none
"none"
end

private

def find_matches(label, handlers)
[*handlers].select{ |h| label =~ h.pattern }
end

def valid_state?(state)
state.is_a?(Symbol) or state == all
end

end

end
79 changes: 51 additions & 28 deletions spec/message-handler_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,27 +11,28 @@
end

it "should be able to set hooks on initialization" do
@handler = Cartan::MessageHandler.new(self, proc{ :default}) do
state(:default) {}
@handler = Cartan::MessageHandler.new(self, proc{ :default }) do
handle("", :default) {}
end

@handler.states.has_key?(:default).should be_true
end

it "should be able to set hooks on handle" do
it "should be able to set hooks on bind" do
@handler = Cartan::MessageHandler.new(self, proc{ :default})
@handler.handle do
state(:default) {}
@handler.bind do
handle("", :default) {}
end

@handler.states.has_key?(:default).should be_true
end

it "should call its hooks" do
self.message_received = false
node = OpenStruct.new
node.message_received = false

@handler = Cartan::MessageHandler.new(self, proc{ :default}) do
state(:default) do |uuid, label, message|
@handler = Cartan::MessageHandler.new(node, proc{ :default }) do
handle("hello", :default) do |uuid, label, message|
uuid.should == "2"
label.should == "hello"
message.should == "sup"
Expand All @@ -41,14 +42,15 @@
end

@handler.receive("2", "hello", "sup")
self.message_received.should be_true
node.message_received.should be_true
end

it "should call the all hook" do
self.message_received = false
node = OpenStruct.new
node.message_received = false

@handler = Cartan::MessageHandler.new(self, proc{ :default}) do
state(all) do |uuid, label, message|
@handler = Cartan::MessageHandler.new(node, proc{ :default}) do
handle(/car.*n/) do |uuid, label, message|
uuid.should == "bleh"
label.should == "cartcartan"
message.should == { :meh => :meh }
Expand All @@ -58,29 +60,43 @@
end

@handler.receive("bleh", "cartcartan", { :meh => :meh })
self.message_received.should be_true
node.message_received.should be_true
end

it "should reject invalid states" do

lambda {
@handler = Cartan::MessageHandler.new(self) do
handle //, "asdf" do |uuid, label, message|

end
end
}.should raise_error

end

it "should pass stress test #1" do
state = :blimey
self.message_received = false
self.all_message = false

@handler = Cartan::MessageHandler.new(self, proc{ state }) do
node = OpenStruct.new
node.message_received = false
node.all_message = false

state(all) do |uuid, label, message|
@handler = Cartan::MessageHandler.new(node, proc{ state }) do

handle(/.*/) do |uuid, label, message|
@node.all_message = true
end

state(:blimey) do |uuid, label, message|
handle(/.*/, [:blimey, :arg]) do |uuid, label, message|
uuid.should == "blimey"
label.should == "blimey"
message.should == "blimey"

@node.message_received = true
end

state(:dark) do |uuid, label, message|
handle(/.*/, [:dark, :night]) do |uuid, label, message|
uuid.should == "dark"
label.should == "dark"
message.should == "dark"
Expand All @@ -90,20 +106,27 @@
end

@handler.receive("blimey", "blimey", "blimey")
self.message_received.should be_true
self.all_message.should be_true

self.message_received = false
self.all_message = false
node.message_received.should be_true
node.all_message.should be_true

node.message_received = false
node.all_message = false
state = :arg
@handler.receive("blimey","blimey","blimey")
node.message_received.should be_true
node.all_message.should be_true

node.message_received = false
node.all_message = false
state = :unknown
@handler.receive("","","")
self.message_received.should be_false
self.all_message.should be_true
node.message_received.should be_false
node.all_message.should be_true

self.message_received = false
node.message_received = false
state = :dark
@handler.receive("dark","dark","dark")
self.message_received.should be_true
node.message_received.should be_true
end

end

0 comments on commit 33ceac9

Please sign in to comment.