client_side_validations 3.2.8 → 4.2.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/HISTORY.md +46 -0
- data/README.md +540 -0
- data/lib/client_side_validations.rb +0 -1
- data/lib/client_side_validations/action_view.rb +4 -3
- data/lib/client_side_validations/action_view/form_builder.rb +101 -84
- data/lib/client_side_validations/action_view/form_helper.rb +99 -110
- data/lib/client_side_validations/action_view/form_tag_helper.rb +13 -9
- data/lib/client_side_validations/active_model.rb +95 -80
- data/lib/client_side_validations/active_model/absence.rb +11 -0
- data/lib/client_side_validations/active_model/acceptance.rb +7 -6
- data/lib/client_side_validations/active_model/exclusion.rb +4 -24
- data/lib/client_side_validations/active_model/format.rb +24 -23
- data/lib/client_side_validations/active_model/inclusion.rb +4 -23
- data/lib/client_side_validations/active_model/length.rb +15 -15
- data/lib/client_side_validations/active_model/numericality.rb +23 -22
- data/lib/client_side_validations/active_model/presence.rb +7 -6
- data/lib/client_side_validations/active_record.rb +2 -2
- data/lib/client_side_validations/active_record/middleware.rb +41 -39
- data/lib/client_side_validations/active_record/uniqueness.rb +24 -23
- data/lib/client_side_validations/config.rb +1 -1
- data/lib/client_side_validations/core_ext/range.rb +1 -2
- data/lib/client_side_validations/core_ext/regexp.rb +6 -4
- data/lib/client_side_validations/engine.rb +0 -1
- data/lib/client_side_validations/generators.rb +2 -3
- data/lib/client_side_validations/generators/rails_validations.rb +2 -3
- data/lib/client_side_validations/middleware.rb +17 -24
- data/lib/client_side_validations/version.rb +1 -1
- data/lib/generators/client_side_validations/copy_assets_generator.rb +7 -11
- data/lib/generators/client_side_validations/install_generator.rb +0 -2
- data/lib/generators/templates/client_side_validations/initializer.rb +1 -2
- data/vendor/assets/javascripts/rails.validations.js +26 -20
- metadata +173 -48
- data/client_side_validations.gemspec +0 -30
@@ -1,25 +1,27 @@
|
|
1
|
-
module ClientSideValidations
|
2
|
-
module
|
1
|
+
module ClientSideValidations
|
2
|
+
module ActiveModel
|
3
|
+
module Numericality
|
4
|
+
OPTION_MAP = {}
|
3
5
|
|
4
|
-
|
6
|
+
def self.included(base)
|
7
|
+
OPTION_MAP.merge!(base::CHECKS.keys.inject({}) { |a, e| a.merge!(e => e) })
|
8
|
+
end
|
5
9
|
|
6
|
-
|
7
|
-
|
8
|
-
|
10
|
+
def client_side_hash(model, attribute, force = nil)
|
11
|
+
options = self.options.dup
|
12
|
+
hash = { messages: { numericality: model.errors.generate_message(attribute, :not_a_number, options) } }
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
if options[:only_integer]
|
15
|
+
hash[:messages][:only_integer] = model.errors.generate_message(attribute, :not_an_integer, options)
|
16
|
+
hash[:only_integer] = true
|
17
|
+
end
|
13
18
|
|
14
|
-
|
15
|
-
hash[:messages][:only_integer] = model.errors.generate_message(attribute, :not_an_integer, options)
|
16
|
-
hash[:only_integer] = true
|
17
|
-
end
|
19
|
+
hash[:allow_blank] = true if options[:allow_nil] || options[:allow_blank]
|
18
20
|
|
19
|
-
|
21
|
+
OPTION_MAP.each do |option, message_type|
|
22
|
+
count = options[option]
|
23
|
+
next unless count
|
20
24
|
|
21
|
-
OPTION_MAP.each do |option, message_type|
|
22
|
-
if count = options[option]
|
23
25
|
if count.respond_to?(:call)
|
24
26
|
if force
|
25
27
|
count = count.call(model)
|
@@ -27,16 +29,15 @@ module ClientSideValidations::ActiveModel
|
|
27
29
|
next
|
28
30
|
end
|
29
31
|
end
|
30
|
-
|
32
|
+
|
33
|
+
hash[:messages][option] = model.errors.generate_message(attribute, message_type, options.merge(count: count))
|
31
34
|
hash[option] = count
|
32
35
|
end
|
33
|
-
end
|
34
36
|
|
35
|
-
|
37
|
+
copy_conditional_attributes(hash, options)
|
36
38
|
|
37
|
-
|
39
|
+
hash
|
40
|
+
end
|
38
41
|
end
|
39
|
-
|
40
42
|
end
|
41
43
|
end
|
42
|
-
|
@@ -2,10 +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
|
5
|
+
%w(uniqueness).each do |validator|
|
6
6
|
require "client_side_validations/active_record/#{validator}"
|
7
7
|
validator.capitalize!
|
8
|
-
|
8
|
+
ActiveRecord::Validations.const_get("#{validator}Validator").send :include, ClientSideValidations::ActiveRecord.const_get(validator)
|
9
9
|
end
|
10
10
|
|
11
11
|
ActiveRecord::Base.send(:include, ClientSideValidations::ActiveModel::Validations)
|
@@ -1,51 +1,53 @@
|
|
1
|
-
module ClientSideValidations
|
2
|
-
|
1
|
+
module ClientSideValidations
|
2
|
+
module ActiveRecord
|
3
|
+
class Middleware
|
4
|
+
def self.class?(klass)
|
5
|
+
klass.abstract_class.blank? && klass < ::ActiveRecord::Base
|
6
|
+
end
|
3
7
|
|
4
|
-
|
5
|
-
|
6
|
-
|
8
|
+
def self.unique?(klass, attribute, value, params)
|
9
|
+
klass = find_topmost_superclass(klass)
|
10
|
+
column = klass.columns_hash[attribute.to_s]
|
11
|
+
value = type_cast_value(column, value)
|
12
|
+
value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if value.is_a?(String)
|
7
13
|
|
8
|
-
|
9
|
-
klass = find_topmost_superclass(klass)
|
10
|
-
value = type_cast_value(klass, attribute, value)
|
11
|
-
column = klass.columns_hash[attribute.to_s]
|
12
|
-
value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?
|
13
|
-
|
14
|
-
t = klass.arel_table
|
15
|
-
|
16
|
-
sql = []
|
17
|
-
if params[:case_sensitive] == 'true'
|
18
|
-
sql << 'BINARY' if t.engine.connection.adapter_name =~ /^mysql/i
|
19
|
-
sql << t[attribute].eq(value).to_sql
|
20
|
-
else
|
21
|
-
escaped_value = value.gsub(/[%_]/, '\\\\\0')
|
22
|
-
sql << "#{t[attribute].matches(escaped_value).to_sql} ESCAPE '\\'"
|
23
|
-
end
|
14
|
+
t = klass.arel_table
|
24
15
|
|
25
|
-
|
16
|
+
sql = []
|
17
|
+
if params[:case_sensitive] == 'true'
|
18
|
+
sql << 'BINARY' if t.engine.connection.adapter_name =~ /^mysql/i
|
19
|
+
sql << t[attribute].eq(value).to_sql
|
20
|
+
else
|
21
|
+
escaped_value = value.gsub(/[%_]/, '\\\\\0')
|
22
|
+
sql << "#{t[attribute].matches(escaped_value).to_sql} ESCAPE '\\'"
|
23
|
+
end
|
26
24
|
|
27
|
-
|
28
|
-
value = type_cast_value(klass, attribute, value)
|
29
|
-
sql << "AND #{t[attribute].eq(value).to_sql}"
|
30
|
-
end
|
25
|
+
sql << "AND #{t[klass.primary_key].not_eq(params[:id]).to_sql}" if params[:id]
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
|
27
|
+
(params[:scope] || {}).each do |scope_attribute, scope_value|
|
28
|
+
scope_value = type_cast_value(klass.columns_hash[scope_attribute], scope_value)
|
29
|
+
sql << "AND #{t[scope_attribute].eq(scope_value).to_sql}"
|
30
|
+
end
|
35
31
|
|
36
|
-
|
32
|
+
relation = Arel::Nodes::SqlLiteral.new(sql.join(' '))
|
33
|
+
!klass.where(relation).exists?
|
34
|
+
end
|
37
35
|
|
38
|
-
|
39
|
-
|
40
|
-
|
36
|
+
def self.type_cast_value(column, value)
|
37
|
+
if column.respond_to?(:type_cast)
|
38
|
+
column.type_cast(value)
|
39
|
+
else
|
40
|
+
column.type_cast_from_database(value)
|
41
|
+
end
|
42
|
+
end
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
def self.find_topmost_superclass(klass)
|
45
|
+
if class?(klass.superclass)
|
46
|
+
find_topmost_superclass(klass.superclass)
|
47
|
+
else
|
48
|
+
klass
|
49
|
+
end
|
47
50
|
end
|
48
51
|
end
|
49
|
-
|
50
52
|
end
|
51
53
|
end
|
@@ -1,32 +1,33 @@
|
|
1
|
-
module ClientSideValidations
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
module ClientSideValidations
|
2
|
+
module ActiveRecord
|
3
|
+
module Uniqueness
|
4
|
+
def client_side_hash(model, attribute, _force = nil)
|
5
|
+
hash = {}
|
6
|
+
hash[:message] = model.errors.generate_message(attribute, message_type, options.except(:scope))
|
7
|
+
hash[:case_sensitive] = options[:case_sensitive]
|
8
|
+
hash[:id] = model.id unless model.new_record?
|
9
|
+
hash[:allow_blank] = true if options[:allow_blank]
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
if options.key?(:client_validations) && options[:client_validations].key?(:class)
|
12
|
+
hash[:class] = options[:client_validations][:class].underscore
|
13
|
+
elsif model.class.name.demodulize != model.class.name
|
14
|
+
hash[:class] = model.class.name.underscore
|
15
|
+
end
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
17
|
+
if options.key?(:scope) && options[:scope].present?
|
18
|
+
hash[:scope] = Array.wrap(options[:scope]).inject({}) do |scope_hash, scope_item|
|
19
|
+
scope_hash.merge!(scope_item => model.send(scope_item))
|
20
|
+
end
|
19
21
|
end
|
20
|
-
end
|
21
22
|
|
22
|
-
|
23
|
-
|
23
|
+
hash
|
24
|
+
end
|
24
25
|
|
25
|
-
|
26
|
+
private
|
26
27
|
|
27
|
-
|
28
|
-
|
28
|
+
def message_type
|
29
|
+
:taken
|
30
|
+
end
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|
32
|
-
|
@@ -1,13 +1,15 @@
|
|
1
|
+
require 'js_regex'
|
2
|
+
|
1
3
|
class Regexp
|
2
|
-
def as_json(
|
3
|
-
|
4
|
+
def as_json(*)
|
5
|
+
JsRegex.new(self).to_h
|
4
6
|
end
|
5
7
|
|
6
8
|
def to_json(options = nil)
|
7
|
-
as_json(options)
|
9
|
+
as_json(options)
|
8
10
|
end
|
9
11
|
|
10
|
-
def encode_json(
|
12
|
+
def encode_json(_encoder)
|
11
13
|
inspect
|
12
14
|
end
|
13
15
|
end
|
@@ -1,12 +1,11 @@
|
|
1
1
|
module ClientSideValidations
|
2
2
|
module Generators
|
3
|
-
|
3
|
+
ASSETS = []
|
4
4
|
|
5
5
|
def self.register_assets(klass)
|
6
|
-
|
6
|
+
ASSETS.push(*klass.assets)
|
7
7
|
end
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
require 'client_side_validations/generators/rails_validations'
|
12
|
-
|
@@ -3,8 +3,8 @@ module ClientSideValidations
|
|
3
3
|
class RailsValidations
|
4
4
|
def self.assets
|
5
5
|
[{
|
6
|
-
:
|
7
|
-
:
|
6
|
+
path: File.expand_path('../../../../vendor/assets/javascripts', __FILE__),
|
7
|
+
file: 'rails.validations.js'
|
8
8
|
}]
|
9
9
|
end
|
10
10
|
|
@@ -12,4 +12,3 @@ module ClientSideValidations
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
15
|
-
|
@@ -1,9 +1,6 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
require 'client_side_validations/core_ext'
|
4
2
|
|
5
3
|
module ClientSideValidations
|
6
|
-
|
7
4
|
module Middleware
|
8
5
|
class Validators
|
9
6
|
def initialize(app)
|
@@ -11,7 +8,8 @@ module ClientSideValidations
|
|
11
8
|
end
|
12
9
|
|
13
10
|
def call(env)
|
14
|
-
|
11
|
+
matches = %r{\A/validators/(\w+)\z}.match(env['PATH_INFO'])
|
12
|
+
if matches
|
15
13
|
process_request(matches.captures.first, env)
|
16
14
|
else
|
17
15
|
@app.call(env)
|
@@ -19,23 +17,23 @@ module ClientSideValidations
|
|
19
17
|
end
|
20
18
|
|
21
19
|
def process_request(validation, env)
|
22
|
-
if disabled_validators.include?(validation
|
20
|
+
if disabled_validators.include?(validation)
|
23
21
|
error_resp
|
24
22
|
else
|
25
23
|
klass_name = validation.camelize
|
26
24
|
klass_name = "::ClientSideValidations::Middleware::#{klass_name}"
|
27
25
|
klass_name.constantize.new(env).response
|
28
26
|
end
|
29
|
-
rescue
|
27
|
+
rescue
|
30
28
|
error_resp
|
31
29
|
end
|
32
30
|
|
33
31
|
def disabled_validators
|
34
|
-
ClientSideValidations::Config.disabled_validators.map
|
32
|
+
ClientSideValidations::Config.disabled_validators.map(&:to_s)
|
35
33
|
end
|
36
34
|
|
37
35
|
def error_resp
|
38
|
-
[500, {'Content-Type' => 'application/json', 'Content-Length' => '0'}, ['']]
|
36
|
+
[500, { 'Content-Type' => 'application/json', 'Content-Length' => '0' }, ['']]
|
39
37
|
end
|
40
38
|
end
|
41
39
|
|
@@ -51,7 +49,7 @@ module ClientSideValidations
|
|
51
49
|
end
|
52
50
|
|
53
51
|
def response
|
54
|
-
[status, {'Content-Type' => content_type, 'Content-Length' => body.length.to_s}, [body]]
|
52
|
+
[status, { 'Content-Type' => content_type, 'Content-Length' => body.length.to_s }, [body]]
|
55
53
|
end
|
56
54
|
|
57
55
|
def content_type
|
@@ -60,13 +58,13 @@ module ClientSideValidations
|
|
60
58
|
end
|
61
59
|
|
62
60
|
class Uniqueness < Base
|
63
|
-
IGNORE_PARAMS = %w
|
61
|
+
IGNORE_PARAMS = %w(case_sensitive id scope)
|
64
62
|
REGISTERED_ORMS = []
|
65
63
|
class NotValidatable < StandardError; end
|
66
64
|
|
67
65
|
def response
|
68
66
|
begin
|
69
|
-
if
|
67
|
+
if unique?
|
70
68
|
self.status = 404
|
71
69
|
self.body = 'true'
|
72
70
|
else
|
@@ -94,32 +92,29 @@ module ClientSideValidations
|
|
94
92
|
|
95
93
|
private
|
96
94
|
|
97
|
-
def
|
95
|
+
def unique?
|
98
96
|
convert_scope_value_from_null_to_nil
|
99
97
|
klass, attribute, value = extract_resources
|
100
98
|
middleware_class = nil
|
101
99
|
|
102
100
|
unless Array.wrap(klass._validators[attribute.to_sym]).find { |v| v.kind == :uniqueness }
|
103
|
-
|
101
|
+
fail NotValidatable
|
104
102
|
end
|
105
103
|
|
106
104
|
registered_orms.each do |orm|
|
107
|
-
if orm.
|
105
|
+
if orm.class?(klass)
|
108
106
|
middleware_class = orm
|
109
107
|
break
|
110
108
|
end
|
111
109
|
end
|
112
110
|
|
113
|
-
middleware_class.
|
111
|
+
middleware_class.unique?(klass, attribute, value, request.params)
|
114
112
|
end
|
115
113
|
|
116
114
|
def convert_scope_value_from_null_to_nil
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
request.params['scope'][key] = nil
|
121
|
-
end
|
122
|
-
end
|
115
|
+
return unless request.params['scope']
|
116
|
+
request.params['scope'].each do |key, value|
|
117
|
+
request.params['scope'][key] = nil if value == 'null'
|
123
118
|
end
|
124
119
|
end
|
125
120
|
|
@@ -153,14 +148,12 @@ module ClientSideValidations
|
|
153
148
|
|
154
149
|
def nested?(hash = nil, levels = 0)
|
155
150
|
i = 0
|
156
|
-
|
151
|
+
while hash.respond_to? :keys
|
157
152
|
hash = hash[hash.keys.first]
|
158
153
|
i += 1
|
159
154
|
end
|
160
155
|
i > levels
|
161
156
|
end
|
162
|
-
|
163
157
|
end
|
164
158
|
end
|
165
159
|
end
|
166
|
-
|