Giter VIP home page Giter VIP logo

Comments (7)

sylvainpolletvillard avatar sylvainpolletvillard commented on May 18, 2024

Hi Jose,

Ah yes, this one is technically not a bug but I can understand that it does not seem intuitive.

You may have noticed that Extra.test(input2) === true. The difference between test and validate is that validate expects a model instance, and does not provide any kind of automatic casting or duck typing. validate is mainly used internally, it is called whenever a change is detected on your model instance.

TL;DR: test tries to create a model instance from the object passed (and call validate internally on the created instance) then return true if no errors are catched.

The reason I exposed this validate method is that some specific modifications are not detected (example: extending an array through an index assignment), so the user needs a way to manually call this validation step. In practice, it has very few usecases.

I will improve the API documentation to make things clearer

from objectmodel.

sylvainpolletvillard avatar sylvainpolletvillard commented on May 18, 2024

test - model.test(value)
Returns true if the value passed validates the model definition. Works with duck typing.

validate - model.validate(instance, [errorCollector])
Manually triggers the validation of a model instance over its definition. It is automatically done whenever an instance property changes.
Note that contrary to the test method, the object is not cast to its suitable model (a.k.a duck typing) before being validated.

better ?

from objectmodel.

joseandrespg avatar joseandrespg commented on May 18, 2024

Got you! Yep, I think that is clear enough.

The reason why we were using validate is because it gives us a list instead of a single error. Using new Model() gives you only one, and test no one.

So our practical case is we get an external input that we want to validate as the first step and it could have more than one failed validation, so we want to return the complete list of errors.

Are we missing another exposed method to only set defaults to the argument? Something like Extra.applyDefaults(input2). Then the combination of that method and validate will make the job.

Thanks for your time! Is really appreciated your quick responses!

from objectmodel.

sylvainpolletvillard avatar sylvainpolletvillard commented on May 18, 2024

Error collectors should collect all errors, not just the first one. Each error is displayed in a new line, maybe you missed the others ? See this example:

image

See Custom error collectors section for more info on how to collect errors as a list and do whatever you want with them.

To get what you want (cast + validation), calling the model constructor is indeed the best way to do it.

About applyDefaults, remember that defaults is just a helper to set things on the model prototype. So Extra.defaults({ amount: 0 }); is equivalent to Extra.prototype.amount = 0.

Therefore setting the defaults means assigning the prototype. A way to do this and only apply defaults would be Object.setPrototype(input2, Extra.prototype). But I would recommend to always use the model constructor since it does additional controls and is the appropriate way to create model instances.

from objectmodel.

joseandrespg avatar joseandrespg commented on May 18, 2024

OK! you put me in the right path! Before we weren't using the errorCollector when creating an instance.

So, this should be the best way to do it:

   	/**
	 * Validates the input against a Model.
	 * It returns an instance if valid. Or a list of errors if invalid.
	 * @param Model
	 * @param input
	 */
	static validate(Model, input) {
		return new Promise((resolve, reject) => {
			if (!(Model instanceof objectmodel)) {
				return reject([new Error('Model should be an objectmodel class')]);
			}

			// before
			// if (Model.test(input)) return resolve(new Model(input));
			// return Model.validate(input, errors => reject(errors));

			// after
			const errorCollector = _.clone(Model.errorCollector); // backup
			Model.errorCollector = errors => reject(errors); // catch errors

			const instance = new Model(input);
			if (instance) {
				instance.errorCollector = errorCollector; // restore
				return resolve(instance);
			}

			return null;
		});
	}

Does it sounds right to you?

Thanks!

from objectmodel.

sylvainpolletvillard avatar sylvainpolletvillard commented on May 18, 2024

You made a mistake here: instance.errorCollector = errorCollector; // restore ; errorCollector is a property of the model, not the instance

Note that object model validation is synchronous but I guess you want to return a Promise for your convenience. So I would do it this way:

static validate(Model, input) {
	return new Promise((resolve, reject) => {
		if (!(Model instanceof objectmodel)) {
			return reject([new Error('Model should be an objectmodel class')]);
		}

		let errors = null;
		const initialErrorCollector = Model.errorCollector;
		Model.errorCollector = (errorsCatched) => { errors = errorsCatched; };
		const instance = new Model(input);
		Model.errorCollector = initialErrorCollector;
		return errors === null ? resolve(instance) : reject(errors);
	}
}

or with a try/catch block:

static validate(Model, input) {
	return new Promise((resolve, reject) => {
		if (!(Model instanceof objectmodel)) {
			return reject(new Error('Model should be an objectmodel class'));
		}

		try {
			const instance = new Model(input);
			resolve(instance);
		} catch(error){
			reject(error);
		}
	}
}

Of course if you're planning to always validate your model instances this way, it would be easier to globally change the errorCollector on Model.prototype. I assumed you made this for a specific reason such as form input validation.

from objectmodel.

joseandrespg avatar joseandrespg commented on May 18, 2024

Yes, you are right. We want to change the behavior to initially get the list, but we want to roll back to the initial errorCollector so we should get an exception if something is wrong.

The promise interface keeps our code more predictable as it's unified with the rest of validation methods (most of them asynchronous).

Your first example fits 100% what we are looking for, thanks so much!

from objectmodel.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.