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

Dart version update above 3 null safety #6

Open
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

VladimirCores
Copy link

@VladimirCores VladimirCores commented Aug 5, 2023

Conform framework to the Dart 3 version.
Make all tests pass.
image

Example works.
image

Mr @cliffhall could you take a look?

Copy link
Member

@cliffhall cliffhall left a comment

Choose a reason for hiding this comment

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

Thanks for the PR, @VladimirCores.

I've made a few requests, mainly around the refactoring of some params / vars to use underscores. Since this is a stylistic choice that is not backward compatible I ask that they be returned to their previous states. There are some other minor requests/comments as well.

lib/src/interfaces/IController.dart Outdated Show resolved Hide resolved
lib/src/interfaces/IController.dart Outdated Show resolved Hide resolved
/**
* This [INotifications]'s [body]
*/
void set body( dynamic bodyObject );
Copy link
Member

Choose a reason for hiding this comment

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

why are the accessors removed?

Copy link
Author

Choose a reason for hiding this comment

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

Because we have methods: getBody, setBody, the accessors looks excess, they duplicate functionality. And in my practice I always use these methods. What are the reasons for accessors existence?

Copy link
Member

@cliffhall cliffhall Aug 17, 2023

Choose a reason for hiding this comment

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

Accessors are not duplicate functionality, they are alternate functionality. You can say myModel.body or myModel.getBody() whichever may be your preference. They exist as part of the original framework and all ports.

It's important to remember that personal stylistic choices should not distort a port. A port should hew as closely to the reference implementation as is practicable. This is why the ActionScript Developers Guide to PureMVC is generally useful do devs using most ports. Because the implementation and idioms are very similar across ports. If developers used to one port of the framework find that the same idioms they are used to are present in another port, they get to joy quicker. If they arrive here and find that things are different owing to stylistic choices, well, it all becomes a bit loosey-goosey. The promise of easy portability breaks down. If this one thing is arbitrarily different, what others might be?

