Closed lukejagodzinski closed 9 years ago
@jagi Hello, my two cents
Astronomy.validateOnSave = true
.Actually, the questions are hard to answer as we would show the errors on form submit, right? Or, on keyUp from an input. So, we need to use this with forms, and then see, how to implement the error messages, validation. Maybe you have an example on how to use it with forms?
I really would like to use your package, but I don't know where I can use it as for the forms & validation I use aldeed packages. If we can easily make reactive forms and validation with astronomy, I can test or discuss.
I'd stick with simplicity and a fine-grained API. This gives the developer the choice of how to use them and abstract them.
Should validation take place automatically when calling save() method on the object?
It would definitely make sense if the validation happened when you called save. But in this case validation and saving are 2 different actions. You can always create another function that does both of these actions, but its nice to give the developer the choice of how to do it. And someone may want to use save with validate...
In what form would you like to get error message? Should it throw exception that you're gonna catch and display somewhere error or you want just true or false return value from the validate() method? And later you would like to call function like for instance object.getErrors() to get all errors.
Suppose it just returns true or false (my preference). Then you can do something like this:
if (doc.validate()) { doc.save() }
Now you can allow developers to abstract this api in whatever you want, depending on their coding style or whatever framework they're using. I would never do this, but just to give an example of the diversity:
save(doc)
.error( function() { ... })
.success( function() { ... })
Thank you for your feedback. Considering your suggestions I think that the best solution will be leaving user as high flexibility as possible. I will split validation and saving processes. We will have following functions:
save()
- just saves object without validation,validate()
- validates object and returns true / false,validate(validationName)
- runs validation only on validator with the validationName
name and returns true / false,validateAndSave()
- validates and saves object if it's valid and also returns true / false.Moreover I will introduce reactive variables that can be used for displaying errors in a template:
object.isValid(validationName)
- true / false reactive variable. Can be handy when validating single field and when user wants to display green tick that given field is OK after leaving that field (blur
event on the field),object.getErrors()
- return reactive dictionary of fields / validators that didn't pass validation,object.getError(validationName)
- returns reactive var related to particular validator with the name validationName
.As validation is synchronous process, there is no need for callback functions. Having validation and saving as separate tasks I will also not include Astronomy.validateOnSave = true
. In fact in the newest version of Meteor Astronomy (not published yet) validation had been moved to external module which can be installed with meteor add jagi:astronomy-validators
. This leave much more flexibility to user. He/she can decide if he/she wants to use Astronomy Validators or entirely different solution.
@VladShcherbin in following days I will be working on validation extensively so I will post some example of using validation with forms. Stay tuned :). I think you will like it when I'm finished.
Tomorrow I will also ask you about one more thing but I have to go right now.
I you have any other suggestions, please let me know.
For validation, I think it would be best to be as functional as possible.
Rather than have something like:
validator = {
name: {
type: String
min: 2
}
}
I think it would be better to compose functions. This would take a little more thought but maybe something like this:
validator.name = [isString, compose(gt(2), map(len))]
This talk will give you some more inspiration: https://www.youtube.com/watch?v=m3svKOdZijA
This way, the developer has much more freedom for how to validate using functions. And perhaps if you set the context of the function, you can easily compare to other values in the same object using this.field
syntax.
And if you're up for more functional programming, check out this stuff https://vimeo.com/104807358 https://github.com/DrBoolean/lenses
There are also some edge cases to think about like timestamps. Are those validated simply as a number? Or are they written by the server before inserted?
Functional programming and functions composition seems to be nice thing but I don't think that schema definition is a good place to use it. Schema for me is like description. In fact, I would like to avoid defining functions in the schema at all. But we have object methods and events. Maybe validators should also be "functional".
However this example
validator.name = [isString, compose(gt(2), map(len))]
is not intuitive. At the beginning I didn't know what does it do until I've watched talk about composing functions. But in fact such approach is interesting. It would be nice to have something like:
title
However user can write his/her own validator that does the job. Or I could create validator FunctionValidator
or similar name that would allow user to pass function where he/she could use whatever tool he/she wants. In this function this
context would be the object being currently validated. I just don't want to introduce entirely different programming paradigm as a built in feature. I have to think about it in more details.
To answer your question about timestamps. I just use standard Date objects comparison. When you compare Date objects, valueOf
function is used which returns numerical timestamp. So, dates comparison is just timestamps (numbers) comparison.
@jagi just use the simple string
validators with the ability to create a new validator with a title. It is a common practise and works extremely well, there is no need to reinvent the wheel.
The validators should be reusable, so we can write a custom one and use it everywhere we want.
@jagi, so my point wasn't to get everyone using functional composition (although that would be great!).
My point was to give utmost flexibility to the developer.
And by that, I mean every validator should be (or at least accept) a function. That way, the developer isn't constrained to the framework. The biggest problem I have when using a framework is when I having some very specific thing I know how to do but I'm not given the freedom to do it within the framework. With functions for validators, you can validate however you want. It gives the developer the choice of how to do it and makes the framework much easier to create because you don't need to worry about how things are validated. You just need to run the function!
If you want to parse strings and create your own validators that way, you're free to do that, and that would likely be a separate package. Your validators will just be a function that evaluates those strings.
My concern with time stamps is slightly different. You can check they're a number and you can check the time is within some arbitrary number of seconds from now. But that's not the right way to do it. The time stamp should be generated on the server, not the client. And the owner of a document (the current user) should likely be set on the server as well (rather than compared to make sure it is the current user). Auto form has this "autovalue" concept for this. It's just something to think about for this framework.
Also security is interesting to think about. In any large app, you're going to stop using allow-deny rules and start using meteor methods because they're easier to manage. So I'd make sure that security is reasonable to manage in a larger app through this framework.
On Apr 19, 2015, at 08:57, jagi notifications@github.com wrote:
Functional programming and functions composition seems to be nice thing but I don't think that schema definition is a good place to use it. Schema for me is like description. In fact, I would like to avoid defining functions in the schema at all. But we have object methods and events. Maybe validators should also be "functional".
However this example
validator.name = [isString, compose(gt(2), map(len))] is not intuitive. At the beginning I didn't know what does it do until I've watched talk about composing functions. But in fact such approach is interesting. It would be nice to have something like:
Take field title Get length Is grater than 2 However user can write his/her own validator that does the job. Or I could create validator FunctionValidator or similar name that would allow user to pass function where he/she could use whatever tool he/she wants. In this function this context would be the object being currently validated. I just don't want to introduce entirely different programming paradigm as a built in feature. I have to think about it in more details.
To answer your question about timestamps. I just use standard Date objects comparison. When you compare Date objects, valueOf function is used which returns numerical timestamp. So, dates comparison is just timestamps (numbers) comparison.
— Reply to this email directly or view it on GitHub.
@ccorcos if it goes about timestamps, I agree that timestamps should be generated on the server and you can do so. You just create Meteor method (for both environments client and server) that just saves some document and generate timestamp on save. On the client it's only simulation, and on the sever it takes server time and saves document. Even if user on the client modify timestamp it will be updated with the version from the server. So not big deal here. The same is true for any other use case. When working with Meteor applications there were many situations where I had to choose between methods and allow/deny rules for security. And almost always I chose methods. It's a lot saver to use them.
Validation. Ok so let's think about it. Validators are in fact functions they are not atomic right now. So, they for example don't check whether string's min length is 5 but they check many string rules. If it's string, min string length, max string length. What are you proposing is creating atomic validators. So we would have to split it in individual checks: isString()
, gt(5)
, lt(5)
etc. I like such concept but how should it look like? It couldn't look like you presented: [field('title'), gt(5)]
because it would require validation functions like isString
or gt
to be global and I don't want to clutter global namespace. And if we would use Astronomy namespace it would be looooong: [Astronomy.Validators.field('title'), Astornomy.Validators.gt(5)]
or something like this. Instead the only way to achieve that is using function with the this
context equal to Validators module. And it would look like this:
{
fields: {
/* ... */
},
validators: {
firstValidator: function() {
/* this - introduces functions that developers can use to validate */
this.field('title').isString().length().gt(5).lt(10);
},
secondValidator: function() {
this.field('pass1').eq('pass2');
}
}
}
We use chaining here and non of validation function return true/false. These function could set internally set some flag and create error message. This approach is not functions composition but I still don't see possibility to replace it with functions composition and to be it easily readable. I would like to see some example that could work :).
@VladShcherbin I definitely don't want to reinvent the wheel :) I will try to make things as easy as possible. I had been using Doctrine validation for a long time on which I based my approach and I've never had any problem with them. It's easy and readable. But there always can be some areas to improve. I don't know everything and I'm open to discussion :). But as I said I want to make it simple :)
And there is one more thing that would be hard to implement with functional composition, it's error messages customisation. Additional argument passed to function? Could cause mess...
I've found this library http://validatorjs.org/. It look like something worth checking. However checking two fields equality would require such construction:
{
fields: {
/* ... */
},
validators: {
firstValidator: [
Astro.Validators.Field('pass1'),
Astro.Validators.IsString(),
Astro.Validators.GreaterThan(5),
Astro.Validators.EqualTo(Astro.Validators.Field('pass2'))
]
}
}
Each Astro.Validators.***
would return function that is composed with the next one. Maybe there is a lot of writing but we could create some shortcut for long Astronomy.Validators
namespace.
Each function would have to be aware of what field is it working on.
Hey @jagi!
Are you familiar with Rails ActiveRecord Validations? They have a very nice validation interface which is easy to learn, read and extend.
Taking some of those ideas, I could imagine an API syntax similar to this (just brainstorming):
Post.validates('title', { presence: true, max: 20 });
Post.validates('releaseDate', { type: Date });
Post.validates('category', { in: ['software', 'gadgets', 'security'] });
In my view the validators are the constructs which actually run the validations (like type, presence, in, etc.). They are not added to the Astronomy Class, they are just used. Users could define their own validators if they want:
Astronomy.defineValidator('email', {
validate: function (field, doc) {
// check if field is an email, add an error to the doc if not
},
message: "needs to be an email"
});
And then use it like so:
Post.validates('authorEmail', { presence: true, email: true });
Error messages could be stored in a special property on the document object. For example:
var post = new Post({ title: 'New post' });
post.errors; // => undefined
post.validate(); // => false
post.errors; // => { authorEmail: 'needs to be an email', body: 'must be present' }
Using reactivity and some Blaze helpers, the error messages could be displayed to the user automagically.
For custom validations, the validates
method could take an anonymous function as mentioned before:
Post.validates(function (doc) {
if (doc.body.length < doc.title.length) {
doc.addError('body', 'may not be shorter than title');
}
});
oops, didn't see the validations repo
@jagi why are you using such crazy markup? let's see
Post = Astronomy.Class({
name: 'Post',
collection: Posts,
transform: true,
fields: ['title'],
validators: [{
type: 'String',
options: {
field: 'title',
min: 10
},
messages: {
min: 'Title is to short!'
}
}]
});
so, this validator checks for a string, min 10. I guess, two fields would be:
options: {
field: ['title', 'slug']
min: 10
}
What if I want to have the same validator with min:5 for a slug ? Copy-Paste and change the min value ?
{
type: 'String',
options: {
field: 'title',
min: 10
},
messages: {
min: 'Title is to short!'
}
},
{
type: 'String',
options: {
field: 'slug',
min: 5
},
messages: {
min: 'Title is to short!'
}
}
That will be a horrible mess!
Why you go with specifying the validator and passing fields there? We should have a better syntax!
smth more for human like:
validators
title
type: String
min: 10,
max: 30
slug
type: String
min: 5
I've got a php background with Laravel and validators there are awesome. I don't know, if it is possible to create in js smth similar. The syntax there is like:
validators:
title: 'alpha|min:10|max:30',
slug: 'alpha|min:5'
This syntax is very neat, so we can make big validations very compact. eg:
public static $rules = [
'title' => 'required|alpha|min:5|max:200|unique:mass_media_articles',
'slug' => 'required|slug|min:3|max:70|unique:mass_media_articles',
'contents' => 'required',
'published_at' => 'required|date',
'original_author' => 'required|alpha',
'original_website' => 'required|alpha',
'original_url' => 'required|url|unique:mass_media_articles',
'seo_description' => 'required|alpha|min:50|max:200|unique:mass_media_articles',
];
Here, you can see clearly, what field with what rules is validated. If we use current validators, it will be a mess and a pain to find what is validated and where.
This way it is extended:
Validator::extend('slug', function ($attribute, $value)
{
return preg_match('/^[a-z\pN-]+$/u', $value);
});
I don't know, if it is a good idea to parse a string in JS for rules, but this looks very clean. Similar, but much longer would be smth like:
title: {
required: true,
alpha: true,
min: 5,
max: 200,
unique: mass_media_articles
}
Longer, but still better than current version.
We definitely need to make validation work that way:
field - validation rules
Please, consider that :)
@jagi
You just create Meteor method (for both environments client and server) that just saves some document and generate timestamp on save.
Well what does doc.save()
do? It probably uses .insert
or .update
which would require allow-deny validation.
would require validation functions like
isString
orgt
to be global and I don't want to clutter global namespace.
Leave that to the developer! Its trivial. Use _.isString
and gt = _.curry((n, str) -> str.length > n)
. This library doesnt need to provide all that.
And rather than [isString, compose(gt(2), map(len))]
, you would want to do something like and(isString, compose(gt(2), map(len))
And there is one more thing that would be hard to implement with functional composition, it's error messages customisation. Additional argument passed to function? Could cause mess...
Well now its a question of what we want this package to do. A simple true/false validation would make sense for something things. For other things, it would make sense to throw a Meteor.error so we can catch the error.reason through a meteor method. We could return an object with {success:false, reason: 'something'}
. I think the proper way of doing this the functional way is with the railway method:
http://fsharpforfunandprofit.com/rop/
This is similar to the Either
monad.
https://www.npmjs.com/package/monads.either
Check this out:
TwoWayInput = function(value) {
this.value = value
this.error = null
}
TwoWayValidator = function(func, msg) {
return function(twoWayInput) {
if (!twoWayInput.error && !func(twoWayInput.value) {
twoWayInput.error = msg
}
return twoWayInput
}
}
Then we can do something like:
checkName = compose(
TwoWayValidator(_.gt(2), 'Name must have more than 2 characters.'),
TwoWayValidator(_.isString, 'Name must be string'),
TwoWayInput
)
checkName('Dude').error // null
checkName(2).error // "Name must be string"
checkName('A').error // Name must have more than 2 characters.
Now we can do whatever we want to checkName
. We have an error message specific to each validator. We can check if error is null to see if the validation passed.
I think the question here is how exactly we want to use this. Would you expect validate to return true or false? Or an object with an error message? Perhaps this is two different functionalities entirely.
The point I wanted to make here though is that if you give someone a function, then the developer can do whatever they want. They can hook in wherever they want and use whatever programming pattern they want. I still recommend using a functional form of validation rather than using an object description.
@wursttheke is basically describing this exact thing, but in a less functional way. Astronomy.defineValidator
is basically the same thing as TwoWayValidator
. And the in the object description, in: ['software', 'gadgets', 'security']
is basically using partial application of the in
function. Lets call it oneOf
:
f = oneOf(['software', 'gadgets', 'security'])
f('software') // true
f('asdf') // false
Now we can do something like TwoWayValidator(f, 'Not a valid category')
I understand the functional programming is a little scary at first, but its really clean, and in the end, it gives the developer more power because they aren't constrained to the framework.
Ok let's make this discussion a little be more clear :). There are few requirements for validators:
field: [validators]
etc. will not work.Field "xxx" has to be at least 5 character long
or Fields "pass1" and "pass2" have to be equal
.Are you agree with these requirements? If yes then let's just talk only on solutions that provide such features :).
Second thing is that we have two proposals if it goes about how validation rules should be written.
field: "rule1|rule2#param|rule3"
etc. or something that I've proposed and what comes from Doctrine library.[{
type: 'String',
options: {
min: 5
max: 10
},
messages: {
min: '...',
max: '...'
}
}]
If I would have to choose I would use second approach because I can pass custom messages what I can't do in the first example. Ok, it's a lot more of writing but I can customise it :).
gt()
, isString()
etc. and leave to developer the task of creating validation rules. He/she would have to check if something is string and throw proper error message. Or we could implement inside those validators some awareness of on what field(s) are they working.And this second approach seems to be for me more reasonable.
So let's vote on solution and later we will move on :).
To answer @ccorcos question. Yes, it uses insert, update function to save document into database. However, you have take in mind that allow/deny rules are only used on the client. If developer wants to save document directly into collection. When you are using (client/server) methods you can forget about allow/deny rules. In such situation we can assume that we don't care about security on the client. We can add some document to the collection on the client (it's only simulation). The same method is executed on the sever and there we check validation rules too, we add timestamp. And if everything is ok, then we pass added object to the client. And even when client tried to hack the script, the only correct document is the one that was saved on the server.
For me, the first solution would be a killer feature. It is very simple and intuitive.
rules:
rules
name: 'required',
password: 'required|min:8',
email: 'required|email|unique:users'
custom messages? no problem. api
rules
name: 'required',
password: 'required|min:8',
email: 'required|email|unique:users'
messages
// change all required messages
'required': 'Ha ha! You missed the :attribute!',
// change only specified message
'name.required': 'Ooops, you need to input your name'
custom validators
Astronomy.addValidator('slug', {
validate: function (field, doc) {
// slug regex rule here
},
message: 'Please, input the correct url'
});
@VladShcherbin it's not gonna work. I wrote about it. This approach can only work on single fields. How would you check if two fields are equal or one field is grater than other? I will be trying to implement functional approach today. I just want to check how it could work. However I'm still waiting for some idea on how to implement multiple fields validation.
I will let you know when I will have something to show.
@jagi hmm, no?
field_one: 'required|same:field_two'
field_two: 'required'
same with equal, less, more, _requiredif, etc
@VladShcherbin ok I could create such validator but it's still not flexible approach. In Doctrine I had also possibility to create more complex validators like for example OR validator where object is valid when one of two validators passed validation test and many other complex solutions. In the solution required|same:field_two
etc. validation is linear and it's hard to pass some extra options. Such string needs parsing and it's error prune. Of course it easily readable, but we have to have solution that is in the same time readable and powerful.
Right now I see that there are two camps :) (not including mine :P) functional approach and linear string format. And because of that I will probably create two modules astronomy-functional-validators
and astronomy-descriptive-validators
to fit all needs :)
@jagi yes, you can't use extra options but you can make more custom validators to fit your needs ;)
That is why we have validators like: alpha, alpha_num, alpha_dash, etc. The default validators should cover like 90% of the user needs, 10% of custom validators is okay.
Btw, I think you need to choose one version of validators and stick with it. This way, it is easier to maintain, debug, test and have same validators from project to project.
And one more question I wanted to ask. In what format would you like to have error messages?
Field "XXX" should be at least X character long
error = {
field: "age",
validator: "gt"
value: 3,
expected: 5
};
It would inform that title
field is to short and should be at least 5 characters long. This way we don't force developer to use library error messages but he/she can create error messages in multiple languages.
Another example:
error = {
fields: ["pass1", "pass2"],
validator: "equal"
values: ["password1", "pass"]
}
Or something similar. We can discuss the format or messages.
@jagi I think, the first variant with the messages is fine. We should probably think, how to handle multilingual websites to show the correct ones
My TwoWayValidator
function can handle whatever error message you want:
TwoWayValidator(_.gt(2), 'Name must have more than 2 characters.')
or
TwoWayValidator(_.gt(2), { field: "age", validator: "gt" expected: 2, })
or if you wanted more information, you could set the error message as a function:
TwoWayValidator = function(func, errFunc) {
return function(twoWayInput) {
if (!twoWayInput.error && !func(twoWayInput.value) {
twoWayInput.error = errFunc(twoWayInput.value)
}
return twoWayInput
}
}
TwoWayValidator(_.gt(2), function(x) {
return {
field: "age",
validator: "gt"
expected: 2,
value: x.length
}
)
Thats just overkill if you ask me though. I think a simple message is best like in the first example.
For validating fields against each other, I think we should just bind the function context to the object.
TwoWayValidator = function(func, msg) {
return function(twoWayInput, context) {
if (!twoWayInput.error && !func.bind(context)(twoWayInput.value) {
twoWayInput.error = msg
}
return twoWayInput
}
}
Now we can create a validator like this to compare two different fields.
passwordMatch = function(value) {
return value === this.verify
}
TwoWayValidator(passwordMatch, "Password don't match.")
I'm already working on something you proposed and it seems to work great and there is much less code. Example validation rules can look like follows:
validators: {
title: Astronomy.Validators.And(
Astronomy.Validators.IsString('Title has to be a string'),
Astronomy.Validators.MinLength(5, 'Title has to at least 5 characters long'),
Astronomy.Validators.MaxLength(10, 'Title has to at most 10 characters long')
),
pass1: Astronomy.Validators.And(
Astronomy.Validators.IsString('Pass has to be a string'),
Astronomy.Validators.EqualTo('pass2', 'Passwords have to be equal')
)
}
I like your idea of And
validator. We can also create Or
and other validators and just nest them to create more complex rules. Making validators functions instead of objects made code much cleaner.
I will also create some aliases so that it wouldn't require a lot of writing. Right now there is alias Astro
for Astronomy
.
Functional validation will be the main module. I will also create two more modules with different approaches that we've discussed here and both will be using functional validation internally but exposing different APIs for developer. Thanks to that everyone will be happy :).
I have also one more idea for making writing validation rules shorter. Take a look at example before and after examples:
Before:
validators: {
title: Astro.Validators.and(
Astro.Validators.isString(),
Astro.Validators.minLength(5),
Astro.Validators.maxLength(10)
),
pass1: Astro.Validators.and(
Astro.Validators.isString(),
Astro.Validators.equalTo('pass2')
)
}
After:
validators: {
title: function(validators) {
return validators.and(
validators.isString(),
validators.minLength(5),
validators.maxLength(10)
);
},
pass1: function(v) {
return v.and(
v.isString(),
v.equalTo('pass2')
);
}
}
In the AFTER example validators are not global. Instead they are passed as first argument of function, so we can name validators list, it can be validators
or just short v
.
Developers willing to create their own validators will use common for Astronomy syntax:
Astronomy.Validator({
name: 'isString',
validate: function(value, fieldName, message) {
if (!_.isString(value)) {
throw new Error(message || 'The "' + fieldName + '" field has to be a string');
}
}
});
It would create validators.isString
validator.
Why such way of creating validators and defining them in the schema? Thanks to that I have explicit control over the process of creating and executing validators. I can check if all the necessary fields where provided. I can throw errors before validators are even used.
@jagi this validators look ugly as hell :( please, consider adding string validators as well.
@VladShcherbin read carefully what I wrote. This form of validation will be default one but not the only one. There will also be two more validation modules with string and object formats. However internally both approaches will be using functional validators. But you don't have to worry about that. You will just provide simple string in the form of
validators: {
title: "isString&minLength=5&maxLength=30"
}
or something similar. Do you like such approach?
@jagi yes, sure. it would be awesome to have that api
title: 'string|min:5|max:30'
thank you! you are doing a great job! cant wait to see forms with this validators
@jagi, This looks good:
validators: {
title: Astronomy.Validators.And(
Astronomy.Validators.IsString('Title has to be a string'),
Astronomy.Validators.MinLength(5, 'Title has to at least 5 characters long'),
Astronomy.Validators.MaxLength(10, 'Title has to at most 10 characters long')
),
pass1: Astronomy.Validators.And(
Astronomy.Validators.IsString('Pass has to be a string'),
Astronomy.Validators.EqualTo('pass2', 'Passwords have to be equal')
)
}
Very functional and uses the railway method nicely. All that namespacing does look frustrating. What I would do is leave that to the developer. Write in the readme about how TwoWayValidator
works (probably call it Astronomy.Validator
) and how TwoWayInput
works (probably call it Astronomy.ValidatedField
or something). Then people can construct their own methods:
mustBeString = Astronomy.Validator(_.isString, 'Title has to be a string')
minLength = function(n) {Astronomy.Validator(function(x) {return x.length >=n}, ''Title has to at least " + n.toString() + " characters long'')}
Then we can do something like:
validators: {
title: and(mustBeString, minLength(5)),
The point is that we shouldn't be re-inventing the wheel here. I think that this framework should define a ValidatedInput monad and let the developer do the rest however they please. For example, there already exist libraries for all of these functions. Check out Ramdajs
So why constrain the developer and give yourself so much overhead by creating all of these within the framework?
That said, I think should definitely create a separate package for a bunch of these validators so you can reuse them as you please. This should also please @VladShcherbin because he can write his own parser for the syntax he wants to use.
rules = [
{
regex: /string/
func: _.string
msg: 'Must be string'
},
{
regex: /min:([0-9]*)/
func: compose(R.gte(10), Number)
msg: 'Must be greater than 10'
}
]
parser = function(str) {
return function(value) {
for (token in str.split('|')) {
for (rule in rules) {
x = token.match(rule.regex)
if (x) {
valid = x.func.apply(null, x.splice(1).concat([value])
if (!valid) {
return TwoWayInput(value, rule.msg)
}
}
})
})
return TwoWayInput(value, null)
}
The match returns any groups you want to pass as arguments to the function. They're passed as a string so we want to make sure that turns into a number.
Anyways my point is that @VladShcherbin can be very happy with this functional approach as well. He can create his own syntax just like this!
validator = {
title: parser("string|min:5|max:30")
}
And that is the beauty of functional programming.
@ccorcos I don't think, this is the beauty. I like KISS way of things.
I really don't want to reinvent the wheel and write this parsers for just a simple string/number/min,max/same/etc validation.
These are reusable components and they have to be as simple as possible. The simplicity attracts people, noone wants to write tons of code to validate a string and its max length.
I believe, for crazy complex validation you should create custom validator rule and use it anywhere you want.
compare this
validators: {
title: Astronomy.Validators.And(
Astronomy.Validators.IsString(),
Astronomy.Validators.MinLength(5),
Astronomy.Validators.MaxLength(10)
),
pass1: Astronomy.Validators.And(
Astronomy.Validators.IsString(),
Astronomy.Validators.EqualTo('pass2')
)
}
and that
validators: {
title: 'string|min:5|max:10',
pass1: 'string|same:pass2'
}
For me, this is the true beauty. :v:
@VladShcherbin I agree with you that you have a nice scheme there. The point I am making is that the interface with the framework should be as flexible as possible. Using a functional approach with the TwoWayInput monad provides that flexibility to validate both ways along with ways we've never thought of.
I don't think people should have to re-write all of this code every time. This code should be written once and reused. They could be package that extend this package.
It is really that bad if the only difference is a function call?
validators: {
title: f('string|min:5|max:10'),
pass1: f('string|same:pass2')
}
The functional approach with the monad is the least opinionated and the most flexible. I have nothing more to say.
@ccorcos yes, I understand that, but I don't see the point of using it.
We can make a new validation rule, name it, write whatever function we want and use the same way. Yes, we gain more flexibility, we can use more params, but, honestly, do we really need it?
In your example we need to repeat f('...'). It is okay, but for what purpose. If we can get rid of it - why won't we :)
Both ways are fine, but I will definitely pick smaller one every time just because default validators cover 90% of the cases for most developers and I can write 2-5 custom validators when I need. The simplicity is awesome everywhere, in design, in code.
We have such validators in Laravel (PHP) and everyone loves it, I have never heard someone complain, that validators are bad or switch to another ones. So I wish @jagi can bring such simplicity here.
I'm working on validators and it's going well. Everyone will be happy :). Stay tuned :).
It's not fully documented yet but you can start using it. Functional validators: https://github.com/jagi/meteor-astronomy-validators Simple validators with string rules: https://github.com/jagi/meteor-astronomy-simple-validators
@jagi this is f. awesome!
Way to go @jagi. This is very nice!
For your examples, I'd use an alias so you can do V.isString and stuff because it is a lot it type. The string stuff looks great too. Very nice. How'd the railway monad work out?
Sent from my iPhone
On Apr 26, 2015, at 13:30, jagi notifications@github.com wrote:
It's not fully documented yet but you can start using it. Functional validators: https://github.com/jagi/meteor-astronomy-validators Simple validators with string rules: https://github.com/jagi/meteor-astronomy-simple-validators
— Reply to this email directly or view it on GitHub.
@VladShcherbin and @ccorcos thanks. More features to come. I will also do a little work on making documentation better. When the core version is finished I will also create video tutorial.
I haven't tested railway monad.
Right now I'm closing this issue. If you have any suggestions, feature request etc. please open new issues.
Thanks for collaboration :)
@jagi thank you a lot for your work.
Now, when we have awesome validation, maybe you can provide an example with form actions (like insert, update, etc) ?
I think, it is almost the time, when I can replace like 4-6 packages with the Astronomy and give a full test.
Yes of course :). I will improve documentation and provide such examples. I'm also making some refactoring and improving field types module, so there will be a little change. I think that this weekend it will be ready with examples of using validators in forms. Stay tuned :)
@jagi sounds awesome! can't wait.
Ok, I've prepare example project using validators. You can find repo here: https://github.com/jagi/meteor-astronomy-examples and the working example on Meteor server here: http://astronomy.meteor.com/
Tomorrow I will describe how it works. But right now you can investigate the code. It's very simple.
@jagi thanks, it works fine for me. I'll try to setup everything today-tomorrow and run a full test. Thank you!
Hi, I have question to you about validators. I'm working now on implementing them as a module for Meteor Astronomy. Here is the repository. It's not finished yet and I need your support on how it should work to be the most intuitive and easy in use. Please take a look at read me in the repository.
Let me describe here how it works. Right now, we can define array of validators in the class schema. In validator definition, we have to provide name of the
field
on which validation should take place, type of the validator, some options if required and error messages. We can also add validators that works on many fields like Compare validator. To validate object we callvalidate()
function on given object. Now few questions arises.save()
method on the object?true
orfalse
return value from thevalidate()
method? And later you would like to call function like for instanceobject.getErrors()
to get all errors.object.validate('comparePasswords')
?If you have any other idea I'm open for discussion :)