-
Notifications
You must be signed in to change notification settings - Fork 10
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
Feature: Illegal Transitions #10
Comments
State transitions are initiated programmatically through |
That's definitely possible. But it would mean having one method per state that checks the pre-conditions by looking at the previous state. That would be pretty generic boilerplate code that looks the same in every project that uses the plugin. Thats why the other state machine plugin has state transition checks built in:
Sadly that plugin is not up to date (not null-safe). Anyways this is just a suggestion, I solved my problem by implementing a dumbed-down state machine, so I'm good. Feel free to close this! |
I am definitely open to expand this library, I just don't quite understand the need. Happy to see what can be done, if you could provide a minimally reproducible example? |
Hi, I created a guard solution, that allows users to define guards for transitions. Effectively creating legal/illegal transitions. Guards are implemented in the Guard class, and the conditions in the guard are evaluated before notifying listeners. It is hosted on my forked repo: repo. Is this something you could see implemented in the main repository? Or did I overlook some functionality? |
Looks reasonable. What I don't like is the untyped context it introduces |
I have implemented the guard decorator by my best understanding in this branch. However I run into two problems:
The standard machine is wrapped with a guard decorator. When a new state is added to the guard decorator, the underlying standard machine is instructed to add a new state. It creates a new state, and returns it to the decorator for guard initialization. However, the issue arises when the standard machine creates a new state. In the standard
Side-notes:
|
I agree, it is unfortunate that the S newState<S extends State<T>>(T identifier, {S Function(Machine<T>, T)? constructor}) { With tracking states in typed fields, I mean that I don't like to have |
This breaks the invalid machine check,
Changed it to: So context would accept something like varType. Which would be extended by intType, boolType, stringType etc? |
Hi, I have integrated the commit into my decorator branch of the fork. However a lot of tests are now failing and my debugger cannot step into the failing tests. So I cannot debug as to why they fail. I understand that you want to keep the core functionality intact and make the code extensible at the same time, but this commit broke so much that I do not know how to continue. I just needed the guard and context functionality for my studies, and have now sunken too much time into adapting it to the decorator pattern. I do not think that a clean implementation of the decorator pattern is easily doable without refactoring. Because the machine is not a single object, but actually two interwoven (machine&states). |
What about something along ... import 'package:statemachine/statemachine.dart';
class GuardedMachine<T> extends Machine<T> {
@override
GuardedState<T> createState(T identifier) =>
GuardedState<T>(this, identifier);
@override
set current(Object? state) {
final target = state is State<T>
? state
: state is T
? this[state]
: state == null
? null
: throw ArgumentError.value(state, 'state', 'Invalid state');
// Should probably provide a template method or event to validate the
// transition before executing it.
if (current != null &&
(current as GuardedState<T>).canExit(target as GuardedState<T>)) {
throw UnsupportedError('Unable to exit to $target');
}
if (target != null &&
(target as GuardedState<T>).canEnter(current as GuardedState<T>)) {
throw UnsupportedError('Unable to enter to $target');
}
super.current = target;
}
@override
GuardedState<T> newState(T identifier) =>
super.newState(identifier) as GuardedState<T>;
}
class GuardedState<T> extends State<T> {
GuardedState(super.machine, super.identifier);
bool canExit(GuardedState<T>? to) => true;
bool canEnter(GuardedState<T>? from) => true;
} The casts are a bit ugly, but not sure how to avoid them ... |
Implemented your suggestion in: fork branch Tests cases all pass. However they are probably not exhaustive. Two questions
|
Hi, I just tried out the plugin but I couldn't figure out how to define legal vs illegal transitions. I would say they are one of the main reasons to use a state machine. Did I just oversee the feature? Or if not, maybe there's a reason for their absence?
The text was updated successfully, but these errors were encountered: