From f901a2e07acaa2b99e5abb219cc37859bf4bb77d Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 29 Jan 2016 22:15:49 +1000 Subject: [PATCH 1/7] Adding the ability to add an event using notation script. --- Code/TKStateMachine.h | 17 +++++++++++++ Code/TKStateMachine.m | 29 ++++++++++++++++++++++ Specs/TKStateMachineSpec.m | 49 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/Code/TKStateMachine.h b/Code/TKStateMachine.h index 06969d4..371127e 100644 --- a/Code/TKStateMachine.h +++ b/Code/TKStateMachine.h @@ -152,6 +152,18 @@ */ - (void)addEvents:(NSArray *)arrayOfEvents; +/** + Adds an event to the receiver using notation (FromState1,FromState2->EventName->ToState). + + The state names referenced by the notation must be registered with the receiver. + + @param notation The event notation to be parsed and added. + @raises TKStateMachineIsImmutableException Raised if an attempt is made to modify the state machine after it has been activated. + @raises TKStateMachineMalformedEventNotationException Raised if the notation is not properly formed + @raises NSInternalInconsistencyException Raised if the given notation references a `TKState` name that has not been registered with the receiver. + */ +- (void)addEventWithNotation:(NSString *)notation; + /** Retrieves the event with the given name from the receiver. @@ -252,6 +264,11 @@ extern NSString *const TKStateMachineDidChangeStateTransitionUserInfoKey; */ extern NSString *const TKStateMachineIsImmutableException; +/** + An exception raised when adding an event using notations with a malformed notation string. + */ +extern NSString *const TKStateMachineMalformedEventNotationException; + /** Error Codes */ diff --git a/Code/TKStateMachine.m b/Code/TKStateMachine.m index c90a621..8a25644 100644 --- a/Code/TKStateMachine.m +++ b/Code/TKStateMachine.m @@ -44,6 +44,7 @@ @interface TKState () NSString *const TKStateMachineDidChangeStateTransitionUserInfoKey = @"transition"; NSString *const TKStateMachineIsImmutableException = @"TKStateMachineIsImmutableException"; +NSString *const TKStateMachineMalformedEventNotationException = @"TKStateMachineMalformedEventNotationException"; #define TKRaiseIfActive() \ if ([self isActive]) [NSException raise:TKStateMachineIsImmutableException format:@"Unable to modify state machine: The state machine has already been activated."]; @@ -172,6 +173,34 @@ - (void)addEvents:(NSArray *)arrayOfEvents } } +- (void)addEventWithNotation:(NSString *)notation +{ + TKRaiseIfActive(); + NSArray *splits = [notation componentsSeparatedByString:@"->"]; + if (splits.count != 3) { + [NSException raise:TKStateMachineMalformedEventNotationException format:@"The notation provided does not conform to (FromState1,FromState2->EventName->ToState)."]; + } + NSString *fromStatesString = [splits firstObject]; + NSArray *fromStatesNames = [fromStatesString componentsSeparatedByString:@","]; + NSString *eventName = splits[1]; + NSString *toStateName = [splits lastObject]; + + NSMutableArray *fromStates = [[NSMutableArray alloc] initWithCapacity:fromStatesNames.count]; + for (NSString *fromStateName in fromStatesNames) { + TKState *fromState = [self stateNamed:fromStateName]; + if (!fromState) { + [NSException raise:NSInvalidArgumentException format:@"Cannot find a State named '%@'", fromStateName]; + } + [fromStates addObject:fromState]; + } + TKState *toState = [self stateNamed:toStateName]; + if (toState == nil) { + [NSException raise:NSInvalidArgumentException format:@"Cannot find a State named '%@'", toStateName]; + } + TKEvent *event = [TKEvent eventWithName:eventName transitioningFromStates:fromStates toState:toState]; + [self addEvent:event]; +} + - (TKEvent *)eventNamed:(NSString *)name { for (TKEvent *event in self.mutableEvents) { diff --git a/Specs/TKStateMachineSpec.m b/Specs/TKStateMachineSpec.m index ffce220..db81bb6 100644 --- a/Specs/TKStateMachineSpec.m +++ b/Specs/TKStateMachineSpec.m @@ -141,6 +141,55 @@ - (void)startTryingToPickUpCollegeGirls {} }); }); +context(@"when state is initilized and an event is added via annotation", ^{ + __block TKStateMachine *notationMachine = nil; + __block TKState *singleState = nil; + __block TKState *datingState = nil; + __block TKState *marriedState = nil; + + beforeEach(^{ + notationMachine = [TKStateMachine new]; + singleState = [TKState stateWithName:@"Single"]; + datingState = [TKState stateWithName:@"Dating"]; + marriedState = [TKState stateWithName:@"Married"]; + [notationMachine addStates:@[ singleState, datingState, marriedState ]]; + }); + + it(@"adds an event using notation", ^{ + [notationMachine addEventWithNotation:@"Dating,Married->BreakUp->Single"]; + TKEvent *breakUpEvent = [notationMachine eventNamed:@"BreakUp"]; + [[breakUpEvent.sourceStates should] contain:marriedState]; + [[breakUpEvent.sourceStates should] contain:datingState]; + [[breakUpEvent.sourceStates should] haveCountOf:2]; + [[breakUpEvent.destinationState should] equal:singleState]; + }); + + it(@"raise an exception if notation is incorrect", ^{ + [[theBlock(^{ + [notationMachine addEventWithNotation:@"Dating>BreakUp>Single"]; + }) should] raiseWithName:TKStateMachineMalformedEventNotationException reason:@"The notation provided does not conform to (FromState1,FromState2->EventName->ToState)."]; + }); + + it(@"raise an exception if a from state is not found", ^{ + [[theBlock(^{ + [notationMachine addEventWithNotation:@"Widowed->FindGirlfriend->Dating"]; + }) should] raiseWithName:NSInvalidArgumentException reason:@"Cannot find a State named 'Widowed'"]; + }); + + it(@"raise an exception if the to state is not found", ^{ + [[theBlock(^{ + [notationMachine addEventWithNotation:@"Dating->OneNightStand->BigTrouble"]; + }) should] raiseWithName:NSInvalidArgumentException reason:@"Cannot find a State named 'BigTrouble'"]; + }); + + it(@"raise an exception if already activated", ^{ + [[theBlock(^{ + [notationMachine activate]; + [notationMachine addEventWithNotation:@"Dating->Propose->Married"]; + }) should] raiseWithName:TKStateMachineIsImmutableException reason:@"Unable to modify state machine: The state machine has already been activated."]; + }); +}); + context(@"when a state machine is copied", ^{ __block TKState *firstState; __block TKState *secondState; From 5d118b90524ade32472db6589ec8d0ffcf074a37 Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 29 Jan 2016 22:19:29 +1000 Subject: [PATCH 2/7] Adding README for adding an event using notation. --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 299f983..311dc89 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,8 @@ inboxStateMachine.initialState = unread; TKEvent *viewMessage = [TKEvent eventWithName:@"View Message" transitioningFromStates:@[ unread ] toState:read]; TKEvent *deleteMessage = [TKEvent eventWithName:@"Delete Message" transitioningFromStates:@[ read, unread ] toState:deleted]; -TKEvent *markAsUnread = [TKEvent eventWithName:@"Mark as Unread" transitioningFromStates:@[ read, deleted ] toState:unread]; +// Or using notation +TKEvent *markAsUnread = [inboxStateMachine addEventWithNotation:@"Read,Deleted->Mark as Unread->Unread"]; [inboxStateMachine addEvents:@[ viewMessage, deleteMessage, markAsUnread ]]; From 730eb00b730b2acce540a9e6cfdb49977a858bb4 Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 29 Jan 2016 22:21:34 +1000 Subject: [PATCH 3/7] Adding README for adding an event using notation . --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 311dc89..ae0c40e 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ inboxStateMachine.initialState = unread; TKEvent *viewMessage = [TKEvent eventWithName:@"View Message" transitioningFromStates:@[ unread ] toState:read]; TKEvent *deleteMessage = [TKEvent eventWithName:@"Delete Message" transitioningFromStates:@[ read, unread ] toState:deleted]; +TKEvent *markAsUnread = [TKEvent eventWithName:@"Mark as Unread" transitioningFromStates:@[ read, deleted ] toState:unread]; // Or using notation TKEvent *markAsUnread = [inboxStateMachine addEventWithNotation:@"Read,Deleted->Mark as Unread->Unread"]; From 0e7f6ba0e9e26fcdddf114a026093256b9e6187b Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 29 Jan 2016 22:23:02 +1000 Subject: [PATCH 4/7] Adding README for adding an event using notation . --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae0c40e..ad0d40f 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ TKEvent *viewMessage = [TKEvent eventWithName:@"View Message" transitioningFromS TKEvent *deleteMessage = [TKEvent eventWithName:@"Delete Message" transitioningFromStates:@[ read, unread ] toState:deleted]; TKEvent *markAsUnread = [TKEvent eventWithName:@"Mark as Unread" transitioningFromStates:@[ read, deleted ] toState:unread]; // Or using notation -TKEvent *markAsUnread = [inboxStateMachine addEventWithNotation:@"Read,Deleted->Mark as Unread->Unread"]; +[inboxStateMachine addEventWithNotation:@"Read,Deleted->Mark as Unread->Unread"]; [inboxStateMachine addEvents:@[ viewMessage, deleteMessage, markAsUnread ]]; From 80f495199197f0ec740c299f4cc9ddedfd403aa6 Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 29 Jan 2016 22:25:34 +1000 Subject: [PATCH 5/7] Adding README for adding an event using notation . --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ad0d40f..8d7459b 100644 --- a/README.md +++ b/README.md @@ -82,11 +82,13 @@ inboxStateMachine.initialState = unread; TKEvent *viewMessage = [TKEvent eventWithName:@"View Message" transitioningFromStates:@[ unread ] toState:read]; TKEvent *deleteMessage = [TKEvent eventWithName:@"Delete Message" transitioningFromStates:@[ read, unread ] toState:deleted]; TKEvent *markAsUnread = [TKEvent eventWithName:@"Mark as Unread" transitioningFromStates:@[ read, deleted ] toState:unread]; -// Or using notation -[inboxStateMachine addEventWithNotation:@"Read,Deleted->Mark as Unread->Unread"]; [inboxStateMachine addEvents:@[ viewMessage, deleteMessage, markAsUnread ]]; +// Or add events using notation +[inboxStateMachine addEventWithNotation:@"Deleted->UnDelete->Read"]; + + // Activate the state machine [inboxStateMachine activate]; From 0c6337f1d0f2d7a758ee065ce5dd839d441c9d59 Mon Sep 17 00:00:00 2001 From: Hisham Date: Fri, 29 Jan 2016 22:29:00 +1000 Subject: [PATCH 6/7] Adding README for adding an event using notation . --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 8d7459b..54281b3 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,6 @@ TKEvent *markAsUnread = [TKEvent eventWithName:@"Mark as Unread" transitioningFr // Or add events using notation [inboxStateMachine addEventWithNotation:@"Deleted->UnDelete->Read"]; - // Activate the state machine [inboxStateMachine activate]; From ad65c12405f22fc33463cc9ce270a3256f3c1b53 Mon Sep 17 00:00:00 2001 From: Hisham Date: Sat, 30 Jan 2016 06:16:34 +1000 Subject: [PATCH 7/7] Adding README for adding an event using notation . Change to kick off travis ci build. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 54281b3..95d8141 100644 --- a/README.md +++ b/README.md @@ -85,7 +85,7 @@ TKEvent *markAsUnread = [TKEvent eventWithName:@"Mark as Unread" transitioningFr [inboxStateMachine addEvents:@[ viewMessage, deleteMessage, markAsUnread ]]; -// Or add events using notation +// Or add events using notations [inboxStateMachine addEventWithNotation:@"Deleted->UnDelete->Read"]; // Activate the state machine