Simple client side form validation for Rails 3
Judge allows easy live form validation for Rails 3 apps, by porting many ActiveModel::Validation features to JavaScript and exposing your model validations as JSON within HTML5 data attributes. It's fast and it's purely on the client side – no more code duplication for standard validators, no need for gratuitous AJAX.
Judge relies on Underscore.js, along with json2.js, which defers to the native JSON global object in modern browsers.
Add the following to your Gemfile:
gem "judge", "~> 1.5.0"
and then run:
$ bundle install
To copy the Judge JavaScript files to your app, use the generator:
$ rails generate judge [path] [options]
The path parameter is optional and defaults to public/javascripts. So running the generator without specifying a path and with no options will give you:
The only option is --no-dep, which prevents the generator from copying Underscore.js and json2.js into your chosen directory if you already have them.
$ rails generate judge
Then do whatever it is you usually do to ensure that JavaScript files are included properly (e.g. add them to assets.yml if you are using Jammit).
Run the generator as follows to copy judge.js and its dependencies to your app:
$ rails generate judge app/assets/javascripts
These files should now be included, thanks to the following line in your manifest file (app/assets/javascripts/application.js):
//= require_tree .
If you ever come across problems with your require order, you can ensure that Judge's dependencies are required first by doing something like this:
//= require underscore
//= require json2
//= require_tree .
Add some validation to your model:
class Post < ActiveRecord::Base
validates :title, :presence => true
end
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 %>
<% end %>
Most Judge functionality on the client side is based on the use of watchers. A watcher is a sort of pointer object for a DOM element. Create a watcher for an input element:
var watcher = new judge.Watcher(document.getElementById('post_title'));
Validate the watched element and give some simple visual feedback – this checks validity at the time you call validate(), not the time at which the watcher was instantiated:
watcher.validate(function(valid, messages, element) {
if (!valid) {
element.style.border = '1px solid red';
}
});
If you want to do a quick validation and see no benefit in creating a watcher, you can do that too. This example will validate all input elements on the page, provided they have been created with Judge::FormBuilder:
judge.validate(document.getElementsByTagName('input'));
// => [ { valid:false, messages:["can't be blank"], element:HTMLInputElement }, ... ]
judge.validate accepts a callback function too, which is called once for every element passed in the first argument.
You can use any of the standard form builders from ActionView::Helpers::FormBuilder – just add :validate => true to the options hash.
Validators:
judge.customValidators.The allow_blank option is available everywhere it should be. Error messages are looked up according to the Rails i18n API.
If you have any custom form builders defined that inherit from ActionView::Helpers::FormBuilder in your app, you could make them validatable on the client side by inheriting Judge::FormBuilder instead:
class MyCustomFormBuilder < Judge::FormBuilder
# methods like text_field, text_area etc. go here
end
If you create a custom EachValidator, Judge provides a way to ensure that your I18n error messages are available on the client side. Simply pass to declare_messages any number of message keys and Judge will look up the translated messages. Let's run through an example.
# autoload this file
class FooValidator < ActiveModel::EachValidator
declare_messages :not_foo
def validate_each(record, attribute, value)
unless value == "foo"
record.errors.add(:title, :not_foo)
end
end
end
We'll use the validator in the example above to validate the title attribute of a Post object:
# your model
class Post < ActiveRecord::Base
validates :title, :foo => true
end
# your view
<%= form_for(@post, :builder => Judge::FormBuilder) do |f| %>
<%= text_field :title, :validate => true %>
<% end %>
Judge will look for the not_foo message at
activerecord.errors.models.post.attributes.title.not_foo
first and then onwards down the Rails I18n lookup chain.
Our client side validator would then look something like this:
judge.customValidators.foo = function(value, options, messages) {
var errorMessages = [];
if (value !== "foo") {
errorMessages.push(messages.not_foo);
}
return errorMessages;
};
The tokenizer option for the length validator is currently missing. Options like if, unless and on, which require more involved interactions with your Rails application, aren't really viable in a universal way on the client side and are simply ignored. Validating uniqueness with Judge might happen in the future, provided the method arrived at:
For now, you can define a custom validator that makes an XMLHttpRequest if you need to.
This is the judge.js API documentation. For some literate programming-type goodness, check out the annotated JavaScript source, generated by Docco.
The global namespace.
judge.validate(elements, [callback])Perform quick and easy validation on a single element or an array of elements (including array-like collections such as NodeList), without making public any watchers. An array of objects is returned, each object containing the validated element, a Boolean valid flag and an array of error messages, which is empty if valid is true.
judge.validate(document.getElementsByTagName('input'));
// => [ { valid:true, messages:[], element:HTMLInputElement },
// { valid:false, messages:['must be even'], element:HTMLInputElement } ]
The callback function, if provided, will be executed once for each element passed in the first argument. The callback receives three arguments: (valid, messages, element).
var elements = document.querySelectorAll('input[type=text]');
judge.validate(elements, function(valid, messages, element) {
if (!valid) {
element.style.border = '1px solid red';
}
});
judge.customValidatorsAn empty object. Add your own validators here. For example:
judge.customValidators.myValidator = function(value, options, messages) {
var errorMessages = [];
// add validation logic here
return errorMessages;
};
Judge validator methods receive the form element value and the validator options as defined in your model. The third parameter, messages, contains any error messages that may be used to validate the form element including those explicitly declared inside custom validator classes.
Any validator methods added to judge.customValidators will be called automatically whenever a watcher validator of the same name is detected. Custom methods added here will override the standard validator types (presence, length etc.) if you use the same names – be cautious.
Custom validator methods must return an array of error messages. If the value is valid, they must return an empty array.
Constructor for watchers. Watchers are wrapper objects that contain a DOM element and various validation methods. The constructor takes one argument, a DOM element. For example:
var watcher = new judge.Watcher(document.getElementById('foo'));
watcher.validate([callback])Validate the current value of the wrapped DOM element. An object is returned, containing the validated element, a Boolean valid flag and an array of error messages, which is empty if valid is true.
watcher.validate();
// => { valid:false, messages:['must be even'], element:HTMLInputElement }
The callback function, if provided, receives three arguments: (valid, messages, element).
watcher.validate(function(valid, messages, element) {
if (!valid) {
element.style.border = '1px solid red';
}
});
watcher.validatorsReturns an array of validators (JavaScript object representations of the associated ActiveModel validators) for the wrapped element.
watcher.validators;
// => [ { kind:'presence', options:{ … } }, { kind:'format', options:{ … } } ]
Contains some methods for storing and retrieving watchers in groups, for more customised validation scenarios. Have you ever wanted to validate a number of form elements when the user triggers an event? Storing your reference to the elements can make things easier.
judge.store.save(key, element)Store watcher(s) for element(s) against a key, for later use. If the key doesn't already exist, it will be created. Returns the store object.
judge.store.save('mykey', document.getElementById('foo'));
// => [ { element:HTMLInputElement, … } ]
judge.store.save('mykey', document.getElementsByTagName('input'));
// => [ { element:HTMLInputElement, … }, { element:HTMLInputElement, … }, … ]
judge.store.get(key)Return an array of watchers saved against the key. If no key is given, the store object is returned.
judge.store.get('mykey');
// => [ { element:HTMLInputElement, … } ]
judge.store.get();
// => { mykey:[ { element:HTMLInputElement, … } ], … }
judge.store.getDOM(key)Return an array of DOM elements from watchers stored against a key. If no key is given, the store object is returned, with all watchers converted to their wrapped DOM elements.
judge.store.getDOM('mykey');
// => [ HTMLInputElement, HTMLInputElement … ]
judge.store.getDOM();
// => { mykey: [ HTMLInputElement ], mykey2: [ HTMLInputElement … ] }
judge.store.validate(key, [callback])A shortcut for judge.validate(judge.store.getDOM(key)). Validate all elements stored within watchers against the given key. Returns null if no key is passed or no watchers are found.
judge.store.validate('mykey');
// => [ { valid:true, messages:[], element:HTMLInputElement },
// { valid:false, messages:['must be even'], element:HTMLInputElement } ]
judge.store.validate();
// => null
The callback function will be executed once for each watcher stored against the given key. Same behaviour as judge.validate.
judge.store.remove(key, element)Remove individual stored watcher. Returns remaining watchers stored at key, or undefined if none remain (store property is then deleted). If you want to remove all watchers stored against a key, or all stored watchers, use the clear method.
judge.store.remove('mykey', document.getElementById('foo'));
// => { mykey:[ … ], mykey2:[ … ] }
judge.store.clear(key)Remove all watchers stored against a key or, if no key is given, all stored watchers. Returns store object after watchers were removed.
judge.store.save('inputs', document.getElementsByTagName('input'));
judge.store.save('textareas', document.getElementsByTagName('textarea'));
judge.store.clear('inputs');
// => { textareas: [ HTMLInputElement … ] }
judge.store.clear();
// => {}
Judge uses Travis for continuous integration. Current status of master branch: 
Since Travis is still quite young, you might want to run the test suite yourself:
# All specs, JavaScript specs run headlessly (requires phantomjs and casperjs)
$ bundle exec rake
# Only Ruby specs
$ bundle exec rake spec
# Only JavaScript specs, headless (requires phantomjs and casperjs)
$ bundle exec rake jasmine:headless
# Only JavaScript specs, with Selenium
$ bundle exec rake jasmine:ci
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 FormBuilder input method.
https://github.com/joecorcoran/judge-formtastic
gem "judge-formtastic", "~> x.x.x", :require => "judge/formtastic"
<%= semantic_form_for(@user) do |f| %>
<%= f.input :name, :validate => true %>
<% end %>
https://github.com/joecorcoran/judge-simple_form
gem "judge-simple_form", "~> x.x.x", :require => "judge/simple_form"
<%= simple_form_for(@user) do |f| %>
<%= f.input :name, :validate => true %>
<% end %>
1.5.0 Added interface for declaring localised messages within EachValidators, which means we can reliably pass custom messages to the client side. Some internal implementation details have been altered and underscore.js was updated.
1.4.0 judge.store.validate now accepts callback function too.
1.3.0 Validate methods now accept callbacks; judge.utils removed in favour of functions within a closure; some specs were tidied/deleted as appropriate.
1.2.0 Changes to the format of validator functions; improved method of including custom validators; updated dependencies; fixed RegExp flag bug.
1.1.0 Fixed incorrect Enumerable implementation in ValidatorCollection. Lots more internal tidying, including extraction of HTML attribute building.
1.0.0 Validator code extracted into classes; Form builder methods no longer require “validated_” prefix; Added judge.store.validate() shortcut method; Some minor implementation updates to judge.js; No more dummy app in tests, no more crappy Gemfile, no more Jeweler.
0.5.0 Error messages looked up through Rails i18n.
0.4.3 IE bug fixes: No longer checking for element type in the Watcher constructor, to avoid IE object string "quirk"; now using typeof to check for undefined properties of window.
0.4.2 Fixed bug introduced in the last bug fix – now globally replacing double slashes :)
0.4.1 Fixed slash escaping bug in judge.js RegExp converter; removed uniqueness data from data attributes to prevent judge.js from expecting a validation method.
0.4.0 Added new form builders.
0.3.1 Removed unused keys from validator options in data attributes. Include Judge::FormHelper in ApplicationHelper to avoid clash with haml aliases.
0.3.0 Added confirmation and acceptance validation, more form builders.
0.2.0 Remove jQuery dependency; refactored to include new namespace, watchers and store; added headless testing.
0.1.1 Removed duplicate dependencies in gemspec.
0.1.0 First release.