Error Handling and Validation

Standard Methods

First of all, take a look at Class ActiveRecord::Errors. Here are pretty decent error methods to start with.

Dirty Details

The error_messages_for method outputs neatly formatted error messages for a given object. The catch is that you need to specify the name of the instance variable here, not the object name itself. Take the following example.

If you have this instance variable in your controller:

  @user = PremiumUser.find(your_id)

Then you should use this code in your view to output the error messages:

  <%= error_messages_for 'user' %>

Gotcha: validating a hashed password

If you play it safe, you store your passwords in some encrypted format, e.g. an md5 hash. Here's the code to do this:

  def password=(password)      
    write_attribute(:password, Digest::MD5.hexdigest(password) )
  end
  ## NOTE: do NOT use this code

Now, you also want to safeguard your web application against brute force password guessing cracks. Nobody should be allowed to have a password of less than 8 characters. So, you add in a validation:

  validates_length_of(
    :password, :minimum => 8, 
    :message => 'too short (minimum 8 characters)'.t)

But as you test this code, you notice that even one character-sized passwords get validated! This is because every md5 hash generates a string with a predetermined length (usually 128 bits), which almost always exceeds your minimum password length.

So, how do you validate the password's length? By converting it into a hash after validation has taken place! Scrap the write accessor, and put in this code:

  def prepare_password
    self.password = Digest::MD5.hexdigest(self.password)         
  end 

How do we run this converter after validation? Simple. Put in this callback:

after_validation :prepare_password

Globalization and localization

