Skip to content

Commit

Permalink
Merge pull request ManageIQ#13560 from chrisarcand/try-to-fix-job
Browse files Browse the repository at this point in the history
Normalize Job::StateMachine
  • Loading branch information
gtanzillo authored Jan 23, 2017
2 parents 656bbc8 + 58958f1 commit ad96f07
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 59 deletions.
60 changes: 30 additions & 30 deletions app/models/job/state_machine.rb
Original file line number Diff line number Diff line change
@@ -1,39 +1,39 @@
class Job
module StateMachine
def transitions
@transitions ||= load_transitions
end
module Job::StateMachine
extend ActiveSupport::Concern

def state=(next_state)
# '*' refers to any state; if next_state is '*', remain in current state.
super unless next_state.nil? || next_state == '*'
end
def transitions
@transitions ||= load_transitions
end

def state=(next_state)
# '*' refers to any state; if next_state is '*', remain in current state.
super unless next_state.nil? || next_state == '*'
end

# test whether the transition is allowed; if yes, transit to next state
def transit_state(signal)
permitted_transitions = transitions[signal.to_sym]
unless permitted_transitions.nil?
next_state = permitted_transitions[state]
# test whether the transition is allowed; if yes, transit to next state
def transit_state(signal)
permitted_transitions = transitions[signal.to_sym]
unless permitted_transitions.nil?
next_state = permitted_transitions[state]

# if current state is not explicitly permitted, is any state (referred by '*') permitted?
next_state = permitted_transitions['*'] unless next_state
self.state = next_state
end
!!next_state
# if current state is not explicitly permitted, is any state (referred by '*') permitted?
next_state = permitted_transitions['*'] unless next_state
self.state = next_state
end
!!next_state
end

def signal_abort(*args)
signal(:abort, *args)
end
def signal_abort(*args)
signal(:abort, *args)
end

def signal(signal, *args)
signal = :abort_job if signal == :abort
if transit_state(signal)
save
send(signal, *args) if respond_to?(signal)
else
raise _("%{signal} is not permitted at state %{state}") % {:signal => signal, :state => state}
end
def signal(signal, *args)
signal = :abort_job if signal == :abort
if transit_state(signal)
save
send(signal, *args) if respond_to?(signal)
else
raise _("%{signal} is not permitted at state %{state}") % {:signal => signal, :state => state}
end
end
end
52 changes: 23 additions & 29 deletions spec/models/job/state_machine_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
describe Job::StateMachine do
before do
module TestStateMachine
describe Job, "::StateMachine" do
subject(:job) do
# Job is expected to be subclassed by something
# that implements load_transitions
Class.new(described_class) {
def load_transitions
self.state ||= 'initialize'
{
Expand All @@ -12,51 +14,43 @@ def load_transitions
:error => {'*' => '*'}
}
end
end

@obj = Class.new(Job) do
include TestStateMachine
end.new
end

after do
Object.send(:remove_const, :TestStateMachine)
}.new
end

it "should transition from one state to another by a signal" do
@obj.signal(:initializing)
expect(@obj.state).to eq 'waiting'
job.signal(:initializing)
expect(job.state).to eq 'waiting'
end

it "should transition to another by a signal according to its current state" do
@obj.state = 'waiting'
@obj.signal(:start)
expect(@obj.state).to eq 'doing'
job.state = 'waiting'
job.signal(:start)
expect(job.state).to eq 'doing'

@obj.state = 'retrying'
@obj.signal(:start)
expect(@obj.state).to eq 'working'
job.state = 'retrying'
job.signal(:start)
expect(job.state).to eq 'working'
end

it "should transition to some selected state from any state" do
@obj.signal(:cancel)
expect(@obj.state).to eq 'canceling'
job.signal(:cancel)
expect(job.state).to eq 'canceling'
end

it "should leave the state unchanged for some selected signal" do
@obj.state = 'doing'
@obj.signal(:error)
expect(@obj.state).to eq 'doing'
job.state = 'doing'
job.signal(:error)
expect(job.state).to eq 'doing'
end

it "should raise an error if the transition is not allowed" do
@obj.state = 'working'
expect { @obj.signal(:initializing) }
job.state = 'working'
expect { job.signal(:initializing) }
.to raise_error(RuntimeError, /initializing is not permitted at state working/)
end

it "should raise an error if the signal is not defined" do
@obj.state = 'working'
expect { @obj.signal(:wrong) }.to raise_error(RuntimeError, /wrong is not permitted at state working/)
job.state = 'working'
expect { job.signal(:wrong) }.to raise_error(RuntimeError, /wrong is not permitted at state working/)
end
end

0 comments on commit ad96f07

Please sign in to comment.