client_side_validations 3.2.0.beta.6 → 3.2.0.rc.1

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.
@@ -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]