-
-
Notifications
You must be signed in to change notification settings - Fork 14
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
Eliminate trivial underscore #147
base: master
Are you sure you want to change the base?
Eliminate trivial underscore #147
Conversation
This is the version that passes all the existing tests. The HTTP requests used to be handled by a callback that had three arguments: one for the errors if there are any, one for the response and one for the payload within the response. Now, the error has been turned into rejected promises (exceptions when awaited) and the payload has simply been dropped. It's extracted from the response via the .body attribute.
Gazing through the differences, this PR also seems to build upon #146 - that wasn't intentional but honestly, it's not "wrong enough" for me to try to go back. The content of that has been around for a long enough time, it's not going to change, and I think eventually it should get merged either way... |
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.
I've reviewed your commit about removing outdated usage of underscore-plus
.
Largely, I totally agree with the ways you've done so. Many of them being rather clever uses of built in features of the language.
But a recurring comment you'll find in my review is obscurity.
One obvious example:
// === Original Code
return _.compact(_.uniq(packageNames));
Here, even if you aren't familiar with underscore-plus
you can quickly assume what it's doing based on the function name uniq
and can hopefully reasonably guess at what compact
is doing.
But if we take what it's changed to in this PR:
// === New Code
return Array.from(new Set(packageNames)).filter(Boolean);
While this code is generally faster by about 0.1ms
we've lost the clues at what's going on. Now requiring someone to be familiar enough with JavaScript to know that a Set
only allows unique values in each collection. Sure _.compact() => .filter()
is more obvious, but you get what I'm saying.
So in many places I've added requests for just some simple comments explaining the purpose of some code. Since we want to try and keep the barrier to entry low and not require too much knowledge of how everything works in JavaScript. But this really only applies to the more esoteric uses. Otherwise considering this is my only major point to make, the rest of this looks pretty rad. Thanks! I'll try and get to the async commit soon
@@ -54,7 +53,11 @@ but cannot be used to install new packages or dependencies.\ | |||
|
|||
fs.makeTreeSync(this.atomDirectory); | |||
|
|||
const env = _.extend({}, process.env, {HOME: this.atomNodeDirectory, RUSTUP_HOME: config.getRustupHomeDirPath()}); | |||
const env = { | |||
...process.env, |
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 wanting to add, since this one made me a bit nervous, I've tested to confirm this behavior will match what underscore-plus
does on it's particular version.
Ensuring that variables are overriden properly based on these news ones assigned. Smart use, and one of my favourites, of spread syntax.
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.
I also wanted to be sure and specifically checked MDN - it's guaranteed that what comes later overrides earlier values. I really share the mixed feelings here: if everybody knew this, probably we wouldn't fear it but still, it doesn't seem trivial...
src/command.js
Outdated
@@ -58,7 +57,7 @@ class Command { | |||
sanitizePackageNames(packageNames) { | |||
packageNames ??= []; | |||
packageNames = packageNames.map(packageName => packageName.trim()); | |||
return _.compact(_.uniq(packageNames)); | |||
return Array.from(new Set(packageNames)).filter(Boolean); |
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.
Awesome work on this new methodology. I do want to point out that I did some light testing of this method compared to the original, and this new method does in fact shave off about 0.1ms
on average compared to our usage of underscore-plus
. So a small win, but still a win.
The only thing I'd like to request, removing the names of the previous functions compact
and uniq
does obscure the purpose of this code a bit. Maybe could we add a comment such as // Creates a unique valued truthy only list
to ensure we know what this is supposed to do a year from now.
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.
It would be hard to object against a banal comment for some logic that will probably never change but still, I do think that this concern is overreaching. I specifically chose .filter(Boolean)
because I felt that was the clearest way to do it - way clearer than the misleading _.compact
itself - and I also think it's common knowledge that you turn something to a set and then back in order to eliminate duplicates. Of all the ppm code, I definitely wouldn't be the most concerned about this...
src/develop.js
Outdated
|
||
return new Install().run(installOptions); | ||
return new Install().run({...options}); |
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.
Initially I was going to say we should hold off using spread syntax instead of clone()
since it only deep copies initial object values. Leaving nested object values as a shallow copy.
This worried me, but then I tested and checked with underscore-plus
, which does the exact same thing.
Considering the object we are working with is nested, I wonder how much we really need to be doing anything other than passing the value on here. Hard to say, and a discussion for another time. But this should be fine.
Although, like my previous comment, removing our function name obscures these purposes, maybe a comment like // Shallow copy top level properties
to ensure someone doesn't haplessly just pass options
like one could very easily assume
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.
Again, almost sure I checked all functions and what they do, and also, I don't think it's all that obscure; specifically, I feel that merely passing a shallow copy without saying anything about why that's necessary is way more obscure than the spread syntax way of creating a shallow copy.
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.
I totally agree that doing a shallow copy with no comment originally is more obscure, because when I wrote that even I was wondering why they ever needed to do that.
But I'm sure you checked the functions and what they do because I couldn't find any differing behavior in your changes which is awesome.
src/star.js
Outdated
@@ -77,7 +76,7 @@ Run \`ppm stars\` to see all your starred packages.\ | |||
} | |||
} | |||
|
|||
return _.uniq(installedPackages); | |||
return Array.from(new Set(installedPackages)); |
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.
// Unique only array
src/disable.js
Outdated
|
||
if (packageNames.length === 0) { | ||
return "Please specify a package to disable"; //errors as return values atm | ||
} | ||
|
||
const keyPath = '*.core.disabledPackages'; | ||
const disabledPackages = _.valueForKeyPath(settings, keyPath) ?? []; | ||
const result = _.union(disabledPackages, packageNames); | ||
const result = Array.from(new Set([...disabledPackages, ...packageNames])); |
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.
I'm a bit worried about this code being rather esoteric looking, as in not being immediately understood in it's purpose at a glance.
Although it is significantly more performant than compared to both underscore's union()
and the most verbose single liner I could think of:
const result = disabledPackages.concat(packageNames.filter(elem => !disabledPackages.includes(elem)));
With performance being roughly:
- Underscore Plus:
0.167ms
- Your Method:
0.007ms
- My Method:
0.017ms
So considering this we should stick with what you've written, but I'd suggest again we add a comment to ensure the purpose of this is understood at a glance. Likely being able to use the comment from underscore
s union
method: Produce an array that contains the union: each distinct element from all of the passed-in arrays
or something similar
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.
This is kind of a hack, after all; one that I probably found on SO. I don't know whose idea was to release Set in Javascript without adding set operations... they are coming with Node 22 iirc. I'm rather surprised I didn't outright add a TODO to replace these calls once the actual set operations land... it needs to be taken care of, one way or another.
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 reviewed your changes to async for requests.
Overall it seems solid, I just had to ignore the issues me precursor PR is trying to resolve, as those aren't on you.
Other than a few mistakes of leaving in variables that don't seem to exist, this seems rather solid and should function fine.
Ideally, once merged I can refactor my PR and add back the changes I'm trying to get for better error logging.
src/search.js
Outdated
return packages; | ||
} | ||
|
||
const message = request.getErrorMessage(body, error); |
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.
The second argument error
here is never defined. Is it intended to be collected in a missing try...catch
statement?
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.
I don't know, I didn't create getErrorMessage
to begin with. 😅
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.
Well I just mean, your changes removing the definition of the variable error
. So I was asking if you intended to have it defined elsewhere, but if not then we will want to instead do request.getErrorMessage(body, null);
src/unstar.js
Outdated
const body = response.body ?? {}; | ||
if (response.statusCode !== 204) { | ||
this.logFailure(); | ||
const message = request.getErrorMessage(body, error); |
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.
We will want to make sure to have the second parameter be null
here, as error
doesn't exist
src/install.js
Outdated
resolve(body); | ||
}); | ||
const response = await request.get(requestSettings).catch(error => { | ||
const message = request.getErrorMessage(body, error); |
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.
The first parameter here body
doesn't exist, as we are only inside the catch()
callback.
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.
I feel this was "too clever". I rather don't want to change the logic here at the moment, I will revert to the original way of producing the error message here.
I suppose we must be very different people because I disagree categorically. Of course the strawman to beat would be |
I tend to fall more on @2colours’s side of this disagreement, but some short explanatory comments don't seem like they're a hardship here. |
Alright, I addressed all the open issues. I think. Also, since the other PR is kinda spoiled and we are already addressing the request async topic here, I will just close that. |
Now, only the overly custom part of underscore-plus remains: this config reading and writing that should rather get a separate module/package.
The implementations themselves could be further improved as better Set operations land, mostly in Node v22.