pcai_client_side_validations 3.1.5

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.
Files changed (39) hide show
  1. data/client_side_validations.gemspec +42 -0
  2. data/lib/client_side_validations.rb +13 -0
  3. data/lib/client_side_validations/action_view.rb +14 -0
  4. data/lib/client_side_validations/action_view/form_builder.rb +157 -0
  5. data/lib/client_side_validations/action_view/form_helper.rb +86 -0
  6. data/lib/client_side_validations/action_view/form_tag_helper.rb +12 -0
  7. data/lib/client_side_validations/active_model.rb +60 -0
  8. data/lib/client_side_validations/active_model/acceptance.rb +10 -0
  9. data/lib/client_side_validations/active_model/exclusion.rb +15 -0
  10. data/lib/client_side_validations/active_model/format.rb +10 -0
  11. data/lib/client_side_validations/active_model/inclusion.rb +15 -0
  12. data/lib/client_side_validations/active_model/length.rb +24 -0
  13. data/lib/client_side_validations/active_model/numericality.rb +31 -0
  14. data/lib/client_side_validations/active_model/presence.rb +10 -0
  15. data/lib/client_side_validations/active_record.rb +11 -0
  16. data/lib/client_side_validations/active_record/middleware.rb +33 -0
  17. data/lib/client_side_validations/active_record/uniqueness.rb +28 -0
  18. data/lib/client_side_validations/core_ext.rb +3 -0
  19. data/lib/client_side_validations/core_ext/range.rb +10 -0
  20. data/lib/client_side_validations/core_ext/regexp.rb +14 -0
  21. data/lib/client_side_validations/engine.rb +6 -0
  22. data/lib/client_side_validations/files.rb +8 -0
  23. data/lib/client_side_validations/formtastic.rb +21 -0
  24. data/lib/client_side_validations/middleware.rb +81 -0
  25. data/lib/client_side_validations/mongo_mapper.rb +9 -0
  26. data/lib/client_side_validations/mongo_mapper/middleware.rb +20 -0
  27. data/lib/client_side_validations/mongo_mapper/uniqueness.rb +28 -0
  28. data/lib/client_side_validations/mongoid.rb +9 -0
  29. data/lib/client_side_validations/mongoid/middleware.rb +20 -0
  30. data/lib/client_side_validations/mongoid/uniqueness.rb +28 -0
  31. data/lib/client_side_validations/simple_form.rb +24 -0
  32. data/lib/client_side_validations/version.rb +3 -0
  33. data/lib/generators/client_side_validations/copy_asset_generator.rb +36 -0
  34. data/lib/generators/client_side_validations/install_generator.rb +45 -0
  35. data/lib/generators/templates/client_side_validations/README.rails.3.0 +6 -0
  36. data/lib/generators/templates/client_side_validations/README.rails.3.1 +7 -0
  37. data/lib/generators/templates/client_side_validations/initializer.rb +14 -0
  38. data/vendor/assets/javascripts/rails.validations.js +410 -0
  39. metadata +226 -0
