Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Even more fluent syntax #369

Open
wants to merge 70 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
67979f9
#102 Submitting fix solution file, and DotGraph output.
HenningNT Dec 12, 2017
cadcc0e
Fix: JSonExample project moved into example directory.
Dec 7, 2017
2d49948
Update README.md
HenningNT Dec 12, 2017
e8e8ca3
Add Parameterized Guard Clauses to PermitIf
trgrote Feb 10, 2018
9a49132
Add PermitDynamicIf<T1[,T2]>
trgrote Feb 10, 2018
e639246
Change Guard Condition to Preserve No Arg Guard Description
trgrote Feb 12, 2018
f8ea45d
Add Missing PermitIf w/ Parameterized Guard Clauses
trgrote Feb 12, 2018
a95dcd8
Update Parameterized Guard Condition Unit Test
trgrote Feb 12, 2018
1b44766
Change Name of GetGuardConditionsMet to just GuardConditionsMet
trgrote Feb 12, 2018
6589502
Update Unit tests with new Accessor
trgrote Feb 12, 2018
0485701
Add IgnoreIf unit Tests
trgrote Feb 12, 2018
cb30f43
Update README.md
HenningNT Feb 13, 2018
7ea812e
Add unit tests that should work but don't for some dumb reason
trgrote Feb 14, 2018
dbbaa1b
Add more unit tests
trgrote Feb 14, 2018
9396b61
Remove Irrelevenet unit tests
trgrote Feb 28, 2018
119ec5a
Re-add publicly accessible guard checks
trgrote Feb 28, 2018
43a88ce
Add Region Description to converters
trgrote Feb 28, 2018
7782183
Add Missing IgnoreIfs
trgrote Feb 28, 2018
5f67c34
Add missing InternalTransitionIf
trgrote Feb 28, 2018
f393f52
Update TryFindLocalHandler to evaluate guard conditions only once
trgrote Mar 9, 2018
ccd01e2
Re-add arguments to UnmetGuardConditions that was removed during merge
trgrote Mar 13, 2018
73ca733
Add misssing } to unit test
trgrote Mar 13, 2018
9c740da
#155 Removing old backup folder. The sln file is causing problems for…
Mar 20, 2018
db46ce3
Update appveyor.yml
HenningNT Mar 23, 2018
a14f1e3
Revert "#102 Submitting fix solution file, and DotGraph output."
Mar 23, 2018
a4ba731
Merge branch 'dev' of https://github.com/dotnet-state-machine/statele…
HenningNT Jun 4, 2018
cca2a19
Merge branch 'dev' of https://github.com/dotnet-state-machine/statele…
Feb 3, 2020
3d4d8bb
Merge pull request #6 from dotnet-state-machine/dev
HenningNT Apr 4, 2020
3f323cc
Merge pull request #7 from dotnet-state-machine/master
HenningNT Apr 4, 2020
bf090e5
Added new classes. Added new tests.
HenningNT Apr 5, 2020
3531bad
Added test to fire basic transition.
HenningNT Apr 5, 2020
8142018
Added .If(), not working yet.
HenningNT Apr 5, 2020
ef7f754
Added Self() transition.
HenningTorsteinsenBouvet Apr 6, 2020
4739a4c
Added Internal Transition.
HenningTorsteinsenBouvet Apr 6, 2020
83c2e5d
Added more Assert to Fire_Transition_To_EndsUpInAnotherState
HenningTorsteinsenBouvet Apr 6, 2020
9be9889
Added If(), and tests.
HenningTorsteinsenBouvet Apr 6, 2020
f548302
Added Do()
HenningTorsteinsenBouvet Apr 6, 2020
7c41e78
Finished Do()
HenningTorsteinsenBouvet Apr 7, 2020
fdc25e8
Removed AddTriggerBehaviour in StateConfiguration class.
HenningTorsteinsenBouvet Apr 7, 2020
ed26191
Tidy up...
HenningTorsteinsenBouvet Apr 7, 2020
9fd586f
Added test Fire_Transition_To_DoesAsyncAction
HenningTorsteinsenBouvet Apr 11, 2020
e359847
Added test Fire_Transition_To_If_TrueReceivedTriggerParameters.
HenningTorsteinsenBouvet Apr 11, 2020
1528860
Added one generic parameter to If()
HenningTorsteinsenBouvet Apr 11, 2020
c18963a
Added two and three parameters to If()
HenningTorsteinsenBouvet Apr 11, 2020
cbe707d
Reworked Do(), so the Action takes a Transition object.
HenningTorsteinsenBouvet Apr 11, 2020
ad8d495
Decided on only one generic parameter for If() and Do().
HenningTorsteinsenBouvet Apr 13, 2020
3826118
Finished implemented Dynamic()
HenningTorsteinsenBouvet Apr 14, 2020
5058879
Added Dynamic<TArg>().
HenningTorsteinsenBouvet Apr 14, 2020
c62c770
Changed To() so that it allows chaining a new Transition without If()…
HenningTorsteinsenBouvet Apr 14, 2020
a3f8505
Added XML documentation to new methods.
HenningTorsteinsenBouvet Apr 14, 2020
32a1731
Using var instead of explicit type.
HenningTorsteinsenBouvet Apr 14, 2020
2e86cff
Updated examples to new syntax.
HenningTorsteinsenBouvet Apr 14, 2020
6f32ecb
Fix spelling error in comment.
HenningTorsteinsenBouvet Apr 14, 2020
a96ac27
Some code simplifications
HenningTorsteinsenBouvet Apr 15, 2020
ce15ee4
Merge branch 'dev' of https://github.com/dotnet-state-machine/statele…
HenningTorsteinsenBouvet Feb 6, 2021
71145ba
Merge branch 'dev' into EvenMoreFluentSyntax
HenningTorsteinsenBouvet Feb 6, 2021
83b3b4b
Pick up some changes from upstream dev.
HenningTorsteinsenBouvet Feb 6, 2021
ea62b44
Removed Permit from StateConfiguration.
HenningTorsteinsenBouvet Feb 6, 2021
4707cd7
Removed InternalTransition(TTrigger trigger, Action<Transition> entry…
HenningTorsteinsenBouvet Feb 6, 2021
87aa8df
Removed a bunch of InternalTransitionIf.
HenningTorsteinsenBouvet Feb 6, 2021
d193875
Removed PermitIf(TTrigger trigger, TState destinationState, Func<bool…
HenningTorsteinsenBouvet Feb 6, 2021
d3586fe
Removed rest of PermitIf
HenningTorsteinsenBouvet Feb 6, 2021
ba9fcc9
Removed permitReentry.
HenningTorsteinsenBouvet Feb 7, 2021
e1e5129
Fixed async tests
HenningTorsteinsenBouvet Feb 8, 2021
8ecf32b
Better synchronization of _firing, perhaps?
HenningTorsteinsenBouvet Feb 8, 2021
8eae6eb
Replaced zero lenght array creation by Array.Empty
HenningTorsteinsenBouvet Feb 8, 2021
b8a5d89
Removed and change according to suggestions from IDE.
HenningTorsteinsenBouvet Feb 8, 2021
156911b
Removed one permitDynamic
HenningTorsteinsenBouvet Feb 8, 2021
834e97c
Removed another PaermitDynamic
HenningTorsteinsenBouvet Feb 8, 2021
3c35bf5
Removed permitdynamicif without conditional.
HenningTorsteinsenBouvet Feb 8, 2021
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
88 changes: 88 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Rules in this file were initially inferred by Visual Studio IntelliCode from the C:\Users\henning.torsteinsen\source\repos\stateless\HenningNTStateless codebase based on best match to current usage at 14.04.2020
# You can modify the rules from these initially generated values to suit your own policies
# You can learn more about editorconfig here: https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
[*.cs]

#Core editorconfig formatting - indentation

#use soft tabs (spaces) for indentation
indent_style = space

#Formatting - new line options

#place else statements on a new line
csharp_new_line_before_else = true
#require braces to be on a new line for control_blocks, methods, properties, lambdas, types, and accessors (also known as "Allman" style)
csharp_new_line_before_open_brace = control_blocks, methods, properties, lambdas, types, accessors

#Formatting - organize using options

#sort System.* using directives alphabetically, and place them before other usings
dotnet_sort_system_directives_first = true

#Formatting - spacing options

#require NO space between a cast and the value
csharp_space_after_cast = false
#require a space before the colon for bases or interfaces in a type declaration
csharp_space_after_colon_in_inheritance_clause = true
#require a space after a keyword in a control flow statement such as a for loop
csharp_space_after_keywords_in_control_flow_statements = true
#require a space before the colon for bases or interfaces in a type declaration
csharp_space_before_colon_in_inheritance_clause = true
#remove space within empty argument list parentheses
csharp_space_between_method_call_empty_parameter_list_parentheses = false
#remove space between method call name and opening parenthesis
csharp_space_between_method_call_name_and_opening_parenthesis = false
#do not place space characters after the opening parenthesis and before the closing parenthesis of a method call
csharp_space_between_method_call_parameter_list_parentheses = false
#remove space within empty parameter list parentheses for a method declaration
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
#place a space character after the opening parenthesis and before the closing parenthesis of a method declaration parameter list.
csharp_space_between_method_declaration_parameter_list_parentheses = false

#Formatting - wrapping options

#leave code block on single line
csharp_preserve_single_line_blocks = true
#leave statements and member declarations on the same line
csharp_preserve_single_line_statements = true

#Style - expression bodied member options

#prefer block bodies for accessors
csharp_style_expression_bodied_accessors = false:suggestion
#prefer block bodies for constructors
csharp_style_expression_bodied_constructors = false:suggestion
#prefer block bodies for methods
csharp_style_expression_bodied_methods = false:suggestion
#prefer block bodies for properties
csharp_style_expression_bodied_properties = false:suggestion

#Style - expression level options

#prefer out variables to be declared inline in the argument list of a method call when possible
csharp_style_inlined_variable_declaration = true:suggestion
#prefer the language keyword for member access expressions, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_member_access = true:suggestion

#Style - implicit and explicit types

#prefer var is used to declare variables with built-in system types such as int
csharp_style_var_for_built_in_types = true:suggestion
#prefer var when the type is already mentioned on the right-hand side of a declaration expression
csharp_style_var_when_type_is_apparent = true:suggestion

#Style - language keyword and framework type options

#prefer the language keyword for local variables, method parameters, and class members, instead of the type name, for types that have a keyword to represent them
dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion

#Style - qualification options

#prefer fields not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_field = false:suggestion
#prefer methods not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_method = false:suggestion
#prefer properties not to be prefaced with this. or Me. in Visual Basic
dotnet_style_qualification_for_property = false:suggestion
5 changes: 5 additions & 0 deletions Stateless.sln
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TelephoneCallExample", "exa
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonExample", "example\JsonExample\JsonExample.csproj", "{809A7873-DD78-4D5D-A432-9718C929BECA}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B500BE05-347F-4BD8-8FB4-A24C08CFA610}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down
12 changes: 6 additions & 6 deletions example/BugTrackerExample/Bug.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,21 @@ public Bug(string title)

// Configure the Open state
_machine.Configure(State.Open)
.Permit(Trigger.Assign, State.Assigned);
.Transition(Trigger.Assign).To(State.Assigned);

// Configure the Assigned state
_machine.Configure(State.Assigned)
.SubstateOf(State.Open)
.OnExit(OnDeassigned)
.OnEntryFrom(_assignTrigger, OnAssigned) // This is where the TriggerWithParameters is used. Note that the TriggerWithParameters object is used, not something from the enum
.PermitReentry(Trigger.Assign)
.Permit(Trigger.Close, State.Closed)
.Permit(Trigger.Defer, State.Deferred)
.OnExit(OnDeassigned);
.Transition(Trigger.Assign).Self()
.Transition(Trigger.Close).To(State.Closed)
.Transition(Trigger.Defer).To(State.Deferred);

// Configure the Deferred state
_machine.Configure(State.Deferred)
.OnEntry(() => _assignee = null)
.Permit(Trigger.Assign, State.Assigned);
.Transition(Trigger.Assign).To(State.Assigned);
}

