formtastic_validation 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.textile +100 -0
- data/Rakefile +52 -0
- data/VERSION +1 -0
- data/formtastic_validation.gemspec +84 -0
- data/javascripts/README.textile +20 -0
- data/javascripts/build/formtastic_validation-0.1.0.min.js +1 -0
- data/javascripts/lib/livevalidation-1.3.min.js +4 -0
- data/javascripts/src/formtastic_validation.js +114 -0
- data/lib/formtastic_validation.rb +81 -0
- data/rails_generators/formtastic_validation/formtastic_validation_generator.rb +16 -0
- data/rails_generators/formtastic_validation/templates/config/initializers/formtastic_validation.rb +3 -0
- data/rails_generators/formtastic_validation/templates/public/javascripts/formtastic_validation-0.1.0.min.js +1 -0
- data/rails_generators/formtastic_validation/templates/public/javascripts/livevalidation-1.3.min.js +4 -0
- data/spec/formtastic_validation_spec.rb +93 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +71 -0
- metadata +214 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Wong Liang Zan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
h1. formtastic_validation
|
2
|
+
|
3
|
+
h2. Introduction
|
4
|
+
|
5
|
+
Formtastic validation adds client side validation to your forms.
|
6
|
+
|
7
|
+
h2. Dependencies
|
8
|
+
|
9
|
+
* "Formtastic":http://github.com/justinfrench/formtastic
|
10
|
+
* "Validation Reflection":http://github.com/redinger/validation_reflection
|
11
|
+
* "Live validation":http://github.com/alechill/livevalidation
|
12
|
+
|
13
|
+
h2. Installation
|
14
|
+
|
15
|
+
<pre>
|
16
|
+
$ gem install formtastic_validation
|
17
|
+
</pre>
|
18
|
+
|
19
|
+
Then run the generators to add the files on
|
20
|
+
|
21
|
+
From your rails root directory:
|
22
|
+
|
23
|
+
<pre>
|
24
|
+
$ script/generate formtastic_validation
|
25
|
+
</pre>
|
26
|
+
|
27
|
+
This adds a configuration file to config/initializers and a couple of javascript files to public/javascripts
|
28
|
+
|
29
|
+
After that you need to add this to your application layout
|
30
|
+
|
31
|
+
<pre>
|
32
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
33
|
+
<html lang='en-US' xml:lang='en-US' xmlns='http://www.w3.org/1999/xhtml'>
|
34
|
+
<head>
|
35
|
+
<meta content='text/html; charset=utf-8' http-equiv='Content-type'/>
|
36
|
+
<title>Formtastic Validation</title>
|
37
|
+
<%= javascript_include_tag 'livevalidation-1.3.min' %>
|
38
|
+
<%= javascript_include_tag 'formtastic_validation-0.1.0.min' %>
|
39
|
+
<script type="text/javascript">
|
40
|
+
function initialize() {
|
41
|
+
var formtasticValidation = new FormtasticValidation;
|
42
|
+
formtasticValidation.initialize();
|
43
|
+
}
|
44
|
+
</script>
|
45
|
+
</head>
|
46
|
+
<body onload="initialize()">
|
47
|
+
</body>
|
48
|
+
</html>
|
49
|
+
</pre>
|
50
|
+
|
51
|
+
Two things to add:
|
52
|
+
|
53
|
+
* include the livevalidation and formtastic_validation javascript files
|
54
|
+
* run a function that instantiates a FormatasticValidation object and let it call initialize()
|
55
|
+
h2. Usage
|
56
|
+
|
57
|
+
There is nothing to change in your existing code. You should see "Thankyou!" appearing next to fields which passed the validations. Forms which do not fufil the validations will fail to submit.
|
58
|
+
|
59
|
+
Validation options work.
|
60
|
+
|
61
|
+
h2. CSS Styling
|
62
|
+
|
63
|
+
You can style the validation messages with the css selectors
|
64
|
+
|
65
|
+
<pre>
|
66
|
+
.LV_validation_message { ... } /* the validation message */
|
67
|
+
.LV_valid { ... } /* for valid messages */
|
68
|
+
.LV_invalid { ... } /* for invalid messages */
|
69
|
+
</pre>
|
70
|
+
|
71
|
+
h2. Caveats
|
72
|
+
|
73
|
+
Only the following validations work:
|
74
|
+
|
75
|
+
# validates_presence_of
|
76
|
+
# validates_acceptance_of
|
77
|
+
# validates_confirmation_of
|
78
|
+
# validates_exclusion_of
|
79
|
+
# validates_inclusion_of
|
80
|
+
# validates_numericality_of
|
81
|
+
# validates_format_of
|
82
|
+
# validates_length_of
|
83
|
+
|
84
|
+
validates_size_of does not work. Do use my "fork":http://rubygems.org/gems/liangzan-validation_reflection of the validation_reflection gem for it to work.
|
85
|
+
|
86
|
+
The rest do not work as they depend on server queries.
|
87
|
+
|
88
|
+
h2. Note on Patches/Pull Requests
|
89
|
+
|
90
|
+
* Fork the project.
|
91
|
+
* Make your feature addition or bug fix.
|
92
|
+
* Add tests for it. This is important so I don't break it in a
|
93
|
+
future version unintentionally.
|
94
|
+
* Commit, do not mess with rakefile, version, or history.
|
95
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
96
|
+
* Send me a pull request. Bonus points for topic branches.
|
97
|
+
|
98
|
+
h2. Copyright
|
99
|
+
|
100
|
+
Copyright (c) 2010 Wong Liang Zan. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "formtastic_validation"
|
8
|
+
gem.summary = %Q{Client side validation with formtastic}
|
9
|
+
gem.description = %Q{A formtastic extension that does client side validation}
|
10
|
+
gem.email = "zan@liangzan.net"
|
11
|
+
gem.homepage = "http://github.com/liangzan/formtastic_validation"
|
12
|
+
gem.authors = ["Wong Liang Zan"]
|
13
|
+
gem.add_dependency "validation_reflection", "= 0.3.8"
|
14
|
+
gem.add_dependency "formtastic", "= 1.0.0"
|
15
|
+
gem.add_development_dependency "rspec", "= 1.3.0"
|
16
|
+
gem.add_development_dependency "rspec-rails", "= 1.3.2"
|
17
|
+
gem.add_development_dependency "rspec_tag_matchers", "= 1.0.0"
|
18
|
+
gem.add_development_dependency "nokogiri", "= 1.4.2"
|
19
|
+
gem.add_development_dependency "activesupport", "= 2.3.8"
|
20
|
+
gem.add_development_dependency "actionpack", "= 2.3.8"
|
21
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
22
|
+
end
|
23
|
+
Jeweler::GemcutterTasks.new
|
24
|
+
rescue LoadError
|
25
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
26
|
+
end
|
27
|
+
|
28
|
+
require 'spec/rake/spectask'
|
29
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
30
|
+
spec.libs << 'lib' << 'spec'
|
31
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
32
|
+
end
|
33
|
+
|
34
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
35
|
+
spec.libs << 'lib' << 'spec'
|
36
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
37
|
+
spec.rcov = true
|
38
|
+
end
|
39
|
+
|
40
|
+
task :spec => :check_dependencies
|
41
|
+
|
42
|
+
task :default => :spec
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
|
+
|
48
|
+
rdoc.rdoc_dir = 'rdoc'
|
49
|
+
rdoc.title = "formtastic_validation #{version}"
|
50
|
+
rdoc.rdoc_files.include('README*')
|
51
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
52
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{formtastic_validation}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Wong Liang Zan"]
|
12
|
+
s.date = %q{2010-08-31}
|
13
|
+
s.description = %q{A formtastic extension that does client side validation}
|
14
|
+
s.email = %q{zan@liangzan.net}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.textile"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".document",
|
21
|
+
".gitignore",
|
22
|
+
"LICENSE",
|
23
|
+
"README.textile",
|
24
|
+
"Rakefile",
|
25
|
+
"VERSION",
|
26
|
+
"formtastic_validation.gemspec",
|
27
|
+
"javascripts/README.textile",
|
28
|
+
"javascripts/build/formtastic_validation-0.1.0.min.js",
|
29
|
+
"javascripts/lib/livevalidation-1.3.min.js",
|
30
|
+
"javascripts/src/formtastic_validation.js",
|
31
|
+
"lib/formtastic_validation.rb",
|
32
|
+
"rails_generators/formtastic_validation/formtastic_validation_generator.rb",
|
33
|
+
"rails_generators/formtastic_validation/templates/config/initializers/formtastic_validation.rb",
|
34
|
+
"rails_generators/formtastic_validation/templates/public/javascripts/formtastic_validation-0.1.0.min.js",
|
35
|
+
"rails_generators/formtastic_validation/templates/public/javascripts/livevalidation-1.3.min.js",
|
36
|
+
"spec/formtastic_validation_spec.rb",
|
37
|
+
"spec/spec.opts",
|
38
|
+
"spec/spec_helper.rb"
|
39
|
+
]
|
40
|
+
s.homepage = %q{http://github.com/liangzan/formtastic_validation}
|
41
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
42
|
+
s.require_paths = ["lib"]
|
43
|
+
s.rubygems_version = %q{1.3.7}
|
44
|
+
s.summary = %q{Client side validation with formtastic}
|
45
|
+
s.test_files = [
|
46
|
+
"spec/formtastic_validation_spec.rb",
|
47
|
+
"spec/spec_helper.rb"
|
48
|
+
]
|
49
|
+
|
50
|
+
if s.respond_to? :specification_version then
|
51
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
52
|
+
s.specification_version = 3
|
53
|
+
|
54
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
55
|
+
s.add_runtime_dependency(%q<validation_reflection>, ["= 0.3.8"])
|
56
|
+
s.add_runtime_dependency(%q<formtastic>, ["= 1.0.0"])
|
57
|
+
s.add_development_dependency(%q<rspec>, ["= 1.3.0"])
|
58
|
+
s.add_development_dependency(%q<rspec-rails>, ["= 1.3.2"])
|
59
|
+
s.add_development_dependency(%q<rspec_tag_matchers>, ["= 1.0.0"])
|
60
|
+
s.add_development_dependency(%q<nokogiri>, ["= 1.4.2"])
|
61
|
+
s.add_development_dependency(%q<activesupport>, ["= 2.3.8"])
|
62
|
+
s.add_development_dependency(%q<actionpack>, ["= 2.3.8"])
|
63
|
+
else
|
64
|
+
s.add_dependency(%q<validation_reflection>, ["= 0.3.8"])
|
65
|
+
s.add_dependency(%q<formtastic>, ["= 1.0.0"])
|
66
|
+
s.add_dependency(%q<rspec>, ["= 1.3.0"])
|
67
|
+
s.add_dependency(%q<rspec-rails>, ["= 1.3.2"])
|
68
|
+
s.add_dependency(%q<rspec_tag_matchers>, ["= 1.0.0"])
|
69
|
+
s.add_dependency(%q<nokogiri>, ["= 1.4.2"])
|
70
|
+
s.add_dependency(%q<activesupport>, ["= 2.3.8"])
|
71
|
+
s.add_dependency(%q<actionpack>, ["= 2.3.8"])
|
72
|
+
end
|
73
|
+
else
|
74
|
+
s.add_dependency(%q<validation_reflection>, ["= 0.3.8"])
|
75
|
+
s.add_dependency(%q<formtastic>, ["= 1.0.0"])
|
76
|
+
s.add_dependency(%q<rspec>, ["= 1.3.0"])
|
77
|
+
s.add_dependency(%q<rspec-rails>, ["= 1.3.2"])
|
78
|
+
s.add_dependency(%q<rspec_tag_matchers>, ["= 1.0.0"])
|
79
|
+
s.add_dependency(%q<nokogiri>, ["= 1.4.2"])
|
80
|
+
s.add_dependency(%q<activesupport>, ["= 2.3.8"])
|
81
|
+
s.add_dependency(%q<actionpack>, ["= 2.3.8"])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
h1. Formtastic Validation JS Lib
|
2
|
+
|
3
|
+
h2. Dependencies
|
4
|
+
|
5
|
+
Minifying is done using "yui-compressor":http://developer.yahoo.com/yui/compressor/
|
6
|
+
|
7
|
+
Do install yui-compressor before attempting to build the files.
|
8
|
+
|
9
|
+
h2. Testing
|
10
|
+
|
11
|
+
Testing is done using cuucmber on the formtastic validation app
|
12
|
+
|
13
|
+
h2. Building
|
14
|
+
|
15
|
+
Run this on the commandline
|
16
|
+
|
17
|
+
<pre>
|
18
|
+
$ yui-compressor -o build/formtastic_validation.min.js src/formtastic_validation.js
|
19
|
+
</pre>
|
20
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
var FormtasticValidation=function(){this.VERSION="0.1.0";this.selectInputElements=function(){var d=document.getElementsByTagName("input");var e=new Array;for(var b=0;b<d.length;b++){var a=d[b];var c=a.getAttribute("type");if(this.isValidInputType(c)){e.push(d[b])}}return e};this.isValidInputType=function(a){switch(a){case"text":case"password":case"radio":case"checkbox":return true;default:return false}};this.getValidationAttributes=function(e){var b={};var f=e.attributes;var d,a;for(var c=0;c<f.length;c++){d=this.extractValidationKey(f[c].name);a=f[c].value;if(d!=null){b[d]=a}}return b};this.extractValidationKey=function(b){var c=/validation_(\w+)/;var a=b.match(c);return a!=null?a[1]:null};this.extractFormatRegex=function(b){var c=/^\/(.*)\/$/;var a=b.match(c);return a!=null?a[1]:null};this.confirmationID=function(b){var a=b.getAttribute("id");return a+"_confirmation"};this.bindInputElements=function(c,a,b){var e=new LiveValidation(c);var f,d;switch(a){case"validates_acceptance_of":e.add(Validate.Acceptance,{failureMessage:b.message});break;case"validates_confirmation_of":d=new LiveValidation(this.confirmationID(c));d.add(Validate.Confirmation,{match:c,failureMessage:b.message});break;case"validates_exclusion_of":e.add(Validate.Exclusion,{within:JSON.parse(b["in"]),allowNull:b.allow_nil,failureMessage:b.message});break;case"validates_format_of":f=this.extractFormatRegex(b["with"]);e.add(Validate.Format,{pattern:new RegExp(f),failureMessage:b.message});break;case"validates_inclusion_of":e.add(Validate.Inclusion,{within:JSON.parse(b["in"]),allowNull:b.allow_nil,failureMessage:b.message});break;case"validates_size_of":case"validates_length_of":e.add(Validate.Length,{is:b.is,minimum:b.minimum,maximum:b.maximum,wrongLengthMessage:b.wrong_length,tooShortMessage:b.too_short,tooLongMessage:b.too_long});break;case"validates_numericality_of":e.add(Validate.Numericality,{is:b.equal_to,minimum:b.greater_than_or_equal_to,maximum:b.less_than_or_equal_to,onlyinteger:b.only_integer,failureMessage:b.message});break;case"validates_presence_of":e.add(Validate.Presence,{failureMessage:b.message});break}}};FormtasticValidation.prototype.initialize=function(){var f=this.selectInputElements();var d,e,a;for(var c=0;c<f.length;c++){d=f[c].getAttribute("validation");if(d!=null){e=d.split(" ");for(var b=0;b<e.length;b++){a=this.getValidationAttributes(f[c]);this.bindInputElements(f[c],e[b],a)}}}};
|
@@ -0,0 +1,4 @@
|
|
1
|
+
// LiveValidation 1.3 (standalone version)
|
2
|
+
// Copyright (c) 2007-2008 Alec Hill (www.livevalidation.com)
|
3
|
+
// LiveValidation is licensed under the terms of the MIT License
|
4
|
+
var LiveValidation=function(B,A){this.initialize(B,A);};LiveValidation.VERSION="1.3 standalone";LiveValidation.TEXTAREA=1;LiveValidation.TEXT=2;LiveValidation.PASSWORD=3;LiveValidation.CHECKBOX=4;LiveValidation.SELECT=5;LiveValidation.FILE=6;LiveValidation.massValidate=function(C){var D=true;for(var B=0,A=C.length;B<A;++B){var E=C[B].validate();if(D){D=E;}}return D;};LiveValidation.prototype={validClass:"LV_valid",invalidClass:"LV_invalid",messageClass:"LV_validation_message",validFieldClass:"LV_valid_field",invalidFieldClass:"LV_invalid_field",initialize:function(D,C){var A=this;if(!D){throw new Error("LiveValidation::initialize - No element reference or element id has been provided!");}this.element=D.nodeName?D:document.getElementById(D);if(!this.element){throw new Error("LiveValidation::initialize - No element with reference or id of '"+D+"' exists!");}this.validations=[];this.elementType=this.getElementType();this.form=this.element.form;var B=C||{};this.validMessage=B.validMessage||"Thankyou!";var E=B.insertAfterWhatNode||this.element;this.insertAfterWhatNode=E.nodeType?E:document.getElementById(E);this.onValid=B.onValid||function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();};this.onInvalid=B.onInvalid||function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();};this.onlyOnBlur=B.onlyOnBlur||false;this.wait=B.wait||0;this.onlyOnSubmit=B.onlyOnSubmit||false;if(this.form){this.formObj=LiveValidationForm.getInstance(this.form);this.formObj.addField(this);}this.oldOnFocus=this.element.onfocus||function(){};this.oldOnBlur=this.element.onblur||function(){};this.oldOnClick=this.element.onclick||function(){};this.oldOnChange=this.element.onchange||function(){};this.oldOnKeyup=this.element.onkeyup||function(){};this.element.onfocus=function(F){A.doOnFocus(F);return A.oldOnFocus.call(this,F);};if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:this.element.onclick=function(F){A.validate();return A.oldOnClick.call(this,F);};case LiveValidation.SELECT:case LiveValidation.FILE:this.element.onchange=function(F){A.validate();return A.oldOnChange.call(this,F);};break;default:if(!this.onlyOnBlur){this.element.onkeyup=function(F){A.deferValidation();return A.oldOnKeyup.call(this,F);};}this.element.onblur=function(F){A.doOnBlur(F);return A.oldOnBlur.call(this,F);};}}},destroy:function(){if(this.formObj){this.formObj.removeField(this);this.formObj.destroy();}this.element.onfocus=this.oldOnFocus;if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:this.element.onclick=this.oldOnClick;case LiveValidation.SELECT:case LiveValidation.FILE:this.element.onchange=this.oldOnChange;break;default:if(!this.onlyOnBlur){this.element.onkeyup=this.oldOnKeyup;}this.element.onblur=this.oldOnBlur;}}this.validations=[];this.removeMessageAndFieldClass();},add:function(A,B){this.validations.push({type:A,params:B||{}});return this;},remove:function(B,D){var E=false;for(var C=0,A=this.validations.length;C<A;C++){if(this.validations[C].type==B){if(this.validations[C].params==D){E=true;break;}}}if(E){this.validations.splice(C,1);}return this;},deferValidation:function(B){if(this.wait>=300){this.removeMessageAndFieldClass();}var A=this;if(this.timeout){clearTimeout(A.timeout);}this.timeout=setTimeout(function(){A.validate();},A.wait);},doOnBlur:function(A){this.focused=false;this.validate(A);},doOnFocus:function(A){this.focused=true;this.removeMessageAndFieldClass();},getElementType:function(){switch(true){case (this.element.nodeName.toUpperCase()=="TEXTAREA"):return LiveValidation.TEXTAREA;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="TEXT"):return LiveValidation.TEXT;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="PASSWORD"):return LiveValidation.PASSWORD;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="CHECKBOX"):return LiveValidation.CHECKBOX;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="FILE"):return LiveValidation.FILE;case (this.element.nodeName.toUpperCase()=="SELECT"):return LiveValidation.SELECT;case (this.element.nodeName.toUpperCase()=="INPUT"):throw new Error("LiveValidation::getElementType - Cannot use LiveValidation on an "+this.element.type+" input!");default:throw new Error("LiveValidation::getElementType - Element must be an input, select, or textarea!");}},doValidations:function(){this.validationFailed=false;for(var C=0,A=this.validations.length;C<A;++C){var B=this.validations[C];switch(B.type){case Validate.Presence:case Validate.Confirmation:case Validate.Acceptance:this.displayMessageWhenEmpty=true;this.validationFailed=!this.validateElement(B.type,B.params);break;default:this.validationFailed=!this.validateElement(B.type,B.params);break;}if(this.validationFailed){return false;}}this.message=this.validMessage;return true;},validateElement:function(A,C){var D=(this.elementType==LiveValidation.SELECT)?this.element.options[this.element.selectedIndex].value:this.element.value;if(A==Validate.Acceptance){if(this.elementType!=LiveValidation.CHECKBOX){throw new Error("LiveValidation::validateElement - Element to validate acceptance must be a checkbox!");}D=this.element.checked;}var E=true;try{A(D,C);}catch(B){if(B instanceof Validate.Error){if(D!==""||(D===""&&this.displayMessageWhenEmpty)){this.validationFailed=true;this.message=B.message;E=false;}}else{throw B;}}finally{return E;}},validate:function(){if(!this.element.disabled){var A=this.doValidations();if(A){this.onValid();return true;}else{this.onInvalid();return false;}}else{return true;}},enable:function(){this.element.disabled=false;return this;},disable:function(){this.element.disabled=true;this.removeMessageAndFieldClass();return this;},createMessageSpan:function(){var A=document.createElement("span");var B=document.createTextNode(this.message);A.appendChild(B);return A;},insertMessage:function(B){this.removeMessage();if((this.displayMessageWhenEmpty&&(this.elementType==LiveValidation.CHECKBOX||this.element.value==""))||this.element.value!=""){var A=this.validationFailed?this.invalidClass:this.validClass;B.className+=" "+this.messageClass+" "+A;if(this.insertAfterWhatNode.nextSibling){this.insertAfterWhatNode.parentNode.insertBefore(B,this.insertAfterWhatNode.nextSibling);}else{this.insertAfterWhatNode.parentNode.appendChild(B);}}},addFieldClass:function(){this.removeFieldClass();if(!this.validationFailed){if(this.displayMessageWhenEmpty||this.element.value!=""){if(this.element.className.indexOf(this.validFieldClass)==-1){this.element.className+=" "+this.validFieldClass;}}}else{if(this.element.className.indexOf(this.invalidFieldClass)==-1){this.element.className+=" "+this.invalidFieldClass;}}},removeMessage:function(){var A;var B=this.insertAfterWhatNode;while(B.nextSibling){if(B.nextSibling.nodeType===1){A=B.nextSibling;break;}B=B.nextSibling;}if(A&&A.className.indexOf(this.messageClass)!=-1){this.insertAfterWhatNode.parentNode.removeChild(A);}},removeFieldClass:function(){if(this.element.className.indexOf(this.invalidFieldClass)!=-1){this.element.className=this.element.className.split(this.invalidFieldClass).join("");}if(this.element.className.indexOf(this.validFieldClass)!=-1){this.element.className=this.element.className.split(this.validFieldClass).join(" ");}},removeMessageAndFieldClass:function(){this.removeMessage();this.removeFieldClass();}};var LiveValidationForm=function(A){this.initialize(A);};LiveValidationForm.instances={};LiveValidationForm.getInstance=function(A){var B=Math.random()*Math.random();if(!A.id){A.id="formId_"+B.toString().replace(/\./,"")+new Date().valueOf();}if(!LiveValidationForm.instances[A.id]){LiveValidationForm.instances[A.id]=new LiveValidationForm(A);}return LiveValidationForm.instances[A.id];};LiveValidationForm.prototype={initialize:function(B){this.name=B.id;this.element=B;this.fields=[];this.oldOnSubmit=this.element.onsubmit||function(){};var A=this;this.element.onsubmit=function(C){return(LiveValidation.massValidate(A.fields))?A.oldOnSubmit.call(this,C||window.event)!==false:false;};},addField:function(A){this.fields.push(A);},removeField:function(C){var D=[];for(var B=0,A=this.fields.length;B<A;B++){if(this.fields[B]!==C){D.push(this.fields[B]);}}this.fields=D;},destroy:function(A){if(this.fields.length!=0&&!A){return false;}this.element.onsubmit=this.oldOnSubmit;LiveValidationForm.instances[this.name]=null;return true;}};var Validate={Presence:function(B,C){var C=C||{};var A=C.failureMessage||"Can't be empty!";if(B===""||B===null||B===undefined){Validate.fail(A);}return true;},Numericality:function(J,E){var A=J;var J=Number(J);var E=E||{};var F=((E.minimum)||(E.minimum==0))?E.minimum:null;var C=((E.maximum)||(E.maximum==0))?E.maximum:null;var D=((E.is)||(E.is==0))?E.is:null;var G=E.notANumberMessage||"Must be a number!";var H=E.notAnIntegerMessage||"Must be an integer!";var I=E.wrongNumberMessage||"Must be "+D+"!";var B=E.tooLowMessage||"Must not be less than "+F+"!";var K=E.tooHighMessage||"Must not be more than "+C+"!";if(!isFinite(J)){Validate.fail(G);}if(E.onlyInteger&&(/\.0+$|\.$/.test(String(A))||J!=parseInt(J))){Validate.fail(H);}switch(true){case (D!==null):if(J!=Number(D)){Validate.fail(I);}break;case (F!==null&&C!==null):Validate.Numericality(J,{tooLowMessage:B,minimum:F});Validate.Numericality(J,{tooHighMessage:K,maximum:C});break;case (F!==null):if(J<Number(F)){Validate.fail(B);}break;case (C!==null):if(J>Number(C)){Validate.fail(K);}break;}return true;},Format:function(C,E){var C=String(C);var E=E||{};var A=E.failureMessage||"Not valid!";var B=E.pattern||/./;var D=E.negate||false;if(!D&&!B.test(C)){Validate.fail(A);}if(D&&B.test(C)){Validate.fail(A);}return true;},Email:function(B,C){var C=C||{};var A=C.failureMessage||"Must be a valid email address!";Validate.Format(B,{failureMessage:A,pattern:/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i});return true;},Length:function(F,G){var F=String(F);var G=G||{};var E=((G.minimum)||(G.minimum==0))?G.minimum:null;var H=((G.maximum)||(G.maximum==0))?G.maximum:null;var C=((G.is)||(G.is==0))?G.is:null;var A=G.wrongLengthMessage||"Must be "+C+" characters long!";var B=G.tooShortMessage||"Must not be less than "+E+" characters long!";var D=G.tooLongMessage||"Must not be more than "+H+" characters long!";switch(true){case (C!==null):if(F.length!=Number(C)){Validate.fail(A);}break;case (E!==null&&H!==null):Validate.Length(F,{tooShortMessage:B,minimum:E});Validate.Length(F,{tooLongMessage:D,maximum:H});break;case (E!==null):if(F.length<Number(E)){Validate.fail(B);}break;case (H!==null):if(F.length>Number(H)){Validate.fail(D);}break;default:throw new Error("Validate::Length - Length(s) to validate against must be provided!");}return true;},Inclusion:function(H,F){var F=F||{};var K=F.failureMessage||"Must be included in the list!";var G=(F.caseSensitive===false)?false:true;if(F.allowNull&&H==null){return true;}if(!F.allowNull&&H==null){Validate.fail(K);}var D=F.within||[];if(!G){var A=[];for(var C=0,B=D.length;C<B;++C){var I=D[C];if(typeof I=="string"){I=I.toLowerCase();}A.push(I);}D=A;if(typeof H=="string"){H=H.toLowerCase();}}var J=false;for(var E=0,B=D.length;E<B;++E){if(D[E]==H){J=true;}if(F.partialMatch){if(H.indexOf(D[E])!=-1){J=true;}}}if((!F.negate&&!J)||(F.negate&&J)){Validate.fail(K);}return true;},Exclusion:function(A,B){var B=B||{};B.failureMessage=B.failureMessage||"Must not be included in the list!";B.negate=true;Validate.Inclusion(A,B);return true;},Confirmation:function(C,D){if(!D.match){throw new Error("Validate::Confirmation - Error validating confirmation: Id of element to match must be provided!");}var D=D||{};var B=D.failureMessage||"Does not match!";var A=D.match.nodeName?D.match:document.getElementById(D.match);if(!A){throw new Error("Validate::Confirmation - There is no reference with name of, or element with id of '"+D.match+"'!");}if(C!=A.value){Validate.fail(B);}return true;},Acceptance:function(B,C){var C=C||{};var A=C.failureMessage||"Must be accepted!";if(!B){Validate.fail(A);}return true;},Custom:function(D,E){var E=E||{};var B=E.against||function(){return true;};var A=E.args||{};var C=E.failureMessage||"Not valid!";if(!B(D,A)){Validate.fail(C);}return true;},now:function(A,D,C){if(!A){throw new Error("Validate::now - Validation function must be provided!");}var E=true;try{A(D,C||{});}catch(B){if(B instanceof Validate.Error){E=false;}else{throw B;}}finally{return E;}},fail:function(A){throw new Validate.Error(A);},Error:function(A){this.message=A;this.name="ValidationError";}};
|
@@ -0,0 +1,114 @@
|
|
1
|
+
/* Formtastic Validation JS Lib
|
2
|
+
*
|
3
|
+
* This library selects form input elements, read the attributes
|
4
|
+
* and bind the input field to the corresponding live validation function
|
5
|
+
*/
|
6
|
+
var FormtasticValidation = function() {
|
7
|
+
this.VERSION = "0.1.0";
|
8
|
+
|
9
|
+
this.selectInputElements = function() {
|
10
|
+
var inputElements = document.getElementsByTagName('input');
|
11
|
+
var textInputs = new Array;
|
12
|
+
for (var i = 0; i < inputElements.length; i++) {
|
13
|
+
var element = inputElements[i];
|
14
|
+
var inputType = element.getAttribute("type");
|
15
|
+
if (this.isValidInputType(inputType)) {
|
16
|
+
textInputs.push(inputElements[i]);
|
17
|
+
}
|
18
|
+
}
|
19
|
+
return textInputs;
|
20
|
+
};
|
21
|
+
|
22
|
+
this.isValidInputType = function(type) {
|
23
|
+
switch(type) {
|
24
|
+
case "text":
|
25
|
+
case "password":
|
26
|
+
case "radio":
|
27
|
+
case "checkbox":
|
28
|
+
return true;
|
29
|
+
default:
|
30
|
+
return false;
|
31
|
+
}
|
32
|
+
};
|
33
|
+
|
34
|
+
this.getValidationAttributes = function(element) {
|
35
|
+
var validationOptions = {};
|
36
|
+
var elementAttributes = element.attributes;
|
37
|
+
var validationKey, validationValue;
|
38
|
+
for(var i = 0; i < elementAttributes.length; i++) {
|
39
|
+
validationKey = this.extractValidationKey(elementAttributes[i].name);
|
40
|
+
validationValue = elementAttributes[i].value;
|
41
|
+
if (validationKey != null) {
|
42
|
+
validationOptions[validationKey] = validationValue;
|
43
|
+
}
|
44
|
+
}
|
45
|
+
return validationOptions;
|
46
|
+
};
|
47
|
+
|
48
|
+
this.extractValidationKey = function(key) {
|
49
|
+
var namespaceRegex = /validation_(\w+)/;
|
50
|
+
var result = key.match(namespaceRegex);
|
51
|
+
return result != null ? result[1] : null;
|
52
|
+
};
|
53
|
+
|
54
|
+
this.extractFormatRegex = function(format) {
|
55
|
+
var formatRegex = /^\/(.*)\/$/;
|
56
|
+
var result = format.match(formatRegex);
|
57
|
+
return result != null ? result[1] : null;
|
58
|
+
};
|
59
|
+
|
60
|
+
this.confirmationID = function(element) {
|
61
|
+
var elementID = element.getAttribute("id");
|
62
|
+
return elementID + "_confirmation";
|
63
|
+
};
|
64
|
+
|
65
|
+
this.bindInputElements = function(element, validation, options) {
|
66
|
+
var elementValidation = new LiveValidation(element);
|
67
|
+
var formatRegex, confirmationElement;
|
68
|
+
|
69
|
+
switch(validation) {
|
70
|
+
case "validates_acceptance_of":
|
71
|
+
elementValidation.add(Validate.Acceptance, {failureMessage: options["message"]});
|
72
|
+
break;
|
73
|
+
case "validates_confirmation_of":
|
74
|
+
confirmationElement = new LiveValidation(this.confirmationID(element));
|
75
|
+
confirmationElement.add(Validate.Confirmation, {match: element, failureMessage: options["message"]});
|
76
|
+
break;
|
77
|
+
case "validates_exclusion_of":
|
78
|
+
elementValidation.add(Validate.Exclusion, {within: JSON.parse(options["in"]), allowNull: options["allow_nil"], failureMessage: options["message"]});
|
79
|
+
break;
|
80
|
+
case "validates_format_of":
|
81
|
+
formatRegex = this.extractFormatRegex(options["with"]);
|
82
|
+
elementValidation.add(Validate.Format, {pattern: new RegExp(formatRegex), failureMessage: options["message"]});
|
83
|
+
break;
|
84
|
+
case "validates_inclusion_of":
|
85
|
+
elementValidation.add(Validate.Inclusion, {within: JSON.parse(options["in"]), allowNull: options["allow_nil"], failureMessage: options["message"]});
|
86
|
+
break;
|
87
|
+
case "validates_size_of":
|
88
|
+
case "validates_length_of":
|
89
|
+
elementValidation.add(Validate.Length, {is: options["is"], minimum: options["minimum"], maximum: options["maximum"], wrongLengthMessage: options["wrong_length"], tooShortMessage: options["too_short"], tooLongMessage: options["too_long"]});
|
90
|
+
break;
|
91
|
+
case "validates_numericality_of":
|
92
|
+
elementValidation.add(Validate.Numericality, {is: options["equal_to"], minimum: options["greater_than_or_equal_to"], maximum: options["less_than_or_equal_to"], onlyinteger: options["only_integer"], failureMessage: options["message"]});
|
93
|
+
break;
|
94
|
+
case "validates_presence_of":
|
95
|
+
elementValidation.add(Validate.Presence, {failureMessage: options["message"]});
|
96
|
+
break;
|
97
|
+
}
|
98
|
+
};
|
99
|
+
};
|
100
|
+
|
101
|
+
FormtasticValidation.prototype.initialize = function() {
|
102
|
+
var textInputs = this.selectInputElements();
|
103
|
+
var validations, validationTypes, validationOptions;
|
104
|
+
for (var i = 0; i < textInputs.length; i++) {
|
105
|
+
validations = textInputs[i].getAttribute("validation");
|
106
|
+
if (validations != null) {
|
107
|
+
validationTypes = validations.split(" ");
|
108
|
+
for (var j = 0; j < validationTypes.length; j++) {
|
109
|
+
validationOptions = this.getValidationAttributes(textInputs[i]);
|
110
|
+
this.bindInputElements(textInputs[i], validationTypes[j], validationOptions);
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}
|
114
|
+
};
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module FormtasticValidation
|
2
|
+
|
3
|
+
NAMESPACE = 'validation'
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval {
|
7
|
+
alias_method_chain :input, :validation
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def input_with_validation(method, options = {})
|
12
|
+
if has_validations?(method)
|
13
|
+
options[:input_html] = validation_tags(method, options[:input_html])
|
14
|
+
end
|
15
|
+
input_without_validation(method, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_validations?(attribute)
|
19
|
+
if @object && @object.class.respond_to?(:reflect_on_validations_for)
|
20
|
+
@object.class.reflect_on_validations_for(attribute_sym(attribute)).present?
|
21
|
+
else
|
22
|
+
false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def attribute_sym(attribute)
|
27
|
+
attribute.to_s.sub(/_id$/, '').to_sym
|
28
|
+
end
|
29
|
+
|
30
|
+
def validation_tags(attribute, input_html)
|
31
|
+
tags = []
|
32
|
+
@object.class.reflect_on_validations_for(attribute).each do |validation|
|
33
|
+
tags << { :validation => validation.macro }.merge(validation.options)
|
34
|
+
end
|
35
|
+
stack_tags(tags, input_html)
|
36
|
+
end
|
37
|
+
|
38
|
+
def stack_tags(tags, input_html)
|
39
|
+
vtags = validates_tags(tags.dup)
|
40
|
+
otags = options_tags(tags.dup)
|
41
|
+
if input_html
|
42
|
+
input_html[:validation] = vtags
|
43
|
+
input_html.merge(otags)
|
44
|
+
else
|
45
|
+
{ :validation => vtags }.merge(otags)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def validates_tags(tags)
|
50
|
+
validates_tags = tags.map { |tag| tag[:validation] }
|
51
|
+
validates_tags.join(' ')
|
52
|
+
end
|
53
|
+
|
54
|
+
def options_tags(tags)
|
55
|
+
otags_array = tags.map do |tag|
|
56
|
+
tag.delete_if { |key, value| key.eql?(:validation) }
|
57
|
+
end
|
58
|
+
opts_hash = otags_array.inject { |memo, hash| memo.merge(hash) }
|
59
|
+
namespaced_hash = add_namespace(opts_hash)
|
60
|
+
serialise_options(namespaced_hash)
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_namespace(opts_hash)
|
64
|
+
namespaced_hash = Hash.new
|
65
|
+
opts_hash.each do |key, value|
|
66
|
+
namespaced_key = (NAMESPACE + '_' + key.to_s).to_sym
|
67
|
+
namespaced_hash[namespaced_key] = value
|
68
|
+
end
|
69
|
+
namespaced_hash
|
70
|
+
end
|
71
|
+
|
72
|
+
def serialise_options(opts_hash)
|
73
|
+
serialised_hash = Hash.new
|
74
|
+
opts_hash.each do |key, value|
|
75
|
+
serialised_value = value.class.eql?(String) ? value : value.to_json
|
76
|
+
serialised_hash[key] = serialised_value
|
77
|
+
end
|
78
|
+
serialised_hash
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class FormtasticValidationGenerator < Rails::Generator::Base
|
2
|
+
|
3
|
+
def manifest
|
4
|
+
record do |m|
|
5
|
+
m.file 'public/javascripts/formtastic_validation-0.1.0.min.js', 'public/javascripts/formtastic_validation-0.1.0.min.js'
|
6
|
+
m.file 'public/javascripts/livevalidation-1.3.min.js', 'public/javascripts/livevalidation-1.3.min.js'
|
7
|
+
m.file 'config/initializers/formtastic_validation.rb', 'config/initializers/formtastic_validation.rb'
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def banner
|
14
|
+
%\Usage: #{$0} formtastic_validation\
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
var FormtasticValidation=function(){this.VERSION="0.1.0";this.selectInputElements=function(){var d=document.getElementsByTagName("input");var e=new Array;for(var b=0;b<d.length;b++){var a=d[b];var c=a.getAttribute("type");if(this.isValidInputType(c)){e.push(d[b])}}return e};this.isValidInputType=function(a){switch(a){case"text":case"password":case"radio":case"checkbox":return true;default:return false}};this.getValidationAttributes=function(e){var b={};var f=e.attributes;var d,a;for(var c=0;c<f.length;c++){d=this.extractValidationKey(f[c].name);a=f[c].value;if(d!=null){b[d]=a}}return b};this.extractValidationKey=function(b){var c=/validation_(\w+)/;var a=b.match(c);return a!=null?a[1]:null};this.extractFormatRegex=function(b){var c=/^\/(.*)\/$/;var a=b.match(c);return a!=null?a[1]:null};this.confirmationID=function(b){var a=b.getAttribute("id");return a+"_confirmation"};this.bindInputElements=function(c,a,b){var e=new LiveValidation(c);var f,d;switch(a){case"validates_acceptance_of":e.add(Validate.Acceptance,{failureMessage:b.message});break;case"validates_confirmation_of":d=new LiveValidation(this.confirmationID(c));d.add(Validate.Confirmation,{match:c,failureMessage:b.message});break;case"validates_exclusion_of":e.add(Validate.Exclusion,{within:JSON.parse(b["in"]),allowNull:b.allow_nil,failureMessage:b.message});break;case"validates_format_of":f=this.extractFormatRegex(b["with"]);e.add(Validate.Format,{pattern:new RegExp(f),failureMessage:b.message});break;case"validates_inclusion_of":e.add(Validate.Inclusion,{within:JSON.parse(b["in"]),allowNull:b.allow_nil,failureMessage:b.message});break;case"validates_size_of":case"validates_length_of":e.add(Validate.Length,{is:b.is,minimum:b.minimum,maximum:b.maximum,wrongLengthMessage:b.wrong_length,tooShortMessage:b.too_short,tooLongMessage:b.too_long});break;case"validates_numericality_of":e.add(Validate.Numericality,{is:b.equal_to,minimum:b.greater_than_or_equal_to,maximum:b.less_than_or_equal_to,onlyinteger:b.only_integer,failureMessage:b.message});break;case"validates_presence_of":e.add(Validate.Presence,{failureMessage:b.message});break}}};FormtasticValidation.prototype.initialize=function(){var f=this.selectInputElements();var d,e,a;for(var c=0;c<f.length;c++){d=f[c].getAttribute("validation");if(d!=null){e=d.split(" ");for(var b=0;b<e.length;b++){a=this.getValidationAttributes(f[c]);this.bindInputElements(f[c],e[b],a)}}}};
|
data/rails_generators/formtastic_validation/templates/public/javascripts/livevalidation-1.3.min.js
ADDED
@@ -0,0 +1,4 @@
|
|
1
|
+
// LiveValidation 1.3 (standalone version)
|
2
|
+
// Copyright (c) 2007-2008 Alec Hill (www.livevalidation.com)
|
3
|
+
// LiveValidation is licensed under the terms of the MIT License
|
4
|
+
var LiveValidation=function(B,A){this.initialize(B,A);};LiveValidation.VERSION="1.3 standalone";LiveValidation.TEXTAREA=1;LiveValidation.TEXT=2;LiveValidation.PASSWORD=3;LiveValidation.CHECKBOX=4;LiveValidation.SELECT=5;LiveValidation.FILE=6;LiveValidation.massValidate=function(C){var D=true;for(var B=0,A=C.length;B<A;++B){var E=C[B].validate();if(D){D=E;}}return D;};LiveValidation.prototype={validClass:"LV_valid",invalidClass:"LV_invalid",messageClass:"LV_validation_message",validFieldClass:"LV_valid_field",invalidFieldClass:"LV_invalid_field",initialize:function(D,C){var A=this;if(!D){throw new Error("LiveValidation::initialize - No element reference or element id has been provided!");}this.element=D.nodeName?D:document.getElementById(D);if(!this.element){throw new Error("LiveValidation::initialize - No element with reference or id of '"+D+"' exists!");}this.validations=[];this.elementType=this.getElementType();this.form=this.element.form;var B=C||{};this.validMessage=B.validMessage||"Thankyou!";var E=B.insertAfterWhatNode||this.element;this.insertAfterWhatNode=E.nodeType?E:document.getElementById(E);this.onValid=B.onValid||function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();};this.onInvalid=B.onInvalid||function(){this.insertMessage(this.createMessageSpan());this.addFieldClass();};this.onlyOnBlur=B.onlyOnBlur||false;this.wait=B.wait||0;this.onlyOnSubmit=B.onlyOnSubmit||false;if(this.form){this.formObj=LiveValidationForm.getInstance(this.form);this.formObj.addField(this);}this.oldOnFocus=this.element.onfocus||function(){};this.oldOnBlur=this.element.onblur||function(){};this.oldOnClick=this.element.onclick||function(){};this.oldOnChange=this.element.onchange||function(){};this.oldOnKeyup=this.element.onkeyup||function(){};this.element.onfocus=function(F){A.doOnFocus(F);return A.oldOnFocus.call(this,F);};if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:this.element.onclick=function(F){A.validate();return A.oldOnClick.call(this,F);};case LiveValidation.SELECT:case LiveValidation.FILE:this.element.onchange=function(F){A.validate();return A.oldOnChange.call(this,F);};break;default:if(!this.onlyOnBlur){this.element.onkeyup=function(F){A.deferValidation();return A.oldOnKeyup.call(this,F);};}this.element.onblur=function(F){A.doOnBlur(F);return A.oldOnBlur.call(this,F);};}}},destroy:function(){if(this.formObj){this.formObj.removeField(this);this.formObj.destroy();}this.element.onfocus=this.oldOnFocus;if(!this.onlyOnSubmit){switch(this.elementType){case LiveValidation.CHECKBOX:this.element.onclick=this.oldOnClick;case LiveValidation.SELECT:case LiveValidation.FILE:this.element.onchange=this.oldOnChange;break;default:if(!this.onlyOnBlur){this.element.onkeyup=this.oldOnKeyup;}this.element.onblur=this.oldOnBlur;}}this.validations=[];this.removeMessageAndFieldClass();},add:function(A,B){this.validations.push({type:A,params:B||{}});return this;},remove:function(B,D){var E=false;for(var C=0,A=this.validations.length;C<A;C++){if(this.validations[C].type==B){if(this.validations[C].params==D){E=true;break;}}}if(E){this.validations.splice(C,1);}return this;},deferValidation:function(B){if(this.wait>=300){this.removeMessageAndFieldClass();}var A=this;if(this.timeout){clearTimeout(A.timeout);}this.timeout=setTimeout(function(){A.validate();},A.wait);},doOnBlur:function(A){this.focused=false;this.validate(A);},doOnFocus:function(A){this.focused=true;this.removeMessageAndFieldClass();},getElementType:function(){switch(true){case (this.element.nodeName.toUpperCase()=="TEXTAREA"):return LiveValidation.TEXTAREA;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="TEXT"):return LiveValidation.TEXT;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="PASSWORD"):return LiveValidation.PASSWORD;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="CHECKBOX"):return LiveValidation.CHECKBOX;case (this.element.nodeName.toUpperCase()=="INPUT"&&this.element.type.toUpperCase()=="FILE"):return LiveValidation.FILE;case (this.element.nodeName.toUpperCase()=="SELECT"):return LiveValidation.SELECT;case (this.element.nodeName.toUpperCase()=="INPUT"):throw new Error("LiveValidation::getElementType - Cannot use LiveValidation on an "+this.element.type+" input!");default:throw new Error("LiveValidation::getElementType - Element must be an input, select, or textarea!");}},doValidations:function(){this.validationFailed=false;for(var C=0,A=this.validations.length;C<A;++C){var B=this.validations[C];switch(B.type){case Validate.Presence:case Validate.Confirmation:case Validate.Acceptance:this.displayMessageWhenEmpty=true;this.validationFailed=!this.validateElement(B.type,B.params);break;default:this.validationFailed=!this.validateElement(B.type,B.params);break;}if(this.validationFailed){return false;}}this.message=this.validMessage;return true;},validateElement:function(A,C){var D=(this.elementType==LiveValidation.SELECT)?this.element.options[this.element.selectedIndex].value:this.element.value;if(A==Validate.Acceptance){if(this.elementType!=LiveValidation.CHECKBOX){throw new Error("LiveValidation::validateElement - Element to validate acceptance must be a checkbox!");}D=this.element.checked;}var E=true;try{A(D,C);}catch(B){if(B instanceof Validate.Error){if(D!==""||(D===""&&this.displayMessageWhenEmpty)){this.validationFailed=true;this.message=B.message;E=false;}}else{throw B;}}finally{return E;}},validate:function(){if(!this.element.disabled){var A=this.doValidations();if(A){this.onValid();return true;}else{this.onInvalid();return false;}}else{return true;}},enable:function(){this.element.disabled=false;return this;},disable:function(){this.element.disabled=true;this.removeMessageAndFieldClass();return this;},createMessageSpan:function(){var A=document.createElement("span");var B=document.createTextNode(this.message);A.appendChild(B);return A;},insertMessage:function(B){this.removeMessage();if((this.displayMessageWhenEmpty&&(this.elementType==LiveValidation.CHECKBOX||this.element.value==""))||this.element.value!=""){var A=this.validationFailed?this.invalidClass:this.validClass;B.className+=" "+this.messageClass+" "+A;if(this.insertAfterWhatNode.nextSibling){this.insertAfterWhatNode.parentNode.insertBefore(B,this.insertAfterWhatNode.nextSibling);}else{this.insertAfterWhatNode.parentNode.appendChild(B);}}},addFieldClass:function(){this.removeFieldClass();if(!this.validationFailed){if(this.displayMessageWhenEmpty||this.element.value!=""){if(this.element.className.indexOf(this.validFieldClass)==-1){this.element.className+=" "+this.validFieldClass;}}}else{if(this.element.className.indexOf(this.invalidFieldClass)==-1){this.element.className+=" "+this.invalidFieldClass;}}},removeMessage:function(){var A;var B=this.insertAfterWhatNode;while(B.nextSibling){if(B.nextSibling.nodeType===1){A=B.nextSibling;break;}B=B.nextSibling;}if(A&&A.className.indexOf(this.messageClass)!=-1){this.insertAfterWhatNode.parentNode.removeChild(A);}},removeFieldClass:function(){if(this.element.className.indexOf(this.invalidFieldClass)!=-1){this.element.className=this.element.className.split(this.invalidFieldClass).join("");}if(this.element.className.indexOf(this.validFieldClass)!=-1){this.element.className=this.element.className.split(this.validFieldClass).join(" ");}},removeMessageAndFieldClass:function(){this.removeMessage();this.removeFieldClass();}};var LiveValidationForm=function(A){this.initialize(A);};LiveValidationForm.instances={};LiveValidationForm.getInstance=function(A){var B=Math.random()*Math.random();if(!A.id){A.id="formId_"+B.toString().replace(/\./,"")+new Date().valueOf();}if(!LiveValidationForm.instances[A.id]){LiveValidationForm.instances[A.id]=new LiveValidationForm(A);}return LiveValidationForm.instances[A.id];};LiveValidationForm.prototype={initialize:function(B){this.name=B.id;this.element=B;this.fields=[];this.oldOnSubmit=this.element.onsubmit||function(){};var A=this;this.element.onsubmit=function(C){return(LiveValidation.massValidate(A.fields))?A.oldOnSubmit.call(this,C||window.event)!==false:false;};},addField:function(A){this.fields.push(A);},removeField:function(C){var D=[];for(var B=0,A=this.fields.length;B<A;B++){if(this.fields[B]!==C){D.push(this.fields[B]);}}this.fields=D;},destroy:function(A){if(this.fields.length!=0&&!A){return false;}this.element.onsubmit=this.oldOnSubmit;LiveValidationForm.instances[this.name]=null;return true;}};var Validate={Presence:function(B,C){var C=C||{};var A=C.failureMessage||"Can't be empty!";if(B===""||B===null||B===undefined){Validate.fail(A);}return true;},Numericality:function(J,E){var A=J;var J=Number(J);var E=E||{};var F=((E.minimum)||(E.minimum==0))?E.minimum:null;var C=((E.maximum)||(E.maximum==0))?E.maximum:null;var D=((E.is)||(E.is==0))?E.is:null;var G=E.notANumberMessage||"Must be a number!";var H=E.notAnIntegerMessage||"Must be an integer!";var I=E.wrongNumberMessage||"Must be "+D+"!";var B=E.tooLowMessage||"Must not be less than "+F+"!";var K=E.tooHighMessage||"Must not be more than "+C+"!";if(!isFinite(J)){Validate.fail(G);}if(E.onlyInteger&&(/\.0+$|\.$/.test(String(A))||J!=parseInt(J))){Validate.fail(H);}switch(true){case (D!==null):if(J!=Number(D)){Validate.fail(I);}break;case (F!==null&&C!==null):Validate.Numericality(J,{tooLowMessage:B,minimum:F});Validate.Numericality(J,{tooHighMessage:K,maximum:C});break;case (F!==null):if(J<Number(F)){Validate.fail(B);}break;case (C!==null):if(J>Number(C)){Validate.fail(K);}break;}return true;},Format:function(C,E){var C=String(C);var E=E||{};var A=E.failureMessage||"Not valid!";var B=E.pattern||/./;var D=E.negate||false;if(!D&&!B.test(C)){Validate.fail(A);}if(D&&B.test(C)){Validate.fail(A);}return true;},Email:function(B,C){var C=C||{};var A=C.failureMessage||"Must be a valid email address!";Validate.Format(B,{failureMessage:A,pattern:/^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i});return true;},Length:function(F,G){var F=String(F);var G=G||{};var E=((G.minimum)||(G.minimum==0))?G.minimum:null;var H=((G.maximum)||(G.maximum==0))?G.maximum:null;var C=((G.is)||(G.is==0))?G.is:null;var A=G.wrongLengthMessage||"Must be "+C+" characters long!";var B=G.tooShortMessage||"Must not be less than "+E+" characters long!";var D=G.tooLongMessage||"Must not be more than "+H+" characters long!";switch(true){case (C!==null):if(F.length!=Number(C)){Validate.fail(A);}break;case (E!==null&&H!==null):Validate.Length(F,{tooShortMessage:B,minimum:E});Validate.Length(F,{tooLongMessage:D,maximum:H});break;case (E!==null):if(F.length<Number(E)){Validate.fail(B);}break;case (H!==null):if(F.length>Number(H)){Validate.fail(D);}break;default:throw new Error("Validate::Length - Length(s) to validate against must be provided!");}return true;},Inclusion:function(H,F){var F=F||{};var K=F.failureMessage||"Must be included in the list!";var G=(F.caseSensitive===false)?false:true;if(F.allowNull&&H==null){return true;}if(!F.allowNull&&H==null){Validate.fail(K);}var D=F.within||[];if(!G){var A=[];for(var C=0,B=D.length;C<B;++C){var I=D[C];if(typeof I=="string"){I=I.toLowerCase();}A.push(I);}D=A;if(typeof H=="string"){H=H.toLowerCase();}}var J=false;for(var E=0,B=D.length;E<B;++E){if(D[E]==H){J=true;}if(F.partialMatch){if(H.indexOf(D[E])!=-1){J=true;}}}if((!F.negate&&!J)||(F.negate&&J)){Validate.fail(K);}return true;},Exclusion:function(A,B){var B=B||{};B.failureMessage=B.failureMessage||"Must not be included in the list!";B.negate=true;Validate.Inclusion(A,B);return true;},Confirmation:function(C,D){if(!D.match){throw new Error("Validate::Confirmation - Error validating confirmation: Id of element to match must be provided!");}var D=D||{};var B=D.failureMessage||"Does not match!";var A=D.match.nodeName?D.match:document.getElementById(D.match);if(!A){throw new Error("Validate::Confirmation - There is no reference with name of, or element with id of '"+D.match+"'!");}if(C!=A.value){Validate.fail(B);}return true;},Acceptance:function(B,C){var C=C||{};var A=C.failureMessage||"Must be accepted!";if(!B){Validate.fail(A);}return true;},Custom:function(D,E){var E=E||{};var B=E.against||function(){return true;};var A=E.args||{};var C=E.failureMessage||"Not valid!";if(!B(D,A)){Validate.fail(C);}return true;},now:function(A,D,C){if(!A){throw new Error("Validate::now - Validation function must be provided!");}var E=true;try{A(D,C||{});}catch(B){if(B instanceof Validate.Error){E=false;}else{throw B;}}finally{return E;}},fail:function(A){throw new Validate.Error(A);},Error:function(A){this.message=A;this.name="ValidationError";}};
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "FormtasticValidation#input" do
|
4
|
+
include FormtasticValidationSpecHelper
|
5
|
+
|
6
|
+
before do
|
7
|
+
@output_buffer = ''
|
8
|
+
setup_mocks
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "validation and options attributes" do
|
12
|
+
|
13
|
+
describe "when object is given" do
|
14
|
+
describe 'and validations were called for the method' do
|
15
|
+
|
16
|
+
it 'should add a validation tag' do
|
17
|
+
@bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_whatever, :name => :name, :options => {})])
|
18
|
+
semantic_form_for(@bob) do |builder|
|
19
|
+
concat(builder.input(:name))
|
20
|
+
end
|
21
|
+
output_buffer.should have_tag('input[@validation="validates_whatever"]')
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should add multiple validation tags separated by space" do
|
25
|
+
@bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_whatever, :name => :name, :options => {}), mock('MacroReflection', :macro => :validates_anything, :name => :name, :options => {})])
|
26
|
+
semantic_form_for(@bob) do |builder|
|
27
|
+
concat(builder.input(:name))
|
28
|
+
end
|
29
|
+
output_buffer.should have_tag('input[@validation="validates_whatever validates_anything"]')
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should add a single option as tag' do
|
33
|
+
@bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_whatever, :name => :name, :options => {:one => 'one'})])
|
34
|
+
semantic_form_for(@bob) do |builder|
|
35
|
+
concat(builder.input(:name))
|
36
|
+
end
|
37
|
+
output_buffer.should have_tag('input[@validation="validates_whatever"]')
|
38
|
+
output_buffer.should have_tag('input[@validation_one="one"]')
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should add multiple options as separate tags" do
|
42
|
+
@bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_whatever, :name => :name, :options => {:one => 'one', :two => 'two'})])
|
43
|
+
semantic_form_for(@bob) do |builder|
|
44
|
+
concat(builder.input(:name))
|
45
|
+
end
|
46
|
+
output_buffer.should have_tag('input[@validation="validates_whatever"]')
|
47
|
+
output_buffer.should have_tag('input[@validation_one="one"]')
|
48
|
+
output_buffer.should have_tag('input[@validation_two="two"]')
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should include the input_html" do
|
52
|
+
@bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_whatever, :name => :name, :options => {:one => 'one', :two => 'two'})])
|
53
|
+
semantic_form_for(@bob) do |builder|
|
54
|
+
concat(builder.input(:name, :input_html => {:three => 'three'}))
|
55
|
+
end
|
56
|
+
output_buffer.should have_tag('input[@validation="validates_whatever"]')
|
57
|
+
output_buffer.should have_tag('input[@validation_one="one"]')
|
58
|
+
output_buffer.should have_tag('input[@validation_two="two"]')
|
59
|
+
output_buffer.should have_tag('input[@three="three"]')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "validation options" do
|
65
|
+
it "should serialise regex as json strings" do
|
66
|
+
@bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_format_of, :name => :name, :options => {:with => /^a garden$/})])
|
67
|
+
semantic_form_for(@bob) do |builder|
|
68
|
+
concat(builder.input(:name))
|
69
|
+
end
|
70
|
+
output_buffer.should have_tag('input[@validation="validates_format_of"]')
|
71
|
+
output_buffer.should have_tag('input[@validation_with="/^a garden$/"]')
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should serialise array as json strings" do
|
75
|
+
@bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_inclusion_of, :name => :name, :options => {:in => %w(one two three)})])
|
76
|
+
semantic_form_for(@bob) do |builder|
|
77
|
+
concat(builder.input(:name))
|
78
|
+
end
|
79
|
+
output_buffer.should have_tag('input[@validation="validates_inclusion_of"]')
|
80
|
+
output_buffer.should have_tag(%\input[@validation_in='["one","two","three"]']\)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should serialise range as array" do
|
84
|
+
@bob.class.should_receive(:reflect_on_validations_for).with(:name).any_number_of_times.and_return([mock('MacroReflection', :macro => :validates_inclusion_of, :name => :name, :options => {:in => 1..3})])
|
85
|
+
semantic_form_for(@bob) do |builder|
|
86
|
+
concat(builder.input(:name))
|
87
|
+
end
|
88
|
+
output_buffer.should have_tag('input[@validation="validates_inclusion_of"]')
|
89
|
+
output_buffer.should have_tag(%\input[@validation_in='[1,2,3]']\)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'formtastic_validation'
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
gem 'activesupport', '2.3.8'
|
7
|
+
gem 'actionpack', '2.3.8'
|
8
|
+
require 'active_support'
|
9
|
+
require 'active_record'
|
10
|
+
require 'action_pack'
|
11
|
+
require 'action_view'
|
12
|
+
require 'action_controller'
|
13
|
+
|
14
|
+
gem 'rspec', '>= 1.3.0'
|
15
|
+
gem 'rspec-rails', '>= 1.3.2'
|
16
|
+
gem 'nokogiri', '>= 1.4.2'
|
17
|
+
gem 'rspec_tag_matchers', '>= 1.0.0'
|
18
|
+
gem 'validation_reflection', '>=0.3.8'
|
19
|
+
gem 'formtastic', '>=1.0.0'
|
20
|
+
require 'rspec_tag_matchers'
|
21
|
+
require 'validation_reflection'
|
22
|
+
require 'nokogiri'
|
23
|
+
require 'rspec_tag_matchers'
|
24
|
+
require 'formtastic'
|
25
|
+
|
26
|
+
Spec::Runner.configure do |config|
|
27
|
+
config.include(RspecTagMatchers)
|
28
|
+
end
|
29
|
+
|
30
|
+
module FormtasticValidationSpecHelper
|
31
|
+
include ActionView::Helpers::FormHelper
|
32
|
+
include ActionView::Helpers::FormTagHelper
|
33
|
+
include ActionView::Helpers::FormOptionsHelper
|
34
|
+
include ActionView::Helpers::UrlHelper
|
35
|
+
include ActionView::Helpers::TagHelper
|
36
|
+
include ActionView::Helpers::TextHelper
|
37
|
+
include ActionView::Helpers::ActiveRecordHelper
|
38
|
+
include ActionView::Helpers::RecordIdentificationHelper
|
39
|
+
include ActionView::Helpers::DateHelper
|
40
|
+
include ActionView::Helpers::CaptureHelper
|
41
|
+
include ActionView::Helpers::AssetTagHelper
|
42
|
+
include ActiveSupport
|
43
|
+
include ActionController::PolymorphicRoutes
|
44
|
+
|
45
|
+
include Formtastic::SemanticFormHelper
|
46
|
+
Formtastic::SemanticFormBuilder.class_eval { include FormtasticValidation }
|
47
|
+
|
48
|
+
class ::User ; end
|
49
|
+
|
50
|
+
def setup_mocks
|
51
|
+
def user_path(o); "/users/1"; end
|
52
|
+
def users_path; "/users"; end
|
53
|
+
def new_user_path; "/users/new"; end
|
54
|
+
|
55
|
+
@bob = User.new
|
56
|
+
@bob.stub!(:name).and_return('Bob')
|
57
|
+
@bob.stub!(:email).and_return('bob@mail.com')
|
58
|
+
@bob.stub!(:mobile).and_return('123456')
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.included(base)
|
62
|
+
base.class_eval do
|
63
|
+
attr_accessor :output_buffer
|
64
|
+
|
65
|
+
def protect_against_forgery?
|
66
|
+
false
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
metadata
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: formtastic_validation
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Wong Liang Zan
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-08-31 00:00:00 +08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: validation_reflection
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - "="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
- 3
|
33
|
+
- 8
|
34
|
+
version: 0.3.8
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
37
|
+
- !ruby/object:Gem::Dependency
|
38
|
+
name: formtastic
|
39
|
+
prerelease: false
|
40
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - "="
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
hash: 23
|
46
|
+
segments:
|
47
|
+
- 1
|
48
|
+
- 0
|
49
|
+
- 0
|
50
|
+
version: 1.0.0
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: rspec
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - "="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
hash: 27
|
62
|
+
segments:
|
63
|
+
- 1
|
64
|
+
- 3
|
65
|
+
- 0
|
66
|
+
version: 1.3.0
|
67
|
+
type: :development
|
68
|
+
version_requirements: *id003
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-rails
|
71
|
+
prerelease: false
|
72
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - "="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
hash: 31
|
78
|
+
segments:
|
79
|
+
- 1
|
80
|
+
- 3
|
81
|
+
- 2
|
82
|
+
version: 1.3.2
|
83
|
+
type: :development
|
84
|
+
version_requirements: *id004
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: rspec_tag_matchers
|
87
|
+
prerelease: false
|
88
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - "="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
hash: 23
|
94
|
+
segments:
|
95
|
+
- 1
|
96
|
+
- 0
|
97
|
+
- 0
|
98
|
+
version: 1.0.0
|
99
|
+
type: :development
|
100
|
+
version_requirements: *id005
|
101
|
+
- !ruby/object:Gem::Dependency
|
102
|
+
name: nokogiri
|
103
|
+
prerelease: false
|
104
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - "="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
hash: 3
|
110
|
+
segments:
|
111
|
+
- 1
|
112
|
+
- 4
|
113
|
+
- 2
|
114
|
+
version: 1.4.2
|
115
|
+
type: :development
|
116
|
+
version_requirements: *id006
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: activesupport
|
119
|
+
prerelease: false
|
120
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - "="
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
hash: 19
|
126
|
+
segments:
|
127
|
+
- 2
|
128
|
+
- 3
|
129
|
+
- 8
|
130
|
+
version: 2.3.8
|
131
|
+
type: :development
|
132
|
+
version_requirements: *id007
|
133
|
+
- !ruby/object:Gem::Dependency
|
134
|
+
name: actionpack
|
135
|
+
prerelease: false
|
136
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - "="
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
hash: 19
|
142
|
+
segments:
|
143
|
+
- 2
|
144
|
+
- 3
|
145
|
+
- 8
|
146
|
+
version: 2.3.8
|
147
|
+
type: :development
|
148
|
+
version_requirements: *id008
|
149
|
+
description: A formtastic extension that does client side validation
|
150
|
+
email: zan@liangzan.net
|
151
|
+
executables: []
|
152
|
+
|
153
|
+
extensions: []
|
154
|
+
|
155
|
+
extra_rdoc_files:
|
156
|
+
- LICENSE
|
157
|
+
- README.textile
|
158
|
+
files:
|
159
|
+
- .document
|
160
|
+
- .gitignore
|
161
|
+
- LICENSE
|
162
|
+
- README.textile
|
163
|
+
- Rakefile
|
164
|
+
- VERSION
|
165
|
+
- formtastic_validation.gemspec
|
166
|
+
- javascripts/README.textile
|
167
|
+
- javascripts/build/formtastic_validation-0.1.0.min.js
|
168
|
+
- javascripts/lib/livevalidation-1.3.min.js
|
169
|
+
- javascripts/src/formtastic_validation.js
|
170
|
+
- lib/formtastic_validation.rb
|
171
|
+
- rails_generators/formtastic_validation/formtastic_validation_generator.rb
|
172
|
+
- rails_generators/formtastic_validation/templates/config/initializers/formtastic_validation.rb
|
173
|
+
- rails_generators/formtastic_validation/templates/public/javascripts/formtastic_validation-0.1.0.min.js
|
174
|
+
- rails_generators/formtastic_validation/templates/public/javascripts/livevalidation-1.3.min.js
|
175
|
+
- spec/formtastic_validation_spec.rb
|
176
|
+
- spec/spec.opts
|
177
|
+
- spec/spec_helper.rb
|
178
|
+
has_rdoc: true
|
179
|
+
homepage: http://github.com/liangzan/formtastic_validation
|
180
|
+
licenses: []
|
181
|
+
|
182
|
+
post_install_message:
|
183
|
+
rdoc_options:
|
184
|
+
- --charset=UTF-8
|
185
|
+
require_paths:
|
186
|
+
- lib
|
187
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
188
|
+
none: false
|
189
|
+
requirements:
|
190
|
+
- - ">="
|
191
|
+
- !ruby/object:Gem::Version
|
192
|
+
hash: 3
|
193
|
+
segments:
|
194
|
+
- 0
|
195
|
+
version: "0"
|
196
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
197
|
+
none: false
|
198
|
+
requirements:
|
199
|
+
- - ">="
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
hash: 3
|
202
|
+
segments:
|
203
|
+
- 0
|
204
|
+
version: "0"
|
205
|
+
requirements: []
|
206
|
+
|
207
|
+
rubyforge_project:
|
208
|
+
rubygems_version: 1.3.7
|
209
|
+
signing_key:
|
210
|
+
specification_version: 3
|
211
|
+
summary: Client side validation with formtastic
|
212
|
+
test_files:
|
213
|
+
- spec/formtastic_validation_spec.rb
|
214
|
+
- spec/spec_helper.rb
|