livevalidation 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +21 -0
- data/Rakefile +35 -0
- data/VERSION +1 -0
- data/assets/javascripts/live_validation.js +4 -0
- data/assets/stylesheets/live_validation.css +28 -0
- data/install.rb +1 -0
- data/lib/form_helpers.rb +49 -0
- data/lib/live_validations.rb +73 -0
- data/livevalidation.gemspec +59 -0
- data/rails/init.rb +2 -0
- data/tasks/live_validation_tasks.rake +17 -0
- data/test/form_helpers_test.rb +206 -0
- data/test/live_validations_test.rb +120 -0
- data/test/resource.rb +17 -0
- data/test/test_helper.rb +13 -0
- data/uninstall.rb +1 -0
- metadata +86 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg/
|
data/README.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
LiveValidation is a plugin which allows automatic integration of your Rails application with Javascript library [LiveValidation](http://www.livevalidation.com/). This library implements client-side form validation and you can see a demo in its site.
|
2
|
+
|
3
|
+
## Important note
|
4
|
+
|
5
|
+
This plugin is no longer mantained, and may not work with the newest versions of Rails. I suggest you to use one of the more updated [forks](http://github.com/porras/livevalidation/network).
|
6
|
+
|
7
|
+
## Author
|
8
|
+
|
9
|
+
[Sergio Gil](http://twitter.com/porras)
|
10
|
+
|
11
|
+
## License
|
12
|
+
|
13
|
+
LiveValidation is licensed under the terms of the [MIT License](http://www.opensource.org/licenses/mit-license.php).
|
14
|
+
|
15
|
+
Copyright (c) 2007 Sergio Gil
|
16
|
+
|
17
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
18
|
+
|
19
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
20
|
+
|
21
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test the live_validation plugin.'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.libs << 'lib'
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
12
|
+
t.verbose = true
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Generate documentation for the live_validation plugin.'
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
18
|
+
rdoc.title = 'LiveValidation'
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
20
|
+
rdoc.rdoc_files.include('README')
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
22
|
+
end
|
23
|
+
|
24
|
+
begin
|
25
|
+
require 'jeweler'
|
26
|
+
Jeweler::Tasks.new do |gemspec|
|
27
|
+
gemspec.name = "livevalidation"
|
28
|
+
gemspec.summary = "Easy client side validation for your ActiveRecord models"
|
29
|
+
gemspec.email = "sgilperez@gmail.com"
|
30
|
+
gemspec.homepage = "http://github.com/porras/livevalidation"
|
31
|
+
gemspec.description = "Easy client side validation for your ActiveRecord models"
|
32
|
+
gemspec.authors = ["Sergio Gil"]
|
33
|
+
end
|
34
|
+
rescue LoadError
|
35
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,4 @@
|
|
1
|
+
// LiveValidation 1.3 (prototype.js version)
|
2
|
+
// Copyright (c) 2007-2008 Alec Hill (www.livevalidation.com)
|
3
|
+
// LiveValidation is licensed under the terms of the MIT License
|
4
|
+
var LiveValidation=Class.create();Object.extend(LiveValidation,{VERSION:"1.3 prototype",TEXTAREA:1,TEXT:2,PASSWORD:3,CHECKBOX:4,SELECT:5,FILE:6,massValidate:function(C){var D=true;for(var B=0,A=C.length;B<A;++B){var E=C[B].validate();if(D){D=E;}}return D;}});LiveValidation.prototype={validClass:"LV_valid",invalidClass:"LV_invalid",messageClass:"LV_validation_message",validFieldClass:"LV_valid_field",invalidFieldClass:"LV_invalid_field",initialize:function(B,A){if(!B){throw new Error("LiveValidation::initialize - No element reference or element id has been provided!");}this.element=$(B);if(!this.element){throw new Error("LiveValidation::initialize - No element with reference or id of '"+B+"' exists!");}this.elementType=this.getElementType();this.validations=[];this.form=this.element.form;this.options=Object.extend({validMessage:"Thankyou!",onValid:function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();},onInvalid:function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();},insertAfterWhatNode:this.element,onlyOnBlur:false,wait:0,onlyOnSubmit:false},A||{});var C=this.options.insertAfterWhatNode||this.element;this.options.insertAfterWhatNode=$(C);Object.extend(this,this.options);if(this.form){this.formObj=LiveValidationForm.getInstance(this.form);this.formObj.addField(this);}this.boundFocus=this.doOnFocus.bindAsEventListener(this);Event.observe(this.element,"focus",this.boundFocus);if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:this.boundClick=this.validate.bindAsEventListener(this);Event.observe(this.element,"click",this.boundClick);case LiveValidation.SELECT:case LiveValidation.FILE:this.boundChange=this.validate.bindAsEventListener(this);Event.observe(this.element,"change",this.boundChange);break;default:if(!this.onlyOnBlur){this.boundKeyup=this.deferValidation.bindAsEventListener(this);Event.observe(this.element,"keyup",this.boundKeyup);}this.boundBlur=this.validate.bindAsEventListener(this);Event.observe(this.element,"blur",this.boundBlur);}}},destroy:function(){if(this.formObj){this.formObj.removeField(this);this.formObj.destroy();}Event.stopObserving(this.element,"focus",this.boundFocus);if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:Event.stopObserving(this.element,"click",this.boundClick);case LiveValidation.SELECT:case LiveValidation.FILE:Event.stopObserving(this.element,"change",this.boundChange);break;default:if(!this.onlyOnBlur){Event.stopObserving(this.element,"keyup",this.boundKeyup);}Event.stopObserving(this.element,"blur",this.boundBlur);}}this.validations=[];this.removeMessageAndFieldClass();},add:function(A,B){this.validations.push({type:A,params:B||{}});return this;},remove:function(A,B){this.validations=this.validations.reject(function(C){return(C.type==A&&C.params==B);});return this;},deferValidation:function(A){if(this.wait>=300){this.removeMessageAndFieldClass();}if(this.timeout){clearTimeout(this.timeout);}this.timeout=setTimeout(this.validate.bind(this),this.wait);},doOnBlur:function(){this.focused=false;this.validate();},doOnFocus:function(){this.focused=true;this.removeMessageAndFieldClass();},getElementType:function(){switch(true){case (this.element.nodeName.toUpperCase()=="TEXTAREA"):return LiveValidation.TEXTAREA;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="TEXT"):return LiveValidation.TEXT;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="PASSWORD"):return LiveValidation.PASSWORD;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="CHECKBOX"):return LiveValidation.CHECKBOX;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="FILE"):return LiveValidation.FILE;case (this.element.nodeName.toUpperCase()=="SELECT"):return LiveValidation.SELECT;case (this.element.nodeName.toUpperCase()=="INPUT"):throw new Error("LiveValidation::getElementType - Cannot use LiveValidation on an "+this.element.type+" input!");default:throw new Error("LiveValidation::getElementType - Element must be an input, select, or textarea!");}},doValidations:function(){this.validationFailed=false;for(var C=0,A=this.validations.length;C<A;++C){var B=this.validations[C];switch(B.type){case Validate.Presence:case Validate.Confirmation:case Validate.Acceptance:this.displayMessageWhenEmpty=true;this.validationFailed=!this.validateElement(B.type,B.params);break;default:this.validationFailed=!this.validateElement(B.type,B.params);break;}if(this.validationFailed){return false;}}this.message=this.validMessage;return true;},validateElement:function(A,C){var D=(this.elementType==LiveValidation.SELECT)?this.element.options[this.element.selectedIndex].value:this.element.value;if(A==Validate.Acceptance){if(this.elementType!=LiveValidation.CHECKBOX){throw new Error("LiveValidation::validateElement - Element to validate acceptance must be a checkbox!");}D=this.element.checked;}var E=true;try{A(D,C);}catch(B){if(B instanceof Validate.Error){if(D!==""||(D===""&&this.displayMessageWhenEmpty)){this.validationFailed=true;this.message=B.message;E=false;}}else{throw B;}}finally{return E;}},validate:function(){if(!this.element.disabled){var A=this.doValidations();if(A){this.onValid();return true;}else{this.onInvalid();return false;}}else{return true;}},enable:function(){this.element.disabled=false;return this;},disable:function(){this.element.disabled=true;this.removeMessageAndFieldClass();return this;},createMessageSpan:function(){var A=document.createElement("span");var B=document.createTextNode(this.message);A.appendChild(B);return A;},insertMessage:function(B){this.removeMessage();var A=this.validationFailed?this.invalidClass:this.validClass;if((this.displayMessageWhenEmpty&&(this.elementType==LiveValidation.CHECKBOX||this.element.value==""))||this.element.value!=""){$(B).addClassName(this.messageClass+(" "+A));if(nxtSibling=this.insertAfterWhatNode.nextSibling){this.insertAfterWhatNode.parentNode.insertBefore(B,nxtSibling);}else{this.insertAfterWhatNode.parentNode.appendChild(B);}}},addFieldClass:function(){this.removeFieldClass();if(!this.validationFailed){if(this.displayMessageWhenEmpty||this.element.value!=""){if(!this.element.hasClassName(this.validFieldClass)){this.element.addClassName(this.validFieldClass);}}}else{if(!this.element.hasClassName(this.invalidFieldClass)){this.element.addClassName(this.invalidFieldClass);}}},removeMessage:function(){if(nxtEl=this.insertAfterWhatNode.next("."+this.messageClass)){nxtEl.remove();}},removeFieldClass:function(){this.element.removeClassName(this.invalidFieldClass);this.element.removeClassName(this.validFieldClass);},removeMessageAndFieldClass:function(){this.removeMessage();this.removeFieldClass();}};var LiveValidationForm=Class.create();Object.extend(LiveValidationForm,{instances:{},getInstance:function(A){var B=Math.random()*Math.random();if(!A.id){A.id="formId_"+B.toString().replace(/\./,"")+new Date().valueOf();}if(!LiveValidationForm.instances[A.id]){LiveValidationForm.instances[A.id]=new LiveValidationForm(A);}return LiveValidationForm.instances[A.id];}});LiveValidationForm.prototype={initialize:function(A){this.element=$(A);this.fields=[];this.oldOnSubmit=this.element.onsubmit||function(){};this.element.onsubmit=function(C){var B=(LiveValidation.massValidate(this.fields))?this.oldOnSubmit.call(this.element,C)!==false:false;if(!B){Event.stop(C);}}.bindAsEventListener(this);},addField:function(A){this.fields.push(A);},removeField:function(A){this.fields=this.fields.without(A);},destroy:function(A){if(this.fields.length!=0&&!A){return false;}this.element.onsubmit=this.oldOnSubmit;LiveValidationForm.instances[this.element.id]=null;return true;}};var Validate={Presence:function(A,B){var C=Object.extend({failureMessage:"Can't be empty!"},B||{});if(A===""||A===null||A===undefined){Validate.fail(C.failureMessage);}return true;},Numericality:function(B,C){var A=B;var B=Number(B);var C=C||{};var D={notANumberMessage:C.notANumberMessage||"Must be a number!",notAnIntegerMessage:C.notAnIntegerMessage||"Must be an integer!",wrongNumberMessage:C.wrongNumberMessage||"Must be "+C.is+"!",tooLowMessage:C.tooLowMessage||"Must not be less than "+C.minimum+"!",tooHighMessage:C.tooHighMessage||"Must not be more than "+C.maximum+"!",is:((C.is)||(C.is==0))?C.is:null,minimum:((C.minimum)||(C.minimum==0))?C.minimum:null,maximum:((C.maximum)||(C.maximum==0))?C.maximum:null,onlyInteger:C.onlyInteger||false};if(!isFinite(B)){Validate.fail(D.notANumberMessage);}if(D.onlyInteger&&((/\.0+$|\.$/.test(String(A)))||(B!=parseInt(B)))){Validate.fail(D.notAnIntegerMessage);}switch(true){case (D.is!==null):if(B!=Number(D.is)){Validate.fail(D.wrongNumberMessage);}break;case (D.minimum!==null&&D.maximum!==null):Validate.Numericality(B,{tooLowMessage:D.tooLowMessage,minimum:D.minimum});Validate.Numericality(B,{tooHighMessage:D.tooHighMessage,maximum:D.maximum});break;case (D.minimum!==null):if(B<Number(D.minimum)){Validate.fail(D.tooLowMessage);}break;case (D.maximum!==null):if(B>Number(D.maximum)){Validate.fail(D.tooHighMessage);}break;}return true;},Format:function(A,B){var A=String(A);var C=Object.extend({failureMessage:"Not valid!",pattern:/./,negate:false},B||{});if(!C.negate&&!C.pattern.test(A)){Validate.fail(C.failureMessage);}if(C.negate&&C.pattern.test(A)){Validate.fail(C.failureMessage);}return true;},Email:function(A,B){var C=Object.extend({failureMessage:"Must be a valid email address!"},B||{});Validate.Format(A,{failureMessage:C.failureMessage,pattern:/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i});return true;},Length:function(A,B){var A=String(A);var B=B||{};var C={wrongLengthMessage:B.wrongLengthMessage||"Must be "+B.is+" characters long!",tooShortMessage:B.tooShortMessage||"Must not be less than "+B.minimum+" characters long!",tooLongMessage:B.tooLongMessage||"Must not be more than "+B.maximum+" characters long!",is:((B.is)||(B.is==0))?B.is:null,minimum:((B.minimum)||(B.minimum==0))?B.minimum:null,maximum:((B.maximum)||(B.maximum==0))?B.maximum:null};switch(true){case (C.is!==null):if(A.length!=Number(C.is)){Validate.fail(C.wrongLengthMessage);}break;case (C.minimum!==null&&C.maximum!==null):Validate.Length(A,{tooShortMessage:C.tooShortMessage,minimum:C.minimum});Validate.Length(A,{tooLongMessage:C.tooLongMessage,maximum:C.maximum});break;case (C.minimum!==null):if(A.length<Number(C.minimum)){Validate.fail(C.tooShortMessage);}break;case (C.maximum!==null):if(A.length>Number(C.maximum)){Validate.fail(C.tooLongMessage);}break;default:throw new Error("Validate::Length - Length(s) to validate against must be provided!");}return true;},Inclusion:function(C,D){var E=Object.extend({failureMessage:"Must be included in the list!",within:[],allowNull:false,partialMatch:false,caseSensitive:true,negate:false},D||{});if(E.allowNull&&C==null){return true;}if(!E.allowNull&&C==null){Validate.fail(E.failureMessage);}if(!E.caseSensitive){var A=[];E.within.each(function(F){if(typeof F=="string"){F=F.toLowerCase();}A.push(F);});E.within=A;if(typeof C=="string"){C=C.toLowerCase();}}var B=(E.within.indexOf(C)==-1)?false:true;if(E.partialMatch){B=false;E.within.each(function(F){if(C.indexOf(F)!=-1){B=true;}});}if((!E.negate&&!B)||(E.negate&&B)){Validate.fail(E.failureMessage);}return true;},Exclusion:function(A,B){var C=Object.extend({failureMessage:"Must not be included in the list!",within:[],allowNull:false,partialMatch:false,caseSensitive:true},B||{});C.negate=true;Validate.Inclusion(A,C);return true;},Confirmation:function(A,B){if(!B.match){throw new Error("Validate::Confirmation - Error validating confirmation: Id of element to match must be provided!");}var C=Object.extend({failureMessage:"Does not match!",match:null},B||{});C.match=$(B.match);if(!C.match){throw new Error("Validate::Confirmation - There is no reference with name of, or element with id of '"+C.match+"'!");}if(A!=C.match.value){Validate.fail(C.failureMessage);}return true;},Acceptance:function(A,B){var C=Object.extend({failureMessage:"Must be accepted!"},B||{});if(!A){Validate.fail(C.failureMessage);}return true;},Custom:function(A,B){var C=Object.extend({against:function(){return true;},args:{},failureMessage:"Not valid!"},B||{});if(!C.against(A,C.args)){Validate.fail(C.failureMessage);}return true;},now:function(A,D,C){if(!A){throw new Error("Validate::now - Validation function must be provided!");}var E=true;try{A(D,C||{});}catch(B){if(B instanceof Validate.Error){E=false;}else{throw B;}}finally{return E;}},Error:function(A){this.message=A;this.name="ValidationError";},fail:function(A){throw new Validate.Error(A);}};
|
@@ -0,0 +1,28 @@
|
|
1
|
+
.LV_validation_message{
|
2
|
+
font-weight:bold;
|
3
|
+
margin:0 0 0 5px;
|
4
|
+
}
|
5
|
+
|
6
|
+
.LV_valid {
|
7
|
+
color:#00CC00;
|
8
|
+
}
|
9
|
+
|
10
|
+
.LV_invalid {
|
11
|
+
color:#CC0000;
|
12
|
+
}
|
13
|
+
|
14
|
+
.LV_valid_field,
|
15
|
+
input.LV_valid_field:hover,
|
16
|
+
input.LV_valid_field:active,
|
17
|
+
textarea.LV_valid_field:hover,
|
18
|
+
textarea.LV_valid_field:active {
|
19
|
+
border: 1px solid #00CC00;
|
20
|
+
}
|
21
|
+
|
22
|
+
.LV_invalid_field,
|
23
|
+
input.LV_invalid_field:hover,
|
24
|
+
input.LV_invalid_field:active,
|
25
|
+
textarea.LV_invalid_field:hover,
|
26
|
+
textarea.LV_invalid_field:active {
|
27
|
+
border: 1px solid #CC0000;
|
28
|
+
}
|
data/install.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Install hook code here
|
data/lib/form_helpers.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module ActionView
|
2
|
+
|
3
|
+
mattr_accessor :live_validations
|
4
|
+
ActionView::live_validations = true
|
5
|
+
|
6
|
+
module Helpers
|
7
|
+
module FormHelper
|
8
|
+
[ :text_field, :text_area, :password_field ].each do |field_type|
|
9
|
+
define_method "#{field_type}_with_live_validations" do |object_name, method, options|
|
10
|
+
live = options.delete(:live)
|
11
|
+
live = ActionView::live_validations if live.nil?
|
12
|
+
send("#{field_type}_without_live_validations", object_name, method, options) +
|
13
|
+
( live ? live_validations_for(object_name, method) : '' )
|
14
|
+
end
|
15
|
+
alias_method_chain field_type, :live_validations
|
16
|
+
end
|
17
|
+
|
18
|
+
def live_validations_for(object_name, method)
|
19
|
+
script_tags(live_validation(object_name, method))
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def live_validation(object_name, method)
|
25
|
+
if validations = self.instance_variable_get("@#{object_name.to_s}").class.live_validations[method.to_sym] rescue false
|
26
|
+
field_name = "#{object_name}_#{method}"
|
27
|
+
initialize_validator(field_name) +
|
28
|
+
validations.map do |type, configuration|
|
29
|
+
live_validation_code(field_name, type, configuration)
|
30
|
+
end.join(';')
|
31
|
+
else
|
32
|
+
''
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize_validator(field_name)
|
37
|
+
"var #{field_name} = new LiveValidation('#{field_name}');"
|
38
|
+
end
|
39
|
+
|
40
|
+
def live_validation_code(field_name, type, configuration)
|
41
|
+
"#{field_name}.add(#{ActiveRecord::Validations::VALIDATION_METHODS[type]}" + ( configuration ? ", #{configuration.to_json}" : '') + ')'
|
42
|
+
end
|
43
|
+
|
44
|
+
def script_tags(js_code = '')
|
45
|
+
( js_code.blank? ? '' : "<script>#{js_code}</script>" )
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Validations
|
3
|
+
LIVE_VALIDATIONS_OPTIONS = {
|
4
|
+
:failureMessage => :message,
|
5
|
+
:pattern => :with,
|
6
|
+
:onlyInteger => :only_integer
|
7
|
+
}
|
8
|
+
# more complicated mappings in map_configuration method
|
9
|
+
|
10
|
+
VALIDATION_METHODS = {
|
11
|
+
:presence => "Validate.Presence",
|
12
|
+
:numericality => "Validate.Numericality",
|
13
|
+
:format => "Validate.Format",
|
14
|
+
:length => "Validate.Length",
|
15
|
+
:acceptance => "Validate.Acceptance",
|
16
|
+
:confirmation => "Validate.Confirmation"
|
17
|
+
}
|
18
|
+
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
|
22
|
+
VALIDATION_METHODS.keys.each do |type|
|
23
|
+
define_method "validates_#{type}_of_with_live_validations".to_sym do |*attr_names|
|
24
|
+
send "validates_#{type}_of_without_live_validations".to_sym, *attr_names
|
25
|
+
define_validations(type, attr_names)
|
26
|
+
end
|
27
|
+
alias_method_chain "validates_#{type}_of".to_sym, :live_validations
|
28
|
+
end
|
29
|
+
|
30
|
+
def live_validations
|
31
|
+
@live_validations ||= {}
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def define_validations(validation_type, attr_names)
|
37
|
+
conf = (attr_names.last.is_a?(Hash) ? attr_names.pop : {})
|
38
|
+
attr_names.each do |attr_name|
|
39
|
+
configuration = map_configuration(conf.dup, validation_type, attr_name)
|
40
|
+
add_live_validation(attr_name, validation_type, configuration)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def add_live_validation(attr_name, type, configuration = {})
|
45
|
+
@live_validations ||= {}
|
46
|
+
@live_validations[attr_name] ||= {}
|
47
|
+
@live_validations[attr_name][type] = configuration
|
48
|
+
end
|
49
|
+
|
50
|
+
def map_configuration(configuration, type = nil, attr_name = '')
|
51
|
+
LIVE_VALIDATIONS_OPTIONS.each do |live, rails|
|
52
|
+
configuration[live] = configuration.delete(rails)
|
53
|
+
end
|
54
|
+
if type == :numericality
|
55
|
+
if configuration[:onlyInteger]
|
56
|
+
configuration[:notAnIntegerMessage] = configuration.delete(:failureMessage)
|
57
|
+
else
|
58
|
+
configuration[:notANumberMessage] = configuration.delete(:failureMessage)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
if type == :length and range = ( configuration.delete(:in) || configuration.delete(:within) )
|
62
|
+
configuration[:minimum] = range.begin
|
63
|
+
configuration[:maximum] = range.end
|
64
|
+
end
|
65
|
+
if type == :confirmation
|
66
|
+
configuration[:match] = self.to_s.underscore + '_' + attr_name.to_s + '_confirmation'
|
67
|
+
end
|
68
|
+
configuration[:validMessage] ||= ''
|
69
|
+
configuration.reject {|k, v| v.nil? }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{livevalidation}
|
8
|
+
s.version = "0.0.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Sergio Gil"]
|
12
|
+
s.date = %q{2010-08-30}
|
13
|
+
s.description = %q{Easy client side validation for your ActiveRecord models}
|
14
|
+
s.email = %q{sgilperez@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".gitignore",
|
20
|
+
"README.md",
|
21
|
+
"Rakefile",
|
22
|
+
"VERSION",
|
23
|
+
"assets/javascripts/live_validation.js",
|
24
|
+
"assets/stylesheets/live_validation.css",
|
25
|
+
"install.rb",
|
26
|
+
"lib/form_helpers.rb",
|
27
|
+
"lib/live_validations.rb",
|
28
|
+
"livevalidation.gemspec",
|
29
|
+
"rails/init.rb",
|
30
|
+
"tasks/live_validation_tasks.rake",
|
31
|
+
"test/form_helpers_test.rb",
|
32
|
+
"test/live_validations_test.rb",
|
33
|
+
"test/resource.rb",
|
34
|
+
"test/test_helper.rb",
|
35
|
+
"uninstall.rb"
|
36
|
+
]
|
37
|
+
s.homepage = %q{http://github.com/porras/livevalidation}
|
38
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
39
|
+
s.require_paths = ["lib"]
|
40
|
+
s.rubygems_version = %q{1.3.7}
|
41
|
+
s.summary = %q{Easy client side validation for your ActiveRecord models}
|
42
|
+
s.test_files = [
|
43
|
+
"test/form_helpers_test.rb",
|
44
|
+
"test/live_validations_test.rb",
|
45
|
+
"test/resource.rb",
|
46
|
+
"test/test_helper.rb"
|
47
|
+
]
|
48
|
+
|
49
|
+
if s.respond_to? :specification_version then
|
50
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
51
|
+
s.specification_version = 3
|
52
|
+
|
53
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
54
|
+
else
|
55
|
+
end
|
56
|
+
else
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
data/rails/init.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
namespace :livevalidation do
|
2
|
+
|
3
|
+
PLUGIN_ROOT = File.dirname(__FILE__) + '/../'
|
4
|
+
|
5
|
+
desc 'Installs required javascript and stylesheet files to the public/ directory.'
|
6
|
+
task :install do
|
7
|
+
FileUtils.cp Dir[PLUGIN_ROOT + '/assets/javascripts/*.js'], RAILS_ROOT + '/public/javascripts'
|
8
|
+
FileUtils.cp Dir[PLUGIN_ROOT + '/assets/stylesheets/*.css'], RAILS_ROOT + '/public/stylesheets'
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'Removes the javascript and stylesheet for the plugin.'
|
12
|
+
task :remove do
|
13
|
+
FileUtils.rm %{live_validation.js}.collect { |f| RAILS_ROOT + "/public/javascripts/" + f }
|
14
|
+
FileUtils.rm %{live_validation.css}.collect { |f| RAILS_ROOT + "/public/stylesheets/" + f }
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test/test_helper'
|
2
|
+
|
3
|
+
class ResourcesController < ActionController::Base
|
4
|
+
def without_instance_var
|
5
|
+
render_form(:text, :name)
|
6
|
+
end
|
7
|
+
|
8
|
+
def without_live
|
9
|
+
@resource = Resource.new
|
10
|
+
render :inline => "<% form_for(:resource, :url => resources_path) do |f| %><%= f.text_field :name, :live => false %><% end %>"
|
11
|
+
end
|
12
|
+
|
13
|
+
def with_live
|
14
|
+
@resource = Resource.new
|
15
|
+
render :inline => "<% form_for(:resource, :url => resources_path) do |f| %><%= f.text_field :name, :live => true %><% end %>"
|
16
|
+
end
|
17
|
+
|
18
|
+
def with_string
|
19
|
+
@resource = Resource.new
|
20
|
+
render :inline => "<% form_for(:resource, :url => resources_path) do |f| %><%= f.text_field 'name' %><% end %>"
|
21
|
+
end
|
22
|
+
|
23
|
+
def with_text_area
|
24
|
+
@resource = Resource.new
|
25
|
+
render :inline => "<% form_for(:resource, :url => resources_path) do |f| %><%= f.text_area :name %><% end %>"
|
26
|
+
end
|
27
|
+
|
28
|
+
def name
|
29
|
+
@resource = Resource.new
|
30
|
+
render_form(:text, :name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def amount
|
34
|
+
@resource = Resource.new
|
35
|
+
render_form(:text, :amount)
|
36
|
+
end
|
37
|
+
|
38
|
+
def password
|
39
|
+
@resource = Resource.new
|
40
|
+
render :inline => "<% form_for(:resource, :url => resources_path) do |f| %><%= f.password_field :password %><%= f.password_field :password_confirmation %><% end %>"
|
41
|
+
end
|
42
|
+
|
43
|
+
def rescue_action(e)
|
44
|
+
raise e
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def render_form(type, method)
|
50
|
+
render :inline => "<% form_for(:resource, :url => resources_path) do |f| %><%= f.#{type}_field :#{method} %><% end %>"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
ActionController::Routing::Routes.draw do |map|
|
55
|
+
map.resources :resources
|
56
|
+
map.connect ':controller/:action/:id'
|
57
|
+
end
|
58
|
+
|
59
|
+
class FormHelpersTest < ActionController::TestCase
|
60
|
+
|
61
|
+
def setup
|
62
|
+
@controller = ResourcesController.new
|
63
|
+
@request = ActionController::TestRequest.new
|
64
|
+
@response = ActionController::TestResponse.new
|
65
|
+
Resource.class_eval do # reset live validations
|
66
|
+
@live_validations = {}
|
67
|
+
end
|
68
|
+
ActionView::live_validations = true # reset default behaviour
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_without_instance_var
|
72
|
+
get :without_instance_var
|
73
|
+
check_form_item :type => 'text', :name => 'name'
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_without_live
|
77
|
+
Resource.class_eval do
|
78
|
+
validates_presence_of :name
|
79
|
+
end
|
80
|
+
get :without_live
|
81
|
+
check_form_item :type => 'text', :name => 'name'
|
82
|
+
end
|
83
|
+
|
84
|
+
def test_without_live_with_false_default
|
85
|
+
ActionView::live_validations = false
|
86
|
+
Resource.class_eval do
|
87
|
+
validates_presence_of :name
|
88
|
+
end
|
89
|
+
get :name
|
90
|
+
check_form_item :type => 'text', :name => 'name'
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_with_live_with_false_default
|
94
|
+
ActionView::live_validations = false
|
95
|
+
Resource.class_eval do
|
96
|
+
validates_presence_of :name
|
97
|
+
end
|
98
|
+
get :with_live
|
99
|
+
check_form_item :type => 'text', :name => 'name' do |script|
|
100
|
+
assert_matches script, "var resource_name = new LiveValidation('resource_name');resource_name.add(Validate.Presence, {\"validMessage\": \"\"})"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_with_string
|
105
|
+
Resource.class_eval do
|
106
|
+
validates_presence_of :name
|
107
|
+
end
|
108
|
+
get :with_string
|
109
|
+
check_form_item :type => 'text', :name => 'name' do |script|
|
110
|
+
assert_matches script, "var resource_name = new LiveValidation('resource_name');resource_name.add(Validate.Presence, {\"validMessage\": \"\"})"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def test_with_text_area
|
115
|
+
Resource.class_eval do
|
116
|
+
validates_presence_of :name
|
117
|
+
end
|
118
|
+
get :with_text_area
|
119
|
+
assert_response :ok
|
120
|
+
assert_select 'form[action="/resources"]' do
|
121
|
+
assert_select "textarea[id='resource_name']"
|
122
|
+
assert_select 'script', "var resource_name = new LiveValidation('resource_name');resource_name.add(Validate.Presence, {\"validMessage\": \"\"})"
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_presence
|
127
|
+
Resource.class_eval do
|
128
|
+
validates_presence_of :name
|
129
|
+
end
|
130
|
+
get :name
|
131
|
+
check_form_item :type => 'text', :name => 'name' do |script|
|
132
|
+
assert_matches script, "var resource_name = new LiveValidation('resource_name');resource_name.add(Validate.Presence, {\"validMessage\": \"\"})"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_presence_with_message
|
137
|
+
Resource.class_eval do
|
138
|
+
validates_presence_of :name, :message => 'is required'
|
139
|
+
end
|
140
|
+
get :name
|
141
|
+
check_form_item :type => 'text', :name => 'name' do |script|
|
142
|
+
assert_matches script, /var resource_name = new LiveValidation\('resource_name'\);resource_name.add\(Validate.Presence, \{(.+)\}\)/
|
143
|
+
assert_matches script, "\"validMessage\": \"\""
|
144
|
+
assert_matches script, "\"failureMessage\": \"is required\""
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_numericality
|
149
|
+
Resource.class_eval do
|
150
|
+
validates_numericality_of :amount
|
151
|
+
end
|
152
|
+
get :amount
|
153
|
+
check_form_item :type => 'text', :name => 'amount' do |script|
|
154
|
+
assert_matches script, "var resource_amount = new LiveValidation('resource_amount');resource_amount.add(Validate.Numericality, {\"validMessage\": \"\"})"
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def test_numericality_only_integer
|
159
|
+
Resource.class_eval do
|
160
|
+
validates_numericality_of :amount, :only_integer => true
|
161
|
+
end
|
162
|
+
get :amount
|
163
|
+
check_form_item :type => 'text', :name => 'amount' do |script|
|
164
|
+
assert_matches script, /var resource_amount = new LiveValidation\('resource_amount'\);resource_amount.add\(Validate.Numericality, \{(.*)\}\)/
|
165
|
+
assert_matches script, "\"onlyInteger\": true"
|
166
|
+
assert_matches script, "\"validMessage\": \"\""
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def test_confirmation
|
171
|
+
Resource.class_eval do
|
172
|
+
validates_confirmation_of :password
|
173
|
+
end
|
174
|
+
get :password
|
175
|
+
check_form_item :type => 'password', :name => 'password' do |script|
|
176
|
+
assert_matches script, /var resource_password = new LiveValidation\('resource_password'\);resource_password.add\(Validate.Confirmation, \{(.*)\}\)/
|
177
|
+
assert_matches script, "\"match\": \"resource_password_confirmation\""
|
178
|
+
assert_matches script, "\"validMessage\": \"\""
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
def check_form_item(options = {}, &blk)
|
185
|
+
assert_response :ok
|
186
|
+
assert_select 'form[action="/resources"]' do
|
187
|
+
assert_select "input[type='#{options[:type]}'][id='resource_#{options[:name]}']"
|
188
|
+
if block_given?
|
189
|
+
assert_select 'script' do |element|
|
190
|
+
yield(element.to_s)
|
191
|
+
end
|
192
|
+
else
|
193
|
+
assert_select 'script', 0
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
def assert_matches(string, regexp)
|
199
|
+
if regexp.is_a?(Regexp)
|
200
|
+
assert string =~ regexp, "#{string} doesn't match #{regexp}"
|
201
|
+
else
|
202
|
+
assert string[regexp], "#{string} doesn't match #{regexp}"
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../test/test_helper'
|
2
|
+
|
3
|
+
class LiveValidationTest < ActiveRecord::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
Resource.class_eval do # reset live validations
|
7
|
+
@live_validations = {}
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_live_validations_accessor
|
12
|
+
assert_kind_of(Hash, Resource.live_validations)
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_without_validations
|
16
|
+
assert_equal({}, Resource.live_validations)
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_without_ok_message
|
20
|
+
Resource.class_eval do
|
21
|
+
validates_presence_of :name, :message => "can't be blank"
|
22
|
+
end
|
23
|
+
assert_equal("", Resource.live_validations[:name][:presence][:validMessage])
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_with_ok_message
|
27
|
+
Resource.class_eval do
|
28
|
+
validates_presence_of :name, :message => "can't be blank", :validMessage => 'thank you!'
|
29
|
+
end
|
30
|
+
assert_equal("thank you!", Resource.live_validations[:name][:presence][:validMessage])
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_presence
|
34
|
+
Resource.class_eval do
|
35
|
+
validates_presence_of :name, :message => "can't be blank"
|
36
|
+
end
|
37
|
+
assert_equal("can't be blank", Resource.live_validations[:name][:presence][:failureMessage])
|
38
|
+
end
|
39
|
+
|
40
|
+
def test_presence_more_than_one_attribute
|
41
|
+
Resource.class_eval do
|
42
|
+
validates_presence_of :name, :amount, :message => "can't be blank"
|
43
|
+
end
|
44
|
+
assert_equal("can't be blank", Resource.live_validations[:name][:presence][:failureMessage])
|
45
|
+
assert_equal("can't be blank", Resource.live_validations[:amount][:presence][:failureMessage])
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_numericality
|
49
|
+
Resource.class_eval do
|
50
|
+
validates_numericality_of :amount, :message => "isn't a valid number"
|
51
|
+
end
|
52
|
+
assert_equal("isn't a valid number", Resource.live_validations[:amount][:numericality][:notANumberMessage])
|
53
|
+
assert(!Resource.live_validations[:amount][:numericality][:onlyInteger])
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_numericality_only_integer
|
57
|
+
Resource.class_eval do
|
58
|
+
validates_numericality_of :amount, :only_integer => true, :message => "isn't an integer number"
|
59
|
+
end
|
60
|
+
assert_equal("isn't an integer number", Resource.live_validations[:amount][:numericality][:notAnIntegerMessage])
|
61
|
+
assert(Resource.live_validations[:amount][:numericality][:onlyInteger])
|
62
|
+
end
|
63
|
+
|
64
|
+
def test_format
|
65
|
+
Resource.class_eval do
|
66
|
+
validates_format_of :name, :with => /^\w+$/, :message => "only letters are accepted"
|
67
|
+
end
|
68
|
+
assert_equal("only letters are accepted", Resource.live_validations[:name][:format][:failureMessage])
|
69
|
+
assert_equal(/^\w+$/, Resource.live_validations[:name][:format][:pattern])
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_length_max
|
73
|
+
Resource.class_eval do
|
74
|
+
validates_length_of :name, :maximum => 10, :message => "must be under 10 characters long"
|
75
|
+
end
|
76
|
+
assert_equal("must be under 10 characters long", Resource.live_validations[:name][:length][:failureMessage])
|
77
|
+
assert_equal(10, Resource.live_validations[:name][:length][:maximum])
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_length_min
|
81
|
+
Resource.class_eval do
|
82
|
+
validates_length_of :name, :minimum => 4, :message => "must be more than 4 characters long"
|
83
|
+
end
|
84
|
+
assert_equal("must be more than 4 characters long", Resource.live_validations[:name][:length][:failureMessage])
|
85
|
+
assert_equal(4, Resource.live_validations[:name][:length][:minimum])
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_length_range
|
89
|
+
Resource.class_eval do
|
90
|
+
validates_length_of :name, :in => 4..10, :message => "must be between 4 and 10 characters long"
|
91
|
+
end
|
92
|
+
assert_equal("must be between 4 and 10 characters long", Resource.live_validations[:name][:length][:failureMessage])
|
93
|
+
assert_equal(4, Resource.live_validations[:name][:length][:minimum])
|
94
|
+
assert_equal(10, Resource.live_validations[:name][:length][:maximum])
|
95
|
+
assert_nil(Resource.live_validations[:name][:length][:in])
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_length_exact
|
99
|
+
Resource.class_eval do
|
100
|
+
validates_length_of :name, :is => 5, :message => "must be 5 characters long"
|
101
|
+
end
|
102
|
+
assert_equal("must be 5 characters long", Resource.live_validations[:name][:length][:failureMessage])
|
103
|
+
assert_equal(5, Resource.live_validations[:name][:length][:is])
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_acceptance
|
107
|
+
Resource.class_eval do
|
108
|
+
validates_acceptance_of :conditions, :message => "you must accept conditions"
|
109
|
+
end
|
110
|
+
assert_equal("you must accept conditions", Resource.live_validations[:conditions][:acceptance][:failureMessage])
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_confirmation
|
114
|
+
Resource.class_eval do
|
115
|
+
validates_confirmation_of :name, :message => "doesn't match"
|
116
|
+
end
|
117
|
+
assert_equal("doesn't match", Resource.live_validations[:name][:confirmation][:failureMessage])
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
data/test/resource.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
ActiveRecord::Base.class_eval do
|
2
|
+
alias_method :save, :valid?
|
3
|
+
def self.columns() @columns ||= []; end
|
4
|
+
|
5
|
+
def self.column(name, sql_type = nil, default = nil, null = true)
|
6
|
+
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type, null)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Resource < ActiveRecord::Base
|
11
|
+
column :id, :integer
|
12
|
+
column :name, :string
|
13
|
+
column :amount, :integer
|
14
|
+
column :conditions, :boolean
|
15
|
+
|
16
|
+
attr_accessor :password
|
17
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
gem 'rails', '<= 2.3.0' #tests aren't passing in 2.3.2 (but it seems to work)
|
5
|
+
|
6
|
+
require 'active_record'
|
7
|
+
require 'action_controller'
|
8
|
+
require 'action_controller/test_process'
|
9
|
+
require 'action_view'
|
10
|
+
|
11
|
+
require File.dirname(__FILE__) + '/../lib/live_validations'
|
12
|
+
require File.dirname(__FILE__) + '/../lib/form_helpers'
|
13
|
+
require File.dirname(__FILE__) + '/../test/resource'
|
data/uninstall.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
# Uninstall hook code here
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: livevalidation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 29
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 0.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Sergio Gil
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-30 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Easy client side validation for your ActiveRecord models
|
23
|
+
email: sgilperez@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README.md
|
30
|
+
files:
|
31
|
+
- .gitignore
|
32
|
+
- README.md
|
33
|
+
- Rakefile
|
34
|
+
- VERSION
|
35
|
+
- assets/javascripts/live_validation.js
|
36
|
+
- assets/stylesheets/live_validation.css
|
37
|
+
- install.rb
|
38
|
+
- lib/form_helpers.rb
|
39
|
+
- lib/live_validations.rb
|
40
|
+
- livevalidation.gemspec
|
41
|
+
- rails/init.rb
|
42
|
+
- tasks/live_validation_tasks.rake
|
43
|
+
- test/form_helpers_test.rb
|
44
|
+
- test/live_validations_test.rb
|
45
|
+
- test/resource.rb
|
46
|
+
- test/test_helper.rb
|
47
|
+
- uninstall.rb
|
48
|
+
has_rdoc: true
|
49
|
+
homepage: http://github.com/porras/livevalidation
|
50
|
+
licenses: []
|
51
|
+
|
52
|
+
post_install_message:
|
53
|
+
rdoc_options:
|
54
|
+
- --charset=UTF-8
|
55
|
+
require_paths:
|
56
|
+
- lib
|
57
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
72
|
+
segments:
|
73
|
+
- 0
|
74
|
+
version: "0"
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.3.7
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: Easy client side validation for your ActiveRecord models
|
82
|
+
test_files:
|
83
|
+
- test/form_helpers_test.rb
|
84
|
+
- test/live_validations_test.rb
|
85
|
+
- test/resource.rb
|
86
|
+
- test/test_helper.rb
|