public void Close()
Expand Down
14 changes: 7 additions & 7 deletions example/JsonExample/Member.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ public Member(string name)
[JsonConstructor]
private Member(string state, string name)
{
var memberState = (MembershipState) Enum.Parse(typeof(MembershipState), state);
var memberState = (MembershipState)Enum.Parse(typeof(MembershipState), state);
_stateMachine = new StateMachine<MembershipState, MemberTriggers>(memberState);
Name = name;

Expand All @@ -43,15 +43,15 @@ private Member(string state, string name)
private void ConfigureStateMachine()
{
_stateMachine.Configure(MembershipState.Active)
.Permit(MemberTriggers.Suspend, MembershipState.Inactive)
.Permit(MemberTriggers.Terminate, MembershipState.Terminated);
.Transition(MemberTriggers.Suspend).To(MembershipState.Inactive)
.Transition(MemberTriggers.Terminate).To(MembershipState.Terminated);

_stateMachine.Configure(MembershipState.Inactive)
.Permit(MemberTriggers.Reactivate, MembershipState.Active)
.Permit(MemberTriggers.Terminate, MembershipState.Terminated);
.Transition(MemberTriggers.Reactivate).To(MembershipState.Active)
.Transition(MemberTriggers.Terminate).To(MembershipState.Terminated);

_stateMachine.Configure(MembershipState.Terminated)
.Permit(MemberTriggers.Reactivate, MembershipState.Active);
.Transition(MemberTriggers.Reactivate).To(MembershipState.Active);
}

public void Terminate()
Expand Down Expand Up @@ -86,6 +86,6 @@ public bool Equals(Member anotherMember)
}




}
6 changes: 3 additions & 3 deletions example/OnOffExample/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,16 @@ static void Main(string[] args)
var onOffSwitch = new StateMachine<string, char>(off);

