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
52 changes: 42 additions & 10 deletions Code/TKStateMachine.m
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,38 @@ - (void)addEvents:(NSArray *)arrayOfEvents

- (TKEvent *)eventNamed:(NSString *)name
{
for (TKEvent *event in self.mutableEvents) {
if ([event.name isEqualToString:name]) return event;
NSArray<TKEvent *> *events = [self eventsNamed:name];

return [events firstObject];
}

- (NSArray<TKEvent *> *)eventsNamed:(NSString *)name
{
NSSet<TKEvent *> *events = [self.mutableEvents filteredSetUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(TKEvent *evaluatedEvent, NSDictionary<NSString *,id> * _Nullable bindings) {
return [evaluatedEvent.name isEqualToString:name];
}]];

return [events allObjects];
}

- (TKEvent *)eventForCurrentStateFromEvents:(NSArray<TKEvent *> *)events
{
NSArray<TKEvent *> *eventsForCurrentState = [events filteredArrayUsingPredicate:[NSPredicate predicateWithBlock:^BOOL(TKEvent *evaluatedEvent, NSDictionary<NSString *,id> * _Nullable bindings) {
return evaluatedEvent.sourceStates == nil || [evaluatedEvent.sourceStates containsObject:self.currentState];
}]];
if ([eventsForCurrentState count] > 1) [NSException raise:NSInvalidArgumentException format:@"More than one event with name '%@' from state '%@'", [[events firstObject] name], self.currentState];

return [eventsForCurrentState firstObject];
}

- (NSArray<TKState *> *)sourceStatesForEvents:(NSArray<TKEvent *> *)events
{
NSMutableArray<TKState *> *states = [NSMutableArray array];
for (TKEvent *event in events) {
[states addObjectsFromArray:event.sourceStates];
}
return nil;

return states;
}

- (void)activate
Expand All @@ -196,22 +224,26 @@ - (void)activate
- (BOOL)canFireEvent:(id)eventOrEventName
{
if (! [eventOrEventName isKindOfClass:[TKEvent class]] && ![eventOrEventName isKindOfClass:[NSString class]]) [NSException raise:NSInvalidArgumentException format:@"Expected a `TKEvent` object or `NSString` object specifying the name of an event, instead got a `%@` (%@)", [eventOrEventName class], eventOrEventName];
TKEvent *event = [eventOrEventName isKindOfClass:[TKEvent class]] ? eventOrEventName : [self eventNamed:eventOrEventName];
if (! event) [NSException raise:NSInvalidArgumentException format:@"Cannot find an Event named '%@'", eventOrEventName];
return event.sourceStates == nil || [event.sourceStates containsObject:self.currentState];
NSArray<TKEvent *> *events = [eventOrEventName isKindOfClass:[TKEvent class]] ? @[eventOrEventName] : [self eventsNamed:eventOrEventName];
if ([events count] == 0) [NSException raise:NSInvalidArgumentException format:@"Cannot find an Event named '%@'", eventOrEventName];
return nil != [self eventForCurrentStateFromEvents:events];
}

- (BOOL)fireEvent:(id)eventOrEventName userInfo:(NSDictionary *)userInfo error:(NSError *__autoreleasing *)error
{
[self.lock lock];
if (! self.isActive) [self activate];
if (! [eventOrEventName isKindOfClass:[TKEvent class]] && ![eventOrEventName isKindOfClass:[NSString class]]) [NSException raise:NSInvalidArgumentException format:@"Expected a `TKEvent` object or `NSString` object specifying the name of an event, instead got a `%@` (%@)", [eventOrEventName class], eventOrEventName];
TKEvent *event = [eventOrEventName isKindOfClass:[TKEvent class]] ? eventOrEventName : [self eventNamed:eventOrEventName];
if (! event) [NSException raise:NSInvalidArgumentException format:@"Cannot find an Event named '%@'", eventOrEventName];
NSString *eventName = [eventOrEventName isKindOfClass:[TKEvent class]] ? [eventOrEventName name] : eventOrEventName;
NSArray<TKEvent *> *events = [eventOrEventName isKindOfClass:[TKEvent class]] ? @[eventOrEventName] : [self eventsNamed:eventOrEventName];

if ([events count] == 0) [NSException raise:NSInvalidArgumentException format:@"Cannot find an Event named '%@'", eventOrEventName];

TKEvent *event = [self eventForCurrentStateFromEvents:events];

// Check that this transition is permitted
if (event.sourceStates != nil && ![event.sourceStates containsObject:self.currentState]) {
NSString *failureReason = [NSString stringWithFormat:@"An attempt was made to fire the '%@' event while in the '%@' state, but the event can only be fired from the following states: %@", event.name, self.currentState.name, [[event.sourceStates valueForKey:@"name"] componentsJoinedByString:@", "]];
if (! event) {
NSString *failureReason = [NSString stringWithFormat:@"An attempt was made to fire the '%@' event while in the '%@' state, but the event can only be fired from the following states: %@", eventName, self.currentState.name, [[[self sourceStatesForEvents:events] valueForKey:@"name"] componentsJoinedByString:@", "]];
NSDictionary *userInfo = @{ NSLocalizedDescriptionKey: @"The event cannot be fired from the current state.", NSLocalizedFailureReasonErrorKey: failureReason };
if (error) *error = [NSError errorWithDomain:TKErrorDomain code:TKInvalidTransitionError userInfo:userInfo];
[self.lock unlock];
Expand Down
17 changes: 17 additions & 0 deletions Specs/TKStateMachineSpec.m
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,23 @@ - (void)startTryingToPickUpCollegeGirls {}
TKEvent *fetchedEvent = [stateMachine eventNamed:@"Start Dating"];
[[fetchedEvent should] equal:event];
});

it(@"can contains equel name events", ^{
TKState *problemsState = [TKState stateWithName:@"Problems"];
[stateMachine addState:problemsState];

TKEvent *duplicateEvent = [TKEvent eventWithName:@"Start Dating" transitioningFromStates:@[datingState] toState:problemsState];
[stateMachine addEvent:duplicateEvent];

stateMachine.initialState = datingState;
[stateMachine activate];

[[@([stateMachine canFireEvent:@"Start Dating"]) should] beYes];

[stateMachine fireEvent:@"Start Dating" userInfo:nil error:nil];

[[stateMachine.currentState should] equal:problemsState];
});
});
});

Expand Down