validatious-on-rails 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +52 -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 +12 -0
- data/lib/validatious-on-rails/helpers.rb +20 -0
- data/lib/validatious-on-rails/model_validations.rb +312 -0
- data/lib/validatious-on-rails/rails/action_view_helpers.rb +114 -0
- data/lib/validatious-on-rails/rails.rb +2 -0
- data/lib/validatious-on-rails/validatious/client_side_validator.rb +14 -0
- data/lib/validatious-on-rails/validatious/remote_validator.rb +24 -0
- data/lib/validatious-on-rails/validatious/validator.rb +228 -0
- data/lib/validatious-on-rails/validatious/validators/exclusion_validator.rb +28 -0
- data/lib/validatious-on-rails/validatious/validators/format_validator.rb +33 -0
- data/lib/validatious-on-rails/validatious/validators/inclusion_validator.rb +28 -0
- data/lib/validatious-on-rails/validatious/validators/length/is_validator.rb +27 -0
- data/lib/validatious-on-rails/validatious/validators/length/maximum_validator.rb +27 -0
- data/lib/validatious-on-rails/validatious/validators/length/minimum_validator.rb +27 -0
- data/lib/validatious-on-rails/validatious/validators/numericality/equal_to_validator.rb +25 -0
- data/lib/validatious-on-rails/validatious/validators/numericality/even_validator.rb +24 -0
- data/lib/validatious-on-rails/validatious/validators/numericality/greater_than_or_equal_to_validator.rb +25 -0
- data/lib/validatious-on-rails/validatious/validators/numericality/greater_than_validator.rb +25 -0
- data/lib/validatious-on-rails/validatious/validators/numericality/less_than_or_equal_to_validator.rb +25 -0
- data/lib/validatious-on-rails/validatious/validators/numericality/less_than_validator.rb +25 -0
- data/lib/validatious-on-rails/validatious/validators/numericality/odd_validator.rb +24 -0
- data/lib/validatious-on-rails/validatious/validators/numericality/only_integer_validator.rb +24 -0
- data/lib/validatious-on-rails/validatious/validators.rb +3 -0
- data/lib/validatious-on-rails/validatious.rb +5 -0
- data/lib/validatious-on-rails.rb +41 -0
- data/rails/init.rb +2 -0
- data/test/test_helper.rb +93 -0
- data/test/validatious_on_rails/helpers_test.rb +47 -0
- data/test/validatious_on_rails/model_validations_test.rb +190 -0
- data/test/validatious_on_rails/rails/action_view_helpers_test.rb +168 -0
- data/test/validatious_on_rails/validatious/validator_test.rb +56 -0
- data/test/validatious_on_rails_test.rb +6 -0
- metadata +129 -0
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/rdoctask'
|
6
|
+
|
7
|
+
NAME = "validatious-on-rails"
|
8
|
+
SUMMARY = %Q{Rails plugin that maps model validations to class names on form elements to integrate with Validatious.}
|
9
|
+
HOMEPAGE = "http://github.com/grimen/#{NAME}"
|
10
|
+
AUTHORS = ["Christian Johansen", "Jonas Grimfelt"]
|
11
|
+
EMAIL = "christian@cjohansen.no"
|
12
|
+
SUPPORT_FILES = %w(README)
|
13
|
+
|
14
|
+
begin
|
15
|
+
gem 'technicalpickles-jeweler', '>= 1.2.1'
|
16
|
+
require 'jeweler'
|
17
|
+
Jeweler::Tasks.new do |gemspec|
|
18
|
+
gemspec.name = NAME
|
19
|
+
gemspec.summary = SUMMARY
|
20
|
+
gemspec.description = SUMMARY
|
21
|
+
gemspec.homepage = HOMEPAGE
|
22
|
+
gemspec.authors = AUTHORS
|
23
|
+
gemspec.email = EMAIL
|
24
|
+
|
25
|
+
gemspec.require_paths = %w{lib}
|
26
|
+
gemspec.files = SUPPORT_FILES << %w(Rakefile) <<
|
27
|
+
Dir.glob(File.join('{generators,lib,test,rails}', '**', '*').to_s).reject { |v| v =~ /\.log/i }
|
28
|
+
gemspec.executables = %w()
|
29
|
+
gemspec.extra_rdoc_files = SUPPORT_FILES
|
30
|
+
end
|
31
|
+
rescue LoadError
|
32
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
33
|
+
end
|
34
|
+
|
35
|
+
desc %Q{Default: Run unit tests for "#{NAME}".}
|
36
|
+
task :default => :test
|
37
|
+
|
38
|
+
desc %Q{Run unit tests for "#{NAME}".}
|
39
|
+
Rake::TestTask.new(:test) do |test|
|
40
|
+
test.libs << %w(lib test)
|
41
|
+
test.pattern = File.join('test', '**', '*_test.rb')
|
42
|
+
test.verbose = true
|
43
|
+
end
|
44
|
+
|
45
|
+
desc %Q{Generate documentation for "#{NAME}".}
|
46
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
47
|
+
rdoc.rdoc_dir = 'rdoc'
|
48
|
+
rdoc.title = NAME
|
49
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--charset=UTF-8'
|
50
|
+
rdoc.rdoc_files.include(SUPPORT_FILES)
|
51
|
+
rdoc.rdoc_files.include(File.join('lib', '**', '*.rb'))
|
52
|
+
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,12 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
class ValidatiousGenerator < Rails::Generator::Base
|
4
|
+
|
5
|
+
def manifest
|
6
|
+
record do |m|
|
7
|
+
m.template 'v2.standalone.full.min.js', File.join('public', 'javascripts', 'v2.standalone.full.min.js')
|
8
|
+
m.template 'validatious.config.js', File.join('public', 'javascripts', 'validatious.config.js')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ValidatiousOnRails
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# Helper for the layout to host the custom Validatious-validators for forms rendered.
|
7
|
+
# This will only show up if the current view contains one - or more - forms that
|
8
|
+
# are triggered to be validated with Validatous (i.e. ValidatiousOnRails that is).
|
9
|
+
#
|
10
|
+
def custom_validatious_validators
|
11
|
+
if @content_for_validatious.present?
|
12
|
+
content_tag(:script, @content_for_validatious,
|
13
|
+
:type => 'text/javascript', :id => 'custom_validatious_validators')
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
::ActionController::Base.helper ::ValidatiousOnRails::Helpers
|
@@ -0,0 +1,312 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
begin
|
3
|
+
require 'validation_reflection'
|
4
|
+
rescue LoadError
|
5
|
+
gem 'redinger-validation_reflection', '>= 0.3.2'
|
6
|
+
require 'validation_reflection'
|
7
|
+
end
|
8
|
+
|
9
|
+
require File.join(File.dirname(__FILE__), *%w[validatious validators])
|
10
|
+
|
11
|
+
# Force this, as it seems ValidationReflection don't do this correctly. =S
|
12
|
+
#
|
13
|
+
ActiveRecord::Base.class_eval do
|
14
|
+
include ::ActiveRecordExtensions::ValidationReflection
|
15
|
+
::ActiveRecordExtensions::ValidationReflection.load_config
|
16
|
+
::ActiveRecordExtensions::ValidationReflection.install(self)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Validatious-Rails validation translator.
|
20
|
+
#
|
21
|
+
module ValidatiousOnRails
|
22
|
+
class ModelValidations
|
23
|
+
|
24
|
+
CORE_VALIDATIONS = [
|
25
|
+
:acceptance_of,
|
26
|
+
:associated,
|
27
|
+
:confirmation_of,
|
28
|
+
:exclusion_of,
|
29
|
+
:format_of,
|
30
|
+
:inclusion_of,
|
31
|
+
:length_of,
|
32
|
+
:numericality_of,
|
33
|
+
:presence_of,
|
34
|
+
:uniqueness_of
|
35
|
+
].freeze
|
36
|
+
SUPPORTED_VALIDATIONS = CORE_VALIDATIONS
|
37
|
+
|
38
|
+
class << self
|
39
|
+
|
40
|
+
# References:
|
41
|
+
# http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html
|
42
|
+
# http://github.com/rails/rails/blob/13fb26b714dec0874303f51cc125ff62f65a2729/activerecord/lib/active_record/validations.rb
|
43
|
+
|
44
|
+
# Generates form field helper options for a specified object-attribute to
|
45
|
+
# reflect on it's validations.
|
46
|
+
#
|
47
|
+
# Input may be an ActiveRecord class, a class name (string), or an object
|
48
|
+
# name along with a method/field.
|
49
|
+
#
|
50
|
+
def options_for(object_name, attribute_method, options = {})
|
51
|
+
validation = self.from_active_record(object_name, attribute_method)
|
52
|
+
options.merge!(
|
53
|
+
:class => [options[:class], validation[:classes]].flatten.compact.uniq.join(' '),
|
54
|
+
:validators => validation[:validators].flatten.compact.uniq
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Groks Rails validations, and is able to convert a rails validation to
|
59
|
+
# a Validatious 2.0 compatible class string, and a Validatous validator
|
60
|
+
# for more complex validations. Even some of the Rails core validations
|
61
|
+
# with certain options requires this.
|
62
|
+
#
|
63
|
+
# Input may be an ActiveRecord class, a class name (string), or an object
|
64
|
+
# name along with a method/field.
|
65
|
+
#
|
66
|
+
# Returns a string that will be recognized by Validatious as a class name in
|
67
|
+
# form markup.
|
68
|
+
#
|
69
|
+
def from_active_record(object_or_class, attribute_method)
|
70
|
+
klass = object_or_class.to_s.classify.constantize
|
71
|
+
attribute_validation = {:classes => [], :validators => []}
|
72
|
+
|
73
|
+
# Iterate thorugh the validations for the current class,
|
74
|
+
# and collect validation options.
|
75
|
+
klass.reflect_on_validations_for(attribute_method.to_sym).each do |validation|
|
76
|
+
validates_type = validation.macro.to_s.sub(/^validates?_/, '')
|
77
|
+
|
78
|
+
# Skip "confirmation_of"-validation info for the attribute that
|
79
|
+
# needs to be confirmed. Validatious expects this validation rule
|
80
|
+
# on the confirmation field. *
|
81
|
+
unless validates_type =~ /^confirmation_of$/
|
82
|
+
validation_options = self.send(validates_type.to_sym, validation)
|
83
|
+
attribute_validation[:classes] << validation_options[:class]
|
84
|
+
# One or multiple validators for each validation (possible to combine validators).
|
85
|
+
attribute_validation[:validators] << validation_options[:validator] || validation_options[:validators]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Special case for "confirmation_of"-validation (see * above).
|
90
|
+
if attribute_method.to_s =~ /(.+)_confirmation$/
|
91
|
+
confirm_attribute_method = $1
|
92
|
+
# Check if validates_confirmation_of(:hello) actually exists,
|
93
|
+
# if :hello_confirmation field exists - just to be safe.
|
94
|
+
klass.reflect_on_validations_for(confirm_attribute_method.to_sym).each do |validation|
|
95
|
+
if validation.macro.to_s =~ /^validates_confirmation_of$/
|
96
|
+
validation_options = self.confirmation_of(validation)
|
97
|
+
attribute_validation[:classes] << validation_options[:class]
|
98
|
+
break
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
attribute_validation
|
103
|
+
end
|
104
|
+
|
105
|
+
# Resolve validation from validates_acceptance_of.
|
106
|
+
#
|
107
|
+
# Alias, but might change: acceptance_of <=> presence_of
|
108
|
+
#
|
109
|
+
# TODO: Make this a custom validator - handle :accept.
|
110
|
+
#
|
111
|
+
# NOTE: Not supported:
|
112
|
+
# * :accept - TODO: not taken into consideration
|
113
|
+
# right now (but must have value "true" for db column values)
|
114
|
+
# * :allow_nil - TODO.
|
115
|
+
# * :on - TODO.
|
116
|
+
# * :if/:unless - hard to port all to client-side JavaScript
|
117
|
+
# (impossible: procs, unaccessible valiables, etc.).
|
118
|
+
#
|
119
|
+
def acceptance_of(validation)
|
120
|
+
{:class => 'required', :validator => nil}
|
121
|
+
end
|
122
|
+
|
123
|
+
# Resolve validation from validates_associated.
|
124
|
+
#
|
125
|
+
# NOTE: Not supported - low prio.
|
126
|
+
#
|
127
|
+
def associated(validation)
|
128
|
+
{:class => '', :validator => nil}
|
129
|
+
end
|
130
|
+
|
131
|
+
# Resolve validation from validates_confirmation_of.
|
132
|
+
# This validation is treated a bit differently in compare
|
133
|
+
# to the other validations. See "from_active_record".
|
134
|
+
#
|
135
|
+
# TODO: Message should be Rails I18n message, not Validatious.
|
136
|
+
#
|
137
|
+
# NOTE: Not supported:
|
138
|
+
# * :on - TODO.
|
139
|
+
# * :if/:unless - hard to port all to client-side JavaScript
|
140
|
+
# (impossible: procs, unaccessible valiables, etc.).
|
141
|
+
#
|
142
|
+
def confirmation_of(validation)
|
143
|
+
field_id_to_confirm = unless validation.active_record.present?
|
144
|
+
"#{validation.active_record.name.tableize.singularize.gsub('/', '_')}_#{validation.name}"
|
145
|
+
else
|
146
|
+
"#{validation.name}"
|
147
|
+
end
|
148
|
+
{:class => "confirmation-of_#{field_id_to_confirm}", :validator => nil}
|
149
|
+
end
|
150
|
+
|
151
|
+
# Resolve validation from validates_exclusion_of.
|
152
|
+
#
|
153
|
+
# Attaching custom validator - with a unique name based on the exclusion values.
|
154
|
+
#
|
155
|
+
# NOTE: Not supported:
|
156
|
+
# * :on - TODO.
|
157
|
+
# * :if/:unless - hard to port all to client-side JavaScript
|
158
|
+
# (impossible: procs, unaccessible valiables, etc.).
|
159
|
+
#
|
160
|
+
def exclusion_of(validation)
|
161
|
+
validator = Validatious::ExclusionValidator.new(validation)
|
162
|
+
{:class => validator.name, :validator => validator}
|
163
|
+
end
|
164
|
+
|
165
|
+
# Resolve validation from validates_format_of.
|
166
|
+
#
|
167
|
+
# Attaching custom validator, with a unique name based on the regular expression.
|
168
|
+
# Needs regexp.inspect to get it right.
|
169
|
+
#
|
170
|
+
# NOTE: Not supported:
|
171
|
+
# * :on - TODO.
|
172
|
+
# * :if/:unless - hard to port all to client-side JavaScript
|
173
|
+
# (impossible: procs, unaccessible valiables, etc.).
|
174
|
+
#
|
175
|
+
def format_of(validation)
|
176
|
+
validator = Validatious::FormatValidator.new(validation)
|
177
|
+
{:class => validator.name, :validator => validator}
|
178
|
+
end
|
179
|
+
|
180
|
+
# Resolve validation from validates_inclusion_of.
|
181
|
+
#
|
182
|
+
# Attaching custom validator - with a unique name based on the inclusion values.
|
183
|
+
#
|
184
|
+
# NOTE: Not supported:
|
185
|
+
# * :on - TODO.
|
186
|
+
# * :if/:unless - hard to port all to client-side JavaScript
|
187
|
+
# (impossible: procs, unaccessible valiables, etc.).
|
188
|
+
#
|
189
|
+
def inclusion_of(validation)
|
190
|
+
validator = Validatious::InclusionValidator.new(validation)
|
191
|
+
{:class => validator.name, :validator => validator}
|
192
|
+
end
|
193
|
+
|
194
|
+
# Resolve validation from validates_length_of.
|
195
|
+
#
|
196
|
+
# Example (of generated field classes):
|
197
|
+
# length-is_5, length-maximum_2, length-minimum_2, etc.
|
198
|
+
#
|
199
|
+
# NOTE: Not supported:
|
200
|
+
# * :tokenizer - see: :if/:unless
|
201
|
+
# * :on - TODO.
|
202
|
+
# * :if/:unless - hard to port all to client-side JavaScript
|
203
|
+
# (impossible: procs, unaccessible valiables, etc.).
|
204
|
+
#
|
205
|
+
def length_of(validation)
|
206
|
+
# TODO: DRY up this with the neat idea/combo:
|
207
|
+
# Validator#new(name, validation, *args)-idea + Validator#to_class
|
208
|
+
validators, min, max = case true
|
209
|
+
when validation.options[:is].present?
|
210
|
+
[Validatious::Length::IsValidator.new(validation),
|
211
|
+
validation.options[:is], validation.options[:is]]
|
212
|
+
when [:in, :within].any? { |k| validation.options[k].present? } ||
|
213
|
+
[:minimum, :maximum].all? { |k| validation.options[k].present? }
|
214
|
+
validation.options[:within] ||=
|
215
|
+
validation.options[:in] ||
|
216
|
+
(validation.options[:minimum].to_i..validation.options[:maximum].to_i)
|
217
|
+
[[Validatious::Length::MinimumValidator.new(validation),
|
218
|
+
Validatious::Length::MaximumValidator.new(validation)],
|
219
|
+
validation.options[:within].min, validation.options[:within].max]
|
220
|
+
when validation.options[:minimum].present?
|
221
|
+
[Validatious::Length::MinimumValidator.new(validation),
|
222
|
+
validation.options[:minimum], nil]
|
223
|
+
when validation.options[:maximum].present?
|
224
|
+
[Validatious::Length::MaximumValidator.new(validation),
|
225
|
+
nil, validation.options[:maximum]]
|
226
|
+
end
|
227
|
+
validators = [*validators]
|
228
|
+
# This piece of code is a bit diffuse, but works. =)
|
229
|
+
classes = [
|
230
|
+
("#{validators.first.name}_#{min}" if min),
|
231
|
+
("#{validators.last.name}_#{max}" if max)
|
232
|
+
].compact.uniq.join(' ')
|
233
|
+
{:class => classes, :validator => validators}
|
234
|
+
end
|
235
|
+
alias :size_of :length_of
|
236
|
+
|
237
|
+
# Resolve validation from validates_numericality_of.
|
238
|
+
#
|
239
|
+
# Example (of generated field classes):
|
240
|
+
# numericality-odd, numericality-only-integer, numericality-equal-to_5, etc.
|
241
|
+
#
|
242
|
+
# NOTE: Not supported:
|
243
|
+
# * :on - TODO.
|
244
|
+
# * :if/:unless - hard to port all to client-side JavaScript
|
245
|
+
# (impossible: procs, unaccessible valiables, etc.).
|
246
|
+
#
|
247
|
+
def numericality_of(validation)
|
248
|
+
validators = []
|
249
|
+
values = {}
|
250
|
+
|
251
|
+
if validation.options[:odd] && !validation.options[:even]
|
252
|
+
validators << Validatious::Numericality::OddValidator.new(validation)
|
253
|
+
end
|
254
|
+
if validation.options[:even] && !validation.options[:odd]
|
255
|
+
validators << Validatious::Numericality::EvenValidator.new(validation)
|
256
|
+
end
|
257
|
+
|
258
|
+
(validation.options.keys & [:only_integer, :equal_to, :less_than, :less_than_or_equal_to,
|
259
|
+
:greater_than, :greater_than_or_equal_to]).each { |v|
|
260
|
+
klass = "::ValidatiousOnRails::Validatious::Numericality::#{v.to_s.classify}Validator".constantize
|
261
|
+
validators << (validator = klass.new(validation))
|
262
|
+
values.merge!(validator.name.to_sym => validation.options[v]) if validation.options[v].is_a?(::Numeric)
|
263
|
+
}
|
264
|
+
|
265
|
+
# TODO: Needs DRYer solution,
|
266
|
+
# Maybe: validator.args = [...] => validator.to_class => "#{validator.name}_params[0]_params[1]_etc..."
|
267
|
+
classes = validators.collect { |validator|
|
268
|
+
[validator.name,
|
269
|
+
(values[validator.name.to_sym] if values[validator.name.to_sym].present?)
|
270
|
+
].compact.join('_')
|
271
|
+
}.join(' ')
|
272
|
+
{:class => classes, :validator => validators}
|
273
|
+
end
|
274
|
+
|
275
|
+
# Resolve validation from validates_presence_of.
|
276
|
+
#
|
277
|
+
# Alias, but might change: acceptance_of <=> presence_of
|
278
|
+
#
|
279
|
+
# NOTE: Not supported:
|
280
|
+
# * :on - TODO.
|
281
|
+
# * :if/:unless - hard to port all to client-side JavaScript
|
282
|
+
# (impossible: procs, unaccessible valiables, etc.).
|
283
|
+
#
|
284
|
+
def presence_of(validation)
|
285
|
+
{:class => 'required', :validator => nil}
|
286
|
+
end
|
287
|
+
|
288
|
+
# Resolve validation from validates_uniqueness_of.
|
289
|
+
#
|
290
|
+
# TODO: Implement using RemoteValidator.
|
291
|
+
#
|
292
|
+
def uniqueness_of(validation)
|
293
|
+
{:class => '', :validator => nil}
|
294
|
+
end
|
295
|
+
|
296
|
+
# Unknown validations - if no matching custom validator is found/registered.
|
297
|
+
#
|
298
|
+
def method_missing(sym, *args, &block)
|
299
|
+
::ValidatiousOnRails.log "Unknown validation: #{sym}." <<
|
300
|
+
" No custom Validatious validator found for this validation makro. " <<
|
301
|
+
"Maybe you forgot to register you custom validation using: " <<
|
302
|
+
"ValidatiousOnRails::ModelValidations.add(<CustomValidationClass>)", :warn
|
303
|
+
{:class => '', :validator => nil}
|
304
|
+
end
|
305
|
+
|
306
|
+
# TODO: Include custom validations here...
|
307
|
+
#
|
308
|
+
# @custom_validators.each { |validator_class| ... }
|
309
|
+
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# Tap into the built-in form/input helpers to add validatious class names from
|
4
|
+
# model validations.
|
5
|
+
#
|
6
|
+
module ActionView # :nodoc:
|
7
|
+
module Helpers # :nodoc:
|
8
|
+
module FormHelper # :nodoc:
|
9
|
+
|
10
|
+
FIELD_TYPES = [:text_field, :password_field, :text_area, :file_field, :radio_button].freeze
|
11
|
+
|
12
|
+
# Only altering the options hash is interesting - we want to set a validator class for fields,
|
13
|
+
# so the hooking of these helpers don't have to be very explicit.
|
14
|
+
#
|
15
|
+
FIELD_TYPES.each do |field_type|
|
16
|
+
define_method :"#{field_type}_with_validation" do |*args|
|
17
|
+
options = args.extract_options!
|
18
|
+
# Get the validation options.
|
19
|
+
options = ::ValidatiousOnRails::ModelValidations.options_for(args.first, args.second, options)
|
20
|
+
|
21
|
+
# Attach custom validator - if any - to the layout (in the <head>-tag - the unobtrusive way).
|
22
|
+
# Don't attach validator(s) if it/they already attached.
|
23
|
+
# TODO: Refactor into helper.
|
24
|
+
validators_js = options.delete(:validators).collect { |v|
|
25
|
+
v.to_s unless /#{v.name}/ =~ @content_for_validatious
|
26
|
+
}.join(' ')
|
27
|
+
content_for :validatious, validators_js if validators_js.present?
|
28
|
+
|
29
|
+
self.send :"#{field_type}_without_validation", *(args << options)
|
30
|
+
end
|
31
|
+
alias_method_chain field_type, :validation
|
32
|
+
end
|
33
|
+
|
34
|
+
# Special case...no hash as last argument.
|
35
|
+
#
|
36
|
+
def check_box_with_validation(object_name, method, options = {}, checked_value = '1', unchecked_value = '0')
|
37
|
+
# Get the validation options.
|
38
|
+
options = ::ValidatiousOnRails::ModelValidations.options_for(object_name, method, options)
|
39
|
+
|
40
|
+
# Attach custom validator - if any - to the layout (in the <head>-tag - the unobtrusive way).
|
41
|
+
# Don't attach validator(s) if it/they already attached.
|
42
|
+
# TODO: Refactor into helper.
|
43
|
+
validators_js = options.delete(:validators).collect { |v|
|
44
|
+
v.to_s unless /#{v.name}/ =~ @content_for_validatious
|
45
|
+
}.join(' ')
|
46
|
+
content_for :validatious, validators_js if validators_js.present?
|
47
|
+
|
48
|
+
self.check_box_without_validation object_name, method, options, checked_value, unchecked_value
|
49
|
+
end
|
50
|
+
alias_method_chain :check_box, :validation
|
51
|
+
|
52
|
+
# Adds the title attribute to label tags when there is no title
|
53
|
+
# set, and the label text is provided. The title is set to object_name.humanize
|
54
|
+
#
|
55
|
+
def label_with_title(object_name, method, text = nil, options = {})
|
56
|
+
options[:title] ||= method.to_s.humanize unless text.nil?
|
57
|
+
label_without_title(object_name, method, text, options)
|
58
|
+
end
|
59
|
+
alias_method_chain :label, :title
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
module FormOptionsHelper
|
64
|
+
|
65
|
+
FIELD_TYPES = [:time_zone_select, :select, :collection_select, :grouped_options_for_select].freeze
|
66
|
+
|
67
|
+
FIELD_TYPES.each do |field_type|
|
68
|
+
define_method :"#{field_type}_with_validation" do |*args|
|
69
|
+
options = args.extract_options!
|
70
|
+
# Get the validation options.
|
71
|
+
options = ::ValidatiousOnRails::ModelValidations.options_for(args.first, args.second, options)
|
72
|
+
|
73
|
+
# Attach custom validator - if any - to the layout (in the <head>-tag - the unobtrusive way).
|
74
|
+
# Don't attach validator(s) if it/they already attached.
|
75
|
+
# TODO: Refactor into helper.
|
76
|
+
validators_js = options.delete(:validators).collect { |v|
|
77
|
+
v.to_s unless /#{v.name}/ =~ @content_for_validatious
|
78
|
+
}.join(' ')
|
79
|
+
content_for :validatious, validators_js if validators_js.present?
|
80
|
+
|
81
|
+
self.send :"#{field_type}_without_validation", *(args << options)
|
82
|
+
end
|
83
|
+
alias_method_chain field_type, :validation
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
module DateHelper
|
89
|
+
|
90
|
+
FIELD_TYPES = [:date_select, :datetime_select, :time_select].freeze
|
91
|
+
|
92
|
+
FIELD_TYPES.each do |field_type|
|
93
|
+
define_method :"#{field_type}_with_validation" do |*args|
|
94
|
+
options = args.extract_options!
|
95
|
+
# Get the validation options.
|
96
|
+
options = ::ValidatiousOnRails::ModelValidations.options_for(args.first, args.second, options)
|
97
|
+
|
98
|
+
# Attach custom validator - if any - to the layout (in the <head>-tag - the unobtrusive way).
|
99
|
+
# Don't attach validator(s) if it/they already attached.
|
100
|
+
# TODO: Refactor into helper.
|
101
|
+
validators_js = options.delete(:validators).collect { |v|
|
102
|
+
v.to_s unless /#{v.name}/ =~ @content_for_validatious
|
103
|
+
}.join(' ')
|
104
|
+
content_for :validatious, validators_js if validators_js.present?
|
105
|
+
|
106
|
+
self.send :"#{field_type}_without_validation", *(args << options)
|
107
|
+
end
|
108
|
+
alias_method_chain field_type, :validation
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
end
|