Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
vendor
bin
composer.lock
.idea
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you put it in your own .git/info/exclude file?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can... generally don't b/c some projects do check this in. 👍

20 changes: 20 additions & 0 deletions spec/SM/StateMachine/StateMachineSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -204,4 +204,24 @@ function it_returns_possible_transitions($object, $callbackFactory, CallbackInte

$this->getPossibleTransitions()->shouldReturn(array('create', 'confirm'));
}

function it_can_accept_specific_from_to_transition_mappings($object, $dispatcher, $callbackFactory, CallbackInterface $guard)
{
$transition = $this->config['transitions']['create'];

$this->config['transitions']['create'] = array(
$transition
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like you only test that an array of 1 transition works the same as before.
Could you add a test for the case where a transition name contains multiple transitions defined on different from states? So that we're sure that we call the right transition, depending on the current state.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 great catch, I absolutely will.

);

$object->getState()->shouldBeCalled()->willReturn('checkout');
$object->setState(Argument::any())->shouldNotBeCalled();

$dispatcher->dispatch(SMEvents::TEST_TRANSITION, Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled();

$callbackFactory->get($this->config['callbacks']['guard']['guard-confirm'])->shouldBeCalled()->willReturn($guard);

$guard->__invoke(Argument::type('SM\\Event\\TransitionEvent'))->shouldBeCalled()->willReturn(true);

$this->can('create')->shouldReturn(true);
}
}
59 changes: 43 additions & 16 deletions src/SM/StateMachine/StateMachine.php
Original file line number Diff line number Diff line change
Expand Up @@ -83,23 +83,16 @@ public function __construct(
/**
* {@inheritDoc}
*/
public function can($transition)
public function can($transitionName)
{
if (!isset($this->config['transitions'][$transition])) {
throw new SMException(sprintf(
'Transition "%s" does not exist on object "%s" with graph "%s"',
$transition,
get_class($this->object),
$this->config['graph']
));
}
$transition = $this->getTransition($transitionName);

if (!in_array($this->getState(), $this->config['transitions'][$transition]['from'])) {
if (!$transition) {
return false;
}

$can = true;
$event = new TransitionEvent($transition, $this->getState(), $this->config['transitions'][$transition], $this);
$event = new TransitionEvent($transitionName, $this->getState(), $transition, $this);
if (null !== $this->dispatcher) {
$this->dispatcher->dispatch(SMEvents::TEST_TRANSITION, $event);

Expand All @@ -112,23 +105,25 @@ public function can($transition)
/**
* {@inheritDoc}
*/
public function apply($transition, $soft = false)
public function apply($transitionName, $soft = false)
{
if (!$this->can($transition)) {
if (!$this->can($transitionName)) {
if ($soft) {
return false;
}

throw new SMException(sprintf(
'Transition "%s" cannot be applied on state "%s" of object "%s" with graph "%s"',
$transition,
$transitionName,
$this->getState(),
get_class($this->object),
$this->config['graph']
));
}

$event = new TransitionEvent($transition, $this->getState(), $this->config['transitions'][$transition], $this);
$transition = $this->getTransition($transitionName);

$event = new TransitionEvent($transitionName, $this->getState(), $transition, $this);

if (null !== $this->dispatcher) {
$this->dispatcher->dispatch(SMEvents::PRE_TRANSITION, $event);
Expand All @@ -140,7 +135,7 @@ public function apply($transition, $soft = false)

$this->callCallbacks($event, 'before');

$this->setState($this->config['transitions'][$transition]['to']);
$this->setState($transition['to']);

$this->callCallbacks($event, 'after');

Expand Down Expand Up @@ -232,4 +227,36 @@ protected function callCallbacks(TransitionEvent $event, $position)
}
return $result;
}

/**
* @param $transitionName
*
* @return bool|mixed
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

null|array maybe?
It's more consistent to return null than false when there is no matching transition.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 agreed; it's more semantic this way. Thanks again. I'll include this in an update later today/tonight.

* @throws SMException
*/
private function getTransition($transitionName)
{
if (!isset($this->config['transitions'][$transitionName])) {
throw new SMException(sprintf(
'Transition "%s" does not exist on object "%s" with graph "%s"',
$transitionName,
get_class($this->object),
$this->config['graph']
));
}

$transition = $this->config['transitions'][$transitionName];

if (array_key_exists('from', $transition)) {
$transition = array($transition);
}

$state = $this->getState();

$results = array_filter($transition, function($value) use ($state) {
return in_array($state, $value['from']);
});

return count($results) > 0 ? array_shift($results) : false;
}
}
10 changes: 5 additions & 5 deletions src/SM/StateMachine/StateMachineInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,25 @@ interface StateMachineInterface
/**
* Can the transition be applied on the underlying object
*
* @param string $transition
* @param string $transitionName
*
* @return bool
*
* @throws SMException If transition doesn't exist
*/
public function can($transition);
public function can($transitionName);

/**
* Applies the transition on the underlying object
*
* @param string $transition Transition to apply
* @param bool $soft Soft means do nothing if transition can't be applied (no exception thrown)
* @param string $transitionName Transition to apply
* @param bool $soft Soft means do nothing if transition can't be applied (no exception thrown)
*
* @return bool If the transition has been applied or not (in case of soft apply or rejected pre transition event)
*
* @throws SMException If transition can't be applied or doesn't exist
*/
public function apply($transition, $soft = false);
public function apply($transitionName, $soft = false);

/**
* Returns the current state
Expand Down