grimen-validatious_on_rails 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +15 -0
- data/Rakefile +51 -0
- data/generators/validatious/templates/v2.standalone.full.min.js +7 -0
- data/generators/validatious/templates/validatious_config.js +83 -0
- data/generators/validatious/validatious_generator.rb +10 -0
- data/lib/validatious/form_helper.rb +66 -0
- data/lib/validatious/rails_validation.rb +167 -0
- data/lib/validatious.rb +9 -0
- data/rails/init.rb +1 -0
- data/test/test_helper.rb +56 -0
- data/test/validatious/form_helper_test.rb +61 -0
- data/test/validatious/rails_validation_test.rb +87 -0
- data/test/validatious_test.rb +5 -0
- metadata +82 -0
data/README
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Validatious on Rails
|
2
|
+
####################
|
3
|
+
|
4
|
+
Official URL:
|
5
|
+
github.com/cjohansen/juicer/tree/master
|
6
|
+
|
7
|
+
Original Author:
|
8
|
+
Christian Johansen
|
9
|
+
www.cjohansen.no
|
10
|
+
|
11
|
+
Validatious client side validation framework:
|
12
|
+
www.validatious.org
|
13
|
+
|
14
|
+
Released under the MIT license. No Support. No Warranty.
|
15
|
+
Copyright (c) 2008 Christian Johansen
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
|
6
|
+
NAME = "validatious_on_rails"
|
7
|
+
SUMMARY = %Q{Rails plugin that maps model validations to class names on form elements to integrate with Validatious.}
|
8
|
+
HOMEPAGE = "http://github.com/grimen/#{NAME}"
|
9
|
+
AUTHORS = ["Christian Johansen", "Jonas Grimfelt"]
|
10
|
+
EMAIL = "christian@cjohansen.no"
|
11
|
+
SUPPORT_FILES = %w(README)
|
12
|
+
|
13
|
+
begin
|
14
|
+
gem 'technicalpickles-jeweler', '>= 1.2.1'
|
15
|
+
require 'jeweler'
|
16
|
+
Jeweler::Tasks.new do |gemspec|
|
17
|
+
gemspec.name = NAME
|
18
|
+
gemspec.summary = SUMMARY
|
19
|
+
gemspec.description = SUMMARY
|
20
|
+
gemspec.homepage = HOMEPAGE
|
21
|
+
gemspec.authors = AUTHORS
|
22
|
+
gemspec.email = EMAIL
|
23
|
+
|
24
|
+
gemspec.require_paths = %w{lib}
|
25
|
+
gemspec.files = SUPPORT_FILES << %w(Rakefile) <<
|
26
|
+
Dir.glob(File.join('{generators,lib,test,rails}', '**', '*').to_s).reject { |v| v =~ /\.log/i }
|
27
|
+
gemspec.executables = %w()
|
28
|
+
gemspec.extra_rdoc_files = SUPPORT_FILES
|
29
|
+
end
|
30
|
+
rescue LoadError
|
31
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
32
|
+
end
|
33
|
+
|
34
|
+
desc %Q{Default: Run unit tests for "#{NAME}".}
|
35
|
+
task :default => :test
|
36
|
+
|
37
|
+
desc %Q{Run unit tests for "#{NAME}".}
|
38
|
+
Rake::TestTask.new(:test) do |test|
|
39
|
+
test.libs << %w(lib test)
|
40
|
+
test.pattern = File.join('test', '**', '*_test.rb')
|
41
|
+
test.verbose = true
|
42
|
+
end
|
43
|
+
|
44
|
+
desc %Q{Generate documentation for "#{NAME}".}
|
45
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
46
|
+
rdoc.rdoc_dir = 'rdoc'
|
47
|
+
rdoc.title = NAME
|
48
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--charset=UTF-8'
|
49
|
+
rdoc.rdoc_files.include(SUPPORT_FILES)
|
50
|
+
rdoc.rdoc_files.include(File.join('lib', '**', '*.rb'))
|
51
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
/**
|
2
|
+
* TERMS OF USE - Validatious 2.0
|
3
|
+
* Open source under the BSD License.
|
4
|
+
* Copyright 2008 Christian Johansen.
|
5
|
+
* All rights reserved.
|
6
|
+
*/
|
7
|
+
var addDOMLoadEvent=(function(){var F=[],A,E,C,B,D,G=function(){C=true;clearInterval(A);while(B=F.shift()){B()}if(E){E.onreadystatechange=""}};return function(H){if(C){return H()}if(!F[0]){if(document.addEventListener){document.addEventListener("DOMContentLoaded",G,false)}if(/WebKit/i.test(navigator.userAgent)){A=setInterval(function(){if(/loaded|complete/.test(document.readyState)){G()}},10)}D=window.onload;window.onload=function(){G();if(D){D()}}}F.push(H)}})();var Base=function(){};Base.extend=function(B,E){var F=Base.prototype.extend;Base._prototyping=true;var D=new this;F.call(D,B);delete Base._prototyping;var C=D.constructor;var A=D.constructor=function(){if(!Base._prototyping){if(this._constructing||this.constructor==A){this._constructing=true;C.apply(this,arguments);delete this._constructing}else{if(arguments[0]!=null){return(arguments[0].extend||F).call(arguments[0],D)}}}};A.ancestor=this;A.extend=this.extend;A.forEach=this.forEach;A.implement=this.implement;A.prototype=D;A.toString=this.toString;A.valueOf=function(G){return(G=="object")?A:C.valueOf()};F.call(A,E);if(typeof A.init=="function"){A.init()}return A};Base.prototype={extend:function(B,H){if(arguments.length>1){var E=this[B];if(E&&(typeof H=="function")&&(!E.valueOf||E.valueOf()!=H.valueOf())&&/\bbase\b/.test(H)){var A=H.valueOf();H=function(){var K=this.base||Base.prototype.base;this.base=E;var J=A.apply(this,arguments);this.base=K;return J};H.valueOf=function(J){return(J=="object")?H:A};H.toString=Base.toString}this[B]=H}else{if(B){var G=Base.prototype.extend;if(!Base._prototyping&&typeof this!="function"){G=this.extend||G}var D={toSource:null};var F=["constructor","toString","valueOf"];var C=Base._prototyping?0:1;while(I=F[C++]){if(B[I]!=D[I]){G.call(this,I,B[I])}}for(var I in B){if(!D[I]){G.call(this,I,B[I])}}}}return this},base:function(){}};Base=Base.extend({constructor:function(){this.extend(arguments[0])}},{ancestor:Object,version:"1.1",forEach:function(A,D,C){for(var B in A){if(this.prototype[B]===undefined){D.call(C,A[B],B,A)}}},implement:function(){for(var A=0;A<arguments.length;A++){if(typeof arguments[A]=="function"){arguments[A](this.prototype)}else{this.prototype.extend(arguments[A])}}return this},toString:function(){return String(this.valueOf())}});function addEvent(B,D,C){if(B.addEventListener){B.addEventListener(D,C,false)}else{if(!C.$$guid){C.$$guid=addEvent.guid++}if(!B.events){B.events={}}var A=B.events[D];if(!A){A=B.events[D]={};if(B["on"+D]){A[0]=B["on"+D]}}A[C.$$guid]=C;B["on"+D]=handleEvent}}addEvent.guid=1;function removeEvent(A,C,B){if(A.removeEventListener){A.removeEventListener(C,B,false)}else{if(A.events&&A.events[C]){delete A.events[C][B.$$guid]}}}function handleEvent(D){var C=true;D=D||fixEvent(((this.ownerDocument||this.document||this).parentWindow||window).event);var A=this.events[D.type];for(var B in A){this.$$handleEvent=A[B];if(this.$$handleEvent(D)===false){C=false}}return C}function fixEvent(A){A.preventDefault=fixEvent.preventDefault;A.stopPropagation=fixEvent.stopPropagation;return A}fixEvent.preventDefault=function(){this.returnValue=false};fixEvent.stopPropagation=function(){this.cancelBubble=true};if(typeof v2==="undefined"||v2===null){v2={}}v2.empty=function empty(A){return typeof A=="undefined"||A===null||A===""};v2.array=function array(C){var A=[];if(C&&typeof C.item==="function"){for(var B=0;B<C.length;B++){A[B]=C[B]}C=A}return v2.empty(C)?[]:(typeof C.shift=="undefined"?[C]:C)};v2.Object={extend:function(C,A,B){B=typeof B==="undefined"?false:B;for(var D in A){if(B&&typeof C[D]!=="undefined"){continue}if(A.hasOwnProperty(D)&&typeof A[D]!=="undefined"){C[D]=A[D]}}return C}};v2.Object.extend(String.prototype,{strip:function(){return this.replace(/^\s+|\s+$/,"")}});v2.Object.extend(Array.prototype,{indexOf:function(C,A){for(var B=0;B<this.length;B++){if(this[B]===C){return B}}return -1}});v2.$=function(A,C){if(typeof A=="string"){A=document.getElementById(A)}C=typeof C=="undefined"?true:C;if(A===null||typeof A.hide!="undefined"||!C){return A}for(var B in v2.Element){if(v2.Element.hasOwnProperty(B)){(function(){var D=B;var E={};E[D]=function(){var F=[A];for(var G=0;G<arguments.length;G++){F.push(arguments[G])}return v2.Element[D].apply(v2.Element,F)};v2.Object.extend(A,E,true)})()}}return A};v2.$$=function $$(E,N){N=N||document;if(document.querySelectorAll){v2.$$=function(Q,R){return(R||document).querySelectorAll(Q)};return v2.$$(E,N)}var O=E.split(",");var J,H,F,B,K,D,A,I,C,G,L,M;var P=[];for(J=0;(E=O[J]);J++){B=E.strip().split(/[^a-zA-Z]/)[0]||"*";K=E.replace(/^\./,"_.").split(/\./);C=[];G=[];for(H=1;H<K.length;H++){C.push(K[H].split(/[^a-zA-Z\-_]/)[0])}K=E.replace("][","].[").split(/[\[\]]/);for(H=0;H<K.length-1;H+=2){D=K[H+1].split("=");D[1]=D[1]||true;G.push(D)}A=N.getElementsByTagName(B);elementLoop:for(H=0;(I=A[H]);H++){for(F=0;F<C.length;F++){if(!v2.Element.hasClassName(I,C[F])){continue elementLoop}}for(F=0;F<G.length;F++){L=G[F][0];M=G[F][1];if(I.hasAttribute){if(!I.hasAttribute(L)||(M!==true&&I.getAttribute(L)!==M)){continue elementLoop}}else{L=L==="for"?"htmlFor":L;if(typeof I[L]==="undefined"||(M!==true&&I[L]!==M)){continue elementLoop}}}if(P.indexOf(I)<0){P.push(I)}}}return P};v2.Element={observe:function(B,C,A){addEvent(B,C,A)},computedStyle:function(A,C){var B="";if(document.defaultView&&document.defaultView.getComputedStyle){B=document.defaultView.getComputedStyle(A,"").getPropertyValue(C)}else{if(A.currentStyle){C=C.replace(/\-(\w)/g,function(D,E){return E.toUpperCase()});B=A.currentStyle[C]}}return B},visible:function(A){return v2.Element.computedStyle(A,"display")!="none"&&v2.Element.computedStyle(A,"visibility")!="hidden"&&(A.parentNode===null||A.parentNode.nodeType!=1||v2.Element.visible(A.parentNode))},hasClassName:function(B,A){return new RegExp("(^|\\s)"+A+"(\\s|$)").test(B.className)},addClassName:function(B,A){B.className+=v2.Element.hasClassName(B,A)?"":" "+A;B.className=B.className.replace(" "," ").replace(/^\s|\s$/,"");return B},removeClassName:function(B,A){var C=new RegExp("(^|\\s)"+A+"(\\s|$)");B.className=B.className.replace(C," ").replace(" "," ").replace(/^\s|\s$/,"");return B},position:function(A){var B={x:0,y:0};while(A!==null){B.x+=A.offsetLeft;B.y+=A.offsetTop;A=A.offsetParent}return B},scrollTo:function(A){var B=v2.Element.position(A);window.scrollTo(B.x,B.y)},previous:function(A){do{A=A.previousSibling}while(A&&A.nodeType!==1);return A}};v2.Object.extend(Function.prototype,{bind:function(B){var A=this;A.__cc=A.__cc||[];window.__coc=window.__coc||0;if(typeof B.__id=="undefined"){B.__id=window.__coc++}A.__cc[B.__id]=A.__cc[B.__id]||function(){try{return A.apply(B,arguments)}catch(C){}};return A.__cc[B.__id]}});if(typeof addDOMLoadEvent!="undefined"){v2.addDOMLoadEvent=addDOMLoadEvent}v2.CompositeFormItem=Base.extend({type:"generic",constructor:function(){if(v2.empty(this.__validators)){this.__validators=[]}if(v2.empty(this.__errors)){this.__errors=[]}this.__passOnAny=false;this.__message=null;this.parent=null},add:function(A){this.__validators.push(A);A.parent=this},get:function(A){return !v2.empty(A)?this.__validators[A]:this.__validators},validate:function(){this.__errors=[];var A=this.test("validate");if(A){this.__errors=[];this.onSuccess()}else{this.onFailure()}return A},test:function(C){var B,A,D=0;C=C||"test";for(B=0;(A=this.__validators[B]);B++){if(A[C]()){D++}else{this.__errors.push(A)}}return this.passOnAny()&&D>0||!this.passOnAny()&&D===this.__validators.length},getInvalid:function(){return this.__errors.length===0?null:this.__errors},setMessage:function(A){this.__message=A},getMessages:function(){if(!v2.empty(this.__message)){return[this.__message]}var C=[],B,A;for(B=0,A;(A=this.__errors[B]);B++){C=C.concat(A.getMessages())}return C},passOnAny:function(A){if(typeof A!=="undefined"){this.__passOnAny=!!A}return this.__passOnAny},onSuccess:function(){},onFailure:function(){}});v2.InputElement=Base.extend({constructor:function(B,A){this.__name=null;this.__events=v2.empty(A)?["blur"]:v2.array(A);this.__elements=v2.array(B)},monitor:function(D){this.__monitored=true;var C,A,B,E;for(C=0;(B=this.__elements[C]);C++){for(A=0;(E=this.__events[A]);A++){v2.Element.observe(B,E,D)}}},getValue:function(){return this.__elements[0].value},getLabel:function(){var B=this.__elements[0];var A=v2.$$("label[for="+(B.id||B.name)+"]",B.form);return A&&A[0]},getName:function(){if(this.__name){return this.__name}var A=this.getLabel();if(A){return A.title!==""?A.title:A.innerHTML}var B=this.__elements[0];return B.id||B.name},setName:function(A){this.__name=!!A?A:null;return this},getParent:function(){return this.__elements[0].parentNode},getElements:function(){return this.__elements},visible:function(){if(!v2.$(this.getParent()).visible()){return false}for(var A=0,B;(B=this.__elements[A]);A++){if(v2.Element.visible(B)){return true}}return false}},{__fields:{},get:function(B){if(B&&B.constructor===v2.InputElement){return B}var C=v2.$(B),H,G,F,A,D,E;if(!C){A="input$, select$, textarea$";F=v2.$$(A.replace(/\$/g,"[name="+B+"]"));C=F[0]}if(!C){throw new TypeError(B+" does not resolve to an HTML element!")}H=C.name||C.id;if((G=v2.InputElement.__fields[H])){return G}if(C.options){G=new v2.SelectElement(C)}else{if(C.tagName.toLowerCase()==="textarea"){G=new v2.TextareaElement(C)}else{if(C.type&&C.type=="radio"){F=v2.$$("input[type=radio][name="+C.name+"]",C.form);G=new v2.RadioElement(F)}else{if(C.type&&C.type=="checkbox"){D=C.className;E=/\bg_([^\s]*)\b/;F=E.test(D)?v2.$$("input[type=checkbox].g_"+D.match(E)[1],C.form):[C];G=new v2.CheckboxElement(F)}else{G=new v2.InputElement(C)}}}}return(v2.InputElement.__fields[H]=G)}});v2.$f=v2.InputElement.get;v2.RadioElement=v2.InputElement.extend({constructor:function(A){this.base(A,["click","change"])},getValue:function(){for(var B=0,A;(A=this.__elements[B]);B++){if(A.checked){return A.value}}return null},getLabel:function(){var B=this.__elements[0].parentNode;if(B.tagName.toLowerCase()==="li"){return v2.$(B.parentNode).previous()}var A=v2.$(this.__elements[0]).previous();return A||this.base()},getParent:function(){var A=this.__elements[0].parentNode;return A.tagName.toLowerCase()==="li"?A.parentNode.parentNode:A}});v2.SelectElement=v2.InputElement.extend({constructor:function(A){this.base([A],"change")},getValue:function(){var A=this.__elements[0];if(!A.multiple){return A.options[A.selectedIndex].value}var B=[];for(var C=0,D;(D=A.options[C]);C++){if(D.selected){B.push(D.value)}}return B}});v2.TextareaElement=v2.InputElement.extend({});v2.CheckboxElement=v2.RadioElement.extend({getValue:function(){var A=[],B,C;for(B=0,C;(C=this.__elements[B]);B++){if(C.checked){A.push(C.value)}}return A}});v2.Message=Base.extend({constructor:function(B,C,A){this.message=B;this.params=C||[];this.values=A||[]},copy:function(){return new v2.Message(this.message,this.params,this.values)},toString:function(){var C=this.message+"";for(var A=0,B=null;(B=this.params[A]);A++){C=C.replace("${"+B+"}",this.values[A])}return C}});v2.Validator=Base.extend({constructor:function(B,C,D,E,A){this.__name=B;this.__test=C;this.__message=new v2.Message(D||"${field} does not pass "+B+" validator",E);this.__aliases=v2.array(A);this.acceptEmpty=true},test:function(C,E,D){D=typeof D==="undefined"?false:D;var B=C.getValue();var A=(this.acceptEmpty&&B==="")||this.__test(C,B,E);return(A&&!D)||(!A&&D)},getName:function(){return this.__name},getMessage:function(){return this.__message},setMessage:function(A){this.__message.message=A}},{validators:{},add:function(B){if(!B.name||!B.fn){throw new TypeError("Options object should contain name and fn")}B=v2.Object.extend({params:[],aliases:[],acceptEmpty:true},B,false);var F=v2.array(B.params);var D=new v2.Validator(B.name,B.fn,B.message,F,B.aliases);D.acceptEmpty=B.acceptEmpty;var E=v2.array(B.aliases).concat([B.name]);for(var C=0,A;(A=E[C]);C++){v2.Validator.validators[A]=D}return D},reg:function(B,C,F,D,A,E){return v2.Validator.add({name:B,fn:C,message:D,params:F,aliases:A,acceptEmpty:E})},get:function(A){var B;if(A.constructor===v2.Validator){return A}if((B=v2.Validator.validators[A])){return B}return null}});v2.$v=function(A){return v2.Validator.get(A)};v2.$msg=function(){if(arguments.length===2){return v2.Validator.get(arguments[0]).setMessage(arguments[1])}for(var A in arguments[0]){try{v2.Validator.get(A).setMessage(arguments[0][A])}catch(B){}}};v2.Field=v2.CompositeFormItem.extend({validateHidden:false,instant:false,instantWhenValidated:true,type:"field",constructor:function(B,A,C){this.base();this.__monitored=false;this.element=v2.$f(B);this.instant=typeof A!=="undefined"?A:this.instant;this.instantWhenValidated=typeof C!=="undefined"?C:this.instantWhenValidated;this.__monitor(this.instant)},test:function(){if(!this.validateHidden&&!this.element.visible()){return true}return this.base()},validate:function(){this.__monitor(this.instantWhenValidated&&!this.__monitored);return this.base()},addValidator:function(A,E,C){var B=v2.$v(A);if(B===null){throw new Error(A+" is not a valid validator")}if(typeof C!=="undefined"&&C!==null){C=new v2.Message(C,B.getMessage().params)}var D=new v2.FieldValidator(this.element,B,v2.array(E),C);this.add(D);return D},getParent:function(){return this.element.getParent()},__monitor:function(C){if(!C||this.__monitored){return }this.__monitored=true;var A=this.parent&&this.parent.type==="fieldset"?this.parent:this;var B=A.validate.bind(this);this.element.monitor(function(D){B(D)})}});v2.Fieldset=v2.CompositeFormItem.extend({type:"fieldset",constructor:function(A){this.base();this.element=A},getParent:function(){return this.element}});v2.FieldValidator=Base.extend({invert:false,constructor:function(C,A,E,D){this.__field=C;this.__validator=A;this.__params=v2.array(E);this.__message=D||A.__message.copy();var B=this.__message.params;if(B.length<1||B[0]!=="field"){this.__message.params=["field"].concat(B)}this.__message.values=[this.__field.getName()].concat(this.__params)},test:function(){return this.__validator.test(this.__field,this.__params,this.invert)},validate:function(){return this.test()},getInvalid:function(){return !this.test()?this:null},setMessage:function(A){this.__message.message=A},getMessages:function(){return[this.__message.toString()]},add:function(){},remove:function(){},get:function(){},passOnAny:function(){},onSuccess:function(){},onFailure:function(){}});v2.Form=v2.CompositeFormItem.extend({type:"form",constructor:function(A){this.base();this.__form=v2.$(A);this.__form.observe("submit",this.validate.bind(this));this.__buttons=[];this.__activeButton=null},addButton:function(A){this.__buttons.push(A);v2.Element.observe(A,"click",(function(B){this.__activeButton=A}).bind(this))},validate:function(){var D=arguments.length>0?arguments[0]:null;var C=this.__buttons;var A=this.__activeButton;this.__activeButton=null;if(C.length>0&&C.indexOf(A)<0){return true}var B=this.base();if(!B&&D){D.preventDefault();D.returnValue=false}return B}},{forms:{},get:function(C){var B=v2.$(C),A;if(B===null||B.tagName.toLowerCase()!=="form"){throw new ArgumentError("idOrElement should represent a form element")}if(!v2.empty(A=v2.Form.forms[B.id])){return A}return(v2.Form.forms[B.id]=new v2.Form(B))}});(function(){var A=v2.Validator;A.reg("alpha",function(C,B,D){return/^[a-zA-Z\u00A1-\uFFFF]*$/.test(B)});A.reg("alphanum",function(C,B,D){return/^([a-zA-Z\u00A1-\uFFFF0-9])*$/.test(B)});A.reg("confirmation-of",function(C,B,D){return B===v2.$f(D[0]).getValue()},"field-id","${field} should be an exact match",null,false);A.reg("email",function(C,B,D){return/^[a-z0-9!#$%&'*+\/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+\/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(B)});A.reg("max-length",function(C,B,D){return B.length<D[0]},"max");A.reg("max-val",function(C,B,D){return B<=D[0]},"max");A.reg("min-length",function(C,B,D){return B.length>=D[0]},"min");A.reg("min-val",function(C,B,D){return B>=D[0]},"min");A.reg("numeric",function(C,B,D){return/^[0-9]*(\.[0-9]+)?$/.test(B)});A.reg("required",function(C,B,D){return !v2.empty(B)&&!(typeof B.length!=="undefined"&&B.length===0)},null,null,"not-empty",false);A.reg("url",function(C,B,D){return/^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i.test(B)});A.reg("word",function(C,B,D){return/^([a-zA-Z\u00A1-\uFFFF0-9_\-\s\t])*$/.test(B)})})();v2.$msg({alpha:"${field} should only contain letters",alphanum:"${field} should only contain letters and numbers","car-regnum-nor":"${field} should be a valid norwegian auto registration number","confirmation-of":"${field} should be a confirmation of ${field-id}",email:"${field} should be a valid email address","max-length":"${field} should be no more than ${max} characters long","max-val":"${field} should be no bigger than ${min}","min-length":"${field} should be atleast ${min} characters long","min-val":"${field} should be atleast ${min}",numeric:"${field} should only contain numbers","phone-nor":"${field} should be a valid norwegian phone number",required:"${field} is required","ssn-nor":"${field} should be a valid norwegian social security number",url:"${field} should be a valid URL",word:"${field} should only contain letters, numbers and punctuation"});v2.ErrorReporting={displayErrors:-1,positionErrorsAbove:true,failureClass:"error",successClass:"",messagesClass:"messages",onFailure:function(){if(this.parent&&this.parent.type==="fieldset"){return }var I=this.onSuccess();var F="",C,G,A,J;var K=[];var B=this.getMessages();I.addClassName(this.failureClass);I.removeClassName(this.successClass);var D=this.displayErrors;var H=this.__errors.length;D=D<0||D>H?H:D;if(D===0){return }var E=document.createElement("ul");E.id=this.__getId();E.className=this.messagesClass;for(C=0;C<D;C++){G=B[C];if(!G||K.indexOf(G.toString())>=0){continue}K.push(G.toString());J=document.createElement("li");J.innerHTML=G.toString();E.appendChild(J)}if(this.positionErrorsAbove){I.insertBefore(E,I.firstChild)}else{I.appendChild(E)}},onSuccess:function(){if(this.parent&&this.parent.type==="fieldset"){return null}var A=v2.$(this.getParent());var B=v2.$(this.__getId());if(B&&A===B.parentNode){A.removeChild(B)}A.removeClassName(this.failureClass);A.addClassName(this.successClass);return A},__getId:function(){var A=this.element.getElements?this.element.getElements()[0]:null;var C=A||this.getParent();var B=C.className;var D=((C.id||C.name||B.replace(this.failureClass,""))+"_"+this.failureClass).replace(" ","_").replace(/_+/,"_");return D}};v2.Object.extend(v2.Field.prototype,v2.ErrorReporting);v2.Object.extend(v2.Fieldset.prototype,v2.ErrorReporting);v2.Object.extend(v2.Form.prototype,{scrollToFirstWhenFail:true,onFailure:function(){if(!this.scrollToFirstWhenFail){return }var B,A,D,C=this.__form;D=C.all||C.getElementsByTagName("*");for(B=0;(A=D[B]);B++){if(v2.Element.hasClassName(A,v2.ErrorReporting.failureClass)){v2.Element.scrollTo(A);return }}}});v2.Object.extend(v2.Validator,{prefix:""});v2.Object.extend(v2.Form,{autoValidateClass:"validate",actionButtonClass:"action"});v2.html={validateAnyClass:"validate_any",validateAllClass:"validate_all",validatorsFromString:function(I){var E=v2.Validator.prefix;var G=new RegExp("^"+E,"");var F,H,B,D,A,C=[];I=I.split(" ");for(F=0;(H=I[F]);F++){B=false;if(!v2.empty(E)&&!G.test(H)){continue}H=H.replace(G,"");if(/^not_/.test(H)){H=H.replace(/^not_/,"");B=true}D=H.split("_");A=D.shift();if(A&&(A=v2.$v(A))){C.push({validator:A,params:D,invert:B})}}return C},applyValidators:function(A,F,D){if(!v2.empty(D)){F.setMessage(D)}for(var C=0,B,E;(B=A[C]);C++){E=F.addValidator(B.validator,B.params);E.invert=B.invert}return F}};v2.html.Form=Base.extend({constructor:function(A){this.form=v2.Form.get(A);this.__parsed={};this.parseElement(A,this.form);this.form.passOnAny(v2.$(A).hasClassName(v2.html.validateAnyClass))},parseElement:function(C,K,E){var A=v2.$$("div, fieldset, input, select, textarea",C);var H,F,B,I,G,D,L,J;this.__parsed[C.id||C.name]=true;E=E||[];for(H=0;(G=A[H]);H++){if(E.length===0&&(/^\s*$/.test(G.className)||this.__parsed[G.id||G.name])){continue}D=G.tagName.toLowerCase();if(D==="input"&&v2.Element.hasClassName(G,v2.Form.actionButtonClass)){this.form.addButton(G);continue}if(D==="div"||D==="fieldset"){this.parseBlock(G,K);continue}J=E.concat(v2.html.validatorsFromString(G.className));if(J.length>0){L=new v2.Field(G);v2.html.applyValidators(J,L,G.title);this.__parsed[G.id||G.name]=true;K.add(L)}}},parseBlock:function(C,E){var D=new v2.Fieldset(C);var B=true;v2.$(C);if(C.hasClassName(v2.html.validateAnyClass)){}else{if(C.hasClassName(v2.html.validateAllClass)){B=false}else{return }}D.passOnAny(B);var A=v2.html.validatorsFromString(C.className);this.parseElement(C,D,A);if(!/^\s*$/.test(C.title)){D.setMessage(C.title)}if(D.get(0)){E.add(D)}}});v2.addDOMLoadEvent(function(){var A=document.getElementsByTagName("form");for(var B=0,C;(C=A[B]);B++){if(v2.Element.hasClassName(C,v2.Form.autoValidateClass)){new v2.html.Form(C)}}});v2.dsl={__validateTemplate:function(A){return function(){var F=arguments[0];var D=F.element||(F.item?F.item.element:null)||null;if(D===null){while(!!F&&!F.element){F=F.get(0)}}var E=new v2.dsl.Form(D.getElements()[0].form);E.item.passOnAny(A);for(var C=0,B;(B=arguments[C]);C++){E.item.add(B.item||B)}return E}},__andOr:function(A){return function(){var D=new v2.dsl.Collection();D.item.passOnAny(A);for(var C=0,B;(B=arguments[C]);C++){D.item.add(B.item)}return D}}};v2.dsl.expose=function(){var A=v2.dsl;v2.Object.extend(window,{validate:A.validate,validateAll:A.validateAll,validateAny:A.validateAny,and:A.and,or:A.or},false)};v2.dsl.validateAll=v2.dsl.validate=v2.dsl.__validateTemplate(false);v2.dsl.validateAny=v2.dsl.__validateTemplate(true);v2.dsl.and=v2.dsl.__andOr(false);v2.dsl.or=v2.dsl.__andOr(true);v2.Object.extend(String.prototype,{is:function(A,C){var B=new v2.dsl.Field(this.toString());return B.addValidator(A,C)}});(function(){var A=String.prototype;A.isA=A.isAn=A.has=A.hasA=A.hasAn=A.is})();v2.dsl.Field=Base.extend({__currentValidator:null,__and:null,constructor:function(A){this.item=new v2.Field(A)},addValidator:function(A,C,B){if(typeof B!=="undefined"){if(this.__and!==null&&this.__and!==B){throw new Error("Field previously set up with "+(this.__and?"AND":"OR")+", unable to shift")}this.__and=B;this.item.passOnAny(!B)}this.__currentValidator=this.item.addValidator(A,C);return this},and:function(A,B){if(!this.__currentValidator){throw new Error("Cannot add more validators when no validators are added yet")}return this.addValidator(A,B,true)},or:function(A,B){if(!!this.__currentValidator){throw new Error("Cannot add more validators when no validators are added yet")}return this.addValidator(A,B,false)},explain:function(A){if(!this.__currentValidator){throw new Error("No active field validator")}this.__currentValidator.setMessage(A);return this},help:function(A){this.item.setMessage(A);return this},withName:function(A){this.item.element.setName(A);return this}});(function(){var A=v2.dsl.Field.prototype;A.orIs=A.orIsA=A.orIsAn=A.orHas=A.orHasA=A.orHasAn=A.or;A.andIs=A.andIsA=A.andIsAn=A.andHas=A.andHasA=A.andHasAn=A.and})();v2.dsl.Collection=Base.extend({constructor:function(){this.item=new v2.CompositeFormItem()},explain:function(A){this.item.setMessage(A)}});v2.dsl.Form=Base.extend({constructor:function(A){this.item=v2.Form.get(A)},on:function(){var B,A;for(B=0;B<arguments.length;B++){A=v2.$(arguments[B]);this.item.addButton(A.tagName.toLowerCase()!=="input"?A.getElementsByTagName("input")[0]:A)}}});
|
@@ -0,0 +1,83 @@
|
|
1
|
+
// -------------------------------------------
|
2
|
+
// VALIDATIOUS 2.0: Configuration
|
3
|
+
// -------------------------------------------
|
4
|
+
//
|
5
|
+
// More: http://validatious.org/learn/references
|
6
|
+
//
|
7
|
+
|
8
|
+
// Auto-validate form with this class.
|
9
|
+
//
|
10
|
+
// v2.Form.autoValidateClass = 'validate';
|
11
|
+
|
12
|
+
// Trigger validation on action with this class.
|
13
|
+
//
|
14
|
+
v2.Form.actionButtonClass = 'commit'; // Formtastic: Use 'commit'
|
15
|
+
|
16
|
+
// Validate any/all of the validations in a block with this class.
|
17
|
+
//
|
18
|
+
// Example:
|
19
|
+
//
|
20
|
+
// <div class="validate_any">
|
21
|
+
// ...
|
22
|
+
// </div>
|
23
|
+
//
|
24
|
+
// v2.html.validateAnyClass = 'validate_any';
|
25
|
+
// v2.html.validateAnyClass = 'validate_all';
|
26
|
+
|
27
|
+
// Make generated validation classes namespaced to avoid clashes with other classes.
|
28
|
+
//
|
29
|
+
// v2.Validator.prefix = 'v2_';
|
30
|
+
|
31
|
+
// Validate instantly or on submit.
|
32
|
+
//
|
33
|
+
v2.Field.prototype.instant = true;
|
34
|
+
v2.Field.prototype.instantWhenValidated = true;
|
35
|
+
|
36
|
+
// Validate all hidden fields.
|
37
|
+
//
|
38
|
+
// v2.Field.prototype.validateHidden = false;
|
39
|
+
|
40
|
+
// Jump to first invalid field on error.
|
41
|
+
//
|
42
|
+
v2.Form.prototype.scrollToFirstWhenFail = true;
|
43
|
+
|
44
|
+
// Maximum number of errors at the same time.
|
45
|
+
//
|
46
|
+
// * -1, default value, display all messages
|
47
|
+
// * 0, display no messages, only append class names on failing elements
|
48
|
+
// * n, where n is any positive integer, display no more than this many messages
|
49
|
+
//
|
50
|
+
// v2.Field.prototype.displayErrors = 1;
|
51
|
+
|
52
|
+
// Position of errors.
|
53
|
+
//
|
54
|
+
v2.Field.prototype.positionErrorsAbove = false;
|
55
|
+
v2.Fieldset.prototype.positionErrorsAbove = false;
|
56
|
+
|
57
|
+
// Error classes.
|
58
|
+
//
|
59
|
+
// Example:
|
60
|
+
//
|
61
|
+
// <fieldset
|
62
|
+
// <div class="field error"
|
63
|
+
// <label for="name">Name</label
|
64
|
+
// <input type="text" name="name" id="name" class="required word"
|
65
|
+
// <ul class="errors"
|
66
|
+
// <li>Name is required</li
|
67
|
+
// </ul
|
68
|
+
// </div
|
69
|
+
// <div class="field error"
|
70
|
+
// <label for="email">E-mail</label
|
71
|
+
// <input type="text" name="email" id="email" class="email" value="name@"
|
72
|
+
// <ul class="errors"
|
73
|
+
// <li>E-mail should be a valid email address</li
|
74
|
+
// </ul
|
75
|
+
// </div
|
76
|
+
// </fieldset>
|
77
|
+
//
|
78
|
+
v2.Fieldset.prototype.messagesClass = 'errors'; // Formtastic: Use 'errors'
|
79
|
+
v2.Fieldset.prototype.failureClass = 'error'; // Formtastic: Use 'error'
|
80
|
+
v2.Fieldset.prototype.successClass = '';
|
81
|
+
v2.Field.prototype.messagesClass = 'errors'; // Formtastic: Use 'errors'
|
82
|
+
v2.Field.prototype.failureClass = 'error'; // Formtastic: Use 'error'
|
83
|
+
v2.Field.prototype.successClass = '';
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class ValidatiousGenerator < Rails::Generator::Base
|
2
|
+
|
3
|
+
def manifest
|
4
|
+
record do |m|
|
5
|
+
m.template 'v2.standalone.full.min.js', File.join('public', 'javascripts', 'v2.standalone.full.min.js')
|
6
|
+
m.template 'validatious_config.js', File.join('public', 'javascripts', 'validatious_config.js')
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
#
|
2
|
+
# Tap into the built-in form helpers to add validatious class names from
|
3
|
+
# model validations.
|
4
|
+
#
|
5
|
+
module ActionView
|
6
|
+
module Helpers
|
7
|
+
module FormHelper
|
8
|
+
|
9
|
+
#
|
10
|
+
# Add validation class names to: text-fields.
|
11
|
+
#
|
12
|
+
def text_field_with_validation(object_name, method, options = {})
|
13
|
+
options = Validatious::RailsValidation.options_for(object_name, method, options)
|
14
|
+
text_field_without_validation(object_name, method, options)
|
15
|
+
end
|
16
|
+
alias_method_chain :text_field, :validation
|
17
|
+
|
18
|
+
#
|
19
|
+
# Add validation class names to: password-fields.
|
20
|
+
#
|
21
|
+
def password_field_with_validation(object_name, method, options = {})
|
22
|
+
options = Validatious::RailsValidation.options_for(object_name, method, options)
|
23
|
+
password_field_without_validation(object_name, method, options)
|
24
|
+
end
|
25
|
+
alias_method_chain :password_field, :validation
|
26
|
+
|
27
|
+
#
|
28
|
+
# Add validation class names to: text-areas.
|
29
|
+
#
|
30
|
+
def text_area_with_validation(object_name, method, options = {})
|
31
|
+
options = Validatious::RailsValidation.options_for(object_name, method, options)
|
32
|
+
text_area_without_validation(object_name, method, options)
|
33
|
+
end
|
34
|
+
alias_method_chain :text_area, :validation
|
35
|
+
|
36
|
+
#
|
37
|
+
# Add validation class names to: check-boxes.
|
38
|
+
#
|
39
|
+
def check_box_with_validation(object_name, method, options = {}, checked_value = '1', unchecked_value = '0')
|
40
|
+
options = Validatious::RailsValidation.options_for(object_name, method, options)
|
41
|
+
check_box_without_validation(object_name, method, options)
|
42
|
+
end
|
43
|
+
alias_method_chain :check_box, :validation
|
44
|
+
|
45
|
+
#
|
46
|
+
# Add validation class names to: radio-buttons.
|
47
|
+
#
|
48
|
+
def radio_button_with_validation(object_name, method, tag_value, options = {})
|
49
|
+
options = Validatious::RailsValidation.options_for(object_name, method, options)
|
50
|
+
radio_button_without_validation(object_name, method, tag_value, options)
|
51
|
+
end
|
52
|
+
alias_method_chain :radio_button, :validation
|
53
|
+
|
54
|
+
#
|
55
|
+
# Adds the title attribute to label tags when there is no title
|
56
|
+
# set, and the label text is provided. The title is set to object_name.humanize
|
57
|
+
#
|
58
|
+
def label_with_title(object_name, method, text = nil, options = {})
|
59
|
+
options[:title] ||= method.to_s.humanize unless text.nil?
|
60
|
+
label_without_title(object_name, method, text, options)
|
61
|
+
end
|
62
|
+
alias_method_chain :label, :title
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
begin
|
2
|
+
require 'validation_reflection'
|
3
|
+
rescue LoadError
|
4
|
+
gem 'redinger-validation_reflection', '>= 0.3.2'
|
5
|
+
require 'validation_reflection'
|
6
|
+
end
|
7
|
+
|
8
|
+
#
|
9
|
+
# Force this, as it seems ValidationReflection don't do this correctly. =S
|
10
|
+
#
|
11
|
+
ActiveRecord::Base.class_eval do
|
12
|
+
include ::ActiveRecordExtensions::ValidationReflection
|
13
|
+
::ActiveRecordExtensions::ValidationReflection.load_config
|
14
|
+
::ActiveRecordExtensions::ValidationReflection.install(self)
|
15
|
+
end
|
16
|
+
|
17
|
+
#
|
18
|
+
# Validatious-Rails validation translator.
|
19
|
+
#
|
20
|
+
module Validatious
|
21
|
+
class RailsValidation
|
22
|
+
class << self
|
23
|
+
|
24
|
+
# Reference: http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html
|
25
|
+
|
26
|
+
#
|
27
|
+
# Generates form field helper options for a specified object-attribute to
|
28
|
+
# reflect on it's validations.
|
29
|
+
#
|
30
|
+
# Input may be an ActiveRecord class, a class name (string), or an object
|
31
|
+
# name along with a method/field.
|
32
|
+
#
|
33
|
+
def options_for(object_name, method, options = {})
|
34
|
+
validation = self.from_active_record(object_name, method)
|
35
|
+
|
36
|
+
# Loop validation and add/append pairs to options.
|
37
|
+
validation.each_pair do |attr, value|
|
38
|
+
options[attr] ||= ''
|
39
|
+
options[attr] << value
|
40
|
+
|
41
|
+
# Shake out duplicates.
|
42
|
+
options[attr] = options[attr].split.uniq.join(' ').strip
|
43
|
+
end
|
44
|
+
options
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Groks rails validations, and is able to convert a rails validation to
|
49
|
+
# a Validatious 2.0 compatible class string.
|
50
|
+
#
|
51
|
+
# Input may be an ActiveRecord class, a class name (string), or an object
|
52
|
+
# name along with a method/field.
|
53
|
+
#
|
54
|
+
# Returns a string that will be recognized by Validatious as a class name in
|
55
|
+
# form markup.
|
56
|
+
#
|
57
|
+
def from_active_record(object_or_class, method)
|
58
|
+
klass = object_or_class.to_s.classify.constantize
|
59
|
+
options = {:class => ''}
|
60
|
+
validation_classes = []
|
61
|
+
|
62
|
+
# Iterate thorugh the validations for the current class,
|
63
|
+
# and collect validation options.
|
64
|
+
klass.reflect_on_validations_for(method).each do |validation|
|
65
|
+
validation_options = self.send(validation.macro.to_s.sub(/^validates_/, ''), validation)
|
66
|
+
validation_classes << validation_options[:class]
|
67
|
+
end
|
68
|
+
options[:class] = [options[:class], validation_classes].flatten.compact.join(' ')
|
69
|
+
options
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Resolve validation from validates_acceptance_of.
|
74
|
+
#
|
75
|
+
# Note: acceptance_of <=> presence_of (for Validatious)
|
76
|
+
#
|
77
|
+
def acceptance_of(validation)
|
78
|
+
{:class => 'required'}
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# Resolve validation from validates_associated.
|
83
|
+
#
|
84
|
+
def associated(validation)
|
85
|
+
{:class => ''}
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# TODO: Resolve validation from validates_confirmation_of.
|
90
|
+
#
|
91
|
+
# Note: Should be added to "#{field}_confirmation" instead of "#{field}", i.e. non-standard approach.
|
92
|
+
#
|
93
|
+
def confirmation_of(validation)
|
94
|
+
{:class => ''}
|
95
|
+
end
|
96
|
+
|
97
|
+
#
|
98
|
+
# TODO: Resolve validation from validates_exclusion_of.
|
99
|
+
#
|
100
|
+
def exclusion_of(validation)
|
101
|
+
{:class => ''}
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# TODO: Resolve validation from validates_format_of.
|
106
|
+
#
|
107
|
+
# Note: Should be implemented using "Custom validators" - generated and attached to the form on render page.
|
108
|
+
#
|
109
|
+
def format_of(validation)
|
110
|
+
# format_expression = validation.options[:with]
|
111
|
+
# Old: {:class => validation.options.key?(:name) ? validation.options[:name] : ''}
|
112
|
+
{:class => ''}
|
113
|
+
end
|
114
|
+
|
115
|
+
#
|
116
|
+
# TODO: Resolve validation from validates_inclusion_of.
|
117
|
+
#
|
118
|
+
def inclusion_of(validation)
|
119
|
+
{:class => ''}
|
120
|
+
end
|
121
|
+
|
122
|
+
#
|
123
|
+
# Resolve validation from validates_length_of.
|
124
|
+
#
|
125
|
+
def length_of(validation)
|
126
|
+
range = validation.options[:in] || validation.options[:within]
|
127
|
+
min, max = nil, nil
|
128
|
+
min, max = range.min, range.max if range
|
129
|
+
min ||= validation.options[:minimum] || validation.options[:is]
|
130
|
+
max ||= validation.options[:maximum] || validation.options[:is]
|
131
|
+
|
132
|
+
class_name = [
|
133
|
+
("min-length_#{min}" unless min.nil?),
|
134
|
+
("max-length_#{max}" unless max.nil?)
|
135
|
+
].compact.join(' ')
|
136
|
+
|
137
|
+
{:class => class_name}
|
138
|
+
end
|
139
|
+
|
140
|
+
#
|
141
|
+
# Resolve validation from validates_numericality_of.
|
142
|
+
#
|
143
|
+
def numericality_of(validation)
|
144
|
+
{:class => 'numeric'}
|
145
|
+
end
|
146
|
+
|
147
|
+
#
|
148
|
+
# Resolve validation from validates_presence_of.
|
149
|
+
#
|
150
|
+
# Note: acceptance_of <=> presence_of (for Validatious)
|
151
|
+
#
|
152
|
+
def presence_of(validation)
|
153
|
+
{:class => 'required'}
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# TODO: Resolve validation from validates_uniqueness_of.
|
158
|
+
#
|
159
|
+
# Note: A bit tricky on the client-side - especially with many records.
|
160
|
+
#
|
161
|
+
def uniqueness_of(validation)
|
162
|
+
{:class => ''}
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
data/lib/validatious.rb
ADDED
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), *%w(.. lib validatious)))
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#
|
2
|
+
# This file loads up the in-memory database needed to run the tests, and adds a few
|
3
|
+
# convenience methods.
|
4
|
+
#
|
5
|
+
|
6
|
+
begin
|
7
|
+
require File.expand_path(File.join(File.dirname(__FILE__), *%w(.. .. .. .. config environment)))
|
8
|
+
rescue LoadError
|
9
|
+
require 'rubygems'
|
10
|
+
|
11
|
+
gem 'test-unit', '1.2.3'
|
12
|
+
gem 'activerecord', '>= 1.2.3'
|
13
|
+
gem 'actionpack', '>= 1.2.3'
|
14
|
+
|
15
|
+
require 'test/unit'
|
16
|
+
require 'active_record'
|
17
|
+
require 'action_controller'
|
18
|
+
end
|
19
|
+
|
20
|
+
begin
|
21
|
+
require 'acts_as_fu'
|
22
|
+
rescue LoadError
|
23
|
+
gem 'nakajima-acts_as_fu', '>= 0.0.5'
|
24
|
+
require 'acts_as_fu'
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'validatious'
|
28
|
+
|
29
|
+
build_model :bogus_items do
|
30
|
+
string :url
|
31
|
+
string :name
|
32
|
+
string :email
|
33
|
+
string :num
|
34
|
+
string :num2
|
35
|
+
string :num3
|
36
|
+
|
37
|
+
text :body
|
38
|
+
boolean :signed
|
39
|
+
|
40
|
+
validates_presence_of :name, :body
|
41
|
+
validates_acceptance_of :signed
|
42
|
+
validates_format_of :url,
|
43
|
+
:with => /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i,
|
44
|
+
:name => 'url'
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# ValidationReflection seems to expect RAILS_ROOT to be defined, but it's not
|
49
|
+
# if it's tested outside of a Rails project. So, just set it to something random.
|
50
|
+
#
|
51
|
+
RAILS_ROOT = File.join(File.dirname(__FILE__)) unless defined?(RAILS_ROOT)
|
52
|
+
|
53
|
+
#
|
54
|
+
# Log file for testing only.
|
55
|
+
#
|
56
|
+
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), 'debug.log'))
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper'))
|
2
|
+
|
3
|
+
module Test::Unit::Assertions
|
4
|
+
|
5
|
+
#
|
6
|
+
# Assert that a piece of HTML includes the class name.
|
7
|
+
#
|
8
|
+
def assert_has_class(class_name, html, message = nil)
|
9
|
+
classes = /class="([^"]*)"/.match(html)[1].split(" ")
|
10
|
+
full_message = build_message(message, "<?>\nexpected to include class(es) <?>.\n", html, class_name)
|
11
|
+
|
12
|
+
assert_block(full_message) do
|
13
|
+
class_name.split(" ").all? { |cname| classes.include?(cname) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class FormHelperTest < Test::Unit::TestCase
|
20
|
+
|
21
|
+
include ActionView::Helpers::FormHelper
|
22
|
+
|
23
|
+
def test_required_text_field
|
24
|
+
assert_has_class "required", text_field(:bogus_item, :name)
|
25
|
+
assert_has_class "required text", text_field(:bogus_item, :name, :class => "text")
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_required_password_field
|
29
|
+
assert_has_class "required", password_field(:bogus_item, :name)
|
30
|
+
assert_has_class "required text", password_field(:bogus_item, :name, :class => "text")
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_required_text_area
|
34
|
+
assert_has_class "required", text_field(:bogus_item, :body)
|
35
|
+
assert_has_class "required text", text_field(:bogus_item, :body, :class => "text")
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_required_check_box # a.k.a. "acceptance required"
|
39
|
+
assert_has_class "required", text_field(:bogus_item, :signed)
|
40
|
+
assert_has_class "required boolean", text_field(:bogus_item, :signed, :class => "boolean")
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_required_radio_button
|
44
|
+
# TODO
|
45
|
+
end
|
46
|
+
|
47
|
+
def test_normal_label
|
48
|
+
assert_equal "<label for=\"bogus_item_name\">Name</label>", label(:bogus_item, :name)
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_label_with_title
|
52
|
+
assert_equal "<label for=\"bogus_item_name\" title=\"craaazy\">Name</label>",
|
53
|
+
label(:bogus_item, :name, nil, :title => "craaazy")
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_label_without_title
|
57
|
+
assert_equal "<label for=\"bogus_item_name\" title=\"Name\">Your name</label>",
|
58
|
+
label(:bogus_item, :name, "Your name")
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'test_helper'))
|
2
|
+
|
3
|
+
class RailsValidationTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
include ActionView::Helpers::FormHelper
|
6
|
+
|
7
|
+
def test_acceptance_of
|
8
|
+
validation = Validatious::RailsValidation.presence_of(validation(:validates_acceptance_of))
|
9
|
+
assert_equal 'required', validation[:class]
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_associated
|
13
|
+
# TODO: not implemented
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_confirmation_of
|
17
|
+
# TODO: not implemented
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_exclusion_of
|
21
|
+
# TODO: not implemented
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_format_of
|
25
|
+
# pattern = /^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?\/?/i
|
26
|
+
# validation = Validatious::RailsValidation.format_of(validation(:validates_format_of, :with => pattern, :name => "email"))
|
27
|
+
# assert_equal 'email', validation[:class]
|
28
|
+
# TODO: Not fully supported really, should
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_inclusion_of
|
32
|
+
# TODO: not implemented
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_length_of_with_in
|
36
|
+
validation = Validatious::RailsValidation.length_of(validation(:validates_length_of, :in => 1..10))
|
37
|
+
assert_equal 'min-length_1 max-length_10', validation[:class]
|
38
|
+
|
39
|
+
validation = Validatious::RailsValidation.length_of(validation(:validates_length_of, :in => 1...10))
|
40
|
+
assert_equal 'min-length_1 max-length_9', validation[:class]
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_length_of_with_within
|
44
|
+
validation = Validatious::RailsValidation.length_of(validation(:validates_length_of, :within => 1..10))
|
45
|
+
assert_equal 'min-length_1 max-length_10', validation[:class]
|
46
|
+
|
47
|
+
validation = Validatious::RailsValidation.length_of(validation(:validates_length_of, :within => 1...10))
|
48
|
+
assert_equal 'min-length_1 max-length_9', validation[:class]
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_length_of_with_minimum
|
52
|
+
validation = Validatious::RailsValidation.length_of(validation(:validates_length_of, :minimum => 1))
|
53
|
+
assert_equal 'min-length_1', validation[:class]
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_length_of_with_maximum
|
57
|
+
validation = Validatious::RailsValidation.length_of(validation(:validates_length_of, :maximum => 1))
|
58
|
+
assert_equal 'max-length_1', validation[:class]
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_length_of_with_min_and_max
|
62
|
+
validation = Validatious::RailsValidation.length_of(validation(:validates_length_of, :minimum => 1, :maximum => 3))
|
63
|
+
assert_equal 'min-length_1 max-length_3', validation[:class]
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_numericality_of
|
67
|
+
validation = Validatious::RailsValidation.numericality_of(validation(:validates_numericality_of))
|
68
|
+
assert_equal 'numeric', validation[:class]
|
69
|
+
end
|
70
|
+
|
71
|
+
def test_presence_of
|
72
|
+
validation = Validatious::RailsValidation.presence_of(validation(:validates_presence_of))
|
73
|
+
assert_equal 'required', validation[:class]
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_uniqueness_of
|
77
|
+
# TODO: not implemented
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Simulate a validation
|
82
|
+
#
|
83
|
+
def validation(macro, options = {})
|
84
|
+
ActiveRecord::Reflection::MacroReflection.new(macro, :name, options, nil)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: grimen-validatious_on_rails
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christian Johansen
|
8
|
+
- Jonas Grimfelt
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2009-09-26 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies: []
|
16
|
+
|
17
|
+
description: Rails plugin that maps model validations to class names on form elements to integrate with Validatious.
|
18
|
+
email: christian@cjohansen.no
|
19
|
+
executables: []
|
20
|
+
|
21
|
+
extensions: []
|
22
|
+
|
23
|
+
extra_rdoc_files:
|
24
|
+
- README
|
25
|
+
- Rakefile
|
26
|
+
- generators/validatious/templates/v2.standalone.full.min.js
|
27
|
+
- generators/validatious/templates/validatious_config.js
|
28
|
+
- generators/validatious/validatious_generator.rb
|
29
|
+
- lib/validatious.rb
|
30
|
+
- lib/validatious/form_helper.rb
|
31
|
+
- lib/validatious/rails_validation.rb
|
32
|
+
- rails/init.rb
|
33
|
+
- test/test_helper.rb
|
34
|
+
- test/validatious/form_helper_test.rb
|
35
|
+
- test/validatious/rails_validation_test.rb
|
36
|
+
- test/validatious_test.rb
|
37
|
+
files:
|
38
|
+
- README
|
39
|
+
- Rakefile
|
40
|
+
- generators/validatious/templates/v2.standalone.full.min.js
|
41
|
+
- generators/validatious/templates/validatious_config.js
|
42
|
+
- generators/validatious/validatious_generator.rb
|
43
|
+
- lib/validatious.rb
|
44
|
+
- lib/validatious/form_helper.rb
|
45
|
+
- lib/validatious/rails_validation.rb
|
46
|
+
- rails/init.rb
|
47
|
+
- test/test_helper.rb
|
48
|
+
- test/validatious/form_helper_test.rb
|
49
|
+
- test/validatious/rails_validation_test.rb
|
50
|
+
- test/validatious_test.rb
|
51
|
+
has_rdoc: false
|
52
|
+
homepage: http://github.com/grimen/validatious_on_rails
|
53
|
+
licenses:
|
54
|
+
post_install_message:
|
55
|
+
rdoc_options:
|
56
|
+
- --charset=UTF-8
|
57
|
+
require_paths:
|
58
|
+
- lib
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: "0"
|
64
|
+
version:
|
65
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
requirements: []
|
72
|
+
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 1.3.5
|
75
|
+
signing_key:
|
76
|
+
specification_version: 3
|
77
|
+
summary: Rails plugin that maps model validations to class names on form elements to integrate with Validatious.
|
78
|
+
test_files:
|
79
|
+
- test/test_helper.rb
|
80
|
+
- test/validatious/form_helper_test.rb
|
81
|
+
- test/validatious/rails_validation_test.rb
|
82
|
+
- test/validatious_test.rb
|