http://www.developer.com/db/article.php/3712756/Advanced-Active-Record-Validations-with-Rails.htm
In the first installment of this two-part series, you examined the data validation features available within the Rails framework's Active Record implementation. Among other tasks, you learned how to use built-in methods such as validates_presence_of() and validates_length_of() to place constraints on user-supplied data and notify the user if these constraints are violated. Doing so goes a long way towards ensuring not only the validity of your data, but also helps to negate attempts to surreptitiously insert malicious data into the application. In this second installment, I'll introduce you to Active Record's validation callbacks. These callbacks allow you to perform even more powerful operations both prior to and following validation. As a basis for the examples found in this article, you'll continue using the Question model created in the previous article. I'll close the article with an answer to a reader's question regarding how to remove attribute names from the beginning of custom validation error messages. To refresh your memory, the corresponding questions MySQL table schema looks like this: Active Record comes equipped with six validation-specific callbacks that you can use to perform a wide variety of cleanup operations. These callbacks allow you to execute various procedures at predefined points as specified by the callback. I'll define each of the callbacks here, and then you'll work through several practical examples illustrating their use: Although I won't use them within any examples, a number of other useful callbacks are also available, some of which include: Be sure to weigh the meaning of each callback carefully, because of the subtle differences. For instance, if you're only interested in executing a certain procedure before validation of new database objects, you should use before_validation_on_create() instead of before_validation(), because the latter will trigger prior to validation for both new objects and for those being modified. You might recall that, at the conclusion of last week's article, I challenged you to use validates_format_of() to validate a phone number. The difficulty of doing so lies within accounting for a wide variety of phone number formats, (NNN) NNN-NNNN, NNN-NNN-NNNN, and NNN.NNN.NNNN among them. Sure, you could simply enforce a particular format and display an error message if the user doesn't follow it, or divide the phone number input field into three separate input fields, but the most user-friendly approach would be to allow the user to use whatever format he pleases and then strip out any non-numerical characters. You can strip out these characters using the gsub() method. To try it out, fire up the Rails console and execute the following command: In this case, the gsub() method replaces any character not found in the range 0-9 with nothing (essentially removing them), and returning what remains. You can use this approach to deal with any of the aforementioned phone number formats. Logically, you'll still want to verify that the user provided a total of ten digits, so you can strip these characters before validation by using before_validation_on_create(): Of course, because this happens before validation you're free to institute the typical validations required of a phone number, such as whether it exists in the first place (presuming you've decided to make this a mandatory part of user input), and whether it consists of 10 digits: Suppose you added another column, named priority, to the Question model. This column will be used internally to identify requests coming from a particularly lucrative client as particularly urgent. By parsing the email address, you can identify the domain name, and flag it as being of high priority. Because you would only want to do so when the email address has been deemed valid, you should use the after_validation_on_create() method. The following code should do the trick: In a real-world situation, chances are high you'll actually have several high-profile clients, and therefore might identify clients as of a certain priority within the Client model. In such a case, you could modify the above code to look to the appropriate client table and determine whether the support request is coming from a member of one of the high-profile clients. Nonetheless, the above example should give you ample indication of how to implement such a task. One of the oddities regarding how Rails displays error messages is the prefixing of the corresponding Model attribute name to the front of the message. For example, you'll receive this message if you leave the name form field blank when attempting to submit a support request: That works, but what if you wanted to use a somewhat more grammatically correct message, such as "Your first and last name are required."? Based on this default prefixing, the error message would look like this: A reader posed this question to me a few days ago, and after a bit of research I've located a great solution. To remove these prefixes, just install the Custom Error Message plugin. Then, all you have to do is prefix your custom message with ^, like so: Rails' Active Record implementation offers an unbelievable array of validation features that can go a very long way towards ensuring user input is provided exactly as you intend it to be. Hopefully, this two-article series helps you understand these sometimes cryptic features. W. Jason Gilmore is co-founder of IT Enlightenment. He's the author of several books, including the best-selling Beginning PHP and MySQL 5: Novice to Professional, Second Edition (Apress, 2006. 913pp.). Jason loves receiving email, so don't hesitate to write him at wjATwjgilmore.com.
Advanced Active Record Validations with Rails
November 26, 2007
+--------------+------------+----+---+-------+--------------+
|Field |Type |Null|Key|Default|Extra |
+--------------+------------+----+---+-------+--------------+
|id |int(11) |NO |PRI|NULL |auto_increment|
|name |varchar(255)|NO | | | |
|email |varchar(255)|NO | | | |
|phone |varchar(255)|NO | | | |
|contact_method|varchar(255)|NO | | | |
|message |text |NO | | | |
+--------------+------------+----+---+-------+--------------+
Active Record Validation Callbacks
Callback
When It Executes after_validation()
After all validation procedures are complete after_validation_on_create()
After validation of new objects after_validation_on_update()
After validation of existing objects being updated before_validation()
Before the object is validated before_validation_on_create()
Before validation of new objects before_validation_on_update()
Before validation of existing objects being updated
Callback
When It Executes before_create()
Before the object is added to the database before_destroy()
Before an object is destroyed before_save()
Before an object is saved to the database, either by way of creation or an update before_update()
Before an object is modified in the database Sanitizing a Phone Number
>> "(614) 999-9999".gsub(/[^0-9]/, "")
=> "6149999999"
class Question < ActiveRecord::Base
def before_validation_on_create
self.phone = phone.gsub(/[^0-9]/, "")
end
end
class Question < ActiveRecord::Base
def before_validation_on_create
self.phone = phone.gsub(/[^0-9]/, "")
end
validates_presence_of :phone, "can't be blank!"
validates_length_of :phone, :is=>10,
"must consist of 10 digits!"
end
Assigning Support Request Priority Based on Email Address
class Question < ActiveRecord::Base
def after_validation_on_create
address = self.email.split('@')
if address[1] == "example.com"
self.priority = 1
else
self.priority = 0
end
end
end
Removing Attribute Names From Error Messages
Name can't be blank!
Name Your first and last name are required.
validates_presence_of :name,
:message=>"^Your first and last name are required."
Conclusion
About the Author