@@ -0,0 +1,3 @@
1
+ module ClientSideValidations
2
+ VERSION = '3.1.5'
3
+ end
@@ -0,0 +1,36 @@
1
+ module ClientSideValidations
2
+ module Generators
3
+ class CopyAssetGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../../../../vendor/assets/javascripts', __FILE__)
5
+
6
+ private
7
+
8
+ def self.asset_pipeline_enabled?
9
+ (Rails.configuration.respond_to?(:assets) ? (Rails.configuration.assets || {}) : {})[:enabled]
10
+ end
11
+
12
+ def asset_pipeline_enabled?
13
+ self.class.asset_pipeline_enabled?
14
+ end
15
+
16
+ public
17
+
18
+ if asset_pipeline_enabled?
19
+ desc 'Creates a ClientSideValidations initializer and copies rails.validations.js to app/assets/javascripts.'
20
+ else
21
+ desc 'Creates a ClientSideValidations initializer and copies rails.validations.js to public/javascripts.'
22
+ end
23
+
24
+ def copy_javascript_asset
25
+ if asset_pipeline_enabled?
26
+ destination = 'app/assets/javascripts'
27
+ else
28
+ destination = 'public/javascripts'
29
+ end
30
+
31
+ copy_file 'rails.validations.js', "#{destination}/rails.validations.js"
32
+ end
33
+ end
34
+ end
35
+ end
36
+
@@ -0,0 +1,45 @@
1
+ module ClientSideValidations
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../../templates/client_side_validations', __FILE__)
5
+
6
+ private
7
+
8
+ def self.asset_pipeline_enabled?
9
+ (Rails.configuration.respond_to?(:assets) ? (Rails.configuration.assets || {}) : {})[:enabled]
10
+ end
11
+
12
+ def asset_pipeline_enabled?
13
+ self.class.asset_pipeline_enabled?
14
+ end
15
+
16
+ public
17
+
18
+ if asset_pipeline_enabled?
19
+ desc 'Creates a ClientSideValidations initializer.'
20
+ else
21
+ desc 'Creates a ClientSideValidations initializer and copies rails.validations.js to public/javascripts.'
22
+ end
23
+
24
+ def copy_initializer
25
+ copy_file 'initializer.rb', 'config/initializers/client_side_validations.rb'
26
+ end
27
+
28
+ def copy_javascript_asset
29
+ unless asset_pipeline_enabled?
30
+ copy_file '../../../../vendor/assets/javascripts/rails.validations.js', 'public/javascripts/rails.validations.js'
31
+ end
32
+ end
33
+
34
+ def show_readme
35
+ if Rails.version >= '3.1'
36
+ readme 'README.rails.3.1' if behavior == :invoke
37
+ else
38
+ readme 'README.rails.3.0' if behavior == :invoke
39
+ end
40
+ end
41
+
42
+ end
43
+ end
44
+ end
45
+
@@ -0,0 +1,6 @@
1
+ *********************
2
+ ClientSideValidations
3
+ *********************
4
+
5
+ Client Side Validations only works with jQuery.
6
+ You'll need to include jQuery in your layout file before you include 'rails.validations.js'
@@ -0,0 +1,7 @@
1
+ *********************
2
+ ClientSideValidations
3
+ *********************
4
+
5
+ In your app/assets/javascripts/application.js file add the following:
6
+
7
+ //= require rails.validations
@@ -0,0 +1,14 @@
1
+ # ClientSideValidations Initializer
2
+
3
+ require 'client_side_validations/simple_form' if defined?(::SimpleForm)
4
+ require 'client_side_validations/formtastic' if defined?(::Formtastic)
5
+
6
+ # Uncomment the following block if you want each input field to have the validation messages attached.
7
+ # ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
8
+ # unless html_tag =~ /^<label/
9
+ # %{<div class="field_with_errors">#{html_tag}<label for="#{instance.send(:tag_id)}" class="message">#{instance.error_message.first}</label></div>}.html_safe
10
+ # else
11
+ # %{<div class="field_with_errors">#{html_tag}</div>}.html_safe
12
+ # end
13
+ # end
14
+
@@ -0,0 +1,410 @@
1
+ /*!
2
+ * Rails 3 Client Side Validations - v3.1.4
3
+ * https://github.com/bcardarlela/client_side_validations
4
+ *
5
+ * Copyright (c) 2011 Brian Cardarella
6
+ * Licensed under the MIT license
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ */
9
+
10
+ (function($) {
11
+ $.fn.validate = function() {
12
+ return this.filter('form[data-validate]').each(function() {
13
+ var form = $(this);
14
+ var settings = window[form.attr('id')];
15
+
16
+ // Set up the events for the form
17
+ form
18
+ .submit( function() { return form.isValid(settings.validators); })
19
+ .bind('ajax:beforeSend', function(eventData) { if(eventData.target == this) return form.isValid(settings.validators); })
20
+ // Callbacks
21
+ .bind('form:validate:after', function(eventData) { clientSideValidations.callbacks.form.after( form, eventData); })
22
+ .bind('form:validate:before', function(eventData) { clientSideValidations.callbacks.form.before(form, eventData); })
23
+ .bind('form:validate:fail', function(eventData) { clientSideValidations.callbacks.form.fail( form, eventData); })
24
+ .bind('form:validate:pass', function(eventData) { clientSideValidations.callbacks.form.pass( form, eventData); })
25
+
26
+ // Set up the events for each validatable form element
27
+ .find('[data-validate]:input:not(:radio)')
28
+ .live('focusout', function() { $(this).isValid(settings.validators); })
29
+ .live('change', function() { $(this).data('changed', true); })
30
+ // Callbacks
31
+ .live('element:validate:after', function(eventData) { clientSideValidations.callbacks.element.after( $(this), eventData); })
32
+ .live('element:validate:before', function(eventData) { clientSideValidations.callbacks.element.before($(this), eventData); })
33
+ .live('element:validate:fail', function(eventData, message) {
34
+ var element = $(this);
35
+ clientSideValidations.callbacks.element.fail(element, message, function() {
36
+ addError(element, message);
37
+ }, eventData) })
38
+ .live('element:validate:pass', function(eventData) {
39
+ var element = $(this);
40
+ clientSideValidations.callbacks.element.pass(element, function() {
41
+ removeError(element);
42
+ }, eventData) })
43
+ // Checkboxes - Live events don't support filter
44
+ .end().find('[data-validate]:checkbox')
45
+ .live('click', function() { $(this).isValid(settings.validators); })
46
+ // Inputs for confirmations
47
+ .end().find('[id*=_confirmation]').each(function() {
48
+ var confirmationElement = $(this),
49
+ element = form.find('#' + this.id.match(/(.+)_confirmation/)[1] + '[data-validate]:input');
50
+
51
+ if (element[0]) {
52
+ $('#' + confirmationElement.attr('id'))
53
+ .live('focusout', function() {
54
+ element.data('changed', true).isValid(settings.validators);
55
+ })
56
+ .live('keyup', function() {
57
+ element.data('changed', true).isValid(settings.validators);
58
+ })
59
+ }
60
+ });
61
+
62
+ var addError = function(element, message) {
63
+ clientSideValidations.formBuilders[settings.type].add(element, settings, message);
64
+ }
65
+
66
+ var removeError = function(element) {
67
+ clientSideValidations.formBuilders[settings.type].remove(element, settings);
68
+ }
69
+ });
70
+ }
71
+
72
+ $.fn.isValid = function(validators) {
73
+ if ($(this[0]).is('form')) {
74
+ return validateForm($(this[0]), validators);
75
+ } else {
76
+ return validateElement($(this[0]), validators[this[0].name]);
77
+ }
78
+ }
79
+
80
+ var validateForm = function(form, validators) {
81
+ var valid = true;
82
+
83
+ form.trigger('form:validate:before').find('[data-validate]:input').each(function() {
84
+ if (!$(this).isValid(validators)) { valid = false; }
85
+ });
86
+
87
+ if (valid) {
88
+ form.trigger('form:validate:pass');
89
+ } else {
90
+ form.trigger('form:validate:fail');
91
+ }
92
+
93
+ form.trigger('form:validate:after');
94
+ return valid;
95
+ }
96
+
97
+ var validateElement = function(element, validators) {
98
+ element.trigger('element:validate:before');
99
+
100
+ if (element.data('changed') !== false) {
101
+ var valid = true;
102
+ element.data('changed', false);
103
+
104
+ // Because 'length' is defined on the list of validators we cannot call jQuery.each on
105
+ // the clientSideValidations.validators.all() object
106
+ for (kind in clientSideValidations.validators.all()) {
107
+ if (validators[kind] && (message = clientSideValidations.validators.all()[kind](element, validators[kind]))) {
108
+ element.trigger('element:validate:fail', message).data('valid', false);
109
+ valid = false;
110
+ break;
111
+ }
112
+ }
113
+
114
+ if (valid) { element.data('valid', null); element.trigger('element:validate:pass'); }
115
+ }
116
+
117
+ element.trigger('element:validate:after');
118
+ return element.data('valid') === false ? false : true;
119
+ }
120
+
121
+ // Main hook
122
+ // If new forms are dynamically introduced into the DOM the .validate() method
123
+ // must be invoked on that form
124
+ $(function() { $('form[data-validate]').validate(); })
125
+ })(jQuery);
126
+
127
+ var clientSideValidations = {
128
+ validators: {
129
+ all: function() { return jQuery.extend({}, clientSideValidations.validators.local, clientSideValidations.validators.remote) },
130
+ local: {
131
+ presence: function(element, options) {
132
+ if (/^\s*$/.test(element.val() || "")) {
133
+ return options.message;
134
+ }
135
+ },
136
+ acceptance: function(element, options) {
137
+ switch (element.attr('type')) {
138
+ case 'checkbox':
139
+ if (!element.attr('checked')) {
140
+ return options.message;
141
+ }
142
+ break;
143
+ case 'text':
144
+ if (element.val() != (options.accept || '1')) {
145
+ return options.message;
146
+ }
147
+ break;
148
+ }
149
+ },
150
+ format: function(element, options) {
151
+ if ((message = this.presence(element, options)) && options.allow_blank == true) {
152
+ return;
153
+ } else if (message) {
154
+ return message;
155
+ } else {
156
+ if (options['with'] && !options['with'].test(element.val())) {
157
+ return options.message;
158
+ } else if (options['without'] && options['without'].test(element.val())) {
159
+ return options.message;
160
+ }
161
+ }
162
+ },
163
+ numericality: function(element, options) {
164
+ if (!/^-?(?:\d+|\d{1,3}(?:,\d{3})+)(?:\.\d*)?$/.test(element.val()) && element.val() != '') {
165
+ return options.messages.numericality;
166
+ }
167
+
168
+ if (options.only_integer && !/^[+-]?\d+$/.test(element.val())) {
169
+ return options.messages.only_integer;
170
+ }
171
+
172
+ var CHECKS = { greater_than: '>', greater_than_or_equal_to: '>=',
173
+ equal_to: '==', less_than: '<', less_than_or_equal_to: '<=' }
174
+
175
+ for (var check in CHECKS) {
176
+ if (options[check] != undefined && !(new Function("return " + element.val() + CHECKS[check] + options[check])())) {
177
+ return options.messages[check];
178
+ }
179
+ }
180
+
181
+ if (options.odd && !(parseInt(element.val()) % 2)) {
182
+ return options.messages.odd;
183
+ }
184
+
185
+ if (options.even && (parseInt(element.val()) % 2)) {
186
+ return options.messages.even;
187
+ }
188
+ },
189
+ length: function(element, options) {
190
+ var blankOptions = {};
191
+ if (options.is) {
192
+ blankOptions.message = options.messages.is;
193
+ } else if (options.minimum) {
194
+ blankOptions.message = options.messages.minimum;
195
+ }
196
+ if ((message = this.presence(element, blankOptions)) && options.allow_blank == true) {
197
+ return;
198
+ } else if (message) {
199
+ return message;
200
+ } else {
201
+ var CHECKS = { is: '==', minimum: '>=', maximum: '<=' }
202
+ var tokenizer = options.js_tokenizer || "split('')";
203
+ var tokenized_length = new Function("element", "return (element.val()." + tokenizer + " || '').length;")(element);
204
+
205
+ for (var check in CHECKS) {
206
+ if (options[check] && !(new Function("return " + tokenized_length + CHECKS[check] + options[check])())) {
207
+ return options.messages[check];
208
+ }
209
+ }
210
+ }
211
+ },
212
+ exclusion: function(element, options) {
213
+ if ((message = this.presence(element, options)) && options.allow_blank == true) {
214
+ return;
215
+ } else if (message) {
216
+ return message;
217
+ } else {
218
+ if (options['in']) {
219
+ for (var i = 0; i < options['in'].length; i++) {
220
+ if (options['in'][i] == element.val()) {
221
+ return options.message;
222
+ }
223
+ }
224
+ } else if (options['range']) {
225
+ var lower = options['range'][0],
226
+ upper = options['range'][1];
227
+ if (element.val() >= lower && element.val() <= upper) {
228
+ return options.message;
229
+ }
230
+ }
231
+ }
232
+ },
233
+ inclusion: function(element, options) {
234
+ if ((message = this.presence(element, options)) && options.allow_blank == true) {
235
+ return;
236
+ } else if (message) {
237
+ return message;
238
+ } else {
239
+ if (options['in']) {
240
+ for (var i = 0; i < options['in'].length; i++) {
241
+ if (options['in'][i] == element.val()) {
242
+ return;
243
+ }
244
+ }
245
+ return options.message;
246
+ } else if (options['range']) {
247
+ var lower = options['range'][0],
248
+ upper = options['range'][1];
249
+
250
+ if (element.val() >= lower && element.val() <= upper) {
251
+ return;
252
+ } else {
253
+ return options.message;
254
+ }
255
+ }
256
+ }
257
+ },
258
+ confirmation: function(element, options) {
259
+ if (element.val() != jQuery('#' + element.attr('id') + '_confirmation').val()) {
260
+ return options.message;
261
+ }
262
+ }
263
+ },
264
+ remote: {
265
+ uniqueness: function(element, options) {
266
+ if ((message = clientSideValidations.validators.local.presence(element, options)) && options.allow_blank === true) {
267
+ return;
268
+ } else if (message) {
269
+ return message;
270
+ } else {
271
+ var data = {};
272
+ data['case_sensitive'] = !!options.case_sensitive;
273
+ if (options.id) {
274
+ data['id'] = options.id;
275
+ }
276
+
277
+ if (options.scope) {
278
+ data.scope = {}
279
+ for (key in options.scope) {
280
+ var scoped_element = jQuery('[name="' + element.attr('name').replace(/\[\w+]$/, '[' + key + ']' + '"]'));
281
+ if (scoped_element[0] && scoped_element.val() != options.scope[key]) {
282
+ data.scope[key] = scoped_element.val();
283
+ scoped_element.unbind('change.' + element.id).bind('change.' + element.id, function() { element.trigger('change'); element.trigger('focusout'); });
284
+ } else {
285
+ data.scope[key] = options.scope[key];
286
+ }
287
+ }
288
+ }
289
+
290
+ // Kind of a hack but this will isolate the resource name and attribute.
291
+ // e.g. user[records_attributes][0][title] => records[title]
292
+ // e.g. user[record_attributes][title] => record[title]
293
+ // Server side handles classifying the resource properly
294
+ if (/_attributes]/.test(element.attr('name'))) {
295
+ var name = element.attr('name').match(/\[\w+_attributes]/g).pop().match(/\[(\w+)_attributes]/).pop();
296
+ name += /(\[\w+])$/.exec(element.attr('name'))[1];
297
+ } else {
298
+ var name = element.attr('name');
299
+ }
300
+
301
+ // Override the name if a nested module class is passed
302
+ if (options['class']) {
303
+ name = options['class'] + '[' + name.split('[')[1]
304
+ }
305
+ data[name] = element.val();
306
+
307
+ if (jQuery.ajax({
308
+ url: '/validators/uniqueness',
309
+ data: data,
310
+ async: false
311
+ }).status == 200) {
312
+ return options.message;
313
+ }
314
+ }
315
+ }
316
+ }
317
+ },
318
+ formBuilders: {
319
+ 'ActionView::Helpers::FormBuilder': {
320
+ add: function(element, settings, message) {
321
+ if (element.data('valid') !== false && jQuery('label.message[for="' + element.attr('id') + '"]')[0] == undefined) {
322
+ var inputErrorField = jQuery(settings.input_tag),
323
+ labelErrorField = jQuery(settings.label_tag),
324
+ label = jQuery('label[for="' + element.attr('id') + '"]:not(.message)');
325
+
326
+ if (element.attr('autofocus')) { element.attr('autofocus', false) };
327
+ element.before(inputErrorField);
328
+ inputErrorField.find('span#input_tag').replaceWith(element);
329
+ inputErrorField.find('label.message').attr('for', element.attr('id'));
330
+ labelErrorField.find('label.message').attr('for', element.attr('id'));
331
+ label.replaceWith(labelErrorField);
332
+ labelErrorField.find('label#label_tag').replaceWith(label);
333
+ }
334
+ jQuery('label.message[for="' + element.attr('id') + '"]').text(message);
335
+ },
336
+ remove: function(element, settings) {
337
+ var errorFieldClass = jQuery(settings.input_tag).attr('class'),
338
+ inputErrorField = element.closest('.' + errorFieldClass),
339
+ label = jQuery('label[for="' + element.attr('id') + '"]:not(.message)'),
340
+ labelErrorField = label.closest('.' + errorFieldClass);
341
+
342
+ if (inputErrorField[0]) {
343
+ inputErrorField.find('#' + element.attr('id')).detach();
344
+ inputErrorField.replaceWith(element);
345
+ label.detach();
346
+ labelErrorField.replaceWith(label);
347
+ }
348
+ }
349
+ },
350
+ 'SimpleForm::FormBuilder': {
351
+ add: function(element, settings, message) {
352
+ if (element.data('valid') !== false) {
353
+ var wrapper = element.closest(settings.wrapper_tag);
354
+ wrapper.addClass(settings.wrapper_error_class);
355
+ var errorElement = $('<' + settings.error_tag + ' class="' + settings.error_class + '">' + message + '</' + settings.error_tag + '>');
356
+ wrapper.append(errorElement);
357
+ } else {
358
+ element.parent().find(settings.error_tag + '.' + settings.error_class).text(message);
359
+ }
360
+ },
361
+ remove: function(element, settings) {
362
+ var wrapper = element.closest(settings.wrapper_tag + '.' + settings.wrapper_error_class);
363
+ wrapper.removeClass(settings.wrapper_error_class);
364
+ var errorElement = wrapper.find(settings.error_tag + '.' + settings.error_class);
365
+ errorElement.remove();
366
+ }
367
+
368
+ },
369
+ 'Formtastic::FormBuilder': {
370
+ add: function(element, settings, message) {
371
+ if (element.data('valid') !== false) {
372
+ var wrapper = element.closest('li');
373
+ wrapper.addClass('error');
374
+ var errorElement = $('<p class="' + settings.inline_error_class + '">' + message + '</p>');
375
+ wrapper.append(errorElement);
376
+ } else {
377
+ element.parent().find('p.' + settings.inline_error_class).text(message);
378
+ }
379
+ },
380
+ remove: function(element, settings) {
381
+ var wrapper = element.closest('li.error');
382
+ wrapper.removeClass('error');
383
+ var errorElement = wrapper.find('p.' + settings.inline_error_class);
384
+ errorElement.remove();
385
+ }
386
+ },
387
+ 'NestedForm::Builder': {
388
+ add: function(element, settings, message) {
389
+ clientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'].add(element, settings, message);
390
+ },
391
+ remove: function(element, settings, message) {
392
+ clientSideValidations.formBuilders['ActionView::Helpers::FormBuilder'].remove(element, settings, message);
393
+ }
394
+ }
395
+ },
396
+ callbacks: {
397
+ element: {
398
+ after: function(element, eventData) { },
399
+ before: function(element, eventData) { },
400
+ fail: function(element, message, addError, eventData) { addError() },
401
+ pass: function(element, removeError, eventData) { removeError() }
402
+ },
403
+ form: {
404
+ after: function(form, eventData) { },
405
+ before: function(form, eventData) { },
406
+ fail: function(form, eventData) { },
407
+ pass: function(form, eventData) { }
408
+ }
409
+ }
410
+ };