Easy Rails Patching

If you need to extend Rails for whatever reason, here’s a quick tip. Simply add your Ruby code to the config/initializers folder. Rails initializers (available since Rails 2 came out) are loaded after the Rails framework and plugins, so any custom script executed at this stage will be last.

Here’s an example patch which makes “:disable_with” play nicely with the submit_tag when a remote form tag is used*.

module ActionView
  module Helpers
    module FormTagHelper
 
      def submit_tag(value = "Save changes", options = {})
        options.stringify_keys!
 
        if disable_with = options.delete("disable_with")
          disable_with = "this.value='#{disable_with}'"
          disable_with << ";#{options.delete('onclick')}" if options['onclick']
          disable_with << ";result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit())"
          disable_with << ";if (result == false && this.form.onsubmit && (this.form.onsubmit+'').indexOf('Ajax.Request') == -1) { this.value = this.getAttribute('originalValue'); this.disabled = false }"
          disable_with << ";return result"
 
          options["onclick"] = [
            "this.setAttribute('originalValue', this.value)",
            "this.disabled=true",
            disable_with
          ].join(";")
        end
 
        if confirm = options.delete("confirm")
          options["onclick"] ||= ''
          options["onclick"] += "return #{confirm_javascript_function(confirm)};"
        end
 
        tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys)
      end
 
    end
  end
end

Be sure you restart your Rails app in order to see your patch take effect.

The above example overrides the existing Rails framework helper. However, you can also use the same technique to extend methods via alias_method_chain.

*The Rails remote_form_for tag adds a ‘return false’ to the form’s onsubmit event handler. However, the Rails submit_tag helper evaluates whether false was returned via onsubmit and will revert back to the original text (and re-enable the button) if this is the case. This makes sense when you are validating a form… the validation fails and you need everything reset so that you can correct your error and resubmit the form. However, for remote forms, in which an AJAX call is made to submit the form, we want the RJS response to drive the form’s (and page’s) behavior. So the patch evaluates whether an AJAX call is made during onsubmit; if so, it lets the submit button stay disabled. Hacky, I know. But it allows us to do what we need without having to touch the PrototypeHelper module. I tested with normal form_tag’s that use disable_with and it works in both cases.


About this entry