Tuesday, October 18, 2005

Further Ruby on Rails reading. Validation is in the right spot.

I've just finished Chapter 6 of, "Agile Web Development with Rails". It took a little longer than expected because Rails wasn't jiving with my MySQL install, and the new installation of MySQL 4.1 was hanging on to some old settings from MySQL 4.0. Thanks Windows registry! (errrr)

Anyway, I found something I really like about the Ruby on Rails framework. It places the validation of form fields/member variables (of a model object) in the right place. :)

When I first started with web development I was writing really bad PHP scripts, then I graduated into hacking (poorly) Coldfusion. All the while writing the validation of my forms in PHP and Coldfusion code, usually embedded into the same file as the form. In my worst days a code file would look like this:

if form submitted then validate
//validation "logic"
...if form valid write to database
...//display confirmation

else display empty form
//display form, set action to self (so it posts to itself)

When I learned about the Fusebox framework I began to see the error of my way. (Fusebox is a framework to help organize your PHP or CF development.) Fusebox breaks apart your code files into "action" and "display" files. All "logic" goes in the "action" files and all the display stuff goes in the.... you guessed it, "display" files. The two types of files are signified by their prefixes, "act_", and "dsp_". Working with Fusebox starts to move you toward posting to somewhere other than the form and has you begin to break apart your "logic" and your "display" code. But all of your logic, validation and otherwise is stuck inside of the view tier - the PHP or CF code that executes when they are requested through the URL. You start running into problems when you want to reuse some of that functionality and business logic. Now, you can start breaking things into functions (or in Coldfusion, Custom Tags), which will help in calling the business logic in more than one place. But as your business logic becomes more complex, or just plain larger in size, the procedural programming model, and all your business logic only being accessible through functions or file includes, can become constraining. If another system on the same box wants to re-use or interface with your existing code base.... Um, you can have fun with that one!

Once I started studying Java and object-oriented programming I saw a couple of things.

You want all your application "logic" to live in "objects". (The objects that implement your business logic are usually referred to as "business objects".) All of the logic surrounding a particular object should live as close to that object as possible. It's a great organization step in programming. I won't give you the whole OO pitch, but by using objects it becomes easier to deal with complex business logic, encapsulate your data access (DB? Web Service? Flat File? Your front-end and controllers won't care, it's only concerned with the objects.), and allows for easier re-purposing of your logic (the objects) for multiple views.

Where am I going with this?

As I mentioned before, in the past (a long time ago, I swear) all of my validation logic was in my "view" layer. The scripting languages of Coldfusion or PHP. When I learned how to use the Spring MVC framework (Java/JSP) I learned about how you can build the validation into the model of your application. When I say "model" of your application I'm referring to the business objects, the place where your logic should reside.

In Spring MVC a form and it's submission would follow this pattern:

1) Controller intercepts a request (framework does this)
2) Controller figures out if it's a form POST or if the user has just browsed to the page (framework does this)
3) If the user has landed on the form for the first time it renders the form. (framework does this) Otherwise #4
4) The Controller fires off the Validator object that is associated with this Controller. (framework does this, but you write the Validator object and the validation code yourself.)

The Spring people tell you to put the Validator object in the model of your application. For example, in Java you might organize your code in the following package (folder) structure:

com.michaelsica.system.SomeBusinessObject
com.michaelsica.system.SomeBusinessObjectValidator

com.michaelsica.website.SomeBusinessObjectFormController

Everything under, "com.michaelsica.system", would be considered the "model"/"business objects". The code under "com.michaelsica.website" is code that is specific to the web site.

5) And then on the file system you'd have a JSP file that the the Controller would use to render everything.

Now if you needed another view for this business object, let's say for a remote interface, you'd create another package that may look like this:

com.michaelsica.remoteinterface.SomeBusinessObjectRemoteController

The way the above is setup is great because your validation logic is a part of your logical "model". It can be easily re-used in both the ".website." and ".remoteinterface." packages. Now you CAN actually have the validator class in the ".website." package and re-use it in the ".remoteinterface." package, but that could get confusing. You would be creating an ackward dependency between 2 packages that shouldn't even know about each other. (The web site doesn't care about the remote interface and the remote interface doesn't care about the web site. Don't create weird dependencies like that.)

Now, how does Ruby on Rails do this better?

In Rails, the validation logic is actually apart of the business object itself.

In my above Java/Spring MVC example I had two objects, SomeBusinessObject and SomeBusinessObjectValidator. The SomeBusinessObject would typically contain any business rules related to it, and the Validator object would contain ... validating rules. (Like making sure all the fields/member variables were populated with properly formatted data, etc.... FYI, Spring MVC has a ton of helper functions for this in it's ValidationUtils class that you can use in your Validator object.)

The Rails equivalent would put all the validation logic in the SomeBusinessObject, and it would be super easy to read. Like this (taken from the "Agile Web Development with Rails" book):

validates_presence_of :title, :description, :image_url
validates_numericality_of :price
validates_uniqueness_of :title

I don't even have to explain to you what that code is doing. If you're a programmer you can probably figure it out.

So what's the big deal?

Ruby on Rails is not only putting the validation in the model, it's forcing you to put it in the specific business object that needs it. 1 less file to worry about (compared to Spring MVC), and that validation logic will ALWAYS be with the business object.

The Rails framework actually knows when the object is invalid, and it uses that information to display errors on your form. Rails comes with a nifty sub-framework called scaffolding. It auto-generates a GUI for you so you can quickly begin working with your model/business objects and data.

I looked through the autogenerated GUI code and this is the line that spits out a the list of errors.

<%= error_messages_for 'product' %>

('product' is the name of the business object this form was created for)

The scaffolding GUI puts a red outline around the form fields that are invalid. The form fields are bound to the model member variables like so:

<%= text_field 'product', 'title' %>

('product' again being the business object, and 'title' being the member variable of the product object. With the scaffolding framework it automatically creates 1 form field for every member variable of your business object. Obviously you take out the stuff you don't want on the form. The scaffolding isn't even meant to be used as your final GUI. It just gets you going.)

The "text_field" operation is looking at the member variable inside the model object. When it goes to render the HTML for the cooresponding form field it adds a little extra HTML/CSS to get the red outline effect.

This binding between the view and the business object is very powerful because your forms are direct representations of your business objects. It's a safe route because if anything needs to change inside your business object, the change happens in the business object and the form continues to operate against the business object's existing interface. (An example of a change would be that one day you your data is stored by calling a 3rd party's web service, instead of your own database.)

The point of this very long blog post is that I really like the way Ruby on Rails handles the placement of the object model's validation logic. :)

0 Comments:

<< Home