Use the globalize plugin to localize the error messages. Insert the following code in rails_apps/your_rails_app/vendor/plugins/for-1.1/lib/globalize/rails/active_record_helper.rb. This is the only code that really works in Rails 1.1.6 (there are several other code sources, but I couldn't get them to work). I found the code on the Spongecell Techblog

def error_messages_for(object_list, options = {})
        return "" if object_list.nil?
        
        options = options.symbolize_keys
        
        bullets,main_obj_name = bullets_from_errors(object_list, options)
        
        if !bullets.blank?
           #default to standard error message. replace the ${NUM_ERRORS} with the number of errors.
           #options[:header_message] ||= "%d errors prohibited this %1 from being saved".t(nil,bullets.length).gsub('%1',"#{(main_obj_name || 'object').t}")
           #options[:header_message] ||= ("%d errors prohibited this %1 from being saved".t(nil,bullets.length)).gsub('%1',"#{(main_obj_name || 'object').t}")
           options[:header_message] ||= "The data could not be saved".t
           options[:header_message] = options[:header_message] % bullets.length
           
           content_tag("div",
              content_tag( options[:header_tag] || "h2", options[:header_message]) +
              content_tag("p", (options[:header_sub_message] || "There were problems with the following fields:").t) +
              content_tag("ul", "#{bullets}"),
              "id" => options[:id] || "errorExplanation", "class" => options[:class] || "errorExplanation"
           )
        else
          ""
        end
      end
      
      
      
      def bullets_from_errors(object_list,options)
    
        subs = options[:sub] ? options[:sub].stringify_keys : {}
        options[:skip] ||= []
        main_obj_name = nil
        
        #create a list of bullets (html <li> tags) by concatenating the objects
        bullets= [object_list].flatten.inject([]) do |msg_list,object|
        
          #if it is a string get the instance variable
          if object.kind_of?(String)
            main_obj_name ||= "#{object.to_s.gsub("_", " ")}" 
            object = instance_variable_get("@#{object}")
          elsif object
            main_obj_name ||= object.class.to_s.titleize.downcase
          end
              
          #if the object exists and responds have the errors object append each error to the list
          if (object && object.respond_to?('errors') && !object.errors.empty?) 
            object.errors.each do |attr, msg| 
                if (!options[:skip].include?(attr.to_s))
                  obj_name = (subs[attr] || "#{attr == 'base' ? '' : object.class.human_attribute_name(attr)}")
                  #replace the field names with the names specified in the subs hash
                  msg_list << content_tag('li', "#{obj_name} #{msg}".t("#{obj_name.t} #{msg.to_s.t}"))
                end
            end
              
            
          end
          msg_list
        end
        
        return bullets, main_obj_name
      end      

Validating Multiple Form Submits For One Model

Suppose you have a model that contains about thirty required fields, with fifty optional fields mixed in. Every interaction designer will tell you that you need to split up your eighty fields form into at least four separate forms. But as you do so, you notice that some required fields end up in the form for the last step. Now how do you validate the data in each steps? You could save up all data and do one massive insert or update, pointing out all validation errors in a separate form. But your interaction designer somehow doesn't seem to like this idea. In fact, what he wants is this:

  1. User submits data
  2. Validation feedback is immediately presented in the same form
  3. Use goes to next form

Reporting Relevant Validation Feedback Only

This requires us to save the data from each form if the form fields are valid, ignoring all error messages about the attributes that were not on the form. In other words, we need relevant information only. Here's the strategy:

  • See if there are any errors pertaining to our current form. If there are, store them in a temporary hash. Delete all error messages from the original errors object.
  • Copy the relevant from our temporary hash back to the errors object.
  • If there are no relevant errors, use the save_with_validation(false) method to save the data without validation.

Here's an example I've actually used in one of my controllers:

  def save_step_two    

    @user = User.new
    if @user.update_attributes(params[:user])  
      ## Go ahead as usual
      flash[:notice] = 'User was successfully updated.'.t
      redirect_to :controller => 'membership', :action => 'step_three'
      session[:membership] = @user.membership.system_name
    else                  
      ## See if there are any errors pertaining to our current form. If there are, store them in 
      ## a temporary hash. Delete all error messages from the original errors object.      
      temp_errors = Hash.new
      @user.errors.each do |attr, msg|
        temp_errors[attr] = msg[0] if params[:user].has_key?(attr)                  
      end            
      @user.errors.clear      
      
      if temp_errors.empty?                
        ## Save the form without further validation
        ## - and move on to the next step                 
        @user.save_with_validation(false)
        flash[:notice] = 'User was successfully updated.'.t
        redirect_to :action => 'step_three'        
      else
        ## Add the error messages in our temporary hash to the error object
        ## - and go back to the original form
        temp_errors.each do |attr, msg|       
          @user.errors.add(attr, msg)
        end         
        flash[:error] = 'Something went wrong while updating the user.'.t      
        render :action => 'step_two'                
      end
    end            
  end

Generating Required Field Indicators

If you define required attributes for your model with validates_presence_of, you don't have to repeat this information in your views any longer. This forum thread was a source of inspiration for auto-generating css classes on form fields.

Given an example object @user use this method in your views @user.class.attr_presence_required.include?(:firstname) to find out whether firstname is a required field. You can use this information to output an appropriate css class, as shown in the next sample code:

<div class="<%= (@user.class.attr_presence_required.include?(:firstname)) ? 'required' : 'optional' %>">
  <label for="user_firstname"><%= 'First Name'.t %></label>
  <%= text_field 'user', 'firstname' %>    
</div>

Here, the html class attribute is set to required if the firstname field is required, otherwise the html class attribute is set to optional.

You can combine this approach with an error indicator:

<div class="<%= error_message_on('user', 'firstname') ? 'error' : ((@user.class.attr_presence_required.include?(:firstname)) ? 'required' : 'optional') %>">
  <label for="user_firstname"><%= 'First Name'.t %></label>
  <%= text_field 'user', 'firstname' %>    
</div>

Extending the ClassMethods Module

To make it all work, we have to extend the ClassMethods module. Add the following code to a library which you then require in environment.rb.

module ActiveRecord 
  module Validations
    
    module ClassMethods 
      
      alias_method :no_tracking_validates_presence_of, :validates_presence_of
           
      @@attr_presence_required = Hash.new
      
      def validates_presence_of(*attr_names)    
        self.attr_presence_required = Hash.new unless self.attr_presence_required
        attr_names.each {|a| self.attr_presence_required[a]=true}                        
        no_tracking_validates_presence_of(*attr_names)        
      end 
            
      def attr_presence_required
         @@attr_presence_required
      end
      
      def attr_presence_required=(value)
         @@attr_presence_required = value
      end  
      
    end
    
  end      
end

Here, the required fields are copied to a hash which is stored in a class variable. Because the ClassMethods module is mixed in with the model classes, you can access the hash from within your model class. Please note that you're using an instance variable in your views (most of the time). So to get to the hash from within your view, you need to get to the class first: @user.class.attr_presence_required.

Furthermore, you can also access the validation status for each field: the attribute is stored as the key in the attr_presence_required hash, which points to a boolean indicating whether the attribute is valid (true) or not (false).

Finally, there's also a more comprehensive (and complicated) approach by Michael Schuerig. Please visit these links to learn more about it:

LiveValidation Plugin

The livevalidation plugin reads your model-based validation rules (e.g. “validates_presence_of”) and turns them into client-side javascript-based form validation. Here's the API of the plugin: http://livevalidation.rubyforge.org. By and large, this plugin works okay. There are a few pitfalls though.

validate_format_of for email

Some regular expressions which can be used in Ruby do not translate into JavaScript. Livevalidation literally copies everything out of the validation rules. So:

validates_format_of :email, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i, :message => "Please check email format. Example: mike@gmail.com"

Is turned into:

<script type="text/javascript">
  var user_email = new LiveValidation('user_email');user_email.add(Validate.Format, {"validMessage": "", 
                                                                                     "failureMessage": "Please check email format. Example: mike@gmail.com",
                                                                                     "pattern": /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i});
  user_email.add(Validate.Length, {"validMessage": "", "minimum": 3, "maximum": 100});
  var user_email_presence = new LiveValidation('user_email',{onlyOnSubmit: true});
  user_email_presence.add(Validate.Presence, {"validMessage": "", "failureMessage": "Please fill in your email address"})
</script>

This lovely piece of javascript code is inserted right underneath your email form field.

The message here is to check your regular epxressions. See to it that they keep working in javascript!

Password Confirmation

The normal usage pattern of “validates_confirmation_of :password” is that you first have a password field, and then a confirmation field. Livevalidation, however, scans your model and adds its javascript to the first field. For instance,

validates_confirmation_of :password

results in a situation where the confirmation check is applied immediately when you leave the password field and enter the password-confirmation field. This is very awkward of course, so you'll have to reverse and relabel the fields in your view:

  <p>
    <label for="user_password_confirmation">Password</label><br/>    
    <%= f.password_field :password_confirmation, :onchange => "$('user_password').value=''" %>    
  </p>
  
  <p>
    <label for="user_password">Confirm Password</label><br/>    
    <%= f.password_field :password %>
  </p>

If you take a good look at the ids (as shown in the “for” attribute of the label tag), you'll notice that I've put the confirmation field before the actual password field. This is okay as long as you remember to put your onchange attribute (which clears the second field upon changes in the first field) in the first field as well.

You Are Completely Wrong Until Proven Otherwise

Livevalidation's default is to immediately check a form field as you start typing. This is very annoying, as it results in error messages before you've even had a chance to complete the field. So I've made some changes in the javascript file (LiveValidation 1.3, prototype.js version). Take a look at the initialize function (the constructor) of the LiveValidation object. Here you get a chance to change the default values. For example, I've set “onlyOnBlur” to true:

onlyOnBlur: true

Now my form field does not get validated until I leave the field.

Repairing Your Mistake Results in Another Error Message

Another annoying feature is that empty form fields are reported as invalid as well. The following scenario clearly illustrates this:

  • You are in an email field where enter your mail address, but you somehow manage to leave out the @ sign
  • Then you enter the next field, username, whose presence is required (there's a validates_presence_of in your model)
  • Because your email address is invalid you get to see a message on the mail field
  • So, you put your cursor back in the mail field to repair things
  • But all of a sudden, a new error message shows up, because you've left the username field blank!

This is unacceptable, so I've arranged for all validates_presence_of check to be made onSubmit only. Here's my rather ugly hack of the form_helpers.rb file:

      def live_validation(object_name, method)        
        if validations = self.instance_variable_get("@#{object_name.to_s}").class.live_validations[method.to_sym] rescue false
          field_name = "#{object_name}_#{method}" 
          presence_code = nil          
          validation_types = validations.map do |type, configuration|
            
            if type == :presence
              presence_code = live_validation_code("#{field_name}_presence", type, configuration)
              ''
            else
              live_validation_code(field_name, type, configuration)
            end
           
            ##live_validation_code(field_name, type, configuration)
          end.join(';')
          javascript = initialize_validator(field_name) + validation_types
          if presence_code
            javascript + initialize_validator(field_name, "{onlyOnSubmit: true}") + presence_code
          else
            javascript
          end          
        else
          ''
        end
      end
      
      def initialize_validator(field_name, options = nil)
        if options
          "var #{field_name}_presence = new LiveValidation('#{field_name}',#{options});"
        else
          "var #{field_name} = new LiveValidation('#{field_name}');"
        end        
      end

Now, for all validates_presence_of validations, a separate LiveValidation object is instantiated which is called only when you submit the form.

Patching LiveValidation to Cooperate with Rails 2.1

validates_length_of does not take custom messages in Rails 2.1. This needs repairing first. Here is a patch. Now apply this patch to Globalize too, if you use that module. This is the file: plugins/globalize/lib/globalize/rails/active_record.rb, and this is the patched code:

def validates_length_of(*attrs)
  [... stuff omitted ... ]
  #too_short = options[:too_short]
  #too_long  = options[:too_long]
  too_short = (options[:message] || options[:too_short]) % option_value.begin
  too_long  = (options[:message] || options[:too_long])  % option_value.end   
  [... stuff omitted ... ]
end

Now Rails 2.1 supports custom error messages on validates_length_of. Hurray! So, why not have LiveValidation play along? In live_validation.js, look for the Length: function(value, paramsObj) line. Apply these changes:

  Length: function(value, paramsObj){    
    var value = String(value);
    var paramsObj = paramsObj || {};
    // Onno Schuit inserted paramsObj.failureMessage as fallback, to ensure compatibility with Rails' :within parameter.
    var params = {            
      wrongLengthMessage:   paramsObj.wrongLengthMessage ||  paramsObj.failureMessage || "Must be " + paramsObj.is + " characters long!",
      tooShortMessage:      paramsObj.tooShortMessage ||  paramsObj.failureMessage || "Must not be less than " + paramsObj.minimum + " characters long!",
      tooLongMessage:       paramsObj.tooLongMessage ||  paramsObj.failureMessage || "Must not be more than " + paramsObj.maximum + " characters long!",
      is:                   ((paramsObj.is) || (paramsObj.is == 0)) ? paramsObj.is : null,
      minimum:              ((paramsObj.minimum) || (paramsObj.minimum == 0)) ? paramsObj.minimum : null,
      maximum:              ((paramsObj.maximum) || (paramsObj.maximum == 0)) ? paramsObj.maximum : null
    }

That's it!

Patching LiveValidation to Cooperate with Rails 2.3.2

Rails 2.3 saw the introduction of nested models / nested attributes. So, to keep LiveValidation (1.3) running, I've patched vendor/plugins/livevalidation/lib/form_helpers.rb and (in the same directory) live_validations.rb. As an added bonus, the patch also provides:

  • support for select boxes (i.e. the FormOptionsHelper select method)
  • an automatically included destroyer of LiveValidation objects for form elements with the same id (handy for ajax refreshes, where your LiveValidation instances get stale otherwise)

I've also customized the livevalidation.js file, which I have included here as an external backup: livevalidation_patched2.js.txt

Please note that the Rails ajax_wipe method defined below outputs a call to the javascript method destroyById, which is defined in my patched livevalidation.js file.

form_helpers.rb

form_helpers.rb:

module ActionView
  
  mattr_accessor :live_validations
  ActionView::live_validations = true
  
  module Helpers    
    
    module FormHelper            
      [ :text_field, :text_area, :password_field ].each do |field_type|
        define_method "#{field_type}_with_live_validations" do |object_name, method, options|
          live = options.delete(:live)
          live = ActionView::live_validations if live.nil?
          object = options[:object]
          send("#{field_type}_without_live_validations", object_name, method, options) +
          ( live ? live_validations_for(object_name, method, object) : '' )
        end
        alias_method_chain field_type, :live_validations
      end

      def live_validations_for(object_name, method, object = nil)
        script_tags(live_validation(object_name, method, object))
      end
      
      private              

      def tag_id(object_name, method_name)
        "#{sanitized_object_name(object_name)}_#{sanitized_method_name(method_name)}"
      end

      def sanitized_object_name(object_name)
        object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
      end

      def sanitized_method_name(method_name)
        method_name.sub(/\?$/,"")
      end      
      
      def live_validation(object_name, method, object = nil)
        if validations = (object) ? object.class.live_validations[method.to_sym] : self.instance_variable_get("@#{object_name.to_s}").class.live_validations[method.to_sym] rescue false
        #if validations = self.instance_variable_get("@#{object_name.to_s}").class.live_validations[method.to_sym] rescue false
          field_name = tag_id(object_name,"#{method}") 
          presence_code = nil                    
          validation_types = validations.map do |type, configuration|
            
            if type == :presence
              presence_code = live_validation_code("#{field_name}_presence", type, configuration)
              ''
            else
              live_validation_code(field_name, type, configuration)
            end
           
            ##live_validation_code(field_name, type, configuration)
          end.join(';')
          ajax_wipe = "LiveValidationForm.destroyById('#{field_name}'); "
          javascript = (validations.size == 1 and presence_code) ? "" : initialize_validator(field_name) + validation_types 
          if presence_code
            #javascript + ";" + initialize_validator(field_name, "{onlyOnSubmit: true}") + presence_code
            ajax_wipe + initialize_validator(field_name, "{onlyOnSubmit: true}") + presence_code + ";" + javascript
          else
            ajax_wipe + javascript
          end          
        else
          ''
        end
      end
      
      def initialize_validator(field_name, options = nil)
        if options
          "var #{field_name}_presence = new LiveValidation('#{field_name}',#{options});"
        else
          "var #{field_name} = new LiveValidation('#{field_name}');"
        end        
      end
      
      def live_validation_code(field_name, type, configuration)
        "#{field_name}.add(#{ActiveRecord::Validations::VALIDATION_METHODS[type]}" + ( configuration ? ", #{configuration.to_json}" : '') + ')'
      end
      
      def script_tags(js_code = '')
         ( js_code.blank? ? '' : "<script type='text/javascript'>#{js_code}</script>" )
      end
    end
    
    module FormOptionsHelper
    
      def select_with_live_validations(object, method, choices, options = {}, html_options = {})
        live = options.delete(:live)
        live = ActionView::live_validations if live.nil?
        real_object = options[:object]        
        select_without_live_validations(object, method, choices, options, html_options) +
          ( live ? live_validations_for(object, method, real_object) : '' )        
      end
      
      alias_method_chain :select, :live_validations            
    end    
    
    
    class InstanceTag
      
      
      #def to_input_field_tag_with_script(field_type, options = {})
      #  "#{to_input_field_tag_without_script(field_type, options = {})}<script>a='test';</script>"
      #end
      
      #alias_method_chain :to_input_field_tag, :script
      

    end
    
  end
end

live_validations.rb

live_validations.rb:

module ActiveRecord
  module Validations
    LIVE_VALIDATIONS_OPTIONS = {
      :failureMessage => :message,
      :pattern => :with,
      :onlyInteger => :only_integer
    }
    # more complicated mappings in map_configuration method

    VALIDATION_METHODS = {
      :presence => "Validate.Presence",
      :numericality => "Validate.Numericality",
      :format => "Validate.Format",
      :length => "Validate.Length",
      :acceptance => "Validate.Acceptance",
      :confirmation => "Validate.Confirmation"
    }


    module ClassMethods

      VALIDATION_METHODS.keys.each do |type|
        define_method "validates_#{type}_of_with_live_validations".to_sym do |*attr_names|
          send "validates_#{type}_of_without_live_validations".to_sym, *attr_names
          define_validations(type, attr_names)
        end
        alias_method_chain "validates_#{type}_of".to_sym, :live_validations
      end

      def live_validations
        @live_validations ||= {}
      end

      private

      def define_validations(validation_type, attr_names)
        conf = (attr_names.last.is_a?(Hash) ? attr_names.pop : {})
        attr_names.each do |attr_name|
          configuration = map_configuration(conf.dup, validation_type, attr_name)
          add_live_validation(attr_name, validation_type, configuration)
        end
      end

      def add_live_validation(attr_name, type, configuration = {})
        @live_validations ||= {}
        @live_validations[attr_name] ||= {}
        @live_validations[attr_name][type] = configuration
      end

      def map_configuration(configuration, type = nil, attr_name = '')
        LIVE_VALIDATIONS_OPTIONS.each do |live, rails|
          configuration[live] = configuration.delete(rails)
        end
        if type == :numericality
          if configuration[:onlyInteger]
            configuration[:notAnIntegerMessage] = configuration.delete(:failureMessage)
          else
            configuration[:notANumberMessage] = configuration.delete(:failureMessage)
          end
        end
        if type == :length and range = ( configuration.delete(:in) || configuration.delete(:within) )
          configuration[:minimum] = range.begin
          configuration[:maximum] = range.end
        end
        if type == :confirmation
          configuration[:match] = self.to_s.underscore + '_' + attr_name.to_s + '_confirmation'
        end
        configuration[:validMessage] ||= ''
        configuration.reject {|k, v| v.nil? }
      end
    end
  end
end

Validating Radio Buttons With LiveValidation

LiveValidation does not support radio button validation. That's perfectly understandable because, after all, there is not much to validate. Or is there? Well, sometimes you want to make sure that a user has made a choice, without preselecting a default choice. For example, if you want to be politically correct on your forms, you should probably abstain from making a default choice for “gender”.

So, to ensure that the user has made a choice when the form is submitted, store the “choice fact” (“a choice has been made”) in a dummy form field. This is the field that we will actually validate.

<div class="formItem required">    
  <label for="guest_male" class="formLabel" ><%= t("guest.form.gender") %></label>    
  <div class="formField">
    <%= main_form.radio_button :male, true, {:onchange => "recordState('dummy')"}%><%= t("guest.form.male") %>
    <%= main_form.radio_button :male, false, {:onchange => "recordState('dummy')"} %><%= t("guest.form.female") %>
    <%= text_field_tag "dummy", "", :readonly => "readonly", :style => "width:0px;height:18px;padding:0px;margin:0px;border:0px solid white;visibility:hidden;" %>
    <script type="text/javascript">
      var dummy_presence = new LiveValidation('dummy',{onlyOnSubmit: true});dummy_presence.add(Validate.Presence, {"validMessage": "", "failureMessage": "<%= t('guest.validation.male.presence') %>"});
    </script>    
  </div> 
</div>

(No, the actual underlying code is not PC, but the user never gets to see that unless she dives into the html source).

Here's the accompanying little javascript function:

/**
 *	Set a field value to 1 and give it focus (usefull if you want to make sure
 *  that the user has selected at least one item from a range of options)
 *	
 *	@var id {string} - the string of the object
 */
function recordState(id) {  
  $(id).value = "1";
  $(id).focus();
}

Patching LiveValidation v. 2007/10/12 for Rails 2.3.8

Rails 2.3.8 escapes all Ruby strings in Erb templates, including our LiveValidation javascript. To prevent this, add the following code (in addition to the changes mentioned above) in vendor/plugins/livevalidation/lib/form_helpers.rb.

module ActionView
  
  mattr_accessor :live_validations
  ActionView::live_validations = true
  
  module Helpers    
    
    module FormHelper
      ## Nothing changed...
      [ :text_field, :text_area, :password_field ].each do |field_type|
        define_method "#{field_type}_with_live_validations" do |object_name, method, options|
          live = options.delete(:live)
          live = ActionView::live_validations if live.nil?
          object = options[:object]
          send("#{field_type}_without_live_validations", object_name, method, options) +
          ( live ? live_validations_for(object_name, method, object) : '' )
        end
        alias_method_chain field_type, :live_validations
      end

      
      ## Following two methods have been changed to use the 'raw' method
      
      def live_validations_for(object_name, method, object = nil)
        ## As of Rails 2.3.8, strings which end up in Erb templates are always escaped - but we need this one 'raw'
        raw("<script type='text/javascript'>#{live_validation(object_name, method, object)}</script>")
      end
      
      def custom_livevalidation(html_id, validations)
        content = "LiveValidationForm.destroyById('#{html_id}');\n"
        validations.each do |validation, message|          
          message_key = (validation.to_s == "Numericality") ? "notANumberMessage" : "failureMessage"
          only_on_submit = (validation.to_s == "Numericality") ? "false" : "true"
          content += "var val_#{html_id}_#{validation} = new LiveValidation('#{html_id}',{onlyOnSubmit: #{only_on_submit}});\n"
          content += "val_#{html_id}_#{validation}.add(Validate.#{validation}, {\"validMessage\": \"\", \"#{message_key}\": \"#{message}\"});\n"          
        end
        #script_tags(content)
        raw("<script type='text/javascript'>#{content}</script>")
      end 

      
      ## Nothing changed...

      private            

      def tag_id(object_name, method_name)
        "#{sanitized_object_name(object_name)}_#{sanitized_method_name(method_name)}"
      end

      def sanitized_object_name(object_name)
        object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
      end

      def sanitized_method_name(method_name)
        method_name.sub(/\?$/,"")
      end      
      
      def live_validation(object_name, method, object = nil)
        if validations = (object) ? object.class.live_validations[method.to_sym] : self.instance_variable_get("@#{object_name.to_s}").class.live_validations[method.to_sym] rescue false
        #if validations = self.instance_variable_get("@#{object_name.to_s}").class.live_validations[method.to_sym] rescue false
          field_name = tag_id(object_name,"#{method}") 
          presence_code = nil                    
          validation_types = validations.map do |type, configuration|
            
            if type == :presence
              presence_code = live_validation_code("#{field_name}_presence", type, configuration)
              ''
            else
              live_validation_code(field_name, type, configuration)
            end
           
            ##live_validation_code(field_name, type, configuration)
          end.join(';')
          ajax_wipe = "LiveValidationForm.destroyById('#{field_name}'); "
          javascript = (validations.size == 1 and presence_code) ? "" : initialize_validator(field_name) + validation_types 
          if presence_code
            #javascript + ";" + initialize_validator(field_name, "{onlyOnSubmit: true}") + presence_code
            ajax_wipe + initialize_validator(field_name, "{onlyOnSubmit: true}") + presence_code + ";" + javascript
          else
            ajax_wipe + javascript
          end          
        else
          ''
        end
      end
      
      def initialize_validator(field_name, options = nil)
        if options
          "var #{field_name}_presence = new LiveValidation('#{field_name}',#{options});"
        else
          "var #{field_name} = new LiveValidation('#{field_name}');"
        end        
      end
      
      def live_validation_code(field_name, type, configuration)
        "#{field_name}.add(#{ActiveRecord::Validations::VALIDATION_METHODS[type]}" + ( configuration ? ", #{configuration.to_json}" : '') + ')'
      end
      
      def script_tags(js_code = '')
         ( js_code.blank? ? '' : "<script type='text/javascript'>#{js_code}</script>" )
      end
    end
    
    module FormOptionsHelper
    
      def select_with_live_validations(object, method, choices, options = {}, html_options = {})
        live = html_options.delete(:live)
        live = ActionView::live_validations if live.nil?
        real_object = options[:object]        
        select_without_live_validations(object, method, choices, options, html_options) +
          ( live ? live_validations_for(object, method, real_object) : '' )        
      end
      
      alias_method_chain :select, :live_validations            
    end    
    
    
    class InstanceTag
      
      
      #def to_input_field_tag_with_script(field_type, options = {})
      #  "#{to_input_field_tag_without_script(field_type, options = {})}<script>a='test';</script>"
      #end
      
      #alias_method_chain :to_input_field_tag, :script
      

    end
    
  end
end

Personal Tools