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

Add Joi.func().class() #1308

Merged
merged 1 commit into from
Oct 7, 2017
Merged

Add Joi.func().class() #1308

merged 1 commit into from
Oct 7, 2017

Conversation

wswoodruff
Copy link
Contributor

@wswoodruff wswoodruff commented Sep 19, 2017

  • Add Joi.func().class()
  • Ensure a function is not valid against Joi.func().class()
  • Ensure a class is valid against Joi.func().class()

@wswoodruff
Copy link
Contributor Author

Replaces #1305

lib/is-class.js Outdated
@@ -0,0 +1,3 @@
'use strict';

module.exports = (func) => (/^\s*class\s/).test(func.toString());
Copy link
Contributor

Choose a reason for hiding this comment

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

This seems... too simple. Haha. I expected to need more than "stringify and see if it says 'class'", but I can't justify that expectation. So.. good job! 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Haha, credit to @devinivy's haute lib for the one-liner! https://github.com/devinivy/haute/blob/master/lib/index.js#L157

Copy link
Member

Choose a reason for hiding this comment

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

And in turn to Felix Kling on stack overflow :P! https://stackoverflow.com/a/30760236

Just keep in mind the test used in haute is only good if you already know you have a 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.

That's true! We could validate that with a regular string like 'class hahaha Im just a string ya see!'

Copy link
Contributor

@DavidTPate DavidTPate Sep 19, 2017

Choose a reason for hiding this comment

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

We should check to make sure func.toString is a function that we can execute before we try to (or that func is indeed a function). Don't want to throw an error here.

Ideally I think it would be worth making sure it is a function as well. So if this was instead typeof func === 'function' && (/^\s*....); that way we know that there should be a toString() method on it as well.

As this code stands right now the following would pass incorrectly:

(/^\s*class\s/).test(['class '].toString()); // true

Next part would be why is \s* being expected in the RegExp? There aren't any leading whitespace characters from the Function.toString() method from what I've seen in the MDN docs nor from my testing. It seems like we should instead just have /^class\s/ as our RegExp.

Copy link
Member

@devinivy devinivy Sep 19, 2017

Choose a reason for hiding this comment

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

The whitespace is technically allowed at the beginning because according to the spec "The use and placement of white space, line terminators, and semicolons within the representation String is implementation-dependent." It's possible that that's an impractical concern, but I don't see the harm. At the same time I suppose we're not checking for semicolons and it would clearly be silly to do so, so I see your point.

I am similarly concerned about creating a runtime error if the helper is misused, but it's also valid to say as a precondition that the input to the helper must be a function.

const testFunc = function () {};
const testClass = class MyClass {};

