Skip to content

Instantly share code, notes, and snippets.

@tsu-nera
Created March 29, 2014 12:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tsu-nera/9853933 to your computer and use it in GitHub Desktop.
Save tsu-nera/9853933 to your computer and use it in GitHub Desktop.
state pattern with rspec
# -*- 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
# -*- 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