// Configure state machine with the Configure method, supplying the state to be configured as a parameter
onOffSwitch.Configure(off).Permit(space, on);
onOffSwitch.Configure(on).Permit(space, off);
onOffSwitch.Configure(off).Transition(space).To(on);
onOffSwitch.Configure(on).Transition(space).To(off);

Console.WriteLine("Press <space> to toggle the switch. Any other key will exit the program.");

while (true)
{
Console.WriteLine("Switch is in state: " + onOffSwitch.State);
var pressed = Console.ReadKey(true).KeyChar;

// Check if user wants to exit
if (pressed != space) break;

Expand Down
18 changes: 9 additions & 9 deletions example/TelephoneCallExample/PhoneCall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,25 +48,25 @@ public PhoneCall(string caller)
_setCalleeTrigger = _machine.SetTriggerParameters<string>(Trigger.CallDialed);

_machine.Configure(State.OffHook)
.Permit(Trigger.CallDialed, State.Ringing);
.Transition(Trigger.CallDialed).To(State.Ringing);

_machine.Configure(State.Ringing)
.OnEntryFrom(_setCalleeTrigger, callee => OnDialed(callee), "Caller number to call")
.Permit(Trigger.CallConnected, State.Connected);
.Transition(Trigger.CallConnected).To(State.Connected);

_machine.Configure(State.Connected)
.OnEntry(t => StartCallTimer())
.OnExit(t => StopCallTimer())
.InternalTransition(Trigger.MuteMicrophone, t => OnMute())
.InternalTransition(Trigger.UnmuteMicrophone, t => OnUnmute())
.InternalTransition<int>(_setVolumeTrigger, (volume, t) => OnSetVolume(volume))
.Permit(Trigger.LeftMessage, State.OffHook)
.Permit(Trigger.PlacedOnHold, State.OnHold);
.Transition(Trigger.MuteMicrophone).Internal().Do(() => OnMute())
.Transition(Trigger.UnmuteMicrophone).Internal().Do( () => OnUnmute())
.Transition(Trigger.SetVolume).Internal().Do<int>( (volume, t) => OnSetVolume(volume))
.Transition(Trigger.LeftMessage).To(State.OffHook)
.Transition(Trigger.PlacedOnHold).To(State.OnHold);

_machine.Configure(State.OnHold)
.SubstateOf(State.Connected)
.Permit(Trigger.TakenOffHold, State.Connected)
.Permit(Trigger.PhoneHurledAgainstWall, State.PhoneDestroyed);
.Transition(Trigger.TakenOffHold).To(State.Connected)
.Transition(Trigger.PhoneHurledAgainstWall).To(State.PhoneDestroyed);

_machine.OnTransitioned(t => Console.WriteLine($"OnTransitioned: {t.Source} -> {t.Destination} via {t.Trigger}({string.Join(", ", t.Parameters)})"));
}
Expand Down
93 changes: 93 additions & 0 deletions src/Stateless/DestinationConfiguration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using System;