classSchema.validate({ _class: testFunc }, (err, invalidValue) => {
Copy link
Contributor

@DavidTPate DavidTPate Sep 19, 2017

Choose a reason for hiding this comment

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

Can we not use the Helper that we have just like in the other tests?

Also, can we add some additional negative test cases such as undefined, null, ['class '], etc. It looks like there are some cases in the current code where we could throw an error instead of actually responding with a Joi error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure thing, updated!

lib/is-class.js Outdated
@@ -0,0 +1,3 @@
'use strict';

module.exports = (func) => (/^\s*class\s/).test(func.toString());
Copy link
Contributor

@DavidTPate DavidTPate Sep 19, 2017

Choose a reason for hiding this comment

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

We should check to make sure func.toString is a function that we can execute before we try to (or that func is indeed a function). Don't want to throw an error here.

Ideally I think it would be worth making sure it is a function as well. So if this was instead typeof func === 'function' && (/^\s*....); that way we know that there should be a toString() method on it as well.

As this code stands right now the following would pass incorrectly:

(/^\s*class\s/).test(['class '].toString()); // true

Next part would be why is \s* being expected in the RegExp? There aren't any leading whitespace characters from the Function.toString() method from what I've seen in the MDN docs nor from my testing. It seems like we should instead just have /^class\s/ as our RegExp.

lib/language.js Outdated
@@ -73,7 +73,8 @@ exports.errors = {
arity: 'must have an arity of {{n}}',
minArity: 'must have an arity greater or equal to {{n}}',
maxArity: 'must have an arity lesser or equal to {{n}}',
ref: 'must be a Joi reference'
ref: 'must be a Joi reference',
class: 'must be an ES6 class'
Copy link
Contributor

Choose a reason for hiding this comment

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

Wouldn't we just say "must be a class"? We shouldn't be having an ES6 Class and then later an ES7 Class. (This is a bit of a pedantic comment, but I'd rather make it than not)

@@ -366,6 +367,18 @@ internals.Object = class extends Any {
});
}

class() {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I add a check right here to see if this._flags.func === true?

Copy link
Contributor

Choose a reason for hiding this comment

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

The way that this is setup right now the flag this._flags.func could have not been set. The reason why being that a user could do Joi.object().class() or Joi.func().class().

Additionally the flag this._flags.func just ensures that we have set the flag that it should be a function, it doesn't validate that the input coming into isClass is actually a function. I would personally use typeof func in the isClass method.

@Marsup Marsup modified the milestones: 11.1.0, 11.2.0 Sep 20, 2017
@wswoodruff
Copy link
Contributor Author

@DavidTPate This should be ready for another look!

@Marsup
Copy link
Collaborator

Marsup commented Sep 30, 2017

I'm still thinking about the split.

@wswoodruff
Copy link
Contributor Author

Splitting object and func? Might be a cool idea, I'll bet you're trying to weigh how much work that might be. What you think?

@wswoodruff
Copy link
Contributor Author

If you think there should be a change like splitting func / class out of object, lemme know some of your thoughts on approach and I can give it a shot


Helper.validate(classSchema, [
[{ _class: ['class '] }, false, null, {
message: 'child "_class" fails because ["_class" must be a Function]',
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting, so I'm guessing this is the side-effect of this being off Joi.func() where we have the this._flags.func flag set for validation.

It makes sense given the implementation that we are returned the error "_class" must be a Function my initial thought was that we would get "_class" must be a class. But returning the base type first is the same way it works for any of our other types (ie. Joi.string().uuid())

Copy link
Contributor Author

@wswoodruff wswoodruff Sep 30, 2017

Choose a reason for hiding this comment

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

Yup! So in order to cover the additional function type check, typeof func === 'function' I had to use the is-class.js helper directly in the tests

Copy link
Contributor

@DavidTPate DavidTPate left a comment

Choose a reason for hiding this comment

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

The changes look good to me, but also want to get @Marsup's thoughts on this as he mentioned he is thinking about a few things.

@Marsup
Copy link
Collaborator

Marsup commented Oct 7, 2017

Separated the func from the object, can you rebase @wswoodruff ?

@@ -2163,6 +2164,62 @@ describe('object', () => {

});

describe('func().class()', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

It shouldn't be there, there's a file for function tests.

@wswoodruff wswoodruff changed the base branch from master to v12 October 7, 2017 16:44
@wswoodruff
Copy link
Contributor Author

wswoodruff commented Oct 7, 2017

@Marsup Test is failing because eslint doesn't like the object ... rest parameters in joi/lib/types/any/index.js

@Marsup
Copy link
Collaborator

Marsup commented Oct 7, 2017

Why did you take branch v12 in the first place? 😛

@wswoodruff
Copy link
Contributor Author

Ohhh I thought you wanted me to rebase off of v12! My bad

@wswoodruff wswoodruff changed the base branch from v12 to master October 7, 2017 19:54
@Marsup Marsup merged commit bdc7d02 into hapijs:master Oct 7, 2017
Marsup added a commit that referenced this pull request Oct 8, 2017
@lock
Copy link

lock bot commented Jan 9, 2020

This thread has been automatically locked due to inactivity. Please open a new issue for related bugs or questions following the new issue template instructions.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants