Client-side form validation for Rails
JavaScript Ruby Other
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


Build status

Judge allows easy client side form validation for Rails 3 by porting many ActiveModel::Validation features to JavaScript. The most common validations work through JSON strings stored within HTML5 data attributes and are executed purely on the client side. Wherever you need to, Judge provides a simple interface for AJAX validations too.


Whenever we need to give the user instant feedback on their form data, it's common to write some JavaScript to test form element values. Since whatever code we write to manage our data integrity in Ruby has to be copied as closely as possible in JavaScript, we end up with some very unsatisfying duplication of application logic.

In many cases it would be simpler to safely expose the validation information from our models to the client – this is where Judge steps in.


Judge only supports Rails 3.1 or higher.

Judge relies on Underscore.js in general and JSON2.js for browsers that lack proper JSON support. If your application already makes use of these files, you can safely ignore the versions provided with Judge.

With asset pipeline enabled

Add judge to your Gemfile and run bundle install.

Mount the engine in your routes file, as follows:

# config/routes.rb
mount Judge::Engine => '/judge'

Judge makes three JavaScript files available. You'll always need judge.js and underscore.js, whereas json2.js is only needed in older browsers. Add the following lines to application.js:

//= require underscore
//= require json2
//= require judge

Without asset pipeline

Add judge to your Gemfile and run bundle install. Then run

$ rails generate judge:install path/to/your/js/dir

to copy judge.js to your application. There are --json2 and --underscore options to copy the dependencies too.

Mount the engine in your routes file, as follows:

# config/routes.rb
mount Judge::Engine => '/judge'

Getting started

Add a simple validation to your model.

class Post < ActiveRecord::Base
  validates :title, :presence => true

Make sure your form uses the Judge::FormBuilder and add the :validate option to the field.

form_for(@post, :builder => Judge::FormBuilder) do |f|
  f.text_field :title, :validate => true

On the client side, you can now validate the title input.

judge.validate(document.getElementById('post_title'), {
  valid: function(element) { = '1px solid green';
  invalid: function(element, messages) { = '1px solid red';


You can use any of the methods from the standard ActionView::Helpers::FormBuilder – just add :validate => true to the options hash.

f.date_select :birthday, :validate => true

If you need to use Judge in conjunction with your own custom FormBuilder methods, make sure your FormBuilder inherits from Judge::FormBuilder and use the #add_validate_attr! helper.

class MyFormBuilder < Judge::FormBuilder
  def fancy_text_field(method, options = {})
    add_validate_attr!(self.object, method, options)
    # do your stuff here

Available validators

  • presence;
  • length (options: minimum, maximum, is);
  • exclusion (options: in);
  • inclusion (options: in);
  • format (options: with, without); and
  • numericality (options: greater_than, greater_than_or_equal_to, less_than, less_than_or_equal_to, equal_to, odd, even, only_integer);
  • acceptance;
  • confirmation (input and confirmation input must have matching ids);
  • uniqueness;
  • any EachValidator that you have written, provided you add a JavaScript version too and add it to judge.eachValidators.

Options like if, unless and on are not available as they relate to record persistence.

The allow_blank option is available everywhere it should be. Error messages are looked up according to the Rails i18n API.

Validating uniqueness

In order to validate uniqueness Judge sends requests to the mounted Judge::Engine path, which responds with a JSON representation of an error message array. The array is empty if the value is valid.

Since this effectively means adding an open, queryable endpoint to your application, Judge is cautious and requires you to be explicit about which attributes from which models you would like to expose for validation via XHR. Allowed attributes are configurable as in the following example. Note that you are only required to do this for uniqueness and any other validators you write that make requests to the server.

# config/initializers/judge.rb
Judge.configure do
  expose Post, :title, :body

Mounting the engine at a different location

You can choose a path other than '/judge' if you need to; just make sure to set this on the client side too:

# config/routes.rb
mount Judge::Engine => '/whatever'
judge.enginePath = '/whatever';

Writing your own EachValidator

If you write your own ActiveModel::EachValidator, Judge provides a way to ensure that your I18n error messages are available on the client side. Simply pass to uses_messages any number of message keys and Judge will look up the translated messages. Let's run through an example.

class FooValidator < ActiveModel::EachValidator
  uses_messages :not_foo

  def validate_each(record, attribute, value)
    unless value == 'foo'
      record.errors.add(:title, :not_foo)

We'll use the validator in the example above to validate the title attribute of a Post object:

class Post < ActiveRecord::Base
  validates :title, :foo => true
form_for(@post, :builder => Judge::FormBuilder) do |f|
  text_field :title, :validate => true

Judge will look for the not_foo message at first and then onwards down the Rails I18n lookup chain.

We then need to add our own validator method to the judge.eachValidators object on the client side: = function(options, messages) {
  var errorMessages = [];
  // 'this' refers to the form element
  if (this.value !== 'foo') {
  return new judge.Validation(errorMessages);


All client side validators must return a Validation – an object that can exist in three different states: valid, invalid or pending. If your validator function is synchronous, you can return a closed Validation simply by passing an array of error messages to the constructor.

new judge.Validation([]);
  // => empty array, this Validation is 'valid'
new judge.Validation(['must not be blank']);
  // => array has messages, this Validation is 'invalid'

The pending state is provided for asynchronous validation; a Validation object we will close some time in the future. Let's look at an example, using jQuery's popular ajax function: = function() {
  // create a 'pending' validation
  var validation = new judge.Validation();
  $.ajax('/bar-checking-service').done(function(messages) {
    // You can close a Validation with either an array
    // or a string that represents a JSON array
  return validation;

There are helper functions, judge.pending() and judge.closed() for creating a new Validation too. = function() {
  return judge.closed(['not valid']);
}; = function() {
  var validation = new judge.pending();
  doAsyncStuff(function(messages) {
  return validation;

In the unlikely event that you don't already use a library with AJAX capability, a basic function is provided for making GET requests as follows:

judge.get('/something', {
  success: function(status, headers, text) {
    // status code 20x
  error: function(status, headers, text) {
    // any other status code

Judge extensions

If you use Formtastic or SimpleForm, there are extension gems to help you use Judge within your forms without any extra setup. They are essentially basic patches that add the :validate => true option to the input method.


gem 'judge-formtastic'
semantic_form_for(@user) do |f|
  f.input :name, :validate => true


gem 'judge-simple_form'
simple_form_for(@user) do |f|
  f.input :name, :validate => true


Released under an MIT license (see LICENSE.txt). | @josephcorcoran