namespace Stateless
{
partial class StateMachine<TState, TTrigger>
{
/// <summary>
/// This class containd the required trigger information for a transition.
HenningNT marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public class DestinationConfiguration
{
private readonly TransitionConfiguration _transitionConfiguration;
private readonly TriggerBehaviour _triggerBehaviour;
private readonly StateRepresentation _representation;

internal DestinationConfiguration(TransitionConfiguration transitionConfiguration, TriggerBehaviour triggerBehaviour, StateRepresentation representation)
{
_transitionConfiguration = transitionConfiguration;
_triggerBehaviour = triggerBehaviour;
_representation = representation;
}

/// <summary>
/// Adds a guard function to the trigger. This guard function will determine if the transition will occur or not.
/// </summary>
/// <param name="guard">This method is run when the state machine fires the trigger.</param>
/// <param name="description">Optional description of the guard</param>
public DestinationConfiguration If(Func<object[], bool> guard, string description = null)
{
_triggerBehaviour.SetGuard(new TransitionGuard(guard, description));
return this;
}

/// <summary>
/// Adds a guard function to the trigger. This guard function will determine if the transition will occur or not.
/// </summary>
/// <typeparam name="TArg">The parameter to the guard function </typeparam>
/// <param name="guard">This method is run when the state machine fires the trigger.</param>
/// <param name="description">Optional description of the guard</param>
public DestinationConfiguration If<TArg>(Func<TArg, bool> guard, string description = null)
{
_triggerBehaviour.SetGuard(new TransitionGuard(TransitionGuard.ToPackedGuard(guard), description));
return this;
}

/// <summary>
/// Creates a new transition. Use To(), Self(), Internal() or Dynamic() to set up the destination.
/// </summary>
/// <param name="trigger">The event trigger that will trigger this transition.</param>
public TransitionConfiguration Transition(TTrigger trigger)
{
return new TransitionConfiguration(_transitionConfiguration.StateConfiguration, _representation, trigger);
}

/// <summary>
/// Adds an action to a transition. The action will be executed before the Exit action(s) (if any) are executed.
/// </summary>
/// <param name="someAction">The action run when the trigger event is handled.</param>
public StateConfiguration Do(Action someAction)
{
if (someAction == null) throw new ArgumentNullException(nameof(someAction));

_triggerBehaviour.AddAction((t, args) => someAction());
return _transitionConfiguration.StateConfiguration;
}

/// <summary>
/// Adds an action to a transition. The action will be executed before the Exit action(s) (if any) are executed.
/// </summary>
/// <param name="someAction">The action run when the trigger event is handled.</param>
public StateConfiguration Do(Action<Transition> someAction)
{
if (someAction == null) throw new ArgumentNullException(nameof(someAction));

_triggerBehaviour.AddAction(someAction);
return _transitionConfiguration.StateConfiguration;
}

/// <summary>
/// Adds an action to a transition. The action will be executed before the Exit action(s) (if any) are executed.
/// </summary>
/// <typeparam name="TArg">The paramter used by the action.</typeparam>
/// <param name="someAction">The action run when the trigger event is handled.</param>
public StateConfiguration Do<TArg>(Action<TArg, Transition> someAction)
{
if (someAction == null) throw new ArgumentNullException(nameof(someAction));

_triggerBehaviour.AddAction((t, args) => someAction(ParameterConversion.Unpack<TArg>(args, 0), t));
return _transitionConfiguration.StateConfiguration;
}
}
}
}
2 changes: 2 additions & 0 deletions src/Stateless/StateConfiguration.Async.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public partial class StateMachine<TState, TTrigger>
{
public partial class StateConfiguration
{


/// <summary>
/// Add an internal transition to the state machine. An internal action does not cause the Exit and Entry actions to be triggered, and does not change the state of the state machine
/// </summary>
Expand Down
9 changes: 9 additions & 0 deletions src/Stateless/StateConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1710,6 +1710,15 @@ public StateConfiguration InitialTransition(TState targetState)
_representation.SetInitialTransition(targetState);
return this;
}

/// <summary>
/// Creates a new transition. Use To(), Self(), Internal() or Dynamic() to set up the destination.
/// </summary>
/// <param name="trigger">The event trigger that will trigger this transition.</param>
public TransitionConfiguration Transition(TTrigger trigger)
{
return new TransitionConfiguration(this, _representation, trigger);
}
}
}
}
20 changes: 18 additions & 2 deletions src/Stateless/StateMachine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,12 @@ void InternalFireOne(TTrigger trigger, params object[] args)
{
// Handle transition, and set new state
var transition = new Transition(source, handler.Destination, trigger, args);

// If trigger handler has action, execute it
if (result.Handler.HasAction())
{
result.Handler.ExecuteAction(transition, args);
}
HandleReentryTrigger(args, representativeState, transition);
break;
}
Expand All @@ -377,15 +383,25 @@ void InternalFireOne(TTrigger trigger, params object[] args)
{
// Handle transition, and set new state
var transition = new Transition(source, destination, trigger, args);
HandleTransitioningTrigger(args, representativeState, transition);
// If trigger handler has action, execute it
if (result.Handler.HasAction())
{
result.Handler.ExecuteAction(transition, args);
}
HandleTransitioningTrigger(args, representativeState, transition);

break;
}
case InternalTriggerBehaviour _:
{
// Internal transitions does not update the current state, but must execute the associated action.
var transition = new Transition(source, source, trigger, args);
CurrentRepresentation.InternalAction(transition, args);
// If trigger handler has action, execute it
if (result.Handler.HasAction())
{
result.Handler.ExecuteAction(transition, args);
}
CurrentRepresentation.InternalAction(transition, args);
break;
}
default:
Expand Down
Loading