client_side_validations 3.2.0.beta.6 → 3.2.0.rc.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/client_side_validations/action_view/form_builder.rb +15 -105
- data/lib/client_side_validations/action_view/form_helper.rb +24 -8
- data/lib/client_side_validations/active_model.rb +94 -11
- data/lib/client_side_validations/active_model/exclusion.rb +14 -2
- data/lib/client_side_validations/active_model/format.rb +21 -0
- data/lib/client_side_validations/active_model/inclusion.rb +13 -2
- data/lib/client_side_validations/active_model/length.rb +1 -1
- data/lib/client_side_validations/active_model/numericality.rb +8 -1
- data/lib/client_side_validations/active_record/middleware.rb +1 -1
- data/lib/client_side_validations/active_record/uniqueness.rb +1 -1
- data/lib/client_side_validations/core_ext/regexp.rb +1 -2
- data/lib/client_side_validations/version.rb +1 -1
- data/lib/generators/client_side_validations/copy_assets_generator.rb +3 -1
- data/vendor/assets/javascripts/rails.validations.js +342 -331
- metadata +3 -3
@@ -5,7 +5,7 @@ module ClientSideValidations::ActionView::Helpers
|
|
5
5
|
(base.field_helpers.map(&:to_s) - %w(apply_form_for_options! label check_box radio_button fields_for hidden_field)).each do |selector|
|
6
6
|
base.class_eval <<-RUBY_EVAL
|
7
7
|
def #{selector}_with_client_side_validations(method, options = {})
|
8
|
-
|
8
|
+
build_validation_options(method, options)
|
9
9
|
options.delete(:validate)
|
10
10
|
#{selector}_without_client_side_validations(method, options)
|
11
11
|
end
|
@@ -36,7 +36,7 @@ module ClientSideValidations::ActionView::Helpers
|
|
36
36
|
|
37
37
|
def initialize_with_client_side_validations(object_name, object, template, options, proc)
|
38
38
|
initialize_without_client_side_validations(object_name, object, template, options, proc)
|
39
|
-
@options[:validators] = {}
|
39
|
+
@options[:validators] = { object => {} }
|
40
40
|
end
|
41
41
|
|
42
42
|
def fields_for_with_client_side_validations(record_or_name_or_array, *args, &block)
|
@@ -46,137 +46,47 @@ module ClientSideValidations::ActionView::Helpers
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def check_box_with_client_side_validations(method, options = {}, checked_value = "1", unchecked_value = "0")
|
49
|
-
|
49
|
+
build_validation_options(method, options)
|
50
|
+
options.delete(:validate)
|
50
51
|
check_box_without_client_side_validations(method, options, checked_value, unchecked_value)
|
51
52
|
end
|
52
53
|
|
53
54
|
def radio_button_with_client_side_validations(method, tag_value, options = {})
|
54
|
-
|
55
|
+
build_validation_options(method, options)
|
56
|
+
options.delete(:validate)
|
55
57
|
radio_button_without_client_side_validations(method, tag_value, options)
|
56
58
|
end
|
57
59
|
|
58
60
|
def select_with_client_side_validations(method, choices, options = {}, html_options = {})
|
59
|
-
|
61
|
+
build_validation_options(method, html_options.merge(:validate => options[:validate]))
|
60
62
|
select_without_client_side_validations(method, choices, options, html_options)
|
61
63
|
end
|
62
64
|
|
63
65
|
def collection_select_with_client_side_validations(method, collection, value_method, text_method, options = {}, html_options = {})
|
64
|
-
|
66
|
+
build_validation_options(method, html_options.merge(:validate => options[:validate]))
|
65
67
|
collection_select_without_client_side_validations(method, collection, value_method, text_method, options, html_options)
|
66
68
|
end
|
67
69
|
|
68
70
|
def grouped_collection_select_with_client_side_validations(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
69
|
-
|
71
|
+
build_validation_options(method, html_options.merge(:validate => options[:validate]))
|
70
72
|
grouped_collection_select_without_client_side_validations(method, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
71
73
|
end
|
72
74
|
|
73
75
|
def time_zone_select_with_client_side_validations(method, priority_zones = nil, options = {}, html_options = {})
|
74
|
-
|
76
|
+
build_validation_options(method, html_options.merge(:validate => options[:validate]))
|
75
77
|
time_zone_select_without_client_side_validations(method, priority_zones = nil, options, html_options)
|
76
78
|
end
|
77
79
|
|
78
80
|
private
|
79
81
|
|
80
|
-
def
|
81
|
-
if @options[:validate]
|
82
|
-
options.merge!("data-validate" => true)
|
82
|
+
def build_validation_options(method, options = {})
|
83
|
+
if @options[:validate]
|
83
84
|
name = options[:name] || "#{@object_name}[#{method}]"
|
84
85
|
child_index = @options[:child_index] ? "(\\d+|#{Regexp.escape(@options[:child_index])})" : "\\d+"
|
85
|
-
name = name.gsub(/_attributes\]\[#{child_index}\]/, '_attributes][]')
|
86
|
-
|
86
|
+
name = name.to_s.gsub(/_attributes\]\[#{child_index}\]/, '_attributes][]')
|
87
|
+
name = "#{name}#{options[:multiple] ? "[]" : nil}"
|
88
|
+
@options[:validators][@object][method] = { :name => name, :options => options[:validate] }
|
87
89
|
end
|
88
90
|
end
|
89
|
-
|
90
|
-
def filter_validators(method, filters)
|
91
|
-
if validators = @object.client_side_validation_hash[method]
|
92
|
-
unfiltered_validators = validators.inject({}) do |unfiltered_validators, validator|
|
93
|
-
kind = validator.first
|
94
|
-
unfiltered_validators[kind] = validator.last.inject([]) do |validators_array, validator_hash|
|
95
|
-
if has_filter_for_validator?(kind, filters)
|
96
|
-
if filter_validator?(kind, filters)
|
97
|
-
next
|
98
|
-
elsif force_validator_despite_conditional?(kind, filters) && !can_run_validator?(validator_hash, method)
|
99
|
-
next
|
100
|
-
end
|
101
|
-
else
|
102
|
-
if (conditional = (validator_hash[:if] || validator_hash[:unless]))
|
103
|
-
result = case conditional
|
104
|
-
when Symbol
|
105
|
-
if @object.respond_to?(conditional)
|
106
|
-
@object.send(conditional)
|
107
|
-
else
|
108
|
-
raise(ArgumentError, "unknown method called '#{conditional}'")
|
109
|
-
end
|
110
|
-
when String
|
111
|
-
eval(conditional)
|
112
|
-
when Proc
|
113
|
-
conditional.call(@object)
|
114
|
-
end
|
115
|
-
|
116
|
-
# :if was specified and result is false OR :unless was specified and result was true
|
117
|
-
if (validator_hash[:if] && !result) || (validator_hash[:unless] && result)
|
118
|
-
next
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
123
|
-
|
124
|
-
validators_array << validator_hash.clone
|
125
|
-
validators_array.last.delete_if { |key, value| key == :if || key == :unless }
|
126
|
-
validators_array
|
127
|
-
end
|
128
|
-
|
129
|
-
unfiltered_validators
|
130
|
-
end
|
131
|
-
|
132
|
-
unfiltered_validators.delete_if { |k, v| v.nil? }
|
133
|
-
unfiltered_validators.empty? ? nil : unfiltered_validators
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
def has_filter_for_validator?(kind, filters)
|
138
|
-
filters && (filters == true || filters.key?(kind))
|
139
|
-
end
|
140
|
-
|
141
|
-
def filter_validator?(kind, filters)
|
142
|
-
filters != true && filters[kind] == false
|
143
|
-
end
|
144
|
-
|
145
|
-
def force_validator_despite_conditional?(kind, filters)
|
146
|
-
filters == true || filters[kind] == true
|
147
|
-
end
|
148
|
-
|
149
|
-
def can_run_validator?(validator_hash, method)
|
150
|
-
result = true
|
151
|
-
if_result = run_if_validator(validator_hash[:if], method)
|
152
|
-
unless_result = run_unless_validator(validator_hash[:unless], method)
|
153
|
-
result = result && if_result unless if_result.nil?
|
154
|
-
result = result && unless_result unless unless_result.nil?
|
155
|
-
result
|
156
|
-
end
|
157
|
-
|
158
|
-
def run_if_validator(conditional, method)
|
159
|
-
if conditional
|
160
|
-
if conditional.is_a?(Symbol)
|
161
|
-
conditional_method_is_change_method?(conditional, method) ? true : !!@object.send(conditional)
|
162
|
-
else
|
163
|
-
!!conditional.call(@object)
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def run_unless_validator(conditional, method)
|
169
|
-
if conditional
|
170
|
-
if conditional.is_a?(Symbol)
|
171
|
-
conditional_method_is_change_method?(conditional, method) ? true : !@object.send(conditional)
|
172
|
-
else
|
173
|
-
!conditional.call(@object)
|
174
|
-
end
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
def conditional_method_is_change_method?(conditional, method)
|
179
|
-
conditional.to_sym == "#{method}_changed?".to_sym
|
180
|
-
end
|
181
91
|
end
|
182
92
|
end
|
@@ -2,7 +2,7 @@ module ClientSideValidations::ActionView::Helpers
|
|
2
2
|
module FormHelper
|
3
3
|
class Error < StandardError; end
|
4
4
|
|
5
|
-
def form_for(
|
5
|
+
def form_for(record, *args, &proc)
|
6
6
|
options = args.extract_options!
|
7
7
|
if options[:validate]
|
8
8
|
|
@@ -10,13 +10,11 @@ module ClientSideValidations::ActionView::Helpers
|
|
10
10
|
options[:html] ||= {}
|
11
11
|
options[:html][:novalidate] = 'novalidate'
|
12
12
|
|
13
|
-
case
|
13
|
+
case record
|
14
14
|
when String, Symbol
|
15
|
-
raise ClientSideValidations::ActionView::Helpers::FormHelper::Error, 'Using form_for(:name, @resource) is
|
16
|
-
when Array
|
17
|
-
object = record_or_name_or_array.last
|
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.'
|
18
16
|
else
|
19
|
-
object =
|
17
|
+
object = record.is_a?(Array) ? record.last : record
|
20
18
|
end
|
21
19
|
end
|
22
20
|
|
@@ -24,7 +22,7 @@ module ClientSideValidations::ActionView::Helpers
|
|
24
22
|
|
25
23
|
# Order matters here. Rails mutates the options object
|
26
24
|
html_id = options[:html][:id] if options[:html]
|
27
|
-
form = super(
|
25
|
+
form = super(record, *(args << options), &proc)
|
28
26
|
options[:id] = html_id if html_id
|
29
27
|
script = client_side_form_settings(object, options)
|
30
28
|
|
@@ -64,12 +62,30 @@ module ClientSideValidations::ActionView::Helpers
|
|
64
62
|
# But using String#sub has some issues. Undocumented "features"
|
65
63
|
if script
|
66
64
|
script = script.split(/"validator_hash"/)
|
67
|
-
script = "#{script[0]}#{
|
65
|
+
script = "#{script[0]}#{construct_validators.to_json}#{script[1]}"
|
68
66
|
end
|
69
67
|
|
70
68
|
script
|
71
69
|
end
|
72
70
|
|
71
|
+
def construct_validators
|
72
|
+
@validators.inject({}) do |validator_hash, object_opts|
|
73
|
+
option_hash = object_opts[1].inject({}) do |option_hash, attr|
|
74
|
+
option_hash.merge!(attr[0] => attr[1][:options])
|
75
|
+
end
|
76
|
+
|
77
|
+
validation_hash = object_opts[0].client_side_validation_hash(option_hash)
|
78
|
+
|
79
|
+
option_hash.each_key do |attr|
|
80
|
+
if validation_hash[attr]
|
81
|
+
validator_hash.merge!(object_opts[1][attr][:name] => validation_hash[attr])
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
validator_hash
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
73
89
|
def client_side_form_settings(object, options)
|
74
90
|
if options[:validate]
|
75
91
|
builder = options[:parent_builder]
|
@@ -3,9 +3,8 @@ require 'client_side_validations/core_ext'
|
|
3
3
|
module ClientSideValidations::ActiveModel
|
4
4
|
module Validator
|
5
5
|
|
6
|
-
def client_side_hash(model, attribute)
|
7
|
-
|
8
|
-
{ :message => model.errors.generate_message(attribute, message_type, options) }.merge(options.except(*::ActiveModel::Errors::CALLBACKS_OPTIONS - [:allow_blank, :if, :unless]))
|
6
|
+
def client_side_hash(model, attribute, force = nil)
|
7
|
+
build_client_side_hash(model, attribute, self.options.dup)
|
9
8
|
end
|
10
9
|
|
11
10
|
def copy_conditional_attributes(to, from)
|
@@ -14,21 +13,25 @@ module ClientSideValidations::ActiveModel
|
|
14
13
|
|
15
14
|
private
|
16
15
|
|
16
|
+
def build_client_side_hash(model, attribute, options)
|
17
|
+
{ :message => model.errors.generate_message(attribute, message_type, options) }.merge(options.except(*::ActiveModel::Errors::CALLBACKS_OPTIONS - [:allow_blank, :if, :unless]))
|
18
|
+
end
|
19
|
+
|
17
20
|
def message_type
|
18
21
|
kind
|
19
22
|
end
|
20
23
|
end
|
21
24
|
|
22
25
|
module Validations
|
23
|
-
def client_side_validation_hash
|
24
|
-
|
26
|
+
def client_side_validation_hash(force = nil)
|
27
|
+
_validators.inject({}) do |attr_hash, attr|
|
25
28
|
unless [nil, :block].include?(attr[0])
|
26
29
|
|
27
30
|
validator_hash = attr[1].inject(Hash.new { |h,k| h[k] = []}) do |kind_hash, validator|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
31
|
+
if can_use_for_client_side_validation?(attr[0], validator, force)
|
32
|
+
if client_side_hash = validator.client_side_hash(self, attr[0], extract_force_option(attr[0], force))
|
33
|
+
kind_hash[validator.kind] << client_side_hash.except(:on, :if, :unless)
|
34
|
+
end
|
32
35
|
end
|
33
36
|
|
34
37
|
kind_hash
|
@@ -47,8 +50,88 @@ module ClientSideValidations::ActiveModel
|
|
47
50
|
|
48
51
|
private
|
49
52
|
|
50
|
-
def
|
51
|
-
|
53
|
+
def extract_force_option(attr, force)
|
54
|
+
case force
|
55
|
+
when FalseClass, TrueClass, NilClass
|
56
|
+
force
|
57
|
+
when Hash
|
58
|
+
extract_force_option(nil, force[attr])
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def can_use_for_client_side_validation?(attr, validator, force)
|
65
|
+
if validator_turned_off?(attr, validator, force)
|
66
|
+
result = false
|
67
|
+
else
|
68
|
+
# Yeah yeah, #new_record? is not part of ActiveModel :p
|
69
|
+
result = ((self.respond_to?(:new_record?) && validator.options[:on] == (self.new_record? ? :create : :update)) || validator.options[:on].nil?)
|
70
|
+
result = result && validator.kind != :block
|
71
|
+
result = result && uniqueness_validations_allowed_or_not_applicable?(validator)
|
72
|
+
|
73
|
+
if validator.options[:if] || validator.options[:unless]
|
74
|
+
if result = can_force_validator?(attr, validator, force)
|
75
|
+
if validator.options[:if]
|
76
|
+
result = result && run_conditional(validator.options[:if])
|
77
|
+
end
|
78
|
+
if validator.options[:unless]
|
79
|
+
result = result && !run_conditional(validator.options[:unless])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
result
|
86
|
+
end
|
87
|
+
|
88
|
+
def run_conditional(method_name_or_proc)
|
89
|
+
case method_name_or_proc
|
90
|
+
when Proc
|
91
|
+
method_name_or_proc.call(self)
|
92
|
+
else
|
93
|
+
self.send(method_name_or_proc)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def validator_turned_off?(attr, validator, force)
|
98
|
+
case force
|
99
|
+
when FalseClass
|
100
|
+
true
|
101
|
+
when Hash
|
102
|
+
case force[attr]
|
103
|
+
when FalseClass
|
104
|
+
true
|
105
|
+
when Hash
|
106
|
+
force[attr][validator.kind] == false
|
107
|
+
else
|
108
|
+
false
|
109
|
+
end
|
110
|
+
else
|
111
|
+
false
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def can_force_validator?(attr, validator, force)
|
116
|
+
case force
|
117
|
+
when TrueClass
|
118
|
+
true
|
119
|
+
when Hash
|
120
|
+
case force[attr]
|
121
|
+
when TrueClass
|
122
|
+
true
|
123
|
+
when Hash
|
124
|
+
force[attr][validator.kind]
|
125
|
+
else
|
126
|
+
false
|
127
|
+
end
|
128
|
+
else
|
129
|
+
if (validator.options[:if] || validator.options[:unless]) =~ /changed\?/
|
130
|
+
true
|
131
|
+
else
|
132
|
+
false
|
133
|
+
end
|
134
|
+
end
|
52
135
|
end
|
53
136
|
|
54
137
|
def uniqueness_validations_allowed_or_not_applicable?(validator)
|
@@ -1,12 +1,24 @@
|
|
1
1
|
module ClientSideValidations::ActiveModel
|
2
2
|
module Exclusion
|
3
3
|
|
4
|
-
def client_side_hash(model, attribute)
|
5
|
-
|
4
|
+
def client_side_hash(model, attribute, force = nil)
|
5
|
+
if options[:in].respond_to?(:call)
|
6
|
+
if force
|
7
|
+
options = self.options.dup
|
8
|
+
options[:in] = options[:in].call(model)
|
9
|
+
hash = build_client_side_hash(model, attribute, options)
|
10
|
+
else
|
11
|
+
return
|
12
|
+
end
|
13
|
+
else
|
14
|
+
hash = build_client_side_hash(model, attribute, self.options.dup)
|
15
|
+
end
|
16
|
+
|
6
17
|
if hash[:in].is_a?(Range)
|
7
18
|
hash[:range] = hash[:in]
|
8
19
|
hash.delete(:in)
|
9
20
|
end
|
21
|
+
|
10
22
|
hash
|
11
23
|
end
|
12
24
|
|
@@ -1,5 +1,26 @@
|
|
1
1
|
module ClientSideValidations::ActiveModel
|
2
2
|
module Format
|
3
|
+
def client_side_hash(model, attribute, force = nil)
|
4
|
+
options = self.options.dup
|
5
|
+
if options[:with].respond_to?(:call)
|
6
|
+
if force
|
7
|
+
options[:with] = options[:with].call(model)
|
8
|
+
build_client_side_hash(model, attribute, options)
|
9
|
+
else
|
10
|
+
return
|
11
|
+
end
|
12
|
+
elsif options[:without].respond_to?(:call)
|
13
|
+
if force
|
14
|
+
options[:without] = options[:without].call(model)
|
15
|
+
build_client_side_hash(model, attribute, options)
|
16
|
+
else
|
17
|
+
return
|
18
|
+
end
|
19
|
+
else
|
20
|
+
super
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
3
24
|
private
|
4
25
|
|
5
26
|
def message_type
|
@@ -1,8 +1,19 @@
|
|
1
1
|
module ClientSideValidations::ActiveModel
|
2
2
|
module Inclusion
|
3
3
|
|
4
|
-
def client_side_hash(model, attribute)
|
5
|
-
|
4
|
+
def client_side_hash(model, attribute, force = nil)
|
5
|
+
if options[:in].respond_to?(:call)
|
6
|
+
if force
|
7
|
+
options = self.options.dup
|
8
|
+
options[:in] = options[:in].call(model)
|
9
|
+
hash = build_client_side_hash(model, attribute, options)
|
10
|
+
else
|
11
|
+
return
|
12
|
+
end
|
13
|
+
else
|
14
|
+
hash = build_client_side_hash(model, attribute, self.options.dup)
|
15
|
+
end
|
16
|
+
|
6
17
|
if hash[:in].is_a?(Range)
|
7
18
|
hash[:range] = hash[:in]
|
8
19
|
hash.delete(:in)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ClientSideValidations::ActiveModel
|
2
2
|
module Length
|
3
3
|
|
4
|
-
def client_side_hash(model, attribute)
|
4
|
+
def client_side_hash(model, attribute, force = nil)
|
5
5
|
options = self.options.dup
|
6
6
|
hash = { :messages => {} }
|
7
7
|
hash[:js_tokenizer] = options[:js_tokenizer] if options[:js_tokenizer]
|