client_side_validations 6.0.0 → 7.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +3 -109
- data/lib/client_side_validations.rb +2 -5
- data/lib/client_side_validations/active_model.rb +2 -4
- data/lib/client_side_validations/active_record.rb +2 -7
- data/lib/client_side_validations/config.rb +1 -1
- data/lib/client_side_validations/engine.rb +0 -1
- data/lib/client_side_validations/extender.rb +12 -0
- data/lib/client_side_validations/version.rb +1 -1
- data/lib/generators/templates/client_side_validations/initializer.rb +2 -2
- data/vendor/assets/javascripts/rails.validations.js +2 -59
- metadata +10 -17
- data/lib/client_side_validations/active_record/middleware.rb +0 -62
- data/lib/client_side_validations/middleware.rb +0 -159
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a667f554594c134de2b2ab3e77679337a8bfcb22
|
4
|
+
data.tar.gz: fd9fd106426a734c79c9a6f9e3bc96877b4bd152
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 378f957247dbfab1182963dadb3adf802cb4e79b2a2570af0d9507f3e94b2c7d87fe93002300542f4613b5d7a235f52dded3e906d358fdade2903ae36842ba4d
|
7
|
+
data.tar.gz: 04f88efc2d59603ea7189d1aefeda34f3e7678cbecfffed791df36612f35582c940ed4ef6df82b0db1d6e4da1d54023dc2d3722a99dda6ccbd4ebb72b608a790
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 7.0.1 (2017-01-22)
|
4
|
+
|
5
|
+
* Fix `rails.validations` asset not found error
|
6
|
+
|
7
|
+
## 7.0.0 (2017-01-22)
|
8
|
+
|
9
|
+
* Remove middleware for remote validations
|
10
|
+
* Update development dependencies
|
11
|
+
|
3
12
|
## 6.0.0 (2017-01-20)
|
4
13
|
|
5
14
|
* Rails 5.0 compatibility
|
data/README.md
CHANGED
@@ -183,37 +183,6 @@ You can even turn them off per fieldset:
|
|
183
183
|
...
|
184
184
|
```
|
185
185
|
|
186
|
-
## Wrapper objects and remote validations ##
|
187
|
-
|
188
|
-
For example, we have a wrapper class for the User model, UserForm, and it uses remote uniqueness validation for the `email` field.
|
189
|
-
|
190
|
-
```ruby
|
191
|
-
class UserForm
|
192
|
-
include ActiveRecord::Validations
|
193
|
-
attr_accessor :email
|
194
|
-
validates_uniqueness_of :email
|
195
|
-
end
|
196
|
-
...
|
197
|
-
<% form_for(UserForm.new) do %>
|
198
|
-
...
|
199
|
-
```
|
200
|
-
|
201
|
-
However, this won't work since middleware will try to perform validation against UserForm, and it's not persisted.
|
202
|
-
|
203
|
-
This is solved by passing `client_validations` options hash to the validator, that currently supports one key — `:class`, and setting correct name to the form object:
|
204
|
-
|
205
|
-
```ruby
|
206
|
-
class UserForm
|
207
|
-
include ActiveRecord::Validations
|
208
|
-
attr_accessor :email
|
209
|
-
validates_uniqueness_of :email, client_validations: { class:
|
210
|
-
'User' }
|
211
|
-
end
|
212
|
-
...
|
213
|
-
<% form_for(UserForm.new, as: :user) do %>
|
214
|
-
...
|
215
|
-
```
|
216
|
-
|
217
186
|
## Understanding the embedded `<script>` tag ##
|
218
187
|
|
219
188
|
A rendered form with validations will always have a `<script>` appended
|
@@ -259,7 +228,7 @@ passing nothing:
|
|
259
228
|
You can also force validators similarly to the input syntax:
|
260
229
|
|
261
230
|
```erb
|
262
|
-
<%= f.validate :email,
|
231
|
+
<%= f.validate :email, presence: false %>
|
263
232
|
```
|
264
233
|
|
265
234
|
Take care when using this method. The embedded validators are
|
@@ -267,11 +236,11 @@ overwritten based upon the order they are rendered. So if you do
|
|
267
236
|
something like:
|
268
237
|
|
269
238
|
```erb
|
270
|
-
<%= f.text_field :email, validate: {
|
239
|
+
<%= f.text_field :email, validate: { presence: false } %>
|
271
240
|
<%= f.validate %>
|
272
241
|
```
|
273
242
|
|
274
|
-
The `
|
243
|
+
The `presence` validator will not be turned off because the options
|
275
244
|
were overwritten by the call to `FormBuilder#validate`
|
276
245
|
|
277
246
|
|
@@ -357,71 +326,6 @@ end
|
|
357
326
|
|
358
327
|
Client Side Validations will apply the new validator and validate your forms as needed.
|
359
328
|
|
360
|
-
### Remote Validators ###
|
361
|
-
A good example of a remote validator would be for Zipcodes. It wouldn't be reasonable to embed every single zipcode inline, so we'll need to check for its existence with remote javascript call back to our app. Assume we have a zipcode database mapped to the model Zipcode. The primary key is the unique zipcode. Our Rails validator would probably look something like this:
|
362
|
-
|
363
|
-
```ruby
|
364
|
-
class ZipcodeValidator < ActiveModel::EachValidator
|
365
|
-
def validate_each(record, attr_name, value)
|
366
|
-
unless ::Zipcode.where(id: value).exists?
|
367
|
-
record.errors.add(attr_name, :zipcode, options.merge(value: value))
|
368
|
-
end
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
# This allows us to assign the validator in the model
|
373
|
-
module ActiveModel::Validations::HelperMethods
|
374
|
-
def validates_zipcode(*attr_names)
|
375
|
-
validates_with ZipcodeValidator, _merge_attributes(attr_names)
|
376
|
-
end
|
377
|
-
end
|
378
|
-
```
|
379
|
-
|
380
|
-
Of course we still need to add the i18n message:
|
381
|
-
|
382
|
-
```yaml
|
383
|
-
en:
|
384
|
-
errors:
|
385
|
-
messages:
|
386
|
-
zipcode: "Not a valid US zip code"
|
387
|
-
```
|
388
|
-
|
389
|
-
And let's add the Javascript validator. Because this will be remote validator we need to add it to `ClientSideValidations.validators.remote`:
|
390
|
-
|
391
|
-
```js
|
392
|
-
window.ClientSideValidations.validators.remote['zipcode'] = function(element, options) {
|
393
|
-
if ($.ajax({
|
394
|
-
url: '/validators/zipcode',
|
395
|
-
data: { id: element.val() },
|
396
|
-
// async *must* be false
|
397
|
-
async: false
|
398
|
-
}).status == 404) { return options.message; }
|
399
|
-
}
|
400
|
-
```
|
401
|
-
|
402
|
-
All we're doing here is checking to see if the resource exists (in this case the given zipcode) and if it doesn't the error message is returned.
|
403
|
-
|
404
|
-
Notice that the remote call is forced to *async: false*. This is necessary and the validator may not work properly if this is left out.
|
405
|
-
|
406
|
-
Now the extra step for adding a remote validator is to add to the middleware. All ClientSideValidations middleware should inherit from `ClientSideValidations::Middleware::Base`:
|
407
|
-
|
408
|
-
```ruby
|
409
|
-
module ClientSideValidations::Middleware
|
410
|
-
class Zipcode < ClientSideValidations::Middleware::Base
|
411
|
-
def response
|
412
|
-
if ::Zipcode.where(id: request.params[:id]).exists?
|
413
|
-
self.status = 200
|
414
|
-
else
|
415
|
-
self.status = 404
|
416
|
-
end
|
417
|
-
super
|
418
|
-
end
|
419
|
-
end
|
420
|
-
end
|
421
|
-
```
|
422
|
-
|
423
|
-
The `#response` method is always called and it should set the status accessor. Then a call to `super` is required. In the javascript we set the 'id' in the params to the value of the zipcode input, in the middleware we check to see if this zipcode exists in our zipcode database. If it does, we return 200, if it doesn't we return 404.
|
424
|
-
|
425
329
|
## Enabling, Disabling, and Resetting on the client ##
|
426
330
|
|
427
331
|
There are many reasons why you might want to enable, disable, or even completely reset the bound validation events on the client. `ClientSideValidations` offers a simple API for this.
|
@@ -510,16 +414,6 @@ div.field_with_errors div.ui-effects-wrapper {
|
|
510
414
|
|
511
415
|
Finally uncomment the `ActionView::Base.field_error_proc` override in `config/initializers/client_side_validations.rb`
|
512
416
|
|
513
|
-
## Security ##
|
514
|
-
|
515
|
-
Client Side Validations comes with a uniqueness middleware. This can be a potential security issue, so the uniqueness validator is disabled by default. If you want to enable it, set the `disabled_validators` config variable in `config/initializers/client_side_validations.rb`:
|
516
|
-
|
517
|
-
```ruby
|
518
|
-
ClientSideValidations::Config.disabled_validators = []
|
519
|
-
```
|
520
|
-
|
521
|
-
Note that the `FormBuilder` will automatically skip building validators that are disabled.
|
522
|
-
|
523
417
|
## Authors ##
|
524
418
|
|
525
419
|
[Brian Cardarella](https://twitter.com/bcardarella)
|
@@ -1,12 +1,9 @@
|
|
1
|
-
module ClientSideValidations
|
2
|
-
end
|
3
|
-
|
4
1
|
require 'client_side_validations/config'
|
5
2
|
require 'client_side_validations/active_model' if defined?(::ActiveModel)
|
6
3
|
require 'client_side_validations/active_record' if defined?(::ActiveRecord)
|
7
4
|
require 'client_side_validations/action_view' if defined?(::ActionView)
|
5
|
+
|
8
6
|
if defined?(::Rails)
|
9
|
-
require 'client_side_validations/generators'
|
10
|
-
require 'client_side_validations/middleware'
|
11
7
|
require 'client_side_validations/engine'
|
8
|
+
require 'client_side_validations/generators'
|
12
9
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'client_side_validations/core_ext'
|
2
|
+
require 'client_side_validations/extender'
|
2
3
|
require 'client_side_validations/active_model/conditionals'
|
3
4
|
|
4
5
|
module ClientSideValidations
|
@@ -145,7 +146,4 @@ end
|
|
145
146
|
ActiveModel::Validator.send(:include, ClientSideValidations::ActiveModel::Validator)
|
146
147
|
ActiveModel::Validations.send(:include, ClientSideValidations::ActiveModel::Validations)
|
147
148
|
|
148
|
-
%w(Absence Acceptance Exclusion Format Inclusion Length Numericality Presence)
|
149
|
-
require "client_side_validations/active_model/#{validator.downcase}"
|
150
|
-
ActiveModel::Validations.const_get("#{validator}Validator").send :include, ClientSideValidations::ActiveModel.const_get(validator)
|
151
|
-
end
|
149
|
+
ClientSideValidations::Extender.extend 'ActiveModel', %w(Absence Acceptance Exclusion Format Inclusion Length Numericality Presence)
|
@@ -1,11 +1,6 @@
|
|
1
1
|
require 'client_side_validations/active_model'
|
2
|
-
require 'client_side_validations/
|
3
|
-
require 'client_side_validations/active_record/middleware'
|
2
|
+
require 'client_side_validations/extender'
|
4
3
|
|
5
4
|
ActiveRecord::Base.send(:include, ClientSideValidations::ActiveModel::Validations)
|
6
|
-
ClientSideValidations::Middleware::Uniqueness.register_orm(ClientSideValidations::ActiveRecord::Middleware)
|
7
5
|
|
8
|
-
%w(Uniqueness)
|
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
|
6
|
+
ClientSideValidations::Extender.extend 'ActiveRecord', %w(Uniqueness)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ClientSideValidations
|
2
|
+
module Extender
|
3
|
+
module_function
|
4
|
+
|
5
|
+
def extend(klass, validators)
|
6
|
+
validators.each do |validator|
|
7
|
+
require "client_side_validations/#{klass.underscore}/#{validator.downcase}"
|
8
|
+
const_get(klass)::Validations.const_get("#{validator}Validator").send :include, ClientSideValidations.const_get(klass).const_get(validator)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# ClientSideValidations Initializer
|
2
2
|
|
3
|
-
# Disabled validators
|
4
|
-
# ClientSideValidations::Config.disabled_validators = [
|
3
|
+
# Disabled validators
|
4
|
+
# ClientSideValidations::Config.disabled_validators = []
|
5
5
|
|
6
6
|
# Uncomment to validate number format with current I18n locale
|
7
7
|
# ClientSideValidations::Config.number_format_with_locale = true
|
@@ -1,6 +1,6 @@
|
|
1
1
|
|
2
2
|
/*!
|
3
|
-
* Client Side Validations -
|
3
|
+
* Client Side Validations - v7.0.1 (https://github.com/DavyJonesLocker/client_side_validations)
|
4
4
|
* Copyright (c) 2017 Geremia Taglialatela, Brian Cardarella
|
5
5
|
* Licensed under MIT (http://opensource.org/licenses/mit-license.php)
|
6
6
|
*/
|
@@ -505,64 +505,7 @@
|
|
505
505
|
}
|
506
506
|
}
|
507
507
|
},
|
508
|
-
remote: {
|
509
|
-
uniqueness: function(element, options) {
|
510
|
-
var data, key, message, name, ref, scope_value, scoped_element, scoped_name;
|
511
|
-
message = ClientSideValidations.validators.local.presence(element, options);
|
512
|
-
if (message) {
|
513
|
-
if (options.allow_blank === true) {
|
514
|
-
return;
|
515
|
-
}
|
516
|
-
return message;
|
517
|
-
}
|
518
|
-
data = {};
|
519
|
-
data.case_sensitive = !!options.case_sensitive;
|
520
|
-
if (options.id) {
|
521
|
-
data.id = options.id;
|
522
|
-
}
|
523
|
-
if (options.scope) {
|
524
|
-
data.scope = {};
|
525
|
-
ref = options.scope;
|
526
|
-
for (key in ref) {
|
527
|
-
scope_value = ref[key];
|
528
|
-
scoped_name = element.attr('name').replace(/\[\w+\]$/, "[" + key + "]");
|
529
|
-
scoped_element = $("[name='" + scoped_name + "']");
|
530
|
-
$("[name='" + scoped_name + "']:checkbox").each(function() {
|
531
|
-
if (this.checked) {
|
532
|
-
return scoped_element = this;
|
533
|
-
}
|
534
|
-
});
|
535
|
-
if (scoped_element[0] && scoped_element.val() !== scope_value) {
|
536
|
-
data.scope[key] = scoped_element.val();
|
537
|
-
scoped_element.unbind("change." + element.id).bind("change." + element.id, function() {
|
538
|
-
element.trigger('change.ClientSideValidations');
|
539
|
-
return element.trigger('focusout.ClientSideValidations');
|
540
|
-
});
|
541
|
-
} else {
|
542
|
-
data.scope[key] = scope_value;
|
543
|
-
}
|
544
|
-
}
|
545
|
-
}
|
546
|
-
if (/_attributes\]/.test(element.attr('name'))) {
|
547
|
-
name = element.attr('name').match(/\[\w+_attributes\]/g).pop().match(/\[(\w+)_attributes\]/).pop();
|
548
|
-
name += /(\[\w+\])$/.exec(element.attr('name'))[1];
|
549
|
-
} else {
|
550
|
-
name = element.attr('name');
|
551
|
-
}
|
552
|
-
if (options['class']) {
|
553
|
-
name = options['class'] + "[" + (name.split('[')[1]);
|
554
|
-
}
|
555
|
-
data[name] = element.val();
|
556
|
-
if ($.ajax({
|
557
|
-
url: ClientSideValidations.remote_validators_url_for('uniqueness'),
|
558
|
-
data: data,
|
559
|
-
async: false,
|
560
|
-
cache: false
|
561
|
-
}).status === 200) {
|
562
|
-
return options.message;
|
563
|
-
}
|
564
|
-
}
|
565
|
-
}
|
508
|
+
remote: {}
|
566
509
|
};
|
567
510
|
|
568
511
|
window.ClientSideValidations.remote_validators_url_for = function(validator) {
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: client_side_validations
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 7.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geremia Taglialatela
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2017-01-
|
12
|
+
date: 2017-01-22 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -93,14 +93,14 @@ dependencies:
|
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.8.
|
96
|
+
version: 0.8.19
|
97
97
|
type: :development
|
98
98
|
prerelease: false
|
99
99
|
version_requirements: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
101
|
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: 0.8.
|
103
|
+
version: 0.8.19
|
104
104
|
- !ruby/object:Gem::Dependency
|
105
105
|
name: m
|
106
106
|
requirement: !ruby/object:Gem::Requirement
|
@@ -119,22 +119,16 @@ dependencies:
|
|
119
119
|
name: minitest
|
120
120
|
requirement: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: 4.7.5
|
125
|
-
- - "<"
|
122
|
+
- - "~>"
|
126
123
|
- !ruby/object:Gem::Version
|
127
|
-
version:
|
124
|
+
version: '5.10'
|
128
125
|
type: :development
|
129
126
|
prerelease: false
|
130
127
|
version_requirements: !ruby/object:Gem::Requirement
|
131
128
|
requirements:
|
132
|
-
- - "
|
133
|
-
- !ruby/object:Gem::Version
|
134
|
-
version: 4.7.5
|
135
|
-
- - "<"
|
129
|
+
- - "~>"
|
136
130
|
- !ruby/object:Gem::Version
|
137
|
-
version:
|
131
|
+
version: '5.10'
|
138
132
|
- !ruby/object:Gem::Dependency
|
139
133
|
name: mocha
|
140
134
|
requirement: !ruby/object:Gem::Requirement
|
@@ -287,17 +281,16 @@ files:
|
|
287
281
|
- lib/client_side_validations/active_model/numericality.rb
|
288
282
|
- lib/client_side_validations/active_model/presence.rb
|
289
283
|
- lib/client_side_validations/active_record.rb
|
290
|
-
- lib/client_side_validations/active_record/middleware.rb
|
291
284
|
- lib/client_side_validations/active_record/uniqueness.rb
|
292
285
|
- lib/client_side_validations/config.rb
|
293
286
|
- lib/client_side_validations/core_ext.rb
|
294
287
|
- lib/client_side_validations/core_ext/range.rb
|
295
288
|
- lib/client_side_validations/core_ext/regexp.rb
|
296
289
|
- lib/client_side_validations/engine.rb
|
290
|
+
- lib/client_side_validations/extender.rb
|
297
291
|
- lib/client_side_validations/files.rb
|
298
292
|
- lib/client_side_validations/generators.rb
|
299
293
|
- lib/client_side_validations/generators/rails_validations.rb
|
300
|
-
- lib/client_side_validations/middleware.rb
|
301
294
|
- lib/client_side_validations/version.rb
|
302
295
|
- lib/generators/client_side_validations/copy_assets_generator.rb
|
303
296
|
- lib/generators/client_side_validations/install_generator.rb
|
@@ -323,7 +316,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
323
316
|
version: '0'
|
324
317
|
requirements: []
|
325
318
|
rubyforge_project:
|
326
|
-
rubygems_version: 2.6.
|
319
|
+
rubygems_version: 2.6.9
|
327
320
|
signing_key:
|
328
321
|
specification_version: 4
|
329
322
|
summary: Client Side Validations
|
@@ -1,62 +0,0 @@
|
|
1
|
-
module ClientSideValidations
|
2
|
-
module ActiveRecord
|
3
|
-
class Middleware
|
4
|
-
def self.class?(klass)
|
5
|
-
klass.abstract_class.blank? && klass < ::ActiveRecord::Base
|
6
|
-
end
|
7
|
-
|
8
|
-
def self.unique?(klass, attribute, value, params)
|
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
|
-
|
14
|
-
sql = sql_statement(klass, connection, attribute, value, params)
|
15
|
-
relation = Arel::Nodes::SqlLiteral.new(sql)
|
16
|
-
|
17
|
-
klass.where(relation).empty?
|
18
|
-
end
|
19
|
-
|
20
|
-
def self.sql_statement(klass, connection, attribute, value, params)
|
21
|
-
sql = []
|
22
|
-
t = klass.arel_table
|
23
|
-
|
24
|
-
if params[:case_sensitive] == 'true'
|
25
|
-
sql << 'BINARY' if connection.adapter_name =~ /^mysql/i
|
26
|
-
sql << t[attribute].eq(value).to_sql
|
27
|
-
else
|
28
|
-
escaped_value = value.gsub(/[%_]/, '\\\\\0')
|
29
|
-
sql << "#{t[attribute].matches(escaped_value).to_sql} ESCAPE '\\'"
|
30
|
-
end
|
31
|
-
|
32
|
-
sql << "AND #{t[klass.primary_key].not_eq(params[:id]).to_sql}" if params[:id]
|
33
|
-
|
34
|
-
(params[:scope] || {}).each do |scope_attribute, scope_value|
|
35
|
-
scope_value = type_cast_value(connection, klass.columns_hash[scope_attribute], scope_value)
|
36
|
-
sql << "AND #{t[scope_attribute].eq(scope_value).to_sql}"
|
37
|
-
end
|
38
|
-
|
39
|
-
sql.join ' '
|
40
|
-
end
|
41
|
-
|
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]
|
48
|
-
else
|
49
|
-
value
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.find_topmost_superclass(klass)
|
54
|
-
if class?(klass.superclass)
|
55
|
-
find_topmost_superclass(klass.superclass)
|
56
|
-
else
|
57
|
-
klass
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
@@ -1,159 +0,0 @@
|
|
1
|
-
require 'client_side_validations/core_ext'
|
2
|
-
|
3
|
-
module ClientSideValidations
|
4
|
-
module Middleware
|
5
|
-
class Validators
|
6
|
-
def initialize(app)
|
7
|
-
@app = app
|
8
|
-
end
|
9
|
-
|
10
|
-
def call(env)
|
11
|
-
matches = %r{\A/?#{ClientSideValidations::Config.root_path}/validators\/(\w+)\z}.match(env['PATH_INFO'])
|
12
|
-
if matches
|
13
|
-
process_request(matches.captures.first, env)
|
14
|
-
else
|
15
|
-
@app.call(env)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def process_request(validation, env)
|
20
|
-
if disabled_validators.include?(validation)
|
21
|
-
error_resp
|
22
|
-
else
|
23
|
-
klass_name = validation.camelize
|
24
|
-
klass_name = "::ClientSideValidations::Middleware::#{klass_name}"
|
25
|
-
klass_name.constantize.new(env).response
|
26
|
-
end
|
27
|
-
rescue
|
28
|
-
error_resp
|
29
|
-
end
|
30
|
-
|
31
|
-
def disabled_validators
|
32
|
-
ClientSideValidations::Config.disabled_validators.map(&:to_s)
|
33
|
-
end
|
34
|
-
|
35
|
-
def error_resp
|
36
|
-
[500, { 'Content-Type' => 'application/json', 'Content-Length' => '0' }, ['']]
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class Base
|
41
|
-
attr_accessor :request, :body, :status
|
42
|
-
|
43
|
-
def initialize(env)
|
44
|
-
# Filter out cache buster
|
45
|
-
env['QUERY_STRING'] = env['QUERY_STRING'].split('&').select { |p| !p.match(/^_=/) }.join('&')
|
46
|
-
self.body = ''
|
47
|
-
self.status = 200
|
48
|
-
self.request = ActionDispatch::Request.new(env)
|
49
|
-
end
|
50
|
-
|
51
|
-
def response
|
52
|
-
[status, { 'Content-Type' => content_type, 'Content-Length' => body.length.to_s }, [body]]
|
53
|
-
end
|
54
|
-
|
55
|
-
def content_type
|
56
|
-
'application/json'
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
class Uniqueness < Base
|
61
|
-
IGNORE_PARAMS = %w(case_sensitive id scope).freeze
|
62
|
-
@@registered_orms = []
|
63
|
-
class NotValidatable < StandardError; end
|
64
|
-
|
65
|
-
def response
|
66
|
-
begin
|
67
|
-
if unique?
|
68
|
-
self.status = 404
|
69
|
-
self.body = 'true'
|
70
|
-
else
|
71
|
-
self.status = 200
|
72
|
-
self.body = 'false'
|
73
|
-
end
|
74
|
-
rescue NotValidatable
|
75
|
-
self.status = 500
|
76
|
-
self.body = ''
|
77
|
-
end
|
78
|
-
super
|
79
|
-
end
|
80
|
-
|
81
|
-
def self.register_orm(orm)
|
82
|
-
registered_orms << orm
|
83
|
-
end
|
84
|
-
|
85
|
-
def self.registered_orms
|
86
|
-
@@registered_orms
|
87
|
-
end
|
88
|
-
|
89
|
-
def registered_orms
|
90
|
-
self.class.registered_orms
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
def unique?
|
96
|
-
convert_scope_value_from_null_to_nil
|
97
|
-
klass, attribute, value = extract_resources
|
98
|
-
middleware_class = nil
|
99
|
-
|
100
|
-
unless Array.wrap(klass._validators[attribute.to_sym]).find { |v| v.kind == :uniqueness }
|
101
|
-
raise NotValidatable
|
102
|
-
end
|
103
|
-
|
104
|
-
registered_orms.each do |orm|
|
105
|
-
if orm.class?(klass)
|
106
|
-
middleware_class = orm
|
107
|
-
break
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
middleware_class.unique?(klass, attribute, value, request.params)
|
112
|
-
end
|
113
|
-
|
114
|
-
def convert_scope_value_from_null_to_nil
|
115
|
-
return unless request.params['scope']
|
116
|
-
request.params['scope'].each do |key, value|
|
117
|
-
request.params['scope'][key] = nil if value == 'null'
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
def extract_resources
|
122
|
-
parent_key = (request.params.keys - IGNORE_PARAMS).first
|
123
|
-
|
124
|
-
if nested?(request.params[parent_key], 1)
|
125
|
-
klass, attribute, value = uproot(request.params[parent_key])
|
126
|
-
klass = klass.classify.constantize
|
127
|
-
else
|
128
|
-
klass = parent_key.classify.constantize
|
129
|
-
attribute = request.params[parent_key].keys.first
|
130
|
-
value = request.params[parent_key][attribute]
|
131
|
-
end
|
132
|
-
|
133
|
-
[klass, attribute, value]
|
134
|
-
end
|
135
|
-
|
136
|
-
def uproot(nested_hash = nil)
|
137
|
-
uproot_helper(nested_hash)[-3..-1]
|
138
|
-
end
|
139
|
-
|
140
|
-
def uproot_helper(nested_hash = nil, keys = [])
|
141
|
-
if nested_hash.respond_to?(:keys)
|
142
|
-
keys << nested_hash.keys.first
|
143
|
-
uproot_helper(nested_hash[nested_hash.keys.first], keys)
|
144
|
-
else
|
145
|
-
keys << nested_hash
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
def nested?(hash = nil, levels = 0)
|
150
|
-
i = 0
|
151
|
-
while hash.respond_to? :keys
|
152
|
-
hash = hash[hash.keys.first]
|
153
|
-
i += 1
|
154
|
-
end
|
155
|
-
i > levels
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|