validatious-on-rails 0.3.9 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +1 -1
- data/app/controllers/validates_controller.rb +4 -0
- data/config/routes.rb +5 -0
- data/generators/validatious/templates/XMLHttpRequest.js +1 -0
- data/generators/validatious/templates/{validatious.config.js → v2.config.js} +0 -0
- data/generators/validatious/templates/v2.rails.js +102 -0
- data/generators/validatious/validatious_generator.rb +5 -3
- data/lib/validatious-on-rails/controller.rb +56 -0
- data/lib/validatious-on-rails/helpers.rb +2 -0
- data/lib/validatious-on-rails/model_validations.rb +2 -1
- data/lib/validatious-on-rails/validatious/client_side_validator.rb +8 -2
- data/lib/validatious-on-rails/validatious/remote_validator.rb +64 -13
- data/lib/validatious-on-rails/validatious/validator.rb +12 -34
- data/lib/validatious-on-rails/validatious/validators/format_validator.rb +1 -1
- data/lib/validatious-on-rails/validatious/validators/invalid_validator.rb +19 -0
- data/lib/validatious-on-rails/validatious/validators/remote_client_validator.rb +19 -0
- data/lib/validatious-on-rails/validatious/validators/uniqueness_validator.rb +23 -0
- data/lib/validatious-on-rails.rb +4 -2
- data/test/test_helper.rb +8 -0
- data/test/validatious_on_rails/controller_test.rb +97 -0
- data/test/validatious_on_rails/helpers_test.rb +3 -3
- data/test/validatious_on_rails/validatious/validator_test.rb +2 -2
- metadata +23 -4
data/Rakefile
CHANGED
@@ -24,7 +24,7 @@ begin
|
|
24
24
|
|
25
25
|
gemspec.require_paths = %w{lib}
|
26
26
|
gemspec.files = SUPPORT_FILES << %w(Rakefile) <<
|
27
|
-
Dir.glob(File.join('{generators,lib,test,rails}', '**', '*').to_s).reject { |v| v =~ /\.log/i }
|
27
|
+
Dir.glob(File.join('{app,config,generators,lib,test,rails}', '**', '*').to_s).reject { |v| v =~ /\.log/i }
|
28
28
|
gemspec.executables = %w()
|
29
29
|
gemspec.extra_rdoc_files = SUPPORT_FILES
|
30
30
|
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
(function(m,u,n,g,e,d){for(g=u[d[32]]-1;g>=0;g--)n+=e[d[65]][d[70]](u[d[71]](g)-1);u=n[d[69]](' ');for(g=u[d[32]]-1;g>=0;g--)m=m[d[68]](e[d[67]](g%10+(e[d[65]][d[70]](122-e[d[66]][d[72]](g/10))),'g'),u[g]);e[d[3]]('_',m)(d)})("(9z 2w{8y s=6x8x129x;8y b=6w6x8x229x,c=6x8x259x8x169x3w!6x8x439x;9z e2w{5x.a5=s?2y s:2y 6x8x09x(_[7]);5x.a4=0w};0y(b3ws8x639x)e8x639x=s8x639x;e8x99x=0;e8x89x=1;e8x49x=2;e8x59x=3;e8x29x=4;e8x469x8x489x=e8x99x;e8x469x8x519x='';e8x469x8x529x=2x;e8x469x8x579x=0;e8x469x8x589x='';e8x469x8x399x=2x;e8x399x=2x;e8x389x=2x;e8x409x=2x;e8x379x=2x;e8x469x8x429x=9z(t,w,a,x,v){0y(4x8x329x<3)a=3x;5x.a2=a;8y r=5x,m=5x8x489x;0y(c){8y i=9z2w{0y(r.a58x489x7we8x29x){f(r);r8x149x2w}};0y(a)6x8x199x(_[41],i)}5x.a58x399x=9z2w{0y(b3w!a)3y;r8x489x=r.a58x489x;k(r);0y(r.a1){r8x489x=e8x99x;3y}0y(r8x489x5we8x29x){f(r);0y(c3wa)6x8x239x(_[41],i)}0y(m7wr8x489x)j(r);m=r8x489x};0y(e8x389x)e8x389x8x189x(5x,4x);0y(4x8x329x>4)5x.a58x429x(t,w,a,x,v);7z 0y(4x8x329x>3)5x.a58x429x(t,w,a,x);7z 5x.a58x429x(t,w,a);0y(!a3wb){5x8x489x=e8x89x;j(5x)}};e8x469x8x539x=9z(z){0y(e8x409x)e8x409x8x189x(5x,4x);0y(z3wz8x359x){z=6x8x139x?2y 6x8x139x2w8x549x(z):z8x649x;0y(!5x.a38x19x)5x.a58x559x(_[1],_[17])}5x.a58x539x(z);0y(b3w!5x.a2){5x8x489x=e8x89x;k(5x);9y(5x8x489x<e8x29x){5x8x489x0v;j(5x);0y(5x.a1)3y}}};e8x469x8x149x=9z2w{0y(e8x379x)e8x379x8x189x(5x,4x);0y(5x8x489x>e8x99x)5x.a1=3x;5x.a58x149x2w;f(5x)};e8x469x8x279x=9z2w{3y 5x.a58x279x2w};e8x469x8x289x=9z(u){3y 5x.a58x289x(u)};e8x469x8x559x=9z(u,y){0y(!5x.a3)5x.a3=1w;5x.a3[u]=y;3y 5x.a58x559x(u,y)};e8x469x8x159x=9z(u,h,d){8z(8y l=0,q;q=5x.a4[l];l0v)0y(q[0]5wu3wq[1]5wh3wq[2]5wd)3y;5x.a48x479x([u,h,d])};e8x469x8x509x=9z(u,h,d){8z(8y l=0,q;q=5x.a4[l];l0v)0y(q[0]5wu3wq[1]5wh3wq[2]5wd)1z;0y(q)5x.a48x569x(l,1)};e8x469x8x249x=9z(p){8y p={'type':p8x629x,'target':5x,'currentTarget':5x,'eventPhase':2,'bubbles':p8x209x,'cancelable':p8x219x,'timeStamp':p8x609x,'stopPropagation':9z2w1w,'preventDefault':9z2w1w,'0zitEvent':9z2w1w};0y(p8x629x5w_[49]3w5x8x399x)(5x8x399x8x299x4w5x8x399x)8x189x(5x,[p]);8z(8y l=0,q;q=5x.a4[l];l0v)0y(q[0]5wp8x629x3w!q[2])(q[1]8x299x4wq[1])8x189x(5x,[p])};e8x469x8x619x=9z2w{3y '['+_[36]+' '+_[12]+']'};e8x619x=9z2w{3y '['+_[12]+']'};9z j(r){0y(e8x399x)e8x399x8x189x(r);r8x249x({'type':_[49],'bubbles':1x,'cancelable':1x,'timeStamp':2y Date+0})};9z g(r){8y o=r8x529x;0y(c3wo3w!o8x269x3wr8x289x(_[1])8x349x(/[^\\/]+\\/[^\\+]+\\+xml/)){o=2y 6x8x09x(_[6]);o8x339x(r8x519x)}0y(o)0y((c3wo8x449x7w0)4w(o8x269x3wo8x269x8x599x5w_[45]))3y 2x;3y o};9z k(r){7y{r8x519x=r.a58x519x}3z(e)1w7y{r8x529x=g(r.a5)}3z(e)1w7y{r8x579x=r.a58x579x}3z(e)1w7y{r8x589x=r.a58x589x}3z(e)1w};9z f(r){r.a58x399x=2y 6x8x39x;6z r.a3};0y(!6x8x39x8x469x8x189x){6x8x39x8x469x8x189x=9z(r,n){0y(!n)n=0w;r.a0=5x;r.a0(n[0],n[1],n[2],n[3],n[4]);6z r.a0}};6x8x129x=e})2w;",">?!>=!..!,,!>.!>,!>\"!\"\"!>>!}}!\'\'!*)!~|!^\\!^^!\\`\\!uofnvdpe!xpeojx!tjiu!tuofnvhsb!fvsu!mmvo!ftmbg!iujx!fmjix!sbw!zsu!idujxt!gpfqzu!xpsiu!osvufs!xfo!gpfdobutoj!gj!opjudovg!spg!ftmf!fufmfe!umvbgfe!fvojuopd!idubd!ftbd!lbfsc!oj",'',0,this,'ActiveXObject Content-Type DONE Function HEADERS_RECEIVED LOADING Microsoft.XMLDOM Microsoft.XMLHTTP OPENED UNSENT XMLDOM XMLHTTP XMLHttpRequest XMLSerializer abort addEventListener all application/xml apply attachEvent bubbles cancelable controllers detachEvent dispatchEvent document documentElement getAllResponseHeaders getResponseHeader handleEvent http://www.w3.org/XML/1998/namespace http://www.w3.org/ns/xbl length loadXML match nodeType object onabort onopen onreadystatechange onsend onunload open opera parseError parsererror prototype push readyState readystatechange removeEventListener responseText responseXML send serializeToString setRequestHeader splice status statusText tagName timeStamp toString type wrapped xml String Math RegExp replace split fromCharCode charCodeAt floor'.split(' '))
|
File without changes
|
@@ -0,0 +1,102 @@
|
|
1
|
+
/**
|
2
|
+
* Helpers for Validatious-On-Rails, mostly related to remote validations.
|
3
|
+
*/
|
4
|
+
|
5
|
+
if (typeof v2.Rails === 'undefined' || v2.Rails === null) {
|
6
|
+
v2.Rails = {};
|
7
|
+
}
|
8
|
+
|
9
|
+
/**
|
10
|
+
* Generic validator that acts as a client-side validator/helper for remote validator responses.
|
11
|
+
*/
|
12
|
+
v2.Validator.add({acceptEmpty: true, fn: function(field, value, params) { return !!params[0]; }, message: null, name: 'remote-client'});
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Perform a remote validation on a given field the Validatious way, slightly modified
|
16
|
+
*/
|
17
|
+
v2.Rails.performRemoteValidation = function performRemoteValidation(name, field, value, params, message) {
|
18
|
+
var field_element = field.__elements[0];
|
19
|
+
var url = v2.Rails.remoteValidationUrlFor(name, field_element, value, params);
|
20
|
+
|
21
|
+
var xmlHttpRequest = new XMLHttpRequest;
|
22
|
+
xmlHttpRequest.open('GET', url, true);
|
23
|
+
xmlHttpRequest.onreadystatechange = function() {
|
24
|
+
if (this.readyState == XMLHttpRequest.DONE) {
|
25
|
+
var validationResult = (this.responseText == 'true' || this.responseText == '1') ? true : false;
|
26
|
+
/* console.log('Validation result: ' + validationResult); */
|
27
|
+
|
28
|
+
/* Get all validators for this field, except the current validator. */
|
29
|
+
var fieldClasses = field_element.getAttribute('class').replace(new RegExp(name + '\w*'), '').replace(/^\s+|\s+$/g, '');
|
30
|
+
var theOtherValidators = v2.html.validatorsFromString(fieldClasses);
|
31
|
+
|
32
|
+
/* Make remote-client validator trigger validation failure or not. */
|
33
|
+
var thisValidator = field_element.id.is('remote-client', validationResult).explain(message);
|
34
|
+
v2.html.applyValidators(theOtherValidators, thisValidator);
|
35
|
+
thisValidator.item.validate();
|
36
|
+
};
|
37
|
+
};
|
38
|
+
xmlHttpRequest.send(null);
|
39
|
+
return true;
|
40
|
+
};
|
41
|
+
|
42
|
+
/**
|
43
|
+
* Generate a remote validation poll URL the validatious-on-rails-way,
|
44
|
+
* i.e. auto-detect required params from form builder generated DOM.
|
45
|
+
*
|
46
|
+
* Example:
|
47
|
+
* /validates/uniqueness_of?model=article&attribute=title(&id=2)&value=Lorem¶ms[0]=334¶ms[1]=hello&...
|
48
|
+
*/
|
49
|
+
v2.Rails.remoteValidationUrlFor = function remoteValidationUrlFor(name, field, value, params) {
|
50
|
+
var modelName = v2.Rails.modelNameByField(field);
|
51
|
+
var attributeName = v2.Rails.attributeNameByField(field);
|
52
|
+
var recordId = v2.Rails.recordIdByField(field);
|
53
|
+
var paramsString = new Array(params.length);
|
54
|
+
for (var i = 0; i < params.length; i++) {
|
55
|
+
paramsString[i] = 'params[' + i + ']=' + escape(params[i]);
|
56
|
+
}
|
57
|
+
var url = ['/validates/', name, '?',
|
58
|
+
[
|
59
|
+
['model', escape(modelName)].join('='),
|
60
|
+
['attribute', escape(attributeName)].join('='),
|
61
|
+
['id', recordId].join('='),
|
62
|
+
['value', escape(value)].join('='),
|
63
|
+
paramsString.join('&')
|
64
|
+
].join('&')
|
65
|
+
].join('').replace(/\&$/, '');
|
66
|
+
return url;
|
67
|
+
};
|
68
|
+
|
69
|
+
/**
|
70
|
+
* Get form containing a specified field.
|
71
|
+
*/
|
72
|
+
v2.Rails.formByField = function formByField(field) {
|
73
|
+
var parent = field.parentNode;
|
74
|
+
while((parent.tagName != 'FORM')) { parent = parent.parentNode; }
|
75
|
+
return parent;
|
76
|
+
};
|
77
|
+
|
78
|
+
/**
|
79
|
+
* form_element.id="edit_account_34" => 34
|
80
|
+
* form_element.id="new_account" => nil
|
81
|
+
*/
|
82
|
+
v2.Rails.recordIdByField = function recordIdByField(field) {
|
83
|
+
var form = v2.Rails.formByField(field);
|
84
|
+
var recordId = form.id.match(/(\d+)$/);
|
85
|
+
return (recordId && recordId.length > 1) ? +recordId[1] : null;
|
86
|
+
};
|
87
|
+
|
88
|
+
/**
|
89
|
+
* field_element.name="account[login]" => 'account'
|
90
|
+
*/
|
91
|
+
v2.Rails.modelNameByField = function modelNameByField(field) {
|
92
|
+
var modelName = field.name.match(/^(.*)\[.*\]$/);
|
93
|
+
return (modelName.length > 1) ? modelName[1] : '';
|
94
|
+
};
|
95
|
+
|
96
|
+
/**
|
97
|
+
* field_element.name="account[login]" => 'login'
|
98
|
+
*/
|
99
|
+
v2.Rails.attributeNameByField = function attributeNameByField(field) {
|
100
|
+
var attributeName = field.name.match(/^.*\[(.*)\]$/);
|
101
|
+
return (attributeName.length > 1) ? attributeName[1] : '';
|
102
|
+
};
|
@@ -4,9 +4,11 @@ class ValidatiousGenerator < Rails::Generator::Base
|
|
4
4
|
|
5
5
|
def manifest
|
6
6
|
record do |m|
|
7
|
-
m.template '
|
8
|
-
m.template '
|
9
|
-
m.template '
|
7
|
+
m.template 'initializer.rb', File.join(*%[config initializers validatious-on-rails.rb])
|
8
|
+
m.template 'XMLHttpRequest.js', File.join(*%[public javascripts XMLHttpRequest.js])
|
9
|
+
m.template 'v2.standalone.full.min.js', File.join(*%[public javascripts v2.standalone.full.min.js])
|
10
|
+
m.template 'v2.config.js', File.join(*%[public javascripts v2.config.js])
|
11
|
+
m.template 'v2.rails.js', File.join(*%[public javascripts v2.rails.js])
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module ValidatiousOnRails
|
4
|
+
class Controller < ::ActionController::Base
|
5
|
+
|
6
|
+
# To validate, poll request:
|
7
|
+
#
|
8
|
+
# /validates/uniqueness_of?model={MODEL_NAME}&attribute={ATTRIBUTE_NAME}&value={INPUT_VALUE}(&id=RECORD_ID)
|
9
|
+
#
|
10
|
+
# == Example:
|
11
|
+
#
|
12
|
+
# /validates/uniqueness_of?model=article&attribute=title&value=hello123(&id=1)
|
13
|
+
#
|
14
|
+
def method_missing(action, *args, &block)
|
15
|
+
::ValidatiousOnRails.log "Remote validation called: #{action.inspect}, with params: #{params.inspect}", :info
|
16
|
+
|
17
|
+
begin
|
18
|
+
record_klass = "::#{params[:model].to_s.classify}".constantize
|
19
|
+
rescue
|
20
|
+
raise RemoteValidationInvalid, "Not a valid model: #{params[:model].inspect}"
|
21
|
+
end
|
22
|
+
|
23
|
+
if record_klass.blank? || params[:attribute].nil? ||
|
24
|
+
record_klass.content_columns.collect { |c| c.name.to_sym }.include?(params[:attribute].to_sym) == false
|
25
|
+
raise RemoteValidationInvalid, "Not a valid attribute for #{record_klass.inspect}: #{params[:attribute].inspect}"
|
26
|
+
end
|
27
|
+
|
28
|
+
# Only check for method matching: validates_*.
|
29
|
+
if record_klass.present? && defined?(record_klass) &&
|
30
|
+
(record_klass.respond_to?(:"validates_#{action}") || record_klass.respond_to?(:"validates_#{action}_of"))
|
31
|
+
validator_klass_name = "#{action.to_s.gsub(/_of/, '')}_validator".classify
|
32
|
+
remote_validators = ::Object.subclasses_of(::ValidatiousOnRails::Validatious::RemoteValidator)
|
33
|
+
validator_klass = remote_validators.select { |v| v.to_s.split('::').last == validator_klass_name }.first
|
34
|
+
|
35
|
+
if validator_klass.present?
|
36
|
+
# Perform validation.
|
37
|
+
if record = ((params[:id].present? && params[:id] > 0) ? record_klass.find(params[:id]) : record_klass.new)
|
38
|
+
validation_result = validator_klass.perform_validation(record, params[:attribute].to_sym, params[:value], params)
|
39
|
+
::ValidatiousOnRails.log "#{validator_klass} validation result: #{validation_result.to_s.upcase}. #{record_klass}##{params[:attribute]} => #{params[:value].inspect}", :info
|
40
|
+
# validation_result = (validation_result ? 1 : 0)
|
41
|
+
return render(:text => validation_result, :status => 200)
|
42
|
+
else
|
43
|
+
raise RemoteValidationInvalid, "Invalid record ID for class #{record_klass.inspect}: #{params[:id]}. No such record found."
|
44
|
+
end
|
45
|
+
else
|
46
|
+
raise RemoteValidationInvalid, "No remote validator matching: #{validator_klass.inspect}."
|
47
|
+
end
|
48
|
+
else
|
49
|
+
raise RemoteValidationInvalid, "#{record_klass} don't respond to: #{"validates_#{action}".inspect}."
|
50
|
+
end
|
51
|
+
rescue RemoteValidationInvalid => e
|
52
|
+
return render(:text => false, :status => 405)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -283,7 +283,8 @@ module ValidatiousOnRails
|
|
283
283
|
# TODO: Implement using RemoteValidator.
|
284
284
|
#
|
285
285
|
def uniqueness_of(validation)
|
286
|
-
|
286
|
+
validators = []
|
287
|
+
validators << Validatious::UniquenessValidator.new(validation)
|
287
288
|
end
|
288
289
|
|
289
290
|
# Unknown validations - if no matching custom validator is found/registered.
|
@@ -4,11 +4,17 @@ require File.join(File.dirname(__FILE__), 'validator')
|
|
4
4
|
module ValidatiousOnRails
|
5
5
|
module Validatious
|
6
6
|
class ClientSideValidator < Validator
|
7
|
-
|
7
|
+
|
8
8
|
def initialize(name, *args)
|
9
9
|
super
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
|
+
# Override default Validator-fn, with default a ClientSideValidator-fn.
|
13
|
+
#
|
14
|
+
def fn
|
15
|
+
(@fn ||= "function(field, value, params) {return true;}").gsub(/[\n\t]/, '')
|
16
|
+
end
|
17
|
+
|
12
18
|
end
|
13
19
|
end
|
14
20
|
end
|
@@ -4,21 +4,72 @@ require File.join(File.dirname(__FILE__), 'validator')
|
|
4
4
|
module ValidatiousOnRails
|
5
5
|
module Validatious
|
6
6
|
class RemoteValidator < Validator
|
7
|
-
|
8
|
-
def initialize(
|
9
|
-
|
7
|
+
|
8
|
+
def initialize(validation, *args)
|
9
|
+
name = self.class.name.split('::').last.underscore.gsub(/_validator$/, '')
|
10
|
+
super name, *args
|
11
|
+
self.message = self.class.generate_message(validation)
|
10
12
|
end
|
11
|
-
|
12
|
-
#
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
16
|
-
# Perform AJAX-request to a specified/generated URL
|
17
|
-
# (e.g. /validatious/unique?model=article&attribute=...&value=...), with an attached
|
18
|
-
# callback-method that should trigger a client-side validation.
|
19
|
-
# Well, this is one possible approach...
|
13
|
+
|
14
|
+
# Override default Validator-fn, with default a RemoteValidator-fn.
|
15
|
+
#
|
16
|
+
# 1. Perform AJAX request (dependencies: validatious-on-rails.js, XMLHttpRequest.js).
|
17
|
+
# 2. Always return true, callback-function should perform the actual client side validation.
|
20
18
|
#
|
21
|
-
|
19
|
+
def fn
|
20
|
+
(@fn ||= %{
|
21
|
+
function(field, value, params) {
|
22
|
+
return !!v2.Rails.performRemoteValidation('#{self.name}', field, value, params, '#{self.message}');
|
23
|
+
}
|
24
|
+
}).gsub(/[\n\t]/, '')
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
|
29
|
+
# Perform the actual validation on the server-side.
|
30
|
+
# Requires an instance of the class to validate, the attribute that
|
31
|
+
# needs to be validated, and the current (input) value to validate.
|
32
|
+
#
|
33
|
+
# Base case: Return "true".
|
34
|
+
#
|
35
|
+
def perform_validation(record, attribute_name, value, params = {})
|
36
|
+
return true if record.blank?
|
37
|
+
record.send :"#{attribute_name}=", value
|
38
|
+
|
39
|
+
if record.valid?
|
40
|
+
ValidatiousOnRails.log "Validation: SUCCESS"
|
41
|
+
true
|
42
|
+
else
|
43
|
+
return true if record.errors[attribute_name.to_sym].blank?
|
44
|
+
|
45
|
+
# TODO: Refactor this when "the better" namin convention is used (see TODO).
|
46
|
+
validation_macro = ("validates_%s" % self.name.split('::').last.underscore.gsub(/_validator$/, ''))
|
47
|
+
validation = record.class.reflect_on_validations_for(attribute_name.to_sym).select { |v|
|
48
|
+
v.macro.to_s == validation_macro || v.macro.to_s == "#{validation_macro}_of"
|
49
|
+
}.first
|
50
|
+
return true if validation.blank?
|
51
|
+
|
52
|
+
# {{variable}} => .*
|
53
|
+
validation_error_message = self.new(validation).message.gsub(/\{\{.*\}\}/, '.*')
|
54
|
+
|
55
|
+
# Ugly, but probably the only way (?) to identify a certain error without open
|
56
|
+
# up rails core validation methods - not scalable.
|
57
|
+
is_invalid = record.errors[attribute_name.to_sym].any? do |error_message|
|
58
|
+
#ValidatiousOnRails.log error_message + " =~ " + /^#{validation_error_message}$/.inspect
|
59
|
+
error_message =~ /^#{validation_error_message}$/u
|
60
|
+
end
|
61
|
+
|
62
|
+
if is_invalid
|
63
|
+
ValidatiousOnRails.log "Validation: FAIL: " + record.errors[attribute_name.to_sym].to_s
|
64
|
+
false
|
65
|
+
else
|
66
|
+
true
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
22
73
|
end
|
23
74
|
end
|
24
75
|
end
|
@@ -15,13 +15,14 @@
|
|
15
15
|
# }
|
16
16
|
# });
|
17
17
|
#
|
18
|
+
# Reference: http://validatious.org/learn/features/core/custom_validators
|
19
|
+
#
|
18
20
|
module ValidatiousOnRails
|
19
21
|
module Validatious
|
20
|
-
class Validator
|
21
22
|
|
22
|
-
|
23
|
+
ValidatorError = ::Class.new(::StandardError) unless defined?(ValidatorError)
|
23
24
|
|
24
|
-
|
25
|
+
class Validator
|
25
26
|
|
26
27
|
attr_accessor :name
|
27
28
|
attr_writer :message,
|
@@ -30,7 +31,7 @@ module ValidatiousOnRails
|
|
30
31
|
:accept_empty,
|
31
32
|
:fn,
|
32
33
|
:args
|
33
|
-
|
34
|
+
|
34
35
|
def initialize(name, *args)
|
35
36
|
raise ValidatorError, "Parameter :name is required for an Validatious validator" unless name.present?
|
36
37
|
self.name = name
|
@@ -84,6 +85,7 @@ module ValidatiousOnRails
|
|
84
85
|
@aliases ||= []
|
85
86
|
end
|
86
87
|
|
88
|
+
|
87
89
|
# Decides if the validator should pass (return true) when the value is empty.
|
88
90
|
# This is usually a good idea because you can leave it up to the required validator
|
89
91
|
# to specifically check for emptiness. One benefit of this approach is more
|
@@ -124,10 +126,6 @@ module ValidatiousOnRails
|
|
124
126
|
end
|
125
127
|
end
|
126
128
|
|
127
|
-
def fn
|
128
|
-
(@fn ||= "function(field, value, params) {return true;}").gsub(/[\n\t]/, '')
|
129
|
-
end
|
130
|
-
|
131
129
|
def to_js
|
132
130
|
options = {
|
133
131
|
:name => self.name,
|
@@ -191,8 +189,6 @@ module ValidatiousOnRails
|
|
191
189
|
# Generate proper error message using explicit message, or I18n-lookup.
|
192
190
|
# Core validations gets treated by Rails - unless explicit message is set that is.
|
193
191
|
#
|
194
|
-
# NOTE: Might refactor this into a even more abstract class/module.
|
195
|
-
#
|
196
192
|
def generate_message(validation, *args)
|
197
193
|
options = args.extract_options!
|
198
194
|
explicit_message = validation.options[:message]
|
@@ -218,32 +214,14 @@ module ValidatiousOnRails
|
|
218
214
|
message.gsub(/\{\{/, '${').gsub(/\}\}/, '}')
|
219
215
|
end
|
220
216
|
|
221
|
-
# Rails core validation messages:
|
222
|
-
#
|
223
|
-
# messages:
|
224
|
-
# inclusion: "is not included in the list"
|
225
|
-
# exclusion: "is reserved"
|
226
|
-
# invalid: "is invalid"
|
227
|
-
# confirmation: "doesn't match confirmation"
|
228
|
-
# accepted: "must be accepted"
|
229
|
-
# empty: "can't be empty"
|
230
|
-
# blank: "can't be blank"
|
231
|
-
# too_long: "is too long (maximum is {{count}} characters)"
|
232
|
-
# too_short: "is too short (minimum is {{count}} characters)"
|
233
|
-
# wrong_length: "is the wrong length (should be {{count}} characters)"
|
234
|
-
# taken: "has already been taken"
|
235
|
-
# not_a_number: "is not a number"
|
236
|
-
# greater_than: "must be greater than {{count}}"
|
237
|
-
# greater_than_or_equal_to: "must be greater than or equal to {{count}}"
|
238
|
-
# equal_to: "must be equal to {{count}}"
|
239
|
-
# less_than: "must be less than {{count}}"
|
240
|
-
# less_than_or_equal_to: "must be less than or equal to {{count}}"
|
241
|
-
# odd: "must be odd"
|
242
|
-
# even: "must be even"
|
243
|
-
# record_invalid: "is invalid"
|
244
|
-
|
245
217
|
end
|
246
218
|
|
219
|
+
protected
|
220
|
+
|
221
|
+
def generic_name
|
222
|
+
self.class.name.split('::').last.underscore.gsub(/_validator$/, '')
|
223
|
+
end
|
224
|
+
|
247
225
|
end
|
248
226
|
end
|
249
227
|
end
|
@@ -19,7 +19,7 @@ module ValidatiousOnRails
|
|
19
19
|
def initialize(validation, options = {})
|
20
20
|
name, alias_name = self.class.generate_name(validation, :with, self.class.generate_id(validation.options[:with].inspect))
|
21
21
|
super name, options
|
22
|
-
self.message = self.class.generate_message(validation)
|
22
|
+
self.message = self.class.generate_message(validation, :key => :invalid)
|
23
23
|
self.accept_empty = validation.options[:allow_nil]
|
24
24
|
self.fn = %{
|
25
25
|
#{self.class.validate_blank(validation)}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. validator]))
|
3
|
+
|
4
|
+
module ValidatiousOnRails
|
5
|
+
module Validatious
|
6
|
+
class RemoteClientValidator < ClientSideValidator
|
7
|
+
|
8
|
+
def initialize(validation, options = {})
|
9
|
+
super 'remote-client', options
|
10
|
+
self.message = self.class.generate_message(validation)
|
11
|
+
self.accept_empty = false
|
12
|
+
self.fn = %{
|
13
|
+
return !!params[0];
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. validator]))
|
3
|
+
|
4
|
+
module ValidatiousOnRails
|
5
|
+
module Validatious
|
6
|
+
class RemoteClientValidator < ClientSideValidator
|
7
|
+
|
8
|
+
def initialize(validation, options = {})
|
9
|
+
super 'remote-client', options
|
10
|
+
self.message = self.class.generate_message(validation)
|
11
|
+
self.accept_empty = false
|
12
|
+
self.fn = %{
|
13
|
+
return !!params[0];
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. validator]))
|
3
|
+
|
4
|
+
module ValidatiousOnRails
|
5
|
+
module Validatious
|
6
|
+
class UniquenessValidator < RemoteValidator
|
7
|
+
|
8
|
+
def initialize(validation, options = {})
|
9
|
+
super
|
10
|
+
self.accept_empty = false
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
def generate_message(validation)
|
16
|
+
super(validation, :key => :taken)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/validatious-on-rails.rb
CHANGED
@@ -3,6 +3,7 @@ require File.join(File.dirname(__FILE__), *%w[validatious-on-rails validatious])
|
|
3
3
|
require File.join(File.dirname(__FILE__), *%w[validatious-on-rails model_validations])
|
4
4
|
require File.join(File.dirname(__FILE__), *%w[validatious-on-rails rails])
|
5
5
|
require File.join(File.dirname(__FILE__), *%w[validatious-on-rails helpers])
|
6
|
+
require File.join(File.dirname(__FILE__), *%w[validatious-on-rails controller])
|
6
7
|
|
7
8
|
module ValidatiousOnRails # :nodoc:
|
8
9
|
|
@@ -12,10 +13,11 @@ module ValidatiousOnRails # :nodoc:
|
|
12
13
|
#
|
13
14
|
class ValidatiousOnRailsError < ::StandardError
|
14
15
|
def initialize(message)
|
15
|
-
::
|
16
|
+
::ValidatiousOnRails.log message, :error
|
16
17
|
super message
|
17
18
|
end
|
18
19
|
end
|
20
|
+
RemoteValidationInvalid = ::Class.new(::ValidatiousOnRails::ValidatiousOnRailsError)
|
19
21
|
|
20
22
|
@@verbose = ::Object.const_defined?(:RAILS_ENV) ? (::RAILS_ENV.to_sym == :development) : true
|
21
23
|
@@client_side_validations_by_default = true
|
@@ -29,7 +31,7 @@ module ValidatiousOnRails # :nodoc:
|
|
29
31
|
return unless @@verbose
|
30
32
|
level = :info if level.blank?
|
31
33
|
@@logger ||= ::Logger.new(::STDOUT)
|
32
|
-
@@logger.send(level.to_sym, message)
|
34
|
+
@@logger.send(level.to_sym, "[validatious-on-rails:] #{level.to_s.upcase} #{message}")
|
33
35
|
end
|
34
36
|
|
35
37
|
end
|
data/test/test_helper.rb
CHANGED
@@ -33,6 +33,9 @@ extend RR::Adapters::RRMethods
|
|
33
33
|
|
34
34
|
require 'validatious-on-rails'
|
35
35
|
|
36
|
+
require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. app controllers validates_controller]))
|
37
|
+
require File.expand_path(File.join(File.dirname(__FILE__), *%w[.. config routes]))
|
38
|
+
|
36
39
|
# TODO: Should extend Rails validators with this - to test respond_to.
|
37
40
|
module ActiveRecord
|
38
41
|
module Validations
|
@@ -64,6 +67,7 @@ build_model :bogus_items do
|
|
64
67
|
string :field_without_client_side_validations
|
65
68
|
|
66
69
|
validates_presence_of :name, :body, :variant, :file_path, :dummie
|
70
|
+
validates_uniqueness_of :name
|
67
71
|
validates_confirmation_of :name
|
68
72
|
validates_acceptance_of :signed, :accept => true
|
69
73
|
validates_format_of :url,
|
@@ -86,6 +90,10 @@ end
|
|
86
90
|
#
|
87
91
|
RAILS_ROOT = File.join(File.dirname(__FILE__)) unless defined?(RAILS_ROOT)
|
88
92
|
|
93
|
+
# To show debug messages in test output, set this to true.
|
94
|
+
#
|
95
|
+
::ValidatiousOnRails.verbose = false
|
96
|
+
|
89
97
|
#
|
90
98
|
# Log file for testing only.
|
91
99
|
#
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require File.expand_path(File.join(File.dirname(__FILE__), *%w(.. test_helper)))
|
3
|
+
|
4
|
+
require 'action_controller/test_case'
|
5
|
+
|
6
|
+
class ControllerTest < ::ActionController::TestCase
|
7
|
+
|
8
|
+
# /validates/uniqueness_of?model={MODEL_NAME}&attribute={ATTRIBUTE_NAME}&value={INPUT_VALUE}(&id=RECORD_ID)
|
9
|
+
|
10
|
+
before do
|
11
|
+
@controller = ::ValidatesController.new
|
12
|
+
@bogus_item = ::BogusItem.new
|
13
|
+
end
|
14
|
+
|
15
|
+
context "routes" do
|
16
|
+
test "remote validations route" do
|
17
|
+
assert_routing 'validates/craziness_of', :controller => 'validates', :action => 'craziness_of'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context "remote validations" do
|
22
|
+
context "invalid validation" do
|
23
|
+
test "without any params - should fail" do
|
24
|
+
get :bananas
|
25
|
+
assert_response 405
|
26
|
+
assert_equal 'false', @response.body
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
context "valid validation" do
|
31
|
+
test "without any params - should fail" do
|
32
|
+
get :uniqueness_of
|
33
|
+
assert_response 405
|
34
|
+
assert_equal 'false', @response.body
|
35
|
+
end
|
36
|
+
|
37
|
+
test "with params: :model - should fail" do
|
38
|
+
get :uniqueness_of, :model => 'bogus_item'
|
39
|
+
assert_response 405
|
40
|
+
assert_equal 'false', @response.body
|
41
|
+
end
|
42
|
+
|
43
|
+
context "with params: :model, :attribute" do
|
44
|
+
test "any invalid - should fail" do
|
45
|
+
get :uniqueness_of, :model => 'duck', :attribute => 'name'
|
46
|
+
assert_response 405
|
47
|
+
assert_equal 'false', @response.body
|
48
|
+
|
49
|
+
get :uniqueness_of, :model => 'bogus_item', :attribute => 'jedi'
|
50
|
+
assert_response 405
|
51
|
+
assert_equal 'false', @response.body
|
52
|
+
end
|
53
|
+
|
54
|
+
context "all valid" do
|
55
|
+
test "invalid validation methods/actions - should fail" do
|
56
|
+
get :bananas, :model => 'bogus_item', :attribute => 'name'
|
57
|
+
assert_response 405
|
58
|
+
assert_equal 'false', @response.body
|
59
|
+
end
|
60
|
+
|
61
|
+
context "without :value" do
|
62
|
+
# test ":value is not allowed to be blank - should fail" do
|
63
|
+
# get :uniqueness_of, :model => 'bogus_item', :attribute => 'name'
|
64
|
+
# assert_response 405
|
65
|
+
# assert_equal 'false', @response.body
|
66
|
+
# end
|
67
|
+
|
68
|
+
# test ":value is allowed to be blank - should succeed" do
|
69
|
+
# get :uniqueness_of, :model => 'bogus_item', :attribute => 'name'
|
70
|
+
# assert_response 200
|
71
|
+
# assert_equal 'true', @response.body
|
72
|
+
# end
|
73
|
+
end
|
74
|
+
|
75
|
+
context "with :value" do
|
76
|
+
# FIXME: Fails, but why? Works "in practice".
|
77
|
+
test "invalid value - should fail" do
|
78
|
+
@existing_bogus_item = ::BogusItem.new(:name => 'carrot')
|
79
|
+
@existing_bogus_item.save(false)
|
80
|
+
|
81
|
+
get :uniqueness_of, :model => 'bogus_item', :attribute => 'name', :value => 'carrot'
|
82
|
+
assert_response 200
|
83
|
+
assert_equal 'false', @response.body
|
84
|
+
end
|
85
|
+
|
86
|
+
test "valid :value - should succeed" do
|
87
|
+
get :uniqueness_of, :model => 'bogus_item', :attribute => 'name', :value => 'unique carrot'
|
88
|
+
assert_response 200
|
89
|
+
assert_equal 'true', @response.body
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -28,9 +28,9 @@ class HelpersTest < ::ActionView::TestCase
|
|
28
28
|
concat javascript_for_validatious
|
29
29
|
|
30
30
|
# In parts...
|
31
|
-
assert_match /<script.+>.*v2.Validator.*<\/script
|
32
|
-
assert_match /<script.*id="custom_validators"
|
33
|
-
assert_match /<script.*type="text\/javascript"
|
31
|
+
assert_match /<script.+>.*v2.Validator.*<\/script>/m, output_buffer
|
32
|
+
assert_match /<script.*id="custom_validators".*>/m, output_buffer
|
33
|
+
assert_match /<script.*type="text\/javascript".*>/m, output_buffer
|
34
34
|
end
|
35
35
|
|
36
36
|
end
|
@@ -6,8 +6,8 @@ require 'active_support/test_case'
|
|
6
6
|
class ValidatorTest < ::ActiveSupport::TestCase
|
7
7
|
|
8
8
|
def setup
|
9
|
-
@empty_validator = ValidatiousOnRails::Validatious::
|
10
|
-
@custom_validator = returning ValidatiousOnRails::Validatious::
|
9
|
+
@empty_validator = ValidatiousOnRails::Validatious::ClientSideValidator.new('dummie')
|
10
|
+
@custom_validator = returning ValidatiousOnRails::Validatious::ClientSideValidator.new('dummie') do |v|
|
11
11
|
v.message = 'Fail, fail, fail!'
|
12
12
|
v.params = ['some', 'params']
|
13
13
|
v.aliases = ['some', 'aliases']
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: validatious-on-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Grimfelt
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2009-10-
|
13
|
+
date: 2009-10-20 00:00:00 +02:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -22,11 +22,16 @@ extensions: []
|
|
22
22
|
|
23
23
|
extra_rdoc_files:
|
24
24
|
- Rakefile
|
25
|
+
- app/controllers/validates_controller.rb
|
26
|
+
- config/routes.rb
|
27
|
+
- generators/validatious/templates/XMLHttpRequest.js
|
25
28
|
- generators/validatious/templates/initializer.rb
|
29
|
+
- generators/validatious/templates/v2.config.js
|
30
|
+
- generators/validatious/templates/v2.rails.js
|
26
31
|
- generators/validatious/templates/v2.standalone.full.min.js
|
27
|
-
- generators/validatious/templates/validatious.config.js
|
28
32
|
- generators/validatious/validatious_generator.rb
|
29
33
|
- lib/validatious-on-rails.rb
|
34
|
+
- lib/validatious-on-rails/controller.rb
|
30
35
|
- lib/validatious-on-rails/helpers.rb
|
31
36
|
- lib/validatious-on-rails/model_validations.rb
|
32
37
|
- lib/validatious-on-rails/rails.rb
|
@@ -41,6 +46,7 @@ extra_rdoc_files:
|
|
41
46
|
- lib/validatious-on-rails/validatious/validators/exclusion_validator.rb
|
42
47
|
- lib/validatious-on-rails/validatious/validators/format_validator.rb
|
43
48
|
- lib/validatious-on-rails/validatious/validators/inclusion_validator.rb
|
49
|
+
- lib/validatious-on-rails/validatious/validators/invalid_validator.rb
|
44
50
|
- lib/validatious-on-rails/validatious/validators/length/is_validator.rb
|
45
51
|
- lib/validatious-on-rails/validatious/validators/length/maximum_validator.rb
|
46
52
|
- lib/validatious-on-rails/validatious/validators/length/minimum_validator.rb
|
@@ -53,8 +59,11 @@ extra_rdoc_files:
|
|
53
59
|
- lib/validatious-on-rails/validatious/validators/numericality/odd_validator.rb
|
54
60
|
- lib/validatious-on-rails/validatious/validators/numericality/only_integer_validator.rb
|
55
61
|
- lib/validatious-on-rails/validatious/validators/presence_validator.rb
|
62
|
+
- lib/validatious-on-rails/validatious/validators/remote_client_validator.rb
|
63
|
+
- lib/validatious-on-rails/validatious/validators/uniqueness_validator.rb
|
56
64
|
- rails/init.rb
|
57
65
|
- test/test_helper.rb
|
66
|
+
- test/validatious_on_rails/controller_test.rb
|
58
67
|
- test/validatious_on_rails/helpers_test.rb
|
59
68
|
- test/validatious_on_rails/model_validations_test.rb
|
60
69
|
- test/validatious_on_rails/rails/action_view_helpers_test.rb
|
@@ -62,11 +71,16 @@ extra_rdoc_files:
|
|
62
71
|
- test/validatious_on_rails_test.rb
|
63
72
|
files:
|
64
73
|
- Rakefile
|
74
|
+
- app/controllers/validates_controller.rb
|
75
|
+
- config/routes.rb
|
76
|
+
- generators/validatious/templates/XMLHttpRequest.js
|
65
77
|
- generators/validatious/templates/initializer.rb
|
78
|
+
- generators/validatious/templates/v2.config.js
|
79
|
+
- generators/validatious/templates/v2.rails.js
|
66
80
|
- generators/validatious/templates/v2.standalone.full.min.js
|
67
|
-
- generators/validatious/templates/validatious.config.js
|
68
81
|
- generators/validatious/validatious_generator.rb
|
69
82
|
- lib/validatious-on-rails.rb
|
83
|
+
- lib/validatious-on-rails/controller.rb
|
70
84
|
- lib/validatious-on-rails/helpers.rb
|
71
85
|
- lib/validatious-on-rails/model_validations.rb
|
72
86
|
- lib/validatious-on-rails/rails.rb
|
@@ -81,6 +95,7 @@ files:
|
|
81
95
|
- lib/validatious-on-rails/validatious/validators/exclusion_validator.rb
|
82
96
|
- lib/validatious-on-rails/validatious/validators/format_validator.rb
|
83
97
|
- lib/validatious-on-rails/validatious/validators/inclusion_validator.rb
|
98
|
+
- lib/validatious-on-rails/validatious/validators/invalid_validator.rb
|
84
99
|
- lib/validatious-on-rails/validatious/validators/length/is_validator.rb
|
85
100
|
- lib/validatious-on-rails/validatious/validators/length/maximum_validator.rb
|
86
101
|
- lib/validatious-on-rails/validatious/validators/length/minimum_validator.rb
|
@@ -93,8 +108,11 @@ files:
|
|
93
108
|
- lib/validatious-on-rails/validatious/validators/numericality/odd_validator.rb
|
94
109
|
- lib/validatious-on-rails/validatious/validators/numericality/only_integer_validator.rb
|
95
110
|
- lib/validatious-on-rails/validatious/validators/presence_validator.rb
|
111
|
+
- lib/validatious-on-rails/validatious/validators/remote_client_validator.rb
|
112
|
+
- lib/validatious-on-rails/validatious/validators/uniqueness_validator.rb
|
96
113
|
- rails/init.rb
|
97
114
|
- test/test_helper.rb
|
115
|
+
- test/validatious_on_rails/controller_test.rb
|
98
116
|
- test/validatious_on_rails/helpers_test.rb
|
99
117
|
- test/validatious_on_rails/model_validations_test.rb
|
100
118
|
- test/validatious_on_rails/rails/action_view_helpers_test.rb
|
@@ -130,6 +148,7 @@ specification_version: 3
|
|
130
148
|
summary: Rails plugin that maps model validations to class names on form elements to integrate with Validatious.
|
131
149
|
test_files:
|
132
150
|
- test/test_helper.rb
|
151
|
+
- test/validatious_on_rails/controller_test.rb
|
133
152
|
- test/validatious_on_rails/helpers_test.rb
|
134
153
|
- test/validatious_on_rails/model_validations_test.rb
|
135
154
|
- test/validatious_on_rails/rails/action_view_helpers_test.rb
|