client_side_validations 4.2.0 → 6.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +71 -0
- data/README.md +32 -25
- data/lib/client_side_validations/action_view/form_builder.rb +37 -49
- data/lib/client_side_validations/action_view/form_helper.rb +32 -26
- data/lib/client_side_validations/action_view.rb +1 -1
- data/lib/client_side_validations/active_model/conditionals.rb +41 -0
- data/lib/client_side_validations/active_model/format.rb +6 -12
- data/lib/client_side_validations/active_model/numericality.rb +5 -8
- data/lib/client_side_validations/active_model.rb +15 -23
- data/lib/client_side_validations/active_record/middleware.rb +22 -13
- data/lib/client_side_validations/active_record.rb +5 -6
- data/lib/client_side_validations/generators.rb +6 -2
- data/lib/client_side_validations/middleware.rb +5 -5
- data/lib/client_side_validations/version.rb +2 -1
- data/lib/generators/client_side_validations/copy_assets_generator.rb +15 -15
- data/lib/generators/client_side_validations/install_generator.rb +0 -2
- data/vendor/assets/javascripts/rails.validations.js +37 -29
- metadata +60 -69
- data/HISTORY.md +0 -46
|
@@ -6,16 +6,23 @@ module ClientSideValidations
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def self.unique?(klass, attribute, value, params)
|
|
9
|
-
klass
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
value
|
|
9
|
+
klass = find_topmost_superclass(klass)
|
|
10
|
+
connection = klass.connection
|
|
11
|
+
column = klass.columns_hash[attribute.to_s]
|
|
12
|
+
value = type_cast_value(connection, column, value)
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
sql = sql_statement(klass, connection, attribute, value, params)
|
|
15
|
+
relation = Arel::Nodes::SqlLiteral.new(sql)
|
|
15
16
|
|
|
17
|
+
klass.where(relation).empty?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.sql_statement(klass, connection, attribute, value, params)
|
|
16
21
|
sql = []
|
|
22
|
+
t = klass.arel_table
|
|
23
|
+
|
|
17
24
|
if params[:case_sensitive] == 'true'
|
|
18
|
-
sql << 'BINARY' if
|
|
25
|
+
sql << 'BINARY' if connection.adapter_name =~ /^mysql/i
|
|
19
26
|
sql << t[attribute].eq(value).to_sql
|
|
20
27
|
else
|
|
21
28
|
escaped_value = value.gsub(/[%_]/, '\\\\\0')
|
|
@@ -25,19 +32,21 @@ module ClientSideValidations
|
|
|
25
32
|
sql << "AND #{t[klass.primary_key].not_eq(params[:id]).to_sql}" if params[:id]
|
|
26
33
|
|
|
27
34
|
(params[:scope] || {}).each do |scope_attribute, scope_value|
|
|
28
|
-
scope_value = type_cast_value(klass.columns_hash[scope_attribute], scope_value)
|
|
35
|
+
scope_value = type_cast_value(connection, klass.columns_hash[scope_attribute], scope_value)
|
|
29
36
|
sql << "AND #{t[scope_attribute].eq(scope_value).to_sql}"
|
|
30
37
|
end
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
!klass.where(relation).exists?
|
|
39
|
+
sql.join ' '
|
|
34
40
|
end
|
|
35
41
|
|
|
36
|
-
def self.type_cast_value(column, value)
|
|
37
|
-
|
|
38
|
-
|
|
42
|
+
def self.type_cast_value(connection, column, value)
|
|
43
|
+
type = connection.lookup_cast_type_from_column(column)
|
|
44
|
+
value = type.deserialize(value)
|
|
45
|
+
|
|
46
|
+
if column.limit && value.is_a?(String)
|
|
47
|
+
value.mb_chars[0, column.limit]
|
|
39
48
|
else
|
|
40
|
-
|
|
49
|
+
value
|
|
41
50
|
end
|
|
42
51
|
end
|
|
43
52
|
|
|
@@ -2,11 +2,10 @@ require 'client_side_validations/active_model'
|
|
|
2
2
|
require 'client_side_validations/middleware'
|
|
3
3
|
require 'client_side_validations/active_record/middleware'
|
|
4
4
|
|
|
5
|
-
%w(uniqueness).each do |validator|
|
|
6
|
-
require "client_side_validations/active_record/#{validator}"
|
|
7
|
-
validator.capitalize!
|
|
8
|
-
ActiveRecord::Validations.const_get("#{validator}Validator").send :include, ClientSideValidations::ActiveRecord.const_get(validator)
|
|
9
|
-
end
|
|
10
|
-
|
|
11
5
|
ActiveRecord::Base.send(:include, ClientSideValidations::ActiveModel::Validations)
|
|
12
6
|
ClientSideValidations::Middleware::Uniqueness.register_orm(ClientSideValidations::ActiveRecord::Middleware)
|
|
7
|
+
|
|
8
|
+
%w(Uniqueness).each do |validator|
|
|
9
|
+
require "client_side_validations/active_record/#{validator.downcase}"
|
|
10
|
+
ActiveRecord::Validations.const_get("#{validator}Validator").send :include, ClientSideValidations::ActiveRecord.const_get(validator)
|
|
11
|
+
end
|
|
@@ -8,7 +8,7 @@ module ClientSideValidations
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def call(env)
|
|
11
|
-
matches = %r{\A/validators
|
|
11
|
+
matches = %r{\A/?#{ClientSideValidations::Config.root_path}/validators\/(\w+)\z}.match(env['PATH_INFO'])
|
|
12
12
|
if matches
|
|
13
13
|
process_request(matches.captures.first, env)
|
|
14
14
|
else
|
|
@@ -58,8 +58,8 @@ module ClientSideValidations
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
class Uniqueness < Base
|
|
61
|
-
IGNORE_PARAMS = %w(case_sensitive id scope)
|
|
62
|
-
|
|
61
|
+
IGNORE_PARAMS = %w(case_sensitive id scope).freeze
|
|
62
|
+
@@registered_orms = []
|
|
63
63
|
class NotValidatable < StandardError; end
|
|
64
64
|
|
|
65
65
|
def response
|
|
@@ -83,7 +83,7 @@ module ClientSideValidations
|
|
|
83
83
|
end
|
|
84
84
|
|
|
85
85
|
def self.registered_orms
|
|
86
|
-
|
|
86
|
+
@@registered_orms
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
def registered_orms
|
|
@@ -98,7 +98,7 @@ module ClientSideValidations
|
|
|
98
98
|
middleware_class = nil
|
|
99
99
|
|
|
100
100
|
unless Array.wrap(klass._validators[attribute.to_sym]).find { |v| v.kind == :uniqueness }
|
|
101
|
-
|
|
101
|
+
raise NotValidatable
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
registered_orms.each do |orm|
|
|
@@ -9,8 +9,6 @@ module ClientSideValidations
|
|
|
9
9
|
end
|
|
10
10
|
end
|
|
11
11
|
|
|
12
|
-
private
|
|
13
|
-
|
|
14
12
|
def self.asset_directory
|
|
15
13
|
if asset_pipeline_enabled?
|
|
16
14
|
"app#{Rails.configuration.assets.prefix}/javascripts"
|
|
@@ -19,16 +17,8 @@ module ClientSideValidations
|
|
|
19
17
|
end
|
|
20
18
|
end
|
|
21
19
|
|
|
22
|
-
def asset_directory
|
|
23
|
-
CopyAssetsGenerator.asset_directory
|
|
24
|
-
end
|
|
25
|
-
|
|
26
20
|
def self.assets
|
|
27
|
-
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
def assets
|
|
31
|
-
CopyAssetsGenerator.assets
|
|
21
|
+
ClientSideValidations::Generators.assets
|
|
32
22
|
end
|
|
33
23
|
|
|
34
24
|
def self.asset_file_names
|
|
@@ -40,15 +30,25 @@ module ClientSideValidations
|
|
|
40
30
|
defined?(Sprockets).present?
|
|
41
31
|
end
|
|
42
32
|
|
|
43
|
-
def asset_pipeline_enabled?
|
|
44
|
-
self.class.asset_pipeline_enabled?
|
|
45
|
-
end
|
|
46
|
-
|
|
47
33
|
def self.installation_message
|
|
48
34
|
"Copies #{asset_file_names} to #{asset_directory}"
|
|
49
35
|
end
|
|
50
36
|
|
|
51
37
|
desc installation_message
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def asset_directory
|
|
42
|
+
CopyAssetsGenerator.asset_directory
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def assets
|
|
46
|
+
CopyAssetsGenerator.assets
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def asset_pipeline_enabled?
|
|
50
|
+
self.class.asset_pipeline_enabled?
|
|
51
|
+
end
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
end
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
|
|
2
|
+
/*!
|
|
3
|
+
* Client Side Validations - v6.0.0 (https://github.com/DavyJonesLocker/client_side_validations)
|
|
4
|
+
* Copyright (c) 2017 Geremia Taglialatela, Brian Cardarella
|
|
5
|
+
* Licensed under MIT (http://opensource.org/licenses/mit-license.php)
|
|
6
|
+
*/
|
|
7
|
+
|
|
1
8
|
(function() {
|
|
2
9
|
var $, validateElement, validateForm, validatorsFor,
|
|
3
10
|
indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; };
|
|
@@ -49,7 +56,7 @@
|
|
|
49
56
|
for (validator_name in validators) {
|
|
50
57
|
validator = validators[validator_name];
|
|
51
58
|
if (validator_name.match("\\[" + captures[1] + "\\].*\\[\\]\\[" + captures[2] + "\\]$")) {
|
|
52
|
-
name = name.replace(/\[[\da-z_]+\]\[(\w+)\]$/g,
|
|
59
|
+
name = name.replace(/\[[\da-z_]+\]\[(\w+)\]$/g, '[][$1]');
|
|
53
60
|
}
|
|
54
61
|
}
|
|
55
62
|
}
|
|
@@ -111,7 +118,7 @@
|
|
|
111
118
|
};
|
|
112
119
|
if (element.attr('name').search(/\[([^\]]*?)\]$/) >= 0) {
|
|
113
120
|
destroyInputName = element.attr('name').replace(/\[([^\]]*?)\]$/, '[_destroy]');
|
|
114
|
-
if ($("input[name='" + destroyInputName + "']").val() ===
|
|
121
|
+
if ($("input[name='" + destroyInputName + "']").val() === '1') {
|
|
115
122
|
passElement();
|
|
116
123
|
return afterValidate();
|
|
117
124
|
}
|
|
@@ -184,25 +191,25 @@
|
|
|
184
191
|
'submit.ClientSideValidations': function(eventData) {
|
|
185
192
|
if (!$form.isValid(form.ClientSideValidations.settings.validators)) {
|
|
186
193
|
eventData.preventDefault();
|
|
187
|
-
|
|
194
|
+
eventData.stopImmediatePropagation();
|
|
188
195
|
}
|
|
189
196
|
},
|
|
190
197
|
'ajax:beforeSend.ClientSideValidations': function(eventData) {
|
|
191
198
|
if (eventData.target === this) {
|
|
192
|
-
|
|
199
|
+
$form.isValid(form.ClientSideValidations.settings.validators);
|
|
193
200
|
}
|
|
194
201
|
},
|
|
195
202
|
'form:validate:after.ClientSideValidations': function(eventData) {
|
|
196
|
-
|
|
203
|
+
ClientSideValidations.callbacks.form.after($form, eventData);
|
|
197
204
|
},
|
|
198
205
|
'form:validate:before.ClientSideValidations': function(eventData) {
|
|
199
|
-
|
|
206
|
+
ClientSideValidations.callbacks.form.before($form, eventData);
|
|
200
207
|
},
|
|
201
208
|
'form:validate:fail.ClientSideValidations': function(eventData) {
|
|
202
|
-
|
|
209
|
+
ClientSideValidations.callbacks.form.fail($form, eventData);
|
|
203
210
|
},
|
|
204
211
|
'form:validate:pass.ClientSideValidations': function(eventData) {
|
|
205
|
-
|
|
212
|
+
ClientSideValidations.callbacks.form.pass($form, eventData);
|
|
206
213
|
}
|
|
207
214
|
};
|
|
208
215
|
for (event in ref) {
|
|
@@ -220,28 +227,28 @@
|
|
|
220
227
|
$form = $(form);
|
|
221
228
|
ref = {
|
|
222
229
|
'focusout.ClientSideValidations': function() {
|
|
223
|
-
|
|
230
|
+
$(this).isValid(form.ClientSideValidations.settings.validators);
|
|
224
231
|
},
|
|
225
232
|
'change.ClientSideValidations': function() {
|
|
226
|
-
|
|
233
|
+
$(this).data('changed', true);
|
|
227
234
|
},
|
|
228
235
|
'element:validate:after.ClientSideValidations': function(eventData) {
|
|
229
|
-
|
|
236
|
+
ClientSideValidations.callbacks.element.after($(this), eventData);
|
|
230
237
|
},
|
|
231
238
|
'element:validate:before.ClientSideValidations': function(eventData) {
|
|
232
|
-
|
|
239
|
+
ClientSideValidations.callbacks.element.before($(this), eventData);
|
|
233
240
|
},
|
|
234
241
|
'element:validate:fail.ClientSideValidations': function(eventData, message) {
|
|
235
242
|
var element;
|
|
236
243
|
element = $(this);
|
|
237
|
-
|
|
244
|
+
ClientSideValidations.callbacks.element.fail(element, message, function() {
|
|
238
245
|
return form.ClientSideValidations.addError(element, message);
|
|
239
246
|
}, eventData);
|
|
240
247
|
},
|
|
241
248
|
'element:validate:pass.ClientSideValidations': function(eventData) {
|
|
242
249
|
var element;
|
|
243
250
|
element = $(this);
|
|
244
|
-
|
|
251
|
+
ClientSideValidations.callbacks.element.pass(element, function() {
|
|
245
252
|
return form.ClientSideValidations.removeError(element);
|
|
246
253
|
}, eventData);
|
|
247
254
|
}
|
|
@@ -253,7 +260,7 @@
|
|
|
253
260
|
}).on(event, binding);
|
|
254
261
|
}
|
|
255
262
|
$input.filter(':checkbox').on('change.ClientSideValidations', function() {
|
|
256
|
-
|
|
263
|
+
$(this).isValid(form.ClientSideValidations.settings.validators);
|
|
257
264
|
});
|
|
258
265
|
return $input.filter('[id$=_confirmation]').each(function() {
|
|
259
266
|
var confirmationElement, element, ref1, results;
|
|
@@ -262,10 +269,10 @@
|
|
|
262
269
|
if (element[0]) {
|
|
263
270
|
ref1 = {
|
|
264
271
|
'focusout.ClientSideValidations': function() {
|
|
265
|
-
|
|
272
|
+
element.data('changed', true).isValid(form.ClientSideValidations.settings.validators);
|
|
266
273
|
},
|
|
267
274
|
'keyup.ClientSideValidations': function() {
|
|
268
|
-
|
|
275
|
+
element.data('changed', true).isValid(form.ClientSideValidations.settings.validators);
|
|
269
276
|
}
|
|
270
277
|
};
|
|
271
278
|
results = [];
|
|
@@ -325,7 +332,7 @@
|
|
|
325
332
|
}
|
|
326
333
|
},
|
|
327
334
|
numericality: function(element, options) {
|
|
328
|
-
var CHECKS, check,
|
|
335
|
+
var CHECKS, check, checkValue, fn, form, operator, val;
|
|
329
336
|
val = $.trim(element.val());
|
|
330
337
|
if (!ClientSideValidations.patterns.numericality.test(val)) {
|
|
331
338
|
if (options.allow_blank === true && this.presence(element, {
|
|
@@ -335,7 +342,7 @@
|
|
|
335
342
|
}
|
|
336
343
|
return options.messages.numericality;
|
|
337
344
|
}
|
|
338
|
-
val = val.replace(new RegExp("\\" + ClientSideValidations.number_format.delimiter, 'g'),
|
|
345
|
+
val = val.replace(new RegExp("\\" + ClientSideValidations.number_format.delimiter, 'g'), '').replace(new RegExp("\\" + ClientSideValidations.number_format.separator, 'g'), '.');
|
|
339
346
|
if (options.only_integer && !/^[+-]?\d+$/.test(val)) {
|
|
340
347
|
return options.messages.only_integer;
|
|
341
348
|
}
|
|
@@ -352,14 +359,11 @@
|
|
|
352
359
|
if (!(options[check] != null)) {
|
|
353
360
|
continue;
|
|
354
361
|
}
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
} else if (form.find("[name*=" + options[check] + "]").size() === 1) {
|
|
358
|
-
check_value = form.find("[name*=" + options[check] + "]").val();
|
|
359
|
-
} else {
|
|
362
|
+
checkValue = !isNaN(parseFloat(options[check])) && isFinite(options[check]) ? options[check] : form.find("[name*=" + options[check] + "]").length === 1 ? form.find("[name*=" + options[check] + "]").val() : void 0;
|
|
363
|
+
if ((checkValue == null) || checkValue === '') {
|
|
360
364
|
return;
|
|
361
365
|
}
|
|
362
|
-
fn = new Function("return " + val + " " + operator + " " +
|
|
366
|
+
fn = new Function("return " + val + " " + operator + " " + checkValue);
|
|
363
367
|
if (!fn()) {
|
|
364
368
|
return options.messages[check];
|
|
365
369
|
}
|
|
@@ -465,7 +469,9 @@
|
|
|
465
469
|
}
|
|
466
470
|
},
|
|
467
471
|
confirmation: function(element, options) {
|
|
468
|
-
|
|
472
|
+
var regex;
|
|
473
|
+
regex = new RegExp("^" + (element.val()) + "$", options.case_sensitive ? '' : 'i');
|
|
474
|
+
if (!regex.test($("#" + (element.attr('id')) + "_confirmation").val())) {
|
|
469
475
|
return options.message;
|
|
470
476
|
}
|
|
471
477
|
},
|
|
@@ -480,7 +486,7 @@
|
|
|
480
486
|
if (name_prefix && name_suffix) {
|
|
481
487
|
form = element.closest('form');
|
|
482
488
|
valid = true;
|
|
483
|
-
form.find(
|
|
489
|
+
form.find(":input[name^=\"" + name_prefix + "\"][name$=\"" + name_suffix + "\"]").each(function() {
|
|
484
490
|
if ($(this).attr('name') !== name) {
|
|
485
491
|
if ($(this).val() === value) {
|
|
486
492
|
valid = false;
|
|
@@ -544,7 +550,7 @@
|
|
|
544
550
|
name = element.attr('name');
|
|
545
551
|
}
|
|
546
552
|
if (options['class']) {
|
|
547
|
-
name = options['class'] +
|
|
553
|
+
name = options['class'] + "[" + (name.split('[')[1]);
|
|
548
554
|
}
|
|
549
555
|
data[name] = element.val();
|
|
550
556
|
if ($.ajax({
|
|
@@ -646,7 +652,9 @@
|
|
|
646
652
|
}
|
|
647
653
|
};
|
|
648
654
|
|
|
649
|
-
|
|
655
|
+
window.ClientSideValidations.event = (window.Turbolinks != null) && window.Turbolinks.supported ? window.Turbolinks.EVENTS != null ? 'page:change' : 'turbolinks:load' : 'ready';
|
|
656
|
+
|
|
657
|
+
$(document).bind(window.ClientSideValidations.event, function() {
|
|
650
658
|
ClientSideValidations.disableValidators();
|
|
651
659
|
return $(ClientSideValidations.selectors.forms).validate();
|
|
652
660
|
});
|