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

Make 'empty' throw on non-string primitives and functions #812

Merged
merged 5 commits into from Oct 1, 2016
Merged

Make 'empty' throw on non-string primitives and functions #812

merged 5 commits into from Oct 1, 2016

Conversation

shvaikalesh
Copy link
Contributor

Follow-up of #763.

this.assert(
Object.keys(Object(obj)).length === 0
0 === itemsCount
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is there any ESLint config we can add to the project? This will greatly improve contribution/review experience: yoda comparisons and comma-first are not quite popular and it would be awesome to automate codestyle checks.

Copy link
Member

Choose a reason for hiding this comment

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

@shvaikalesh Yes, we thought about that too, we already discussed that here and we came to the conclusion that by moving all our code to separate repos with their own linting rules would be better, however, if you disagree you're welcome to share your thoughts with us 😄 .

case 'function':
throw new TypeError('.empty() was passed a function');
default:
if (val !== Object(val)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Other option is to add case clause for other primitives' types.

case 'string':
itemsCount = val.length;
break;
case 'function':
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This should handle generators too (because of recent updates to type-detect) and async function in the future.

@meeber
Copy link
Contributor

meeber commented Oct 1, 2016

@shvaikalesh Thank you for your continued work on this! Implementation looks fine to me. However, I think we should update the jsdoc to clarify that only non-function objects are allowed.

Other thoughts:

  • Agreed on ESLint. I'll start a new discussion on this after v4 is released.
  • You mentioned previously that you'll be following up this PR with another one that adds support for Map and Set. At that time, I'm wondering if we should begin throwing a TypeError if a WeakMap or WeakSet is passed. It seems to me that it'd likely be a programmer error to attempt to check if one of those two were empty without realizing that you can't do that.
  • It looks like we'll need a 3rd PR to add empty to the assert interface. Lemme know if you're interested in tackling that as well. Otherwise, I'll open a new issue for it.

@shvaikalesh
Copy link
Contributor Author

shvaikalesh commented Oct 1, 2016

@meeber I have updated jsdoc. Throwing on weak collections is nice idea, absolutely agree. Totally interested in adding empty to assert interface.

One more thought: it seems like tests for most assertions are repeated for every interface they are available for. Would be great to have only one test suite for each assertion.

@meeber
Copy link
Contributor

meeber commented Oct 1, 2016

@shvaikalesh Yeah adding tests is painful right now due to the multiple interfaces. There was a discussion about this somewhere but I can't find it. Anyway, the long term idea as we move into a modular system as described in #457 is to only have to write tests once for each assertion. But we're not quite there yet.

Regarding this PR: LGTM!

Copy link
Member

@vieiralucas vieiralucas left a comment

Choose a reason for hiding this comment

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

Hello @shvaikalesh,

First of all thank you so much for all the effort you've been putting into this issue with .empty since #763.

I just add a comment about the messages inside TypeError

itemsCount = val.length;
break;
case 'function':
throw new TypeError('.empty() was passed a function');
Copy link
Member

Choose a reason for hiding this comment

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

What about replacing .empty() with .empty since it is a property assertion?

throw new TypeError('.empty() was passed a function');
default:
if (val !== Object(val)) {
throw new TypeError('.empty() was passed non-string primitive');
Copy link
Member

Choose a reason for hiding this comment

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

What about replacing .empty() with .empty since it is a property assertion?

@vieiralucas
Copy link
Member

vieiralucas commented Oct 1, 2016

I'll just leave my LGTM in case you guys think that my comment is just nitpicking.

Agreed with you and @meeber about Map, Set, WekMap and WeakSet.
Agreed about adding empty to the assert interface.
Agreed about the problem with code style and missing eslint thing. 😸

P.S. This just made me realize that we might consider throwing TypeError instead of plain Error on closeTo, above, least and below.

Copy link
Member

@lucasfcosta lucasfcosta 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 your work @shvaikalesh, this is looking great, however, as @vieiralucas said maybe we could just swap .empty() for .empty in the error messages.
After that I will be very happy to merge this in.
Thanks! 😄

case 'function':
throw new TypeError('.empty() was passed a function');
default:
if (val !== Object(val)) {
Copy link
Member

Choose a reason for hiding this comment

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

Great use of Object(). 😍

Copy link
Member

Choose a reason for hiding this comment

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

That blew my mind to!

this.assert(
Object.keys(Object(obj)).length === 0
0 === itemsCount
Copy link
Member

Choose a reason for hiding this comment

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

@shvaikalesh Yes, we thought about that too, we already discussed that here and we came to the conclusion that by moving all our code to separate repos with their own linting rules would be better, however, if you disagree you're welcome to share your thoughts with us 😄 .

itemsCount = val.length;
break;
case 'function':
throw new TypeError('.empty() was passed a function');
Copy link
Member

Choose a reason for hiding this comment

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

As @vieiralucas said, I think .empty() should be changed to .empty since it's a property assertion.

@meeber
Copy link
Contributor

meeber commented Oct 1, 2016

@lucasfcosta @vieiralucas UH OH THE LUCAS SYNDICATE IS IN FULL FORCE TODAY. Just a reminder to whoever ends up merging: Squash and merge! :D

@lucasfcosta
Copy link
Member

@meeber hahaha yes, thanks for remembering us to do that 😄
Let's just wait for the .empty() change to make the error more self-explanatory (which IMO is a very important thing). Are you guys okay with that or am I being too nitpicky?

@shvaikalesh
Copy link
Contributor Author

I have removed () and added function names and primitives values to messages for better debugging experience (is that OK?).

default:
if (val !== Object(val)) {
throw new TypeError('.empty() was passed non-string primitive');
throw new TypeError('.empty was passed non-string primitive ' + String(val));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need String here because it special-cases symbols. Otherwise, they throw on ToString.

EDIT: oops, that fails in Node 0.12 because of partial implementation. Let's do toString.

Copy link
Member

Choose a reason for hiding this comment

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

You could use the inspect utility.

Copy link
Member

Choose a reason for hiding this comment

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

I think I'm okay without it since the error message is pretty self-explanatory.
Also, if you want to add that anyway, I wouldn't mind, just try using _.type(), since it may be more accurate.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, I just noticed you want to print the value of what was passed, not it's type.
Then @vieiralucas is right, the inspect utility would be ideal.

Copy link
Member

Choose a reason for hiding this comment

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

_.type() is better IMO

Copy link
Member

Choose a reason for hiding this comment

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

okay, so, if we're going to use type we need to rephrase the error message to something like:

.empty was passed non-string primitive of type ' + _.type(val)

PS.: Sorry for all this mess in the comments @shvaikalesh 😆

Copy link
Member

Choose a reason for hiding this comment

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

Okay, calm down everyone hahahha:

THUMBS UP IF YOU VOTE FOR TYPE AND HEART THIS COMMENT IF YOU VOTE FOR INSPECT.
If we three vote there's no way this is going to be a tie.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay, let's do type then. Or not. Huh, need to refresh the page to see new comments, like in good ol' times.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am for inspect because you can definitely tell primitive type by the value, but not otherwise. May save console.log typing for someone.

Copy link
Member

Choose a reason for hiding this comment

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

Okay, so we're going for inspect.
Also, since inspect also adds delimiters which denote the type of the value (like [] for arrays and '' for strings), it is more complete.
Thanks for this @shvaikalesh!

LGTM!

@vieiralucas
Copy link
Member

vieiralucas commented Oct 1, 2016

LGTM again because something went wrong with approvals/lgtm

@lucasfcosta lucasfcosta merged commit 5b170b2 into chaijs:master Oct 1, 2016
@shvaikalesh
Copy link
Contributor Author

Thanks for the merge, awesome contribution experience ❤️ .

@lucasfcosta
Copy link
Member

Squashed and merged!
Awesome work @shvaikalesh, I'm looking forward to see you here again 😄

@vieiralucas
Copy link
Member

Thank you so much @shvaikalesh, looking forward for your next contributions. 😄

@meeber
Copy link
Contributor

meeber commented Oct 1, 2016

No more caffeine for The Lucas Syndicate.

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.

None yet

4 participants