Controller(String key) {
if (instanceMap.containsKey(key)) throw MultitonErrorControllerExists();
_multitonKey = key;
instanceMap[_multitonKey] = this;
Copy link
Member

Choose a reason for hiding this comment

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

Change _multitonKey back to multitonKey.

Copy link
Author

@VladimirCores VladimirCores Aug 13, 2023

Choose a reason for hiding this comment

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

Setter added for every places near getter.
But why multitonKey could be mutable? Can't imagine use case.

Copy link
Member

@cliffhall cliffhall Aug 17, 2023

Choose a reason for hiding this comment

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

Ok, this one is my bad. I failed to realize that Dart had promoted the underscore as an alternative to having protected or private keyword, as was present in the AS3 reference.

So in this case, we legit have to break from the reference implementation and do something different. This means all the Multiton actors need this underscore for the multitonKey property. Please do an exhaustive search to make sure all references are replaced, because I recall that in the original PR, one of the classes still had a loose multitonKey ref without the underscore on it.

proxyMap = new Map<String,IProxy>();
Model(String key) {
if (instanceMap.containsKey(key)) throw MultitonErrorModelExists();
_multitonKey = key;
Copy link
Member

Choose a reason for hiding this comment

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

Change _multitonKey back to multitonKey.

Copy link
Author

Choose a reason for hiding this comment

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

Setter added for every places near getter.


// Make sure caller and callback were set
expect( observer.getNotifyMethod(), isNotNull );
expect( observer.notifyMethod, isNotNull );
Copy link
Member

Choose a reason for hiding this comment

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

why was this test removed?


// Make sure callback method was set
expect( observer.getNotifyMethod(), isNotNull );
expect( observer.notifyMethod, isNotNull );
Copy link
Member

@cliffhall cliffhall Aug 9, 2023

Choose a reason for hiding this comment

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

why was this accessor test removed?

Copy link
Author

Choose a reason for hiding this comment

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

I might guess that accessors are needed to keep original version of the property in case something overrides them.


// Make sure caller was set
expect( observer.getNotifyContext(), isNotNull );
expect( observer.getNotifyContext(), same(this) );
expect( observer.notifyContext, isNotNull );
Copy link
Member

Choose a reason for hiding this comment

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

why was this test removed?

Copy link
Author

Choose a reason for hiding this comment

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

I removed direct access to the value of notifyContext because I though its excess and creates ambiguity in understanding of their use (as we already have methods). But I reverted the remove, and the test as well.

part 'Test_Facade.dart';
part 'Test_MacroCommand.dart';
part 'Test_Mediator.dart';
part 'Test_Model.dart';
// PureMVC Unit Tests
Copy link
Member

@cliffhall cliffhall Aug 9, 2023

Choose a reason for hiding this comment

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

why did this comment get moved to the middle of the list of tests?

Copy link
Author

Choose a reason for hiding this comment

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

Not sure how ... but its corrected, and comment moved to the top of "declaration" of parts.

void write(String message) {
document.querySelector('#status').innerHtml = message;
}
// void onTestResult(TestCase testCase) {
Copy link
Member

Choose a reason for hiding this comment

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

why are these output messages commented out? If no longer needed, we can just remove them. Are they made redundant by the test runner?

Copy link
Author

Choose a reason for hiding this comment

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

They are not needed for test run from IDE (Android Studio) and this commented out code removed.

Copy link
Member

@cliffhall cliffhall left a comment

Choose a reason for hiding this comment

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

Another small request. Could you please update the VERSION and README files to add notes about the changes, and increment this to 2.0.7?

@VladimirCores
Copy link
Author

@cliffhall I read your messages, and will answer (and correct) them I hope soon, have some urgent business. Thanks for your collaboration on this! : )

@VladimirCores VladimirCores force-pushed the dart-version-update-above-3-null-safety branch from 198a5c1 to d344648 Compare August 13, 2023 05:18
@VladimirCores VladimirCores force-pushed the dart-version-update-above-3-null-safety branch from 9d9f4bd to 357356b Compare August 13, 2023 06:34
@VladimirCores
Copy link
Author

@cliffhall I will review this PR once again and make changes you requested.

Copy link
Member

@cliffhall cliffhall left a comment

Choose a reason for hiding this comment

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

There were a lot more changes this time. Moving target. Unfortunately, I'm requesting most of them be reverted under three major themes:

  1. First is my bad. I didn't realize _ was elevated to language level 'private' and no longer just an idiom here. If that's what it takes to make the property private, then so be it, _ on private vars throughout.

  2. For maximum portability, we eschew the local syntactic sugar of a language wherever possible and favor simple, widely supported idioms.

So for instance, in a collection where you're invoking containsKey, you're relying on a Dart-specific feature. While myMap.containsKey('stuff') is 100% how you might go about it in your application, in a portable framework myMap['stuff'] is the favored approach.

That's important to do even in ports of the reference implementation, because someone who uses this port might decide to port it to their new favorite language. If they're going from a very Dart-flavored implementation, then they need to seek equivalent affordances in the target language and that amounts to friction for the porting developer and a reduced likelihood that they will complete the port.

With regards to portability, PureMVC took a hint from C, which is so dirt simple it exists for nearly every processor in the world.

  1. In some places, refactors have been done that, while possibly making more sense or being slightly more efficient, should not have happened. A particularly sensitive one is notifyObservers. It took a long time to get that one working properly. Lots of community feedback led to a stable implementation. I'd like to keep all of the method implementations as close to the original as is allowed by the language. By doing so, we leverage years of troubleshooting and improvement to get the expected behaviors we see in the ports today. As a result we usually don't see new issues cropping up after a port. Simple refactors can lead to bugs that lie dormant because a particular weird use-case has not been tried, as was the case with notifyObservers.

if (instanceMap.containsKey(key)) throw MultitonErrorControllerExists();
_multitonKey = key;
instanceMap[_multitonKey] = this;
commandMap = Map<String, Function>();
Copy link
Member

Choose a reason for hiding this comment

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

Based on the above comment, commandMap and several other properties of the Multitons should be underscored as well. check the AS3 Reference for props that were protected or private.

{
view = View.getInstance( multitonKey );
void initializeController() {
view = View.getInstance(multitonKey)!;
Copy link
Member

Choose a reason for hiding this comment

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

Here's one where it needs to be underscored but wasn't. It's going to be important to sweep all the classes and make sure all the props intended to be private/protected have underscores everywhere they're referenced.

return instanceMap[ key ];
static IController? getInstance(String? key) {
if (key == null || key == "") return null;
if (instanceMap.containsKey(key)) {
Copy link
Member

Choose a reason for hiding this comment

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

Why is this logic refactored? It may do the same thing in the end, but please, if possible, stick with the existing logic implementation, which was inherited from the AS3 port, which had years of improvement associated with it to eliminate hard to detect bugs. While one implementation might be as good as another, it's where new bugs can creep in. We'd like to make sure that we lean on the well-tested reference wherever we can, departing from it only when we have no other choice.

Function commandFactory = commandMap[ note.getName() ];
if ( commandFactory == null ) return;
void executeCommand(INotification note) {
final noteName = note.getName();
Copy link
Member

Choose a reason for hiding this comment

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

Another unnecessary departure from the original.

if ( commandMap[ noteName ] == null ) {
view.registerObserver( noteName, new Observer( executeCommand, this ) );
void registerCommand(String noteName, Function commandFactory) {
if (!hasCommand(noteName)) {
Copy link
Member

Choose a reason for hiding this comment

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

Stick with the original implementation wherever possible.


// The [IFacade] Multiton instanceMap.
static Map<String,IFacade> instanceMap;
static Map<String, IFacade> instanceMap = Map<String, IFacade>();
Copy link
Member

Choose a reason for hiding this comment

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

why is this being initialzed?

*
* - Returns [String] - the type of the [INotification].
*/
String? getType() {
Copy link
Member

Choose a reason for hiding this comment

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

Again, not sure what the ? is for in this context, can we drop it throughout?

/**
* This [INotifications]'s [name]
*/
final String _name;
Copy link
Member

Choose a reason for hiding this comment

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

why are you adding final to _name but not _type? I think dropping it from both is what's needed.

/**
* This [INotifications]'s [type]
*/
String? _type;
Copy link
Member

Choose a reason for hiding this comment

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

questioning the ?. I think. we can drop throughout (in this context)

document.querySelector('#status').innerHtml = message;
}
// void onTestResult(TestCase testCase) {
// write("${testCase.result} ${testCase.currentGroup}");
Copy link
Member

Choose a reason for hiding this comment

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

Can we remove these comments?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants