Validation beyond foul
Usually, only good is said about validation in rails. Today we’ll talk about some situations where the system crashes.
When registering a user, we usually want to make a password confirmation. No problem, add: confirmation => true. After some time, a mobile application appears in the site, in which registration is also implemented, but there is no confirmation of the password there. What to do in this case?
The solution under the cutter.
The most popular answer: in the controller we put password_confirmation in the hands. Wait a moment. What does password confirmation have to do with the user model? What is password confirmation in general? And the confirmation of the email (yes, some do so)?
Same registration. Email is usually required. Product Owner adds the task of integration with the social. networks. After looking at the authorization documentation via Twitter, we understand that we can’t see the email. Someone, of course, after authorization will ask you to enter an email, but in our case the manual is against. It is also necessary to authorize a point without an email, but the registration form must require an email anyway. And what to do in this case?
Answers I heard:
Is email mandatory for a user model with such requirements? Answer: No, our application should work correctly even in its absence.
But what about the registration form?
If you look closely at the first and second situations, it becomes clear that the form is something more than an html piece (in the api, by the way, there is no html, but there is also a “form” there). So it is the form in specific situations that should validate the confirmation of the password, the presence of an email because this behavior is not of a model, but of a specific form in a concrete representation. The funny thing is that there is no such problem in many other frameworks. The form model is in django , zend framework , symfony , yii . And there are even attempts to do similar in rails. You can also try to implement this functionality through ActiveModel.
What personally upsets me in this whole story is that the rails developers themselves show the wrong way to solve these problems. They add validators like: confirmation => true, actually violating the basic principle of mvc: the model is not dependent on the view.
We have found a solution for our projects, and so far it generally suits us, and at the same time solves another well-known problem . It lies in the fact that for specific forms, we create the heirs of our models and actually work through them. Of course, this is the most natural hack, but in an attempt to write separate forms, I ran into difficulties when implementing nested forms and postponed this matter until better times.
1. Make the types folder in apps.
2. Add base_type.rb there
3. Create the desired type. Example:
So, using simple inheritance, we solved the task of custom validation depending on the current view and requirements for it. True, you will have to fix the ties of some gems to the class name, not model_name (carrierwave, for example, builds paths based on class), but so far it has been resolved quite easily.
Why is it called Type and not Form? The api has no form as such, but the requirements are the same. And the name Type is taken from the symfony framework.
The last example shows that another problem related to attr_accessible is being solved. Of course, you can argue that you can use the: as option for this, but in reality it violates encapsulation by adding information about the overlying layer to the model.
Situation times
When registering a user, we usually want to make a password confirmation. No problem, add: confirmation => true. After some time, a mobile application appears in the site, in which registration is also implemented, but there is no confirmation of the password there. What to do in this case?
The solution under the cutter.
The most popular answer: in the controller we put password_confirmation in the hands. Wait a moment. What does password confirmation have to do with the user model? What is password confirmation in general? And the confirmation of the email (yes, some do so)?
Situation two
Same registration. Email is usually required. Product Owner adds the task of integration with the social. networks. After looking at the authorization documentation via Twitter, we understand that we can’t see the email. Someone, of course, after authorization will ask you to enter an email, but in our case the manual is against. It is also necessary to authorize a point without an email, but the registration form must require an email anyway. And what to do in this case?
Answers I heard:
- we skip validation in registration.
- put a fake email - qwerty@twitter.fake.com.
- by hacks we cut out the error message from errors and pretend that everything is fine 0_o.
- Depending on the incoming parameters, custom validation is triggered inside the model.
Is email mandatory for a user model with such requirements? Answer: No, our application should work correctly even in its absence.
But what about the registration form?
The truth is somewhere near
If you look closely at the first and second situations, it becomes clear that the form is something more than an html piece (in the api, by the way, there is no html, but there is also a “form” there). So it is the form in specific situations that should validate the confirmation of the password, the presence of an email because this behavior is not of a model, but of a specific form in a concrete representation. The funny thing is that there is no such problem in many other frameworks. The form model is in django , zend framework , symfony , yii . And there are even attempts to do similar in rails. You can also try to implement this functionality through ActiveModel.
What personally upsets me in this whole story is that the rails developers themselves show the wrong way to solve these problems. They add validators like: confirmation => true, actually violating the basic principle of mvc: the model is not dependent on the view.
We have found a solution for our projects, and so far it generally suits us, and at the same time solves another well-known problem . It lies in the fact that for specific forms, we create the heirs of our models and actually work through them. Of course, this is the most natural hack, but in an attempt to write separate forms, I ran into difficulties when implementing nested forms and postponed this matter until better times.
1. Make the types folder in apps.
2. Add base_type.rb there
module BaseType
extend ActiveSupport::Concern
module ClassMethods
def model_name
superclass.model_name
end
end
end
3. Create the desired type. Example:
class UserEditType < User
include BaseType
attr_accessible :first_name, :second_name
validates :first_name, :presence => true
validates :second_name, :presence => true
end
So, using simple inheritance, we solved the task of custom validation depending on the current view and requirements for it. True, you will have to fix the ties of some gems to the class name, not model_name (carrierwave, for example, builds paths based on class), but so far it has been resolved quite easily.
Why is it called Type and not Form? The api has no form as such, but the requirements are the same. And the name Type is taken from the symfony framework.
The last example shows that another problem related to attr_accessible is being solved. Of course, you can argue that you can use the: as option for this, but in reality it violates encapsulation by adding information about the overlying layer to the model.