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.
@@ -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
- apply_client_side_validators(method, options)
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
- apply_client_side_validators(method, options)
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
- apply_client_side_validators(method, options)
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
- apply_client_side_validators(method, html_options)
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
- apply_client_side_validators(method, html_options)
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
- apply_client_side_validators(method, html_options)
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
- apply_client_side_validators(method, html_options)
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 apply_client_side_validators(method, options = {})
81
- if @options[:validate] && options[:validate] != false && validators = filter_validators(method, 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
- @options[:validators].merge!("#{name}#{options[:multiple] ? "[]" : nil}" => validators)
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(record_or_name_or_array, *args, &proc)
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 record_or_name_or_array
13
+ case record
14
14
  when String, Symbol
15
- raise ClientSideValidations::ActionView::Helpers::FormHelper::Error, 'Using form_for(:name, @resource) is deprecated in Rails and is not supported with ClientSideValidations. Please use form_for(@resource, :as => :name) instead.'
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 = record_or_name_or_array
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(record_or_name_or_array, *(args << options), &proc)
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]}#{@validators.to_json}#{script[1]}"
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
- options = self.options.dup
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
- @client_side_validation_hash ||= _validators.inject({}) do |attr_hash, attr|
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
- client_side_hash = validator.client_side_hash(self, attr[0])
29
- # Yeah yeah, #new_record? is not part of ActiveModel :p
30
- if can_use_for_client_side_validation?(client_side_hash, validator) && uniqueness_validations_allowed_or_not_applicable?(validator)
31
- kind_hash[validator.kind] << client_side_hash.except(:on)
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 can_use_for_client_side_validation?(client_side_hash, validator)
51
- ((self.respond_to?(:new_record?) && validator.options[:on] == (self.new_record? ? :create : :update)) || validator.options[:on].nil?) && validator.kind != :block
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
- hash = super
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
- hash = super
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]