-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
Add Joi.func().class() #1308
Conversation
Replaces #1305 |
lib/is-class.js
Outdated
@@ -0,0 +1,3 @@ | |||
'use strict'; | |||
|
|||
module.exports = (func) => (/^\s*class\s/).test(func.toString()); |
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 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! 👍
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.
Haha, credit to @devinivy's haute
lib for the one-liner! https://github.com/devinivy/haute/blob/master/lib/index.js#L157
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.
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.
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.
That's true! We could validate that with a regular string like 'class hahaha Im just a string ya see!'
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 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.
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 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.
test/types/object.js
Outdated
const testFunc = function () {}; | ||
const testClass = class MyClass {}; | ||
|
||
classSchema.validate({ _class: testFunc }, (err, invalidValue) => { |
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.
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.
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.
Sure thing, updated!
lib/is-class.js
Outdated
@@ -0,0 +1,3 @@ | |||
'use strict'; | |||
|
|||
module.exports = (func) => (/^\s*class\s/).test(func.toString()); |
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 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' |
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.
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)
lib/types/object/index.js
Outdated
@@ -366,6 +367,18 @@ internals.Object = class extends Any { | |||
}); | |||
} | |||
|
|||
class() { | |||
|
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.
Should I add a check right here to see if this._flags.func === true
?
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 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.
@DavidTPate This should be ready for another look! |
I'm still thinking about the split. |
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? |
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 |
test/types/object.js
Outdated
|
||
Helper.validate(classSchema, [ | ||
[{ _class: ['class '] }, false, null, { | ||
message: 'child "_class" fails because ["_class" must be a 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.
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()
)
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.
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
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 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.
Separated the func from the object, can you rebase @wswoodruff ? |
test/types/object.js
Outdated
@@ -2163,6 +2164,62 @@ describe('object', () => { | |||
|
|||
}); | |||
|
|||
describe('func().class()', () => { |
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 shouldn't be there, there's a file for function tests.
@Marsup Test is failing because eslint doesn't like the object |
Why did you take branch v12 in the first place? 😛 |
Ohhh I thought you wanted me to rebase off of |
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. |
function
is not valid againstJoi.func().class()
class
is valid againstJoi.func().class()