state pattern with rspec
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
# -*- coding: utf-8 -*- | |
require 'tk' | |
require 'observer' | |
class AwesomeThread | |
include Observable | |
def initialize | |
Thread.start do | |
loop do | |
sleep 1 | |
action | |
end | |
end | |
end | |
def action | |
changed | |
notify_observers | |
end | |
end | |
class TextWindow | |
def initialize | |
@text = TkText.new().pack | |
end | |
def write(text) | |
@text.insert('end', text + "\n") | |
end | |
end | |
class TkControllButton | |
def initialize(start=nil, stop=nil) | |
@frame = TkFrame.new.pack | |
TkButton.new(@frame, 'text' => '□', 'command' => stop).pack('side' => 'left') | |
TkButton.new(@frame, 'text'=> '||>', 'command' => start).pack | |
end | |
end | |
class Alice | |
def initialize | |
start = lambda { self.start } | |
stop = lambda { self.stop } | |
@button = TkControllButton.new(start, stop) | |
@bob = Offline.new() | |
change_state = Proc.new { |next_state| | |
@bob = next_state | |
} | |
@bob.prepare(BobManager.new, change_state) | |
end | |
def start | |
p @bob = @bob.start | |
end | |
def stop | |
p @bob = @bob.stop | |
end | |
end | |
module COMMAND | |
EXECUTE = 1 | |
ABORT = 2 | |
PAUSE = 3 | |
end | |
module STATE | |
ONLINE = 1 | |
OFFLINE = 2 | |
SUSPEND = 3 | |
ERROR = 4 | |
end | |
class BobManager | |
def boot | |
p "boot now" | |
end | |
def issue(command) | |
p "Issue #{command}" | |
end | |
def waitfor(state) | |
p "Wait for #{state}" | |
return true | |
end | |
def getStatus() | |
return true | |
end | |
end | |
class BobState | |
include COMMAND | |
include STATE | |
def prepare(manager, next_state=nil, thread=nil, text=nil) | |
@@thread = AwesomeThread.new() | |
@@text = TextWindow.new() | |
@@next_state = next_state | |
@@manager = manager | |
end | |
def start | |
raise "to be implemented" | |
end | |
def stop | |
raise "to be implemented" | |
end | |
end | |
class Transition < BobState | |
def initialize(command, wait_status, next_state, pre_state) | |
@wait_status = wait_status | |
@pre_state = pre_state | |
@next_state = next_state | |
# action | |
if !command.nil? | |
@@manager.issue(command) | |
@@thread.add_observer(self) | |
@time_count = 0 | |
else | |
@@next_state.call(@next_state) | |
end | |
end | |
def start | |
self | |
end | |
def stop | |
self | |
end | |
def update | |
@@text.write("状態遷移中") | |
if (@@manager.waitfor(@wait_status)) | |
@@thread.delete_observers | |
@@next_state.call(@next_state) | |
end | |
@time_count += 1 | |
if @time_count > 30 | |
@@next_state.call(@pre_state) | |
end | |
end | |
end | |
class Offline < BobState | |
def start | |
@@manager.boot | |
return Transition.new(COMMAND::EXECUTE, STATE::ONLINE, Online.new, self) | |
end | |
def stop | |
self | |
end | |
end | |
class Online < BobState | |
def initialize() | |
@@thread.add_observer(self) | |
end | |
def start | |
Transition.new(COMMAND::PAUSE, STATE::SUSPEND, Suspend.new, self) | |
end | |
def stop | |
@@thread.delete_observers | |
Transition.new(COMMAND::ABORT, STATE::OFFLINE, Offline.new, self) | |
end | |
def update | |
if (@@manager.getStatus != STATE::EXECUTE) | |
@@thread.delete_observers | |
Transition.new(nil, nil, Offline.new, self) | |
end | |
end | |
end | |
class Suspend < BobState | |
def start | |
Transition.new(COMMAND::EXECUTE, STATE::ONLINE, Online.new, self) | |
end | |
def stop | |
Transition.new(COMMAND::ABORT, STATE::OFFLINE, Offline.new, self) | |
end | |
end | |
=begin | |
Alice.new | |
Tk.mainloop | |
=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
# -*- coding: utf-8 -*- | |
require 'rspec' | |
require 'tk/rubytk_state_machine2' | |
describe "BobState" do | |
context "Offline" do | |
it "start" do | |
manager = double('manager') | |
manager.should_receive(:boot).with(no_args()) | |
manager.should_receive(:issue).with(COMMAND::EXECUTE) | |
manager.should_receive(:waitfor).with(STATE::ONLINE).and_return(true) | |
@state = Offline.new() | |
next_state = Proc.new { |next_state| | |
@state = next_state | |
} | |
@state.prepare(manager, next_state) | |
@state = @state.start() | |
@state.class.should == Transition | |
@state.update | |
@state.class.should == Online | |
end | |
it "stop" do | |
@state = Offline.new() | |
manager = double('manager') | |
@state.prepare(manager) | |
@state = @state.stop | |
@state.class.should == Offline | |
end | |
end | |
context "Online" do | |
it "start" do | |
manager = double('manager') | |
manager.should_receive(:issue).with(COMMAND::PAUSE) | |
manager.should_receive(:waitfor).with(STATE::SUSPEND).and_return(true) | |
@state = Online.new() | |
next_state = Proc.new { |next_state| | |
@state = next_state | |
} | |
@state.prepare(manager, next_state) | |
@state = @state.start() | |
@state.class.should == Transition | |
@state.update | |
@state.class.should == Suspend | |
end | |
it "stop" do | |
manager = double('manager') | |
manager.should_receive(:issue).with(COMMAND::ABORT) | |
manager.should_receive(:waitfor).with(STATE::OFFLINE).and_return(true) | |
@state = Online.new() | |
next_state = Proc.new { |next_state| | |
@state = next_state | |
} | |
@state.prepare(manager, next_state) | |
@state = @state.stop() | |
@state.class.should == Transition | |
@state.update | |
@state.class.should == Offline | |
end | |
it "error occured" do | |
manager = double('manager') | |
manager.should_receive(:issue).with(COMMAND::ABORT) | |
manager.should_receive(:waitfor).with(STATE::OFFLINE).and_return(true) | |
manager.stub(:getStatus).and_return(STATE::ERROR) | |
@state = Online.new() | |
next_state = Proc.new { |next_state| | |
@state = next_state | |
} | |
@state.prepare(manager, next_state) | |
@state = @state.stop() | |
@state.class.should == Transition | |
@state.update | |
@state.class.should == Offline | |
end | |
end | |
context "Suspend" do | |
it "start" do | |
manager = double('manager') | |
manager.should_receive(:issue).with(COMMAND::EXECUTE) | |
manager.should_receive(:waitfor).with(STATE::ONLINE).and_return(true) | |
@state = Suspend.new() | |
next_state = Proc.new { |next_state| | |
@state = next_state | |
} | |
@state.prepare(manager, next_state) | |
@state = @state.start() | |
@state.class.should == Transition | |
@state.update | |
@state.class.should == Online | |
end | |
it "stop" do | |
manager = double('manager') | |
manager.should_receive(:issue).with(COMMAND::ABORT) | |
manager.should_receive(:waitfor).with(STATE::OFFLINE).and_return(true) | |
@state = Suspend.new() | |
next_state = Proc.new { |next_state| | |
@state = next_state | |
} | |
@state.prepare(manager, next_state) | |
@state = @state.stop() | |
@state.class.should == Transition | |
@state.update | |
@state.class.should == Offline | |
end | |
end | |
context "Transisition" do | |
it "do nothing" do | |
manager = double('manager') | |
manager.should_receive(:issue).with(COMMAND::EXECUTE) | |
manager.stub(:waitfor).and_return(false) | |
@state = Suspend.new() | |
next_state = Proc.new { |next_state| | |
@state = next_state | |
} | |
@state.prepare(manager, next_state) | |
@state = @state.start | |
@state.class.should == Transition | |
@state = @state.stop | |
@state.class.should == Transition | |
end | |
it "time out" do | |
manager = double('manager') | |
manager.should_receive(:issue).with(COMMAND::EXECUTE) | |
manager.stub(:waitfor).and_return(false) | |
@state = Suspend.new() | |
next_state = Proc.new { |next_state| | |
@state = next_state | |
} | |
@state.prepare(manager, next_state) | |
@state = @state.start() | |
@state.class.should == Transition | |
31.times do | |
@state.update | |
end | |
@state.class.should == Suspend | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment