rails4_client_side_validations 0.0.2 → 0.0.3
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/README.md +1 -0
- data/lib/generators/rails4_client_side_validations/copy_assets_generator.rb +58 -0
- data/lib/generators/rails4_client_side_validations/install_generator.rb +22 -0
- data/lib/generators/templates/client_side_validations/initializer.rb +20 -0
- data/lib/rails4_client_side_validations.rb +13 -0
- data/lib/rails4_client_side_validations/action_view.rb +14 -0
- data/lib/rails4_client_side_validations/action_view/form_builder.rb +105 -0
- data/lib/rails4_client_side_validations/action_view/form_helper.rb +139 -0
- data/lib/rails4_client_side_validations/action_view/form_tag_helper.rb +12 -0
- data/lib/rails4_client_side_validations/active_model.rb +143 -0
- data/lib/rails4_client_side_validations/active_model/absence.rb +10 -0
- data/lib/rails4_client_side_validations/active_model/acceptance.rb +10 -0
- data/lib/rails4_client_side_validations/active_model/exclusion.rb +27 -0
- data/lib/rails4_client_side_validations/active_model/format.rb +31 -0
- data/lib/rails4_client_side_validations/active_model/inclusion.rb +26 -0
- data/lib/rails4_client_side_validations/active_model/length.rb +26 -0
- data/lib/rails4_client_side_validations/active_model/numericality.rb +42 -0
- data/lib/rails4_client_side_validations/active_model/presence.rb +10 -0
- data/lib/rails4_client_side_validations/active_record.rb +12 -0
- data/lib/rails4_client_side_validations/active_record/middleware.rb +59 -0
- data/lib/rails4_client_side_validations/active_record/uniqueness.rb +29 -0
- data/lib/rails4_client_side_validations/config.rb +13 -0
- data/lib/rails4_client_side_validations/core_ext.rb +3 -0
- data/lib/rails4_client_side_validations/core_ext/range.rb +10 -0
- data/lib/rails4_client_side_validations/core_ext/regexp.rb +13 -0
- data/lib/rails4_client_side_validations/engine.rb +6 -0
- data/lib/rails4_client_side_validations/files.rb +8 -0
- data/lib/rails4_client_side_validations/generators.rb +12 -0
- data/lib/rails4_client_side_validations/generators/rails_validations.rb +15 -0
- data/lib/rails4_client_side_validations/middleware.rb +120 -0
- data/lib/rails4_client_side_validations/version.rb +3 -0
- data/vendor/assets/javascripts/rails.validations.js +639 -0
- metadata +40 -9
data/README.md
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
It's modifed version of [client_side_validations](https://github.com/bcardarella/client_side_validations.git), which supports Rails 4
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Rails4ClientSideValidations
|
2
|
+
module Generators
|
3
|
+
class CopyAssetsGenerator < Rails::Generators::Base
|
4
|
+
|
5
|
+
def copy_javascript_asset
|
6
|
+
if self.class == CopyAssetsGenerator || !asset_pipeline_enabled?
|
7
|
+
assets.each do |asset|
|
8
|
+
source_paths << asset[:path]
|
9
|
+
copy_file asset[:file], "#{asset_directory}/#{asset[:file]}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def self.asset_directory
|
17
|
+
if asset_pipeline_enabled?
|
18
|
+
"app#{Rails.configuration.assets.prefix}/javascripts"
|
19
|
+
else
|
20
|
+
'public/javascripts'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def asset_directory
|
25
|
+
CopyAssetsGenerator.asset_directory
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.assets
|
29
|
+
Assets
|
30
|
+
end
|
31
|
+
|
32
|
+
def assets
|
33
|
+
CopyAssetsGenerator.assets
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.asset_file_names
|
37
|
+
assets.map { |asset| asset[:file] }.join(', ')
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.asset_pipeline_enabled?
|
41
|
+
if Rails.application
|
42
|
+
(Rails.configuration.respond_to?(:assets) ? (Rails.configuration.assets || {}) : {})[:enabled]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def asset_pipeline_enabled?
|
47
|
+
self.class.asset_pipeline_enabled?
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.installation_message
|
51
|
+
"Copies #{asset_file_names} to #{asset_directory}"
|
52
|
+
end
|
53
|
+
|
54
|
+
desc installation_message
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'generators/rails4_client_side_validations/copy_assets_generator'
|
2
|
+
|
3
|
+
module Rails4ClientSideValidations
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < CopyAssetsGenerator
|
6
|
+
|
7
|
+
def copy_initializer
|
8
|
+
source_paths << File.expand_path('../../templates/rails4_client_side_validations', __FILE__)
|
9
|
+
copy_file 'initializer.rb', 'config/initializers/rails4_client_side_validations.rb'
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def self.installation_message
|
15
|
+
"Copies initializer into config/initializers and #{super.downcase}"
|
16
|
+
end
|
17
|
+
|
18
|
+
desc installation_message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# ClientSideValidations Initializer
|
2
|
+
|
3
|
+
# Uncomment to disable uniqueness validator, possible security issue
|
4
|
+
# ClientSideValidations::Config.disabled_validators = [:uniqueness]
|
5
|
+
|
6
|
+
# Uncomment to validate number format with current I18n locale
|
7
|
+
# ClientSideValidations::Config.number_format_with_locale = true
|
8
|
+
|
9
|
+
# Uncomment to set a custom application scope
|
10
|
+
# ClientSideValidations::Config.root_path = '/your/application/scope'
|
11
|
+
|
12
|
+
# Uncomment the following block if you want each input field to have the validation messages attached.
|
13
|
+
# ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
|
14
|
+
# unless html_tag =~ /^<label/
|
15
|
+
# %{<div class="field_with_errors">#{html_tag}<label for="#{instance.send(:tag_id)}" class="message">#{instance.error_message.first}</label></div>}.html_safe
|
16
|
+
# else
|
17
|
+
# %{<div class="field_with_errors">#{html_tag}</div>}.html_safe
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Rails4ClientSideValidations
|
2
|
+
end
|
3
|
+
|
4
|
+
require 'rails4_client_side_validations/config'
|
5
|
+
require 'rails4_client_side_validations/active_model' if defined?(::ActiveModel)
|
6
|
+
require 'rails4_client_side_validations/active_record' if defined?(::ActiveRecord)
|
7
|
+
require 'rails4_client_side_validations/action_view' if defined?(::ActionView)
|
8
|
+
if defined?(::Rails)
|
9
|
+
require 'rails4_client_side_validations/generators'
|
10
|
+
require 'rails4_client_side_validations/middleware'
|
11
|
+
require 'rails4_client_side_validations/engine'
|
12
|
+
end
|
13
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Rails4ClientSideValidations::ActionView
|
2
|
+
module Helpers
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'rails4_client_side_validations/core_ext'
|
7
|
+
require 'rails4_client_side_validations/action_view/form_helper'
|
8
|
+
require 'rails4_client_side_validations/action_view/form_tag_helper'
|
9
|
+
require 'rails4_client_side_validations/action_view/form_builder'
|
10
|
+
|
11
|
+
ActionView::Base.send(:include, Rails4ClientSideValidations::ActionView::Helpers::FormHelper)
|
12
|
+
ActionView::Base.send(:include, Rails4ClientSideValidations::ActionView::Helpers::FormTagHelper)
|
13
|
+
ActionView::Helpers::FormBuilder.send(:include, Rails4ClientSideValidations::ActionView::Helpers::FormBuilder)
|
14
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module Rails4ClientSideValidations::ActionView::Helpers
|
2
|
+
module FormBuilder
|
3
|
+
|
4
|
+
def self.included(base)
|
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
|
+
base.class_eval <<-RUBY_EVAL
|
7
|
+
def #{selector}_with_rails4_client_side_validations(method, options = {})
|
8
|
+
build_validation_options(method, options)
|
9
|
+
options.delete(:validate)
|
10
|
+
#{selector}_without_rails4_client_side_validations(method, options)
|
11
|
+
end
|
12
|
+
RUBY_EVAL
|
13
|
+
|
14
|
+
base.class_eval { alias_method_chain selector, :rails4_client_side_validations }
|
15
|
+
end
|
16
|
+
|
17
|
+
base.class_eval do
|
18
|
+
alias_method_chain :initialize, :rails4_client_side_validations
|
19
|
+
alias_method_chain :fields_for, :rails4_client_side_validations
|
20
|
+
alias_method_chain :check_box, :rails4_client_side_validations
|
21
|
+
alias_method_chain :radio_button, :rails4_client_side_validations
|
22
|
+
alias_method_chain :select, :rails4_client_side_validations
|
23
|
+
alias_method_chain :collection_select, :rails4_client_side_validations
|
24
|
+
alias_method_chain :grouped_collection_select, :rails4_client_side_validations
|
25
|
+
alias_method_chain :time_zone_select, :rails4_client_side_validations
|
26
|
+
|
27
|
+
def client_side_form_settings(options, form_helper)
|
28
|
+
{
|
29
|
+
:type => self.class.to_s,
|
30
|
+
:input_tag => form_helper.class.field_error_proc.call(%{<span id="input_tag" />}, Struct.new(:error_message, :tag_id).new([], "")),
|
31
|
+
:label_tag => form_helper.class.field_error_proc.call(%{<label id="label_tag" />}, Struct.new(:error_message, :tag_id).new([], ""))
|
32
|
+
}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
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
|
+
|
45
|
+
def initialize_with_rails4_client_side_validations(object_name, object, template, options, proc=nil)
|
46
|
+
initialize_without_rails4_client_side_validations(object_name, object, template, options, proc)
|
47
|
+
@options[:validators] = { object => {} }
|
48
|
+
end
|
49
|
+
|
50
|
+
def fields_for_with_rails4_client_side_validations(record_or_name_or_array, *args, &block)
|
51
|
+
options = args.extract_options!
|
52
|
+
options[:validate] ||= @options[:validate] if @options[:validate] && !options.key?(:validate)
|
53
|
+
fields_for_without_rails4_client_side_validations(record_or_name_or_array, *(args << options), &block)
|
54
|
+
end
|
55
|
+
|
56
|
+
def check_box_with_rails4_client_side_validations(method, options = {}, checked_value = "1", unchecked_value = "0")
|
57
|
+
build_validation_options(method, options)
|
58
|
+
options.delete(:validate)
|
59
|
+
check_box_without_rails4_client_side_validations(method, options, checked_value, unchecked_value)
|
60
|
+
end
|
61
|
+
|
62
|
+
def radio_button_with_rails4_client_side_validations(method, tag_value, options = {})
|
63
|
+
build_validation_options(method, options)
|
64
|
+
options.delete(:validate)
|
65
|
+
radio_button_without_rails4_client_side_validations(method, tag_value, options)
|
66
|
+
end
|
67
|
+
|
68
|
+
def select_with_rails4_client_side_validations(method, choices, options = {}, html_options = {})
|
69
|
+
build_validation_options(method, html_options.merge(:name => options[:name]))
|
70
|
+
html_options.delete(:validate)
|
71
|
+
select_without_rails4_client_side_validations(method, choices, options, html_options)
|
72
|
+
end
|
73
|
+
|
74
|
+
def collection_select_with_rails4_client_side_validations(method, collection, value_method, text_method, options = {}, html_options = {})
|
75
|
+
build_validation_options(method, html_options.merge(:name => options[:name]))
|
76
|
+
html_options.delete(:validate)
|
77
|
+
collection_select_without_rails4_client_side_validations(method, collection, value_method, text_method, options, html_options)
|
78
|
+
end
|
79
|
+
|
80
|
+
def grouped_collection_select_with_rails4_client_side_validations(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
81
|
+
build_validation_options(method, html_options.merge(:name => options[:name]))
|
82
|
+
html_options.delete(:validate)
|
83
|
+
grouped_collection_select_without_rails4_client_side_validations(method, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
84
|
+
end
|
85
|
+
|
86
|
+
def time_zone_select_with_rails4_client_side_validations(method, priority_zones = nil, options = {}, html_options = {})
|
87
|
+
build_validation_options(method, html_options.merge(:name => options[:name]))
|
88
|
+
html_options.delete(:validate)
|
89
|
+
time_zone_select_without_rails4_client_side_validations(method, priority_zones, options, html_options)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def build_validation_options(method, options = {})
|
95
|
+
if @options[:validate]
|
96
|
+
index = @default_options[:index].present? ? "[#{@default_options[:index]}]" : ''
|
97
|
+
name = options[:name] || "#{@object_name}#{index}[#{method}]"
|
98
|
+
child_index = @options[:child_index] ? "(\\d+|#{Regexp.escape(@options[:child_index].to_s)})" : "\\d+"
|
99
|
+
name = name.to_s.gsub(/_attributes\]\[#{child_index}\]/, '_attributes][]')
|
100
|
+
name = "#{name}#{options[:multiple] ? "[]" : nil}"
|
101
|
+
@options[:validators][@object][method] = { :name => name, :options => options[:validate] }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module Rails4ClientSideValidations::ActionView::Helpers
|
2
|
+
module FormHelper
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
def form_for(record, *args, &block)
|
6
|
+
raise ArgumentError, "Missing block" unless block_given?
|
7
|
+
options = args.extract_options!
|
8
|
+
if options[:validate]
|
9
|
+
|
10
|
+
# Always turn off HTML5 Validations
|
11
|
+
options[:html] ||= {}
|
12
|
+
options[:html][:novalidate] = 'novalidate'
|
13
|
+
|
14
|
+
case record
|
15
|
+
when String, Symbol
|
16
|
+
raise Rails4ClientSideValidations::ActionView::Helpers::FormHelper::Error, 'Using form_for(:name, @resource) is not supported with ClientSideValidations. Please use form_for(@resource, :as => :name) instead.'
|
17
|
+
else
|
18
|
+
object = record.is_a?(Array) ? record.last : record
|
19
|
+
object_name = options[:as] || model_name_from_record_or_class(object).param_key
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
@validators = {}
|
24
|
+
|
25
|
+
# Order matters here. Rails mutates the options object
|
26
|
+
html_id = options[:html][:id] if options[:html]
|
27
|
+
form = super(record, *(args << options), &block)
|
28
|
+
options[:id] = html_id if html_id
|
29
|
+
|
30
|
+
process_validators options
|
31
|
+
builder = instantiate_builder(object_name, object, options) if object_name and object
|
32
|
+
|
33
|
+
script = client_side_form_settings(object, options, builder)
|
34
|
+
|
35
|
+
# Because of the load order requirement above this sub is necessary
|
36
|
+
# Would be nice to not do this
|
37
|
+
script = insert_validators_into_script(script)
|
38
|
+
|
39
|
+
if assign_script_to_content_for(options[:validate], script)
|
40
|
+
form.html_safe
|
41
|
+
else
|
42
|
+
"#{form}#{script}".html_safe
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def assign_script_to_content_for(name, script)
|
47
|
+
if name && name != true
|
48
|
+
content_for(name) { script.html_safe }
|
49
|
+
true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def apply_form_for_options!(object_or_array, object, options)
|
54
|
+
super
|
55
|
+
options[:html][:validate] = true if options[:validate]
|
56
|
+
end
|
57
|
+
|
58
|
+
def fields_for(record_or_name_or_array, record_object = nil, options = {}, &block)
|
59
|
+
output = super
|
60
|
+
process_validators options
|
61
|
+
output
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def process_validators(options)
|
67
|
+
if @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
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def insert_validators_into_script(script)
|
79
|
+
# There is probably a more performant way of doing this
|
80
|
+
# But using String#sub has some issues. Undocumented "features"
|
81
|
+
if script
|
82
|
+
script = script.split(/"validator_hash"/)
|
83
|
+
script = "#{script[0]}#{construct_validators.to_json}#{script[1]}"
|
84
|
+
end
|
85
|
+
|
86
|
+
script
|
87
|
+
end
|
88
|
+
|
89
|
+
def construct_validators
|
90
|
+
@validators.inject({}) do |validator_hash, object_opts|
|
91
|
+
option_hash = object_opts[1].inject({}) do |option_hash, attr|
|
92
|
+
option_hash.merge!(attr[0] => attr[1][:options])
|
93
|
+
end
|
94
|
+
|
95
|
+
if object_opts[0].respond_to?(:client_side_validation_hash)
|
96
|
+
validation_hash = object_opts[0].client_side_validation_hash(option_hash)
|
97
|
+
else
|
98
|
+
validation_hash = {}
|
99
|
+
end
|
100
|
+
|
101
|
+
option_hash.each_key do |attr|
|
102
|
+
if validation_hash[attr]
|
103
|
+
validator_hash.merge!(object_opts[1][attr][:name] => validation_hash[attr])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
validator_hash
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def client_side_form_settings(object, options, builder)
|
112
|
+
if options[:validate]
|
113
|
+
if options[:id]
|
114
|
+
var_name = options[:id]
|
115
|
+
else
|
116
|
+
var_name = if object.respond_to?(:persisted?) && object.persisted?
|
117
|
+
options[:as] ? "edit_#{options[:as]}" : [options[:namespace], dom_id(object, :edit)].compact.join("_")
|
118
|
+
else
|
119
|
+
options[:as] ? "new_#{options[:as]}" : [options[:namespace], dom_id(object)].compact.join("_")
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
if Rails4ClientSideValidations::Config.number_format_with_locale and defined?(I18n)
|
124
|
+
number_format = I18n.t("number.format").slice(:separator, :delimiter)
|
125
|
+
else
|
126
|
+
number_format = {:separator=>".", :delimiter=>","}
|
127
|
+
end
|
128
|
+
patterns = {:numericality=>"/^(-|\\+)?(?:\\d+|\\d{1,3}(?:\\#{number_format[:delimiter]}\\d{3})+)(?:\\#{number_format[:separator]}\\d*)?$/"}
|
129
|
+
|
130
|
+
|
131
|
+
content_tag(:script) do
|
132
|
+
"//<![CDATA[\nif(window.Rails4ClientSideValidations===undefined)window.Rails4ClientSideValidations={};window.Rails4ClientSideValidations.disabled_validators=#{Rails4ClientSideValidations::Config.disabled_validators.to_json};window.Rails4ClientSideValidations.number_format=#{number_format.to_json};if(window.Rails4ClientSideValidations.patterns===undefined)window.Rails4ClientSideValidations.patterns = {};window.Rails4ClientSideValidations.patterns.numericality=#{patterns[:numericality]};if(window.Rails4ClientSideValidations.remote_validators_prefix===undefined)window.Rails4ClientSideValidations.remote_validators_prefix='#{(Rails4ClientSideValidations::Config.root_path||"").sub(/\/+\Z/,'')}';if(window.Rails4ClientSideValidations.forms===undefined)window.Rails4ClientSideValidations.forms={};window.Rails4ClientSideValidations.forms['#{var_name}'] = #{builder.client_side_form_settings(options, self).merge(:validators => 'validator_hash').to_json};\n//]]>".html_safe
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Rails4ClientSideValidations::ActionView::Helpers
|
2
|
+
module FormTagHelper
|
3
|
+
private
|
4
|
+
def html_options_for_form(url_for_options, options, *parameters_for_url)
|
5
|
+
options.stringify_keys!
|
6
|
+
html_options = {}
|
7
|
+
html_options['data-validate'] = options.delete('validate') if options['validate']
|
8
|
+
html_options.merge!(super(url_for_options, options, *parameters_for_url))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'rails4_client_side_validations/core_ext'
|
2
|
+
|
3
|
+
module Rails4ClientSideValidations::ActiveModel
|
4
|
+
module Validator
|
5
|
+
|
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? }
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
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
|
+
|
20
|
+
def message_type
|
21
|
+
kind
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Validations
|
26
|
+
def client_side_validation_hash(force = nil)
|
27
|
+
_validators.inject({}) do |attr_hash, attr|
|
28
|
+
unless [nil, :block].include?(attr[0])
|
29
|
+
|
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
|
35
|
+
end
|
36
|
+
|
37
|
+
kind_hash
|
38
|
+
end
|
39
|
+
|
40
|
+
if validator_hash.present?
|
41
|
+
attr_hash.merge!(attr[0] => validator_hash)
|
42
|
+
else
|
43
|
+
attr_hash
|
44
|
+
end
|
45
|
+
else
|
46
|
+
attr_hash
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
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 validator.options[:if] && validator.options[:if] =~ /changed\?/
|
74
|
+
result = true
|
75
|
+
else result = can_force_validator?(attr, validator, force)
|
76
|
+
if validator.options[:if]
|
77
|
+
result = result && run_conditional(validator.options[:if])
|
78
|
+
end
|
79
|
+
if validator.options[:unless]
|
80
|
+
result = result && !run_conditional(validator.options[:unless])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
def run_conditional(method_name_value_or_proc)
|
90
|
+
if method_name_value_or_proc.respond_to?(:call)
|
91
|
+
method_name_value_or_proc.call(self)
|
92
|
+
else
|
93
|
+
self.send(method_name_value_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
|
+
::Rails4ClientSideValidations::Config.disabled_validators.include?(validator.kind)
|
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
|
+
false
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
ActiveModel::Validator.send(:include, Rails4ClientSideValidations::ActiveModel::Validator)
|
136
|
+
ActiveModel::Validations.send(:include, Rails4ClientSideValidations::ActiveModel::Validations)
|
137
|
+
|
138
|
+
%w{absence acceptance exclusion inclusion length format numericality presence}.each do |validator|
|
139
|
+
require "rails4_client_side_validations/active_model/#{validator}"
|
140
|
+
validator.capitalize!
|
141
|
+
eval "ActiveModel::Validations::#{validator}Validator.send(:include, Rails4ClientSideValidations::ActiveModel::#{validator})"
|
142
|
+
end
|
143
|
+
|