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.0.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 .
As of version 1.0.0, Judge form builder methods are no longer prefixed with “validated_”. The syntax is now in line with standard Rails form builders, as follows:
<%= form_for(@post, :builder => Judge::FormBuilder) do |f|%> <%= f.text_field :title, :validate => true %> <%= f.text_area :body, :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('foo'));
Check whether the value of the watched input element is valid – this checks validity at the time you call validate(), not the time at which the watcher was instantiated:
watcher.validate().valid; // => true
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:true, messages:[], element:HTMLInputElement },
// { valid:false, messages:['must be even'], element:HTMLInputElement } ]
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 override 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
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)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 } ]
judge.customValidatorsAn empty object. Add your own validators here. Let's say you have something like this set up:
# autoload this file
class FooValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors[attribute] << "Should be foo" unless value == "foo"
end
end
# your model class Thing < ActiveRecord::Base validates :title, :foo => true end
# your view <%= form_for(@thing, :builder => Judge::FormBuilder) do |f| %> <%= text_field :title, :validate => true %> <% end %>
Then Judge will expect a method named foo to have been added to judge.customValidators:
// on the client side
judge.customValidators.foo = function(options, messages) {
// do your foo checking here
};
Judge validator methods receive an options object and a messages object and must return an object literal with properties valid (Boolean) and messages (an array of error messages, only necessary if valid is false).
The options object contains the validation options from your Ruby custom validator. To get a better idea of what options has to offer, open up a browser console and take a look at the JSON stored in the data-validate attribute of any form element created with the Judge form builders.
The messages object contains all possible error messages for this attribute.
Validator methods have access to a variable named watcher, which points to the watcher for the element being validated. So watcher.element gives you access to the element itself.
Any validators added to judge.customValidators will be called automatically whenever a watcher validator of the same name is detected. Phew!
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()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 }
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.getElementByTagName('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)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
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.clear('mykey');
// => { mykey:[], … }
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 tests yourself:
# All tests, JavaScript tests headlessly (requires phantom.js) $ bundle exec rake test # Only Ruby tests $ bundle exec rake test:ruby # Only JavaScript tests, headless (requires phantom.js) $ bundle exec rake jasmine:phantom # Only JavaScript tests, with Selenium $ bundle exec rake jasmine:ci
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.