client_side_validations 3.2.8 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,5 +1,7 @@
|
|
1
|
-
module ClientSideValidations
|
2
|
-
module
|
1
|
+
module ClientSideValidations
|
2
|
+
module ActionView
|
3
|
+
module Helpers
|
4
|
+
end
|
3
5
|
end
|
4
6
|
end
|
5
7
|
|
@@ -11,4 +13,3 @@ require 'client_side_validations/action_view/form_builder'
|
|
11
13
|
ActionView::Base.send(:include, ClientSideValidations::ActionView::Helpers::FormHelper)
|
12
14
|
ActionView::Base.send(:include, ClientSideValidations::ActionView::Helpers::FormTagHelper)
|
13
15
|
ActionView::Helpers::FormBuilder.send(:include, ClientSideValidations::ActionView::Helpers::FormBuilder)
|
14
|
-
|
@@ -1,104 +1,121 @@
|
|
1
|
-
module ClientSideValidations
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
module ClientSideValidations
|
2
|
+
module ActionView
|
3
|
+
module Helpers
|
4
|
+
module FormBuilder
|
5
|
+
def self.included(base)
|
6
|
+
(base.field_helpers.map(&:to_s) - %w(apply_form_for_options! label check_box radio_button fields_for hidden_field)).each do |selector|
|
7
|
+
base.class_eval <<-RUBY_EVAL
|
8
|
+
def #{selector}_with_client_side_validations(method, options = {})
|
9
|
+
build_validation_options(method, options)
|
10
|
+
options.delete(:validate)
|
11
|
+
#{selector}_without_client_side_validations(method, options)
|
12
|
+
end
|
13
|
+
RUBY_EVAL
|
14
|
+
|
15
|
+
base.class_eval { alias_method_chain selector, :client_side_validations }
|
11
16
|
end
|
12
|
-
RUBY_EVAL
|
13
17
|
|
14
|
-
|
15
|
-
|
18
|
+
base.class_eval do
|
19
|
+
alias_method_chain :initialize, :client_side_validations
|
20
|
+
alias_method_chain :fields_for, :client_side_validations
|
21
|
+
alias_method_chain :check_box, :client_side_validations
|
22
|
+
alias_method_chain :radio_button, :client_side_validations
|
23
|
+
alias_method_chain :select, :client_side_validations
|
24
|
+
alias_method_chain :collection_select, :client_side_validations
|
25
|
+
alias_method_chain :collection_check_boxes, :client_side_validations
|
26
|
+
alias_method_chain :collection_radio_buttons, :client_side_validations
|
27
|
+
alias_method_chain :grouped_collection_select, :client_side_validations
|
28
|
+
alias_method_chain :time_zone_select, :client_side_validations
|
29
|
+
end
|
30
|
+
end
|
16
31
|
|
17
|
-
|
18
|
-
alias_method_chain :initialize, :client_side_validations
|
19
|
-
alias_method_chain :fields_for, :client_side_validations
|
20
|
-
alias_method_chain :check_box, :client_side_validations
|
21
|
-
alias_method_chain :radio_button, :client_side_validations
|
22
|
-
alias_method_chain :select, :client_side_validations
|
23
|
-
alias_method_chain :collection_select, :client_side_validations
|
24
|
-
alias_method_chain :grouped_collection_select, :client_side_validations
|
25
|
-
alias_method_chain :time_zone_select, :client_side_validations
|
26
|
-
|
27
|
-
def client_side_form_settings(options, form_helper)
|
32
|
+
def client_side_form_settings(_options, form_helper)
|
28
33
|
{
|
29
|
-
:
|
30
|
-
:
|
31
|
-
:
|
34
|
+
type: self.class.to_s,
|
35
|
+
input_tag: form_helper.class.field_error_proc.call(%(<span id="input_tag" />), Struct.new(:error_message, :tag_id).new([], '')),
|
36
|
+
label_tag: form_helper.class.field_error_proc.call(%(<label id="label_tag" />), Struct.new(:error_message, :tag_id).new([], ''))
|
32
37
|
}
|
33
38
|
end
|
34
|
-
end
|
35
|
-
end
|
36
39
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
40
|
+
def validate(*attrs)
|
41
|
+
options = attrs.pop if attrs.last.is_a?(Hash)
|
42
|
+
(attrs.present? ? attrs : @object._validators.keys).each do |attr|
|
43
|
+
build_validation_options(attr, validate: options)
|
44
|
+
end
|
45
|
+
nil
|
46
|
+
end
|
44
47
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
48
|
+
def initialize_with_client_side_validations(object_name, object, *args)
|
49
|
+
initialize_without_client_side_validations(object_name, object, *args)
|
50
|
+
@options[:validators] = { object => {} }
|
51
|
+
end
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
def fields_for_with_client_side_validations(record_or_name_or_array, *args, &block)
|
54
|
+
options = args.extract_options!
|
55
|
+
options[:validate] ||= @options[:validate] if @options[:validate] && !options.key?(:validate)
|
56
|
+
fields_for_without_client_side_validations(record_or_name_or_array, *(args << options), &block)
|
57
|
+
end
|
55
58
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
59
|
+
def check_box_with_client_side_validations(method, options = {}, checked_value = '1', unchecked_value = '0')
|
60
|
+
build_validation_options(method, options)
|
61
|
+
options.delete(:validate)
|
62
|
+
check_box_without_client_side_validations(method, options, checked_value, unchecked_value)
|
63
|
+
end
|
61
64
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
65
|
+
def radio_button_with_client_side_validations(method, tag_value, options = {})
|
66
|
+
build_validation_options(method, options)
|
67
|
+
options.delete(:validate)
|
68
|
+
radio_button_without_client_side_validations(method, tag_value, options)
|
69
|
+
end
|
67
70
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
71
|
+
def select_with_client_side_validations(method, choices, options = {}, html_options = {}, &block)
|
72
|
+
build_validation_options(method, html_options.merge(name: options[:name]))
|
73
|
+
html_options.delete(:validate)
|
74
|
+
select_without_client_side_validations(method, choices, options, html_options, &block)
|
75
|
+
end
|
73
76
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
77
|
+
def collection_select_with_client_side_validations(method, collection, value_method, text_method, options = {}, html_options = {})
|
78
|
+
build_validation_options(method, html_options.merge(name: options[:name]))
|
79
|
+
html_options.delete(:validate)
|
80
|
+
collection_select_without_client_side_validations(method, collection, value_method, text_method, options, html_options)
|
81
|
+
end
|
79
82
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
83
|
+
def collection_check_boxes_with_client_side_validations(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
84
|
+
build_validation_options(method, html_options.merge(name: options[:name]))
|
85
|
+
html_options.delete(:validate)
|
86
|
+
collection_check_boxes_without_client_side_validations(method, collection, value_method, text_method, options, html_options, &block)
|
87
|
+
end
|
85
88
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
89
|
+
def collection_radio_buttons_with_client_side_validations(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
90
|
+
build_validation_options(method, html_options.merge(name: options[:name]))
|
91
|
+
html_options.delete(:validate)
|
92
|
+
collection_radio_buttons_without_client_side_validations(method, collection, value_method, text_method, options, html_options, &block)
|
93
|
+
end
|
94
|
+
|
95
|
+
def grouped_collection_select_with_client_side_validations(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
96
|
+
build_validation_options(method, html_options.merge(name: options[:name]))
|
97
|
+
html_options.delete(:validate)
|
98
|
+
grouped_collection_select_without_client_side_validations(method, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
99
|
+
end
|
91
100
|
|
92
|
-
|
101
|
+
def time_zone_select_with_client_side_validations(method, priority_zones = nil, options = {}, html_options = {})
|
102
|
+
build_validation_options(method, html_options.merge(name: options[:name]))
|
103
|
+
html_options.delete(:validate)
|
104
|
+
time_zone_select_without_client_side_validations(method, priority_zones, options, html_options)
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
93
108
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
109
|
+
def build_validation_options(method, options = {})
|
110
|
+
return unless @options[:validate]
|
111
|
+
|
112
|
+
index = @default_options[:index].present? ? "[#{@default_options[:index]}]" : ''
|
113
|
+
name = options[:name] || "#{@object_name}#{index}[#{method}]"
|
114
|
+
child_index = @options[:child_index] ? "(\\d+|#{Regexp.escape(@options[:child_index].to_s)})" : '\\d+'
|
115
|
+
name = name.to_s.gsub(/_attributes\]\[#{child_index}\]/, '_attributes][]')
|
116
|
+
name = "#{name}#{options[:multiple] ? '[]' : nil}"
|
117
|
+
@options[:validators][@object][method] = { name: name, options: options[:validate] }
|
118
|
+
end
|
102
119
|
end
|
103
120
|
end
|
104
121
|
end
|
@@ -1,146 +1,135 @@
|
|
1
|
-
module ClientSideValidations
|
2
|
-
module
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
options = args.extract_options!
|
7
|
-
if options[:validate]
|
8
|
-
|
9
|
-
# Always turn off HTML5 Validations
|
10
|
-
options[:html] ||= {}
|
11
|
-
options[:html][:novalidate] = 'novalidate'
|
12
|
-
|
13
|
-
case record
|
14
|
-
when String, Symbol
|
15
|
-
raise ClientSideValidations::ActionView::Helpers::FormHelper::Error, 'Using form_for(:name, @resource) is not supported with ClientSideValidations. Please use form_for(@resource, :as => :name) instead.'
|
16
|
-
else
|
17
|
-
object = record.is_a?(Array) ? record.last : record
|
1
|
+
module ClientSideValidations
|
2
|
+
module ActionView
|
3
|
+
module Helpers
|
4
|
+
module FormHelper
|
5
|
+
class Error < StandardError
|
18
6
|
end
|
19
|
-
end
|
20
7
|
|
21
|
-
|
8
|
+
def form_for(record, options = {}, &block)
|
9
|
+
fail ArgumentError, 'Missing block' unless block_given?
|
10
|
+
if options[:validate]
|
22
11
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
build_bound_validators(options)
|
27
|
-
options[:id] = html_id if html_id
|
28
|
-
script = client_side_form_settings(object, options)
|
12
|
+
# Always turn off HTML5 Validations
|
13
|
+
options[:html] ||= {}
|
14
|
+
options[:html][:novalidate] = 'novalidate'
|
29
15
|
|
30
|
-
|
31
|
-
|
32
|
-
|
16
|
+
case record
|
17
|
+
when String, Symbol
|
18
|
+
fail ClientSideValidations::ActionView::Helpers::FormHelper::Error, 'Using form_for(:name, @resource) is not supported with ClientSideValidations. Please use form_for(@resource, as: :name) instead.'
|
19
|
+
else
|
20
|
+
object = record.is_a?(Array) ? record.last : record
|
21
|
+
object_name = options[:as] || model_name_from_record_or_class(object).param_key
|
22
|
+
end
|
23
|
+
end
|
33
24
|
|
34
|
-
|
35
|
-
form.html_safe
|
36
|
-
else
|
37
|
-
"#{form}#{script}".html_safe
|
38
|
-
end
|
39
|
-
end
|
25
|
+
@validators = {}
|
40
26
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
27
|
+
# Order matters here. Rails mutates the options object
|
28
|
+
html_id = options[:html][:id] if options[:html]
|
29
|
+
form = super(record, options, &block)
|
30
|
+
options[:id] = html_id if html_id
|
47
31
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
32
|
+
build_bound_validators options
|
33
|
+
builder = instantiate_builder(object_name, object, options) if object_name && object
|
34
|
+
script = client_side_form_settings(object, options, builder)
|
52
35
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
output
|
57
|
-
end
|
36
|
+
# Because of the load order requirement above this sub is necessary
|
37
|
+
# Would be nice to not do this
|
38
|
+
script = insert_validators_into_script(script)
|
58
39
|
|
59
|
-
|
60
|
-
|
61
|
-
def build_bound_validators(options)
|
62
|
-
if @validators
|
63
|
-
options[:validators].each do |key, value|
|
64
|
-
if @validators.key?(key)
|
65
|
-
@validators[key].merge! value
|
40
|
+
if assign_script_to_content_for(options[:validate], script)
|
41
|
+
form.html_safe
|
66
42
|
else
|
67
|
-
|
43
|
+
"#{form}#{script}".html_safe
|
68
44
|
end
|
69
45
|
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
def insert_validators_into_script(script)
|
74
|
-
# There is probably a more performant way of doing this
|
75
|
-
# But using String#sub has some issues. Undocumented "features"
|
76
|
-
if script
|
77
|
-
script = script.split(/"validator_hash"/)
|
78
|
-
script = "#{script[0]}#{construct_validators.to_json}#{script[1]}"
|
79
|
-
end
|
80
46
|
|
81
|
-
|
82
|
-
|
47
|
+
def assign_script_to_content_for(name, script)
|
48
|
+
return unless name && name != true
|
49
|
+
content_for(name) { script.html_safe }
|
50
|
+
true
|
51
|
+
end
|
83
52
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
option_hash.merge!(attr[0] => attr[1][:options])
|
53
|
+
def apply_form_for_options!(record, object, options)
|
54
|
+
super
|
55
|
+
options[:html][:validate] = true if options[:validate]
|
88
56
|
end
|
89
57
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
58
|
+
def fields_for(record_or_name_or_array, record_object = nil, options = {}, &block)
|
59
|
+
output = super
|
60
|
+
build_bound_validators options
|
61
|
+
output
|
94
62
|
end
|
95
63
|
|
96
|
-
|
97
|
-
|
98
|
-
|
64
|
+
private
|
65
|
+
|
66
|
+
def build_bound_validators(options)
|
67
|
+
return unless @validators
|
68
|
+
options[:validators].each do |key, value|
|
69
|
+
if @validators.key?(key)
|
70
|
+
@validators[key].merge! value
|
71
|
+
else
|
72
|
+
@validators[key] = value
|
73
|
+
end
|
99
74
|
end
|
100
75
|
end
|
101
76
|
|
102
|
-
|
103
|
-
|
104
|
-
|
77
|
+
def insert_validators_into_script(script)
|
78
|
+
# There is probably a more performant way of doing this
|
79
|
+
# But using String#sub has some issues. Undocumented "features"
|
80
|
+
if script
|
81
|
+
script = script.split(/"validator_hash"/)
|
82
|
+
script = "#{script[0]}#{construct_validators.to_json}#{script[1]}"
|
83
|
+
end
|
105
84
|
|
106
|
-
|
107
|
-
|
108
|
-
builder = options[:parent_builder]
|
85
|
+
script
|
86
|
+
end
|
109
87
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
var_name = if object.respond_to?(:persisted?) && object.persisted?
|
115
|
-
options[:as] ? "edit_#{options[:as]}" : [options[:namespace], dom_id(object, :edit)].compact.join("_")
|
116
|
-
else
|
117
|
-
options[:as] ? "new_#{options[:as]}" : [options[:namespace], dom_id(object)].compact.join("_")
|
88
|
+
def construct_validators
|
89
|
+
@validators.each_with_object({}) do |object_opts, validator_hash|
|
90
|
+
option_hash = object_opts[1].each_with_object({}) do |attr, result|
|
91
|
+
result[attr[0]] = attr[1][:options]
|
118
92
|
end
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
var_name = if object.respond_to?(:persisted?) && object.persisted?
|
123
|
-
options[:as] ? "#{options[:as]}_edit" : dom_id(object, :edit)
|
93
|
+
|
94
|
+
if object_opts[0].respond_to?(:client_side_validation_hash)
|
95
|
+
validation_hash = object_opts[0].client_side_validation_hash(option_hash)
|
124
96
|
else
|
125
|
-
|
97
|
+
validation_hash = {}
|
98
|
+
end
|
99
|
+
|
100
|
+
option_hash.each_key do |attr|
|
101
|
+
if validation_hash[attr]
|
102
|
+
validator_hash.merge!(object_opts[1][attr][:name] => validation_hash[attr])
|
103
|
+
end
|
126
104
|
end
|
127
105
|
end
|
128
106
|
end
|
129
107
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
108
|
+
def client_side_form_settings(object, options, builder)
|
109
|
+
return unless options[:validate]
|
110
|
+
if options[:id]
|
111
|
+
var_name = options[:id]
|
112
|
+
else
|
113
|
+
var_name =
|
114
|
+
if object.respond_to?(:persisted?) && object.persisted?
|
115
|
+
options[:as] ? "edit_#{options[:as]}" : [options[:namespace], dom_id(object, :edit)].compact.join('_'.freeze)
|
116
|
+
else
|
117
|
+
options[:as] ? "new_#{options[:as]}" : [options[:namespace], dom_id(object)].compact.join('_'.freeze)
|
118
|
+
end
|
119
|
+
end
|
136
120
|
|
121
|
+
if ClientSideValidations::Config.number_format_with_locale && defined?(I18n)
|
122
|
+
number_format = I18n.t('number.format').slice(:separator, :delimiter)
|
123
|
+
else
|
124
|
+
number_format = { separator: '.', delimiter: ',' }
|
125
|
+
end
|
126
|
+
patterns = { numericality: "/^(-|\\+)?(?:\\d+|\\d{1,3}(?:\\#{number_format[:delimiter]}\\d{3})+)(?:\\#{number_format[:separator]}\\d*)?$/" }
|
137
127
|
|
138
|
-
|
139
|
-
|
128
|
+
content_tag(:script) do
|
129
|
+
"//<![CDATA[\nif(window.ClientSideValidations===undefined)window.ClientSideValidations={};window.ClientSideValidations.disabled_validators=#{ClientSideValidations::Config.disabled_validators.to_json};window.ClientSideValidations.number_format=#{number_format.to_json};if(window.ClientSideValidations.patterns===undefined)window.ClientSideValidations.patterns = {};window.ClientSideValidations.patterns.numericality=#{patterns[:numericality]};#{"if(window.ClientSideValidations.remote_validators_prefix===undefined)window.ClientSideValidations.remote_validators_prefix='#{(ClientSideValidations::Config.root_path).sub(%r{/+\Z}, '')}';" if ClientSideValidations::Config.root_path.present?}if(window.ClientSideValidations.forms===undefined)window.ClientSideValidations.forms={};window.ClientSideValidations.forms['#{var_name}'] = #{builder.client_side_form_settings(options, self).merge(validators: 'validator_hash').to_json};\n//]]>".html_safe
|
130
|
+
end
|
140
131
|
end
|
141
132
|
end
|
142
133
|
end
|
143
|
-
|
144
134
|
end
|
145
135
|
end
|
146
|
-
|