views:

82

answers:

3

My Rails application has a number of forms. When Joe uses my app, I want each form to provide him with immediate visual feedback as to the validity of his input for each field. One field checks the format of his email address - pretty simple. But another allows him to associate the current resource with a number of other resources, and complex business rules apply. If the form is incomplete or invalid, I want to prevent Joe from moving forward by, for example, disabling the 'submit' button.

I could duplicate the validations that appear in my Rails code by writing JavaScript that does the validation in the browser as well. But this smells bad - any time business rules change, I'll need to update them in two places with two different languages and two sets of tests.

Or I could add a single method to the controller for the resource called 'validate'. It would accept form data in an AJAX request, and return a response that could then be used inside Joe's form to provide real-time validation feedback. Unlike the 'create' action, the 'validate' action would not change the state of the server. The only purpose of 'validate' would be to provide a validation response.

The dilemma is that I don't like adding actions to RESTful controllers in Rails. But I like even less the idea of duplicating validation code in two different contexts.

I noticed this SO question, which touches on this subject. But I'm not interested in a plugin or piece of technology. Nor do I consider this question necessarily Rails-specific. I'm more interested in how best to handle this kind of problem in general in a Web application.

I also notice this SO question, which doesn't include the constraint of maintaining a RESTful architecture.

Given the need to dynamically validate form data with complex business rules in a Web application, and the desirability of maintaining a REST-like server architecture, what is the cleanest, most maintainable way to accomplish both at the same time?

+1  A: 

I hope I understood correctly, but you could send the javascript requests to the same create action. For example:

def create

  @data = DataObject.new(params[:data])

 if request.xhr?
    response = @data.valid? ? { :success =>  true } : { :errors => @data.errors }
    render :json => response
    return
  end

  # @data.save etc..

end

I'm actually using something like this in a multistep wizzard (one page form, with hidden css sections).

vise
@vise, interesting idea. Wouldn't this prevent a JSON-based web API from working in the future? Also, wouldn't this sort of break other RESTful conventions for the meaning of a POST request - that is, to create a resource on the server?
Rich Apodaca
You can always extend the to_json method if you wish. I'm also pretty sure you would use post if you would have a validate method as well. The fact is that the validate method resembles create a lot. Just bare in mind that this approach doesn't include any edit action, nor does it work too well if you have multiple controllers with the same requirement. So, I think it works well if your requirements are simple and you want to avoid overengineering without venturing too far from the rest principles.
vise
+1  A: 

I see no problem in creating a validator "processing resource" that can accept an entity and ensure that it passes all validation rules.

You could do this either with a global validator

POST /validator

where the validator will have to identify the passed representation and perform the appropriate rules, or you could create subresources,

POST/foo/validator

As long as these urls are discovered via hypermedia and the complete representation to validate is passed as a body of the request, I see no REST constraints being violated.

Darrel Miller
@Darrel, I considered this idea briefly. How would you mitigate excessive conditional logic to test for resource type in the create action of the controller (let's say 12 resources used this validator resource)? Maybe a hashtable that maps resource type to a method?
Rich Apodaca
@Rich Personally, I would go the subresource route to avoid the problem.
Darrel Miller
A: 

You are right not to duplicate validation logic on client (javascript) and server side). But adding validation resources also adds maintenance effort and costs server network calls. I like to do basic validation on client side (better user experience) and for data consistency on server side also.

Why don't you attach your model with validation metadata and then the forms + javascript for validation gets generated. On the final submit you also do a final validation on server side based on the model.

I am sure some generic validation logic is possible with Ruby. I guess also some existing validation frameworks could be reused.

manuel aldana