client_side_validations 3.1.0 → 3.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.
- data/client_side_validations.gemspec +4 -16
- data/lib/client_side_validations/action_view/form_builder.rb +31 -79
- data/lib/client_side_validations/action_view/form_helper.rb +59 -24
- 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 +4 -2
- data/lib/client_side_validations/active_model/numericality.rb +12 -1
- data/lib/client_side_validations/active_model.rb +100 -14
- data/lib/client_side_validations/active_record/middleware.rb +29 -3
- data/lib/client_side_validations/active_record/uniqueness.rb +4 -3
- data/lib/client_side_validations/active_record.rb +2 -1
- data/lib/client_side_validations/config.rb +9 -0
- data/lib/client_side_validations/core_ext/regexp.rb +1 -2
- data/lib/client_side_validations/generators/rails_validations.rb +15 -0
- data/lib/client_side_validations/generators.rb +12 -0
- data/lib/client_side_validations/middleware.rb +63 -24
- data/lib/client_side_validations/version.rb +1 -1
- data/lib/client_side_validations.rb +2 -2
- data/lib/generators/client_side_validations/copy_assets_generator.rb +58 -0
- data/lib/generators/client_side_validations/install_generator.rb +9 -21
- data/lib/generators/templates/client_side_validations/initializer.rb +2 -2
- data/vendor/assets/javascripts/rails.validations.js +474 -282
- metadata +156 -301
- data/lib/client_side_validations/formtastic.rb +0 -21
- data/lib/client_side_validations/mongo_mapper/middleware.rb +0 -20
- data/lib/client_side_validations/mongo_mapper/uniqueness.rb +0 -28
- data/lib/client_side_validations/mongo_mapper.rb +0 -9
- data/lib/client_side_validations/mongoid/middleware.rb +0 -20
- data/lib/client_side_validations/mongoid/uniqueness.rb +0 -28
- data/lib/client_side_validations/mongoid.rb +0 -9
- data/lib/client_side_validations/simple_form.rb +0 -24
- data/lib/generators/client_side_validations/copy_asset_generator.rb +0 -23
- data/lib/generators/templates/client_side_validations/README.rails.3.0 +0 -6
- data/lib/generators/templates/client_side_validations/README.rails.3.1 +0 -7
- data/test/action_view/cases/helper.rb +0 -176
- data/test/action_view/cases/test_helpers.rb +0 -600
- data/test/action_view/cases/test_legacy_helpers.rb +0 -217
- data/test/action_view/models/comment.rb +0 -35
- data/test/action_view/models/post.rb +0 -35
- data/test/action_view/models.rb +0 -3
- data/test/active_model/cases/helper.rb +0 -4
- data/test/active_model/cases/test_acceptance_validator.rb +0 -16
- data/test/active_model/cases/test_base.rb +0 -11
- data/test/active_model/cases/test_confirmation_validator.rb +0 -16
- data/test/active_model/cases/test_exclusion_validator.rb +0 -20
- data/test/active_model/cases/test_format_validator.rb +0 -21
- data/test/active_model/cases/test_inclusion_validator.rb +0 -21
- data/test/active_model/cases/test_length_validator.rb +0 -61
- data/test/active_model/cases/test_numericality_validator.rb +0 -46
- data/test/active_model/cases/test_presence_validator.rb +0 -16
- data/test/active_model/cases/test_validations.rb +0 -175
- data/test/active_model/models/person.rb +0 -17
- data/test/active_record/cases/helper.rb +0 -12
- data/test/active_record/cases/test_base.rb +0 -11
- data/test/active_record/cases/test_middleware.rb +0 -175
- data/test/active_record/cases/test_uniqueness_validator.rb +0 -50
- data/test/active_record/models/guid.rb +0 -7
- data/test/active_record/models/user.rb +0 -14
- data/test/base_helper.rb +0 -8
- data/test/core_ext/cases/test_core_ext.rb +0 -46
- data/test/formtastic/cases/helper.rb +0 -7
- data/test/formtastic/cases/test_form_builder.rb +0 -11
- data/test/formtastic/cases/test_form_helper.rb +0 -21
- data/test/generators/cases/test_generators.rb +0 -31
- data/test/javascript/config.ru +0 -3
- data/test/javascript/public/test/callbacks/elementAfter.js +0 -54
- data/test/javascript/public/test/callbacks/elementBefore.js +0 -54
- data/test/javascript/public/test/callbacks/elementFail.js +0 -70
- data/test/javascript/public/test/callbacks/elementPass.js +0 -70
- data/test/javascript/public/test/callbacks/formAfter.js +0 -45
- data/test/javascript/public/test/callbacks/formBefore.js +0 -45
- data/test/javascript/public/test/callbacks/formFail.js +0 -51
- data/test/javascript/public/test/callbacks/formPass.js +0 -50
- data/test/javascript/public/test/form_builders/validateForm.js +0 -66
- data/test/javascript/public/test/form_builders/validateFormtastic.js +0 -54
- data/test/javascript/public/test/form_builders/validateNestedForm.js +0 -66
- data/test/javascript/public/test/form_builders/validateSimpleForm.js +0 -57
- data/test/javascript/public/test/settings.js +0 -15
- data/test/javascript/public/test/validateElement.js +0 -179
- data/test/javascript/public/test/validators/acceptance.js +0 -42
- data/test/javascript/public/test/validators/confirmation.js +0 -25
- data/test/javascript/public/test/validators/exclusion.js +0 -41
- data/test/javascript/public/test/validators/format.js +0 -27
- data/test/javascript/public/test/validators/inclusion.js +0 -42
- data/test/javascript/public/test/validators/length.js +0 -76
- data/test/javascript/public/test/validators/numericality.js +0 -142
- data/test/javascript/public/test/validators/presence.js +0 -21
- data/test/javascript/public/test/validators/uniqueness.js +0 -96
- data/test/javascript/public/vendor/jquery.metadata.js +0 -122
- data/test/javascript/public/vendor/qunit.css +0 -196
- data/test/javascript/public/vendor/qunit.js +0 -1374
- data/test/javascript/server.rb +0 -84
- data/test/javascript/views/index.erb +0 -20
- data/test/javascript/views/layout.erb +0 -21
- data/test/middleware/cases/helper.rb +0 -18
- data/test/middleware/cases/test_middleware.rb +0 -8
- data/test/mongo_mapper/cases/helper.rb +0 -9
- data/test/mongo_mapper/cases/test_base.rb +0 -15
- data/test/mongo_mapper/cases/test_middleware.rb +0 -77
- data/test/mongo_mapper/cases/test_uniqueness_validator.rb +0 -50
- data/test/mongo_mapper/models/magazine.rb +0 -11
- data/test/mongoid/cases/helper.rb +0 -16
- data/test/mongoid/cases/test_base.rb +0 -15
- data/test/mongoid/cases/test_middleware.rb +0 -77
- data/test/mongoid/cases/test_uniqueness_validator.rb +0 -49
- data/test/mongoid/models/book.rb +0 -12
- data/test/simple_form/cases/helper.rb +0 -5
- data/test/simple_form/cases/test_form_builder.rb +0 -14
- data/test/simple_form/cases/test_form_helper.rb +0 -24
- data/test/test_loader.rb +0 -6
|
@@ -13,30 +13,18 @@ Gem::Specification.new do |s|
|
|
|
13
13
|
s.description = %q{Client Side Validations}
|
|
14
14
|
|
|
15
15
|
s.files = `git ls-files -- {lib/*,vendor/*,*.gemspec}`.split("\n")
|
|
16
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
17
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
18
16
|
s.require_paths = ["lib"]
|
|
19
17
|
|
|
20
|
-
s.add_development_dependency 'rails', '3.
|
|
18
|
+
s.add_development_dependency 'rails', '~> 3.2.0'
|
|
21
19
|
s.add_development_dependency 'sqlite3'
|
|
22
|
-
s.add_development_dependency 'bson_ext'
|
|
23
|
-
s.add_development_dependency 'mongoid', '~> 2.0.0'
|
|
24
|
-
s.add_development_dependency 'mongo_mapper','~>0.9.0'
|
|
25
20
|
s.add_development_dependency 'mocha'
|
|
26
|
-
s.add_development_dependency '
|
|
27
|
-
s.add_development_dependency 'formtastic', '~> 2.0.0.rc3'
|
|
21
|
+
s.add_development_dependency 'm'
|
|
28
22
|
|
|
29
23
|
# For QUnit testing
|
|
30
24
|
s.add_development_dependency 'sinatra', '~> 1.0'
|
|
31
25
|
s.add_development_dependency 'shotgun'
|
|
32
26
|
s.add_development_dependency 'thin'
|
|
33
27
|
s.add_development_dependency 'json'
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if ruby_minor_version == 8
|
|
37
|
-
s.add_development_dependency 'minitest'
|
|
38
|
-
s.add_development_dependency 'ruby-debug'
|
|
39
|
-
elsif ruby_minor_version == 9
|
|
40
|
-
s.add_development_dependency 'ruby-debug19'
|
|
41
|
-
end
|
|
28
|
+
s.add_development_dependency 'coffee-script'
|
|
29
|
+
s.add_development_dependency 'jquery-rails'
|
|
42
30
|
end
|
|
@@ -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
|
|
@@ -24,9 +24,9 @@ module ClientSideValidations::ActionView::Helpers
|
|
|
24
24
|
alias_method_chain :grouped_collection_select, :client_side_validations
|
|
25
25
|
alias_method_chain :time_zone_select, :client_side_validations
|
|
26
26
|
|
|
27
|
-
def
|
|
27
|
+
def client_side_form_settings(options, form_helper)
|
|
28
28
|
{
|
|
29
|
-
:type => self.to_s,
|
|
29
|
+
:type => self.class.to_s,
|
|
30
30
|
:input_tag => form_helper.class.field_error_proc.call(%{<span id="input_tag" />}, Struct.new(:error_message, :tag_id).new([], "")),
|
|
31
31
|
:label_tag => form_helper.class.field_error_proc.call(%{<label id="label_tag" />}, Struct.new(:error_message, :tag_id).new([], ""))
|
|
32
32
|
}
|
|
@@ -34,9 +34,17 @@ module ClientSideValidations::ActionView::Helpers
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
def validate(*attrs)
|
|
38
|
+
options = attrs.pop if attrs.last.is_a?(Hash)
|
|
39
|
+
(attrs.present? ? attrs : @object._validators.keys).each do |attr|
|
|
40
|
+
build_validation_options(attr, :validate => options)
|
|
41
|
+
end
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
|
|
37
45
|
def initialize_with_client_side_validations(object_name, object, template, options, proc)
|
|
38
46
|
initialize_without_client_side_validations(object_name, object, template, options, proc)
|
|
39
|
-
@options[:validators] = {}
|
|
47
|
+
@options[:validators] = { object => {} }
|
|
40
48
|
end
|
|
41
49
|
|
|
42
50
|
def fields_for_with_client_side_validations(record_or_name_or_array, *args, &block)
|
|
@@ -46,106 +54,50 @@ module ClientSideValidations::ActionView::Helpers
|
|
|
46
54
|
end
|
|
47
55
|
|
|
48
56
|
def check_box_with_client_side_validations(method, options = {}, checked_value = "1", unchecked_value = "0")
|
|
49
|
-
|
|
57
|
+
build_validation_options(method, options)
|
|
58
|
+
options.delete(:validate)
|
|
50
59
|
check_box_without_client_side_validations(method, options, checked_value, unchecked_value)
|
|
51
60
|
end
|
|
52
61
|
|
|
53
62
|
def radio_button_with_client_side_validations(method, tag_value, options = {})
|
|
54
|
-
|
|
63
|
+
build_validation_options(method, options)
|
|
64
|
+
options.delete(:validate)
|
|
55
65
|
radio_button_without_client_side_validations(method, tag_value, options)
|
|
56
66
|
end
|
|
57
67
|
|
|
58
68
|
def select_with_client_side_validations(method, choices, options = {}, html_options = {})
|
|
59
|
-
|
|
69
|
+
build_validation_options(method, html_options.merge(:name => options[:name]))
|
|
70
|
+
html_options.delete(:validate)
|
|
60
71
|
select_without_client_side_validations(method, choices, options, html_options)
|
|
61
72
|
end
|
|
62
73
|
|
|
63
74
|
def collection_select_with_client_side_validations(method, collection, value_method, text_method, options = {}, html_options = {})
|
|
64
|
-
|
|
75
|
+
build_validation_options(method, html_options.merge(:name => options[:name]))
|
|
76
|
+
html_options.delete(:validate)
|
|
65
77
|
collection_select_without_client_side_validations(method, collection, value_method, text_method, options, html_options)
|
|
66
78
|
end
|
|
67
79
|
|
|
68
80
|
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
|
-
|
|
81
|
+
build_validation_options(method, html_options.merge(:name => options[:name]))
|
|
82
|
+
html_options.delete(:validate)
|
|
70
83
|
grouped_collection_select_without_client_side_validations(method, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
|
71
84
|
end
|
|
72
85
|
|
|
73
86
|
def time_zone_select_with_client_side_validations(method, priority_zones = nil, options = {}, html_options = {})
|
|
74
|
-
|
|
87
|
+
build_validation_options(method, html_options.merge(:name => options[:name]))
|
|
88
|
+
html_options.delete(:validate)
|
|
75
89
|
time_zone_select_without_client_side_validations(method, priority_zones = nil, options, html_options)
|
|
76
90
|
end
|
|
77
91
|
|
|
78
92
|
private
|
|
79
93
|
|
|
80
|
-
def
|
|
81
|
-
if @options[:validate]
|
|
82
|
-
options
|
|
83
|
-
@options[:
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
def filter_validators(validators, filters)
|
|
88
|
-
if validators
|
|
89
|
-
filtered_validators = validators.inject({}) do |filtered_validators, validator|
|
|
90
|
-
filtered_validators[validator.first] = validator.last
|
|
91
|
-
if has_filter_for_validator?(validator, filters)
|
|
92
|
-
if filter_validator?(validator, filters)
|
|
93
|
-
filtered_validators.delete(validator.first)
|
|
94
|
-
elsif force_validator_despite_conditional?(validator, filters) && !can_run_validator?(validator)
|
|
95
|
-
filtered_validators.delete(validator.first)
|
|
96
|
-
end
|
|
97
|
-
else
|
|
98
|
-
if validator.last.key?(:if) || validator.last.key?(:unless)
|
|
99
|
-
filtered_validators.delete(validator.first)
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
filtered_validators[validator.first].delete(:if) if filtered_validators[validator.first]
|
|
103
|
-
filtered_validators[validator.first].delete(:unless) if filtered_validators[validator.first]
|
|
104
|
-
filtered_validators
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
filtered_validators.empty? ? nil : filtered_validators
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def has_filter_for_validator?(validator, filters)
|
|
112
|
-
filters && (filters == true || filters.key?(validator.first))
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
def filter_validator?(validator, filters)
|
|
116
|
-
filters != true && filters[validator.first] == false
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
def force_validator_despite_conditional?(validator, filters)
|
|
120
|
-
filters == true || filters[validator.first] == true
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
def can_run_validator?(validator)
|
|
124
|
-
result = true
|
|
125
|
-
if_result = can_run_if_validator?(validator.last[:if])
|
|
126
|
-
unless_result = can_run_unless_validator?(validator.last[:unless])
|
|
127
|
-
result = result && if_result unless if_result.nil?
|
|
128
|
-
result = result && unless_result unless unless_result.nil?
|
|
129
|
-
result
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
def can_run_if_validator?(conditional)
|
|
133
|
-
if conditional
|
|
134
|
-
if conditional.is_a?(Symbol)
|
|
135
|
-
!!@object.send(conditional)
|
|
136
|
-
else
|
|
137
|
-
!!conditional.call(@object)
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def can_run_unless_validator?(conditional)
|
|
143
|
-
if conditional
|
|
144
|
-
if conditional.is_a?(Symbol)
|
|
145
|
-
!@object.send(conditional)
|
|
146
|
-
else
|
|
147
|
-
!conditional.call(@object)
|
|
148
|
-
end
|
|
94
|
+
def build_validation_options(method, options = {})
|
|
95
|
+
if @options[:validate]
|
|
96
|
+
name = options[:name] || "#{@object_name}[#{method}]"
|
|
97
|
+
child_index = @options[:child_index] ? "(\\d+|#{Regexp.escape(@options[:child_index])})" : "\\d+"
|
|
98
|
+
name = name.to_s.gsub(/_attributes\]\[#{child_index}\]/, '_attributes][]')
|
|
99
|
+
name = "#{name}#{options[:multiple] ? "[]" : nil}"
|
|
100
|
+
@options[:validators][@object][method] = { :name => name, :options => options[:validate] }
|
|
149
101
|
end
|
|
150
102
|
end
|
|
151
103
|
end
|
|
@@ -2,38 +2,46 @@ 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
|
|
|
9
|
-
content_for_name = options[:validate] unless options[:validate] == true
|
|
10
|
-
|
|
11
9
|
# Always turn off HTML5 Validations
|
|
12
10
|
options[:html] ||= {}
|
|
13
11
|
options[:html][:novalidate] = 'novalidate'
|
|
14
12
|
|
|
15
|
-
case
|
|
13
|
+
case record
|
|
16
14
|
when String, Symbol
|
|
17
|
-
raise ClientSideValidations::ActionView::Helpers::FormHelper::Error, 'Using form_for(:name, @resource) is
|
|
18
|
-
when Array
|
|
19
|
-
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.'
|
|
20
16
|
else
|
|
21
|
-
object =
|
|
17
|
+
object = record.is_a?(Array) ? record.last : record
|
|
22
18
|
end
|
|
23
19
|
end
|
|
24
20
|
|
|
25
21
|
@validators = {}
|
|
22
|
+
|
|
26
23
|
# Order matters here. Rails mutates the options object
|
|
24
|
+
html_id = options[:html][:id] if options[:html]
|
|
25
|
+
form = super(record, *(args << options), &proc)
|
|
26
|
+
options[:id] = html_id if html_id
|
|
27
27
|
script = client_side_form_settings(object, options)
|
|
28
|
-
|
|
28
|
+
|
|
29
29
|
# Because of the load order requirement above this sub is necessary
|
|
30
30
|
# Would be nice to not do this
|
|
31
31
|
script = insert_validators_into_script(script)
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
|
|
33
|
+
if assign_script_to_content_for(options[:validate], script)
|
|
34
|
+
form.html_safe
|
|
35
|
+
else
|
|
36
|
+
"#{form}#{script}".html_safe
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def assign_script_to_content_for(name, script)
|
|
41
|
+
if name && name != true
|
|
42
|
+
content_for(name) { script.html_safe }
|
|
43
|
+
true
|
|
35
44
|
end
|
|
36
|
-
"#{form}#{script}".html_safe
|
|
37
45
|
end
|
|
38
46
|
|
|
39
47
|
def apply_form_for_options!(object_or_array, options)
|
|
@@ -41,9 +49,9 @@ module ClientSideValidations::ActionView::Helpers
|
|
|
41
49
|
options[:html][:validate] = true if options[:validate]
|
|
42
50
|
end
|
|
43
51
|
|
|
44
|
-
def fields_for(record_or_name_or_array,
|
|
52
|
+
def fields_for(record_or_name_or_array, record_object = nil, options = {}, &block)
|
|
45
53
|
output = super
|
|
46
|
-
@validators.merge!(
|
|
54
|
+
@validators.merge!(options[:validators]) if @validators
|
|
47
55
|
output
|
|
48
56
|
end
|
|
49
57
|
|
|
@@ -54,30 +62,57 @@ module ClientSideValidations::ActionView::Helpers
|
|
|
54
62
|
# But using String#sub has some issues. Undocumented "features"
|
|
55
63
|
if script
|
|
56
64
|
script = script.split(/"validator_hash"/)
|
|
57
|
-
script = "#{script[0]}#{
|
|
65
|
+
script = "#{script[0]}#{construct_validators.to_json}#{script[1]}"
|
|
58
66
|
end
|
|
59
67
|
|
|
60
68
|
script
|
|
61
69
|
end
|
|
62
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
|
+
|
|
63
89
|
def client_side_form_settings(object, options)
|
|
64
90
|
if options[:validate]
|
|
65
|
-
builder = options[:
|
|
91
|
+
builder = options[:parent_builder]
|
|
66
92
|
|
|
67
|
-
if options[:
|
|
68
|
-
var_name = options[:
|
|
93
|
+
if options[:id]
|
|
94
|
+
var_name = options[:id]
|
|
69
95
|
else
|
|
70
|
-
|
|
71
|
-
|
|
96
|
+
if Rails.version >= '3.2.0'
|
|
97
|
+
var_name = if object.respond_to?(:persisted?) && object.persisted?
|
|
98
|
+
options[:as] ? "edit_#{options[:as]}" : [options[:namespace], dom_id(object, :edit)].compact.join("_")
|
|
99
|
+
else
|
|
100
|
+
options[:as] ? "new_#{options[:as]}" : [options[:namespace], dom_id(object)].compact.join("_")
|
|
101
|
+
end
|
|
72
102
|
else
|
|
73
|
-
|
|
103
|
+
# This is to maintain backward compatibility with Rails 3.1
|
|
104
|
+
# see: https://github.com/rails/rails/commit/e29773f885fd500189ffd964550ae20061d745ba#commitcomment-948052
|
|
105
|
+
var_name = if object.respond_to?(:persisted?) && object.persisted?
|
|
106
|
+
options[:as] ? "#{options[:as]}_edit" : dom_id(object, :edit)
|
|
107
|
+
else
|
|
108
|
+
options[:as] ? "#{options[:as]}_new" : dom_id(object)
|
|
109
|
+
end
|
|
74
110
|
end
|
|
75
111
|
end
|
|
76
112
|
|
|
77
113
|
content_tag(:script) do
|
|
78
|
-
"window['#{var_name}'] = #{builder.client_side_form_settings(options, self).merge(:validators => 'validator_hash').to_json}
|
|
114
|
+
"//<![CDATA[\nif(window.ClientSideValidations==undefined)window.ClientSideValidations={};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
|
|
79
115
|
end
|
|
80
|
-
|
|
81
116
|
end
|
|
82
117
|
end
|
|
83
118
|
|
|
@@ -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]
|
|
@@ -9,13 +9,15 @@ module ClientSideValidations::ActiveModel
|
|
|
9
9
|
|
|
10
10
|
self.class::MESSAGES.each do |option, message_type|
|
|
11
11
|
if count = options[option]
|
|
12
|
-
options[:message] = options[message_type]
|
|
12
|
+
options[:message] = options[message_type] if options[message_type].present?
|
|
13
13
|
options.delete(:message) if options[:message].nil?
|
|
14
14
|
hash[:messages][option] = model.errors.generate_message(attribute, message_type, options.merge(:count => count))
|
|
15
15
|
hash[option] = count
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
copy_conditional_attributes(hash, options)
|
|
20
|
+
|
|
19
21
|
hash
|
|
20
22
|
end
|
|
21
23
|
|
|
@@ -7,7 +7,7 @@ module ClientSideValidations::ActiveModel
|
|
|
7
7
|
OPTION_MAP.merge!(base::CHECKS.keys.inject({}) { |hash, key| hash.merge!(key => key) })
|
|
8
8
|
end
|
|
9
9
|
|
|
10
|
-
def client_side_hash(model, attribute)
|
|
10
|
+
def client_side_hash(model, attribute, force = nil)
|
|
11
11
|
options = self.options.dup
|
|
12
12
|
hash = { :messages => { :numericality => model.errors.generate_message(attribute, :not_a_number, options) } }
|
|
13
13
|
|
|
@@ -16,13 +16,24 @@ module ClientSideValidations::ActiveModel
|
|
|
16
16
|
hash[:only_integer] = true
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
+
hash[:allow_blank] = true if options[:allow_nil]
|
|
20
|
+
|
|
19
21
|
OPTION_MAP.each do |option, message_type|
|
|
20
22
|
if count = options[option]
|
|
23
|
+
if count.respond_to?(:call)
|
|
24
|
+
if force
|
|
25
|
+
count = count.call(model)
|
|
26
|
+
else
|
|
27
|
+
next
|
|
28
|
+
end
|
|
29
|
+
end
|
|
21
30
|
hash[:messages][option] = model.errors.generate_message(attribute, message_type, options.merge(:count => count))
|
|
22
31
|
hash[option] = count
|
|
23
32
|
end
|
|
24
33
|
end
|
|
25
34
|
|
|
35
|
+
copy_conditional_attributes(hash, options)
|
|
36
|
+
|
|
26
37
|
hash
|
|
27
38
|
end
|
|
28
39
|
|
|
@@ -3,31 +3,38 @@ 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
|
-
|
|
6
|
+
def client_side_hash(model, attribute, force = nil)
|
|
7
|
+
build_client_side_hash(model, attribute, self.options.dup)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def copy_conditional_attributes(to, from)
|
|
11
|
+
[:if, :unless].each { |key| to[key] = from[key] if from[key].present? }
|
|
9
12
|
end
|
|
10
13
|
|
|
11
14
|
private
|
|
12
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
|
+
|
|
13
20
|
def message_type
|
|
14
21
|
kind
|
|
15
22
|
end
|
|
16
23
|
end
|
|
17
24
|
|
|
18
25
|
module Validations
|
|
19
|
-
def client_side_validation_hash
|
|
20
|
-
|
|
26
|
+
def client_side_validation_hash(force = nil)
|
|
27
|
+
_validators.inject({}) do |attr_hash, attr|
|
|
21
28
|
unless [nil, :block].include?(attr[0])
|
|
22
29
|
|
|
23
|
-
validator_hash = attr[1].inject({}) do |kind_hash, validator|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
else
|
|
29
|
-
kind_hash.merge!({})
|
|
30
|
+
validator_hash = attr[1].inject(Hash.new { |h,k| h[k] = []}) do |kind_hash, validator|
|
|
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
|
|
30
35
|
end
|
|
36
|
+
|
|
37
|
+
kind_hash
|
|
31
38
|
end
|
|
32
39
|
|
|
33
40
|
if validator_hash.present?
|
|
@@ -43,8 +50,87 @@ module ClientSideValidations::ActiveModel
|
|
|
43
50
|
|
|
44
51
|
private
|
|
45
52
|
|
|
46
|
-
def
|
|
47
|
-
|
|
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
|
+
|
|
72
|
+
if validator.options[:if] || validator.options[:unless]
|
|
73
|
+
if result = can_force_validator?(attr, validator, force)
|
|
74
|
+
if validator.options[:if]
|
|
75
|
+
result = result && run_conditional(validator.options[:if])
|
|
76
|
+
end
|
|
77
|
+
if validator.options[:unless]
|
|
78
|
+
result = result && !run_conditional(validator.options[:unless])
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
result
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def run_conditional(method_name_or_proc)
|
|
88
|
+
case method_name_or_proc
|
|
89
|
+
when Proc
|
|
90
|
+
method_name_or_proc.call(self)
|
|
91
|
+
else
|
|
92
|
+
self.send(method_name_or_proc)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def validator_turned_off?(attr, validator, force)
|
|
97
|
+
case force
|
|
98
|
+
when FalseClass
|
|
99
|
+
true
|
|
100
|
+
when Hash
|
|
101
|
+
case force[attr]
|
|
102
|
+
when FalseClass
|
|
103
|
+
true
|
|
104
|
+
when Hash
|
|
105
|
+
force[attr][validator.kind] == false
|
|
106
|
+
else
|
|
107
|
+
false
|
|
108
|
+
end
|
|
109
|
+
else
|
|
110
|
+
::ClientSideValidations::Config.disabled_validators.include?(validator.kind)
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def can_force_validator?(attr, validator, force)
|
|
115
|
+
case force
|
|
116
|
+
when TrueClass
|
|
117
|
+
true
|
|
118
|
+
when Hash
|
|
119
|
+
case force[attr]
|
|
120
|
+
when TrueClass
|
|
121
|
+
true
|
|
122
|
+
when Hash
|
|
123
|
+
force[attr][validator.kind]
|
|
124
|
+
else
|
|
125
|
+
false
|
|
126
|
+
end
|
|
127
|
+
else
|
|
128
|
+
if (validator.options[:if] || validator.options[:unless]) =~ /changed\?/
|
|
129
|
+
true
|
|
130
|
+
else
|
|
131
|
+
false
|
|
132
|
+
end
|
|
133
|
+
end
|
|
48
134
|
end
|
|
49
135
|
end
|
|
50
136
|
end
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
module ClientSideValidations::ActiveRecord
|
|
2
2
|
class Middleware
|
|
3
3
|
|
|
4
|
+
def self.is_class?(klass)
|
|
5
|
+
klass < ::ActiveRecord::Base
|
|
6
|
+
end
|
|
7
|
+
|
|
4
8
|
def self.is_unique?(klass, attribute, value, params)
|
|
9
|
+
klass = find_topmost_superclass(klass)
|
|
10
|
+
value = type_cast_value(klass, attribute, value)
|
|
5
11
|
column = klass.columns_hash[attribute.to_s]
|
|
6
12
|
value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?
|
|
7
13
|
|
|
8
14
|
t = klass.arel_table
|
|
9
15
|
|
|
10
16
|
if params[:case_sensitive] == 'true'
|
|
11
|
-
if t.engine.connection.instance_variable_get("@config")[:adapter]
|
|
17
|
+
if t.engine.connection.instance_variable_get("@config")[:adapter] =~ /^mysql/
|
|
12
18
|
relation = Arel::Nodes::SqlLiteral.new("BINARY #{t[attribute].eq(value).to_sql}")
|
|
13
19
|
else
|
|
14
20
|
relation = t[attribute].eq(value)
|
|
@@ -23,11 +29,31 @@ module ClientSideValidations::ActiveRecord
|
|
|
23
29
|
relation = relation.and(t.primary_key.not_eq(params[:id])) if params[:id]
|
|
24
30
|
end
|
|
25
31
|
|
|
26
|
-
(params[:scope] || {}).each do |
|
|
27
|
-
|
|
32
|
+
(params[:scope] || {}).each do |attribute, value|
|
|
33
|
+
value = type_cast_value(klass, attribute, value)
|
|
34
|
+
if relation.is_a?(Arel::Nodes::SqlLiteral)
|
|
35
|
+
relation = Arel::Nodes::SqlLiteral.new("#{relation} AND #{t[attribute].eq(value).to_sql}")
|
|
36
|
+
else
|
|
37
|
+
relation = relation.and(t[attribute].eq(value))
|
|
38
|
+
end
|
|
28
39
|
end
|
|
29
40
|
|
|
30
41
|
!klass.where(relation).exists?
|
|
31
42
|
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def self.type_cast_value(klass, attribute, value)
|
|
47
|
+
klass.columns_hash[attribute].type_cast(value)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.find_topmost_superclass(klass)
|
|
51
|
+
if is_class?(klass.superclass)
|
|
52
|
+
find_topmost_superclass(klass.superclass)
|
|
53
|
+
else
|
|
54
|
+
klass
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
32
58
|
end
|
|
33
59
|
end
|