-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Some refactoring of ResolutionSession that makes module implementation very semantic and concise #7527
Some refactoring of ResolutionSession that makes module implementation very semantic and concise #7527
Conversation
Also, I just realize it, instead of using an array to track resolution session state, and having to copy it at fork and things like that, we may consider the following class (let me use class ResolutionSession {
#value?: ResolutionElement = undefined;
#parent?: ResolutionSession = undefined;
constructor(parent?: ResolutionSession, value?: ResolutionElement) {
// parent and value must be defined both or both undefined (ROOT)
this.#parent = parent;
this.#value = value;
}
// We actually don't need fork anymore, because ResolutionSession
// is created for each single invocation of ResolutionAction, storing ptr to the parent
// but parent intentionally does not track children. Instead of popping from array we simply
// forget reference to the lowest accessible instance of ResolutionSession.
fork() {
return new ResolutionSession(
this.#parent,
this.#value
)
}
// UPD: I looked all cases where fork is used in current codebase. It's primary
// goal to counter-play the changing stack. But with this implementation
// we simply pass instance of ResolutionSession and it's perfectly fine
//
// It seems like a way to completely get rid of fragility in this module.
// I may create a proof of concept (a drop-in replacement of existing class), if you want
runWith(value: ResolutionElement, action: ResolutionAction) {
return action(
new ResolutionSession(this, value)
)
}
get stack() { // Called so to show how props from forming implementation can be extracted
var current = this;
var list = [];
while (current && current.#value) {
list.push(current.#value)
current = current.#parent
}
list.reverse()
return list;
}
// All other methods can be implemented using .stack getter
} |
} else if (typeof keyPattern === 'function') { | ||
} | ||
|
||
if (typeof keyPattern === 'function') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure if these changes are relevant.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are not. I may undo what can be considered a style change, if you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's revert it for this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just done it. Reverted.
b660b09
to
188fa93
Compare
I did a Proof of Concept here in a separate branch: This implementation also uncovered the actual semantics for what this module seeks to achieve (by implementing it in simpler terms with fewer concepts). That is: we want modified session to be accessable solely inside actions, and nested actions. Instead of tracking starts and ends of actions, this implementation simply creates a child session for that action. |
The change LGTM. Would you please squash all commits into one? |
188fa93
to
3c7e1e0
Compare
Just did that. Note small changes in test to accommodate using single describe function. This can be reverted too though. |
@@ -74,7 +74,7 @@ describe('ResolutionSession', () => { | |||
expect(session.currentInjection).to.be.exactly(injection); | |||
expect(session.injectionStack).to.eql([injection]); | |||
expect(session.getBindingPath()).to.eql('a --> b'); | |||
expect(session.getInjectionPath()).to.eql('MyController.constructor[0]'); | |||
expect(session.getInjectionPath()).to.eql('@MyController.constructor[0]'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's not break the current injection path. Please revert the @
prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated
…and enterBinding internals Signed-off-by: Phoenix The Fallen <thephoenixofthevoid@gmail.com>
3c7e1e0
to
cdcecac
Compare
Should I wait until it gets merged before discussing more changes? |
@thephoenixofthevoid Thank you for the contribution. Please open new PRs for follow-up enhancements. |
The main point of this PR is changes in
packages/context/src/resolution-session.ts
.The original code contains some complexity:
enterBinding
violates "the single concern" principle: it ensures that ResolutionSession exists or created, and also it is concerned with tracking resolution status.injection
-related methodsSo I managed to get rid of private method, and without changing public interface managed to rewrite runWith- methods in sane way. Now runWithBinding looks this way:
Here we can see the important invariant for order of invocation of
pushBinding
andpopBinding
: push and pop calls must be balanced and the state of stack before push must be restored after pop call. Now "opening" and "closing" happened to end up in the same function, and there are no other places where push/pop methods are invoked. That means that we must treat pushBinding/popBinding/pushInjection/popInjection as private.That itself renders checks inside these methods useless, effectively turning them (since these two kinds do not differ at all if checks and debugging are removed) into a single pair of push and pop methods. So there are no longer need for keeping different runWithBinding/runWithInjection. Now we may have:
There are 2 more changes, but these are more of housekeeping type of changes.
Would like to hear your opinion on that and whether you are interested in me working in that direction.
Regards.
Checklist
npm test
passes on your machinepackages/cli
were updatedexamples/*
were updated👉 Check out how to submit a PR 👈