af-client_side_validations 3.1.4.af1
Sign up to get free protection for your applications and to get access to all the features.
- data/client_side_validations.gemspec +34 -0
- data/lib/client_side_validations/action_view/form_builder.rb +160 -0
- data/lib/client_side_validations/action_view/form_helper.rb +92 -0
- data/lib/client_side_validations/action_view/form_tag_helper.rb +12 -0
- data/lib/client_side_validations/action_view.rb +14 -0
- data/lib/client_side_validations/active_model/acceptance.rb +10 -0
- data/lib/client_side_validations/active_model/exclusion.rb +15 -0
- data/lib/client_side_validations/active_model/format.rb +10 -0
- data/lib/client_side_validations/active_model/inclusion.rb +15 -0
- data/lib/client_side_validations/active_model/length.rb +24 -0
- data/lib/client_side_validations/active_model/numericality.rb +31 -0
- data/lib/client_side_validations/active_model/presence.rb +10 -0
- data/lib/client_side_validations/active_model.rb +60 -0
- data/lib/client_side_validations/active_record/middleware.rb +33 -0
- data/lib/client_side_validations/active_record/uniqueness.rb +28 -0
- data/lib/client_side_validations/active_record.rb +11 -0
- data/lib/client_side_validations/core_ext/range.rb +10 -0
- data/lib/client_side_validations/core_ext/regexp.rb +14 -0
- data/lib/client_side_validations/core_ext.rb +3 -0
- data/lib/client_side_validations/engine.rb +6 -0
- data/lib/client_side_validations/files.rb +8 -0
- data/lib/client_side_validations/formtastic.rb +21 -0
- data/lib/client_side_validations/middleware.rb +81 -0
- data/lib/client_side_validations/mongo_mapper/middleware.rb +20 -0
- data/lib/client_side_validations/mongo_mapper/uniqueness.rb +28 -0
- data/lib/client_side_validations/mongo_mapper.rb +9 -0
- data/lib/client_side_validations/mongoid/middleware.rb +20 -0
- data/lib/client_side_validations/mongoid/uniqueness.rb +28 -0
- data/lib/client_side_validations/mongoid.rb +9 -0
- data/lib/client_side_validations/simple_form.rb +24 -0
- data/lib/client_side_validations/version.rb +3 -0
- data/lib/client_side_validations.rb +13 -0
- data/lib/generators/client_side_validations/copy_asset_generator.rb +36 -0
- data/lib/generators/client_side_validations/install_generator.rb +45 -0
- data/lib/generators/templates/client_side_validations/README.rails.3.0 +6 -0
- data/lib/generators/templates/client_side_validations/README.rails.3.1 +7 -0
- data/lib/generators/templates/client_side_validations/initializer.rb +14 -0
- data/test/action_view/cases/helper.rb +176 -0
- data/test/action_view/cases/test_helpers.rb +666 -0
- data/test/action_view/cases/test_legacy_helpers.rb +217 -0
- data/test/action_view/models/comment.rb +35 -0
- data/test/action_view/models/post.rb +35 -0
- data/test/action_view/models.rb +3 -0
- data/test/active_model/cases/helper.rb +4 -0
- data/test/active_model/cases/test_acceptance_validator.rb +16 -0
- data/test/active_model/cases/test_base.rb +11 -0
- data/test/active_model/cases/test_confirmation_validator.rb +16 -0
- data/test/active_model/cases/test_exclusion_validator.rb +20 -0
- data/test/active_model/cases/test_format_validator.rb +21 -0
- data/test/active_model/cases/test_inclusion_validator.rb +21 -0
- data/test/active_model/cases/test_length_validator.rb +61 -0
- data/test/active_model/cases/test_numericality_validator.rb +46 -0
- data/test/active_model/cases/test_presence_validator.rb +16 -0
- data/test/active_model/cases/test_validations.rb +175 -0
- data/test/active_model/models/person.rb +17 -0
- data/test/active_record/cases/helper.rb +12 -0
- data/test/active_record/cases/test_base.rb +11 -0
- data/test/active_record/cases/test_middleware.rb +175 -0
- data/test/active_record/cases/test_uniqueness_validator.rb +50 -0
- data/test/active_record/models/guid.rb +7 -0
- data/test/active_record/models/user.rb +14 -0
- data/test/base_helper.rb +7 -0
- data/test/core_ext/cases/test_core_ext.rb +46 -0
- data/test/formtastic/cases/helper.rb +7 -0
- data/test/formtastic/cases/test_form_builder.rb +11 -0
- data/test/formtastic/cases/test_form_helper.rb +21 -0
- data/test/generators/cases/test_generators.rb +70 -0
- data/test/javascript/config.ru +3 -0
- data/test/javascript/public/test/callbacks/elementAfter.js +54 -0
- data/test/javascript/public/test/callbacks/elementBefore.js +54 -0
- data/test/javascript/public/test/callbacks/elementFail.js +70 -0
- data/test/javascript/public/test/callbacks/elementPass.js +70 -0
- data/test/javascript/public/test/callbacks/formAfter.js +45 -0
- data/test/javascript/public/test/callbacks/formBefore.js +45 -0
- data/test/javascript/public/test/callbacks/formFail.js +51 -0
- data/test/javascript/public/test/callbacks/formPass.js +50 -0
- data/test/javascript/public/test/form_builders/validateForm.js +66 -0
- data/test/javascript/public/test/form_builders/validateFormtastic.js +54 -0
- data/test/javascript/public/test/form_builders/validateNestedForm.js +66 -0
- data/test/javascript/public/test/form_builders/validateSimpleForm.js +57 -0
- data/test/javascript/public/test/settings.js +15 -0
- data/test/javascript/public/test/validateElement.js +209 -0
- data/test/javascript/public/test/validators/acceptance.js +42 -0
- data/test/javascript/public/test/validators/confirmation.js +25 -0
- data/test/javascript/public/test/validators/exclusion.js +41 -0
- data/test/javascript/public/test/validators/format.js +27 -0
- data/test/javascript/public/test/validators/inclusion.js +42 -0
- data/test/javascript/public/test/validators/length.js +76 -0
- data/test/javascript/public/test/validators/numericality.js +158 -0
- data/test/javascript/public/test/validators/presence.js +21 -0
- data/test/javascript/public/test/validators/uniqueness.js +107 -0
- data/test/javascript/public/vendor/jquery.metadata.js +122 -0
- data/test/javascript/public/vendor/qunit.css +196 -0
- data/test/javascript/public/vendor/qunit.js +1374 -0
- data/test/javascript/server.rb +84 -0
- data/test/javascript/views/index.erb +20 -0
- data/test/javascript/views/layout.erb +21 -0
- data/test/middleware/cases/helper.rb +18 -0
- data/test/middleware/cases/test_middleware.rb +8 -0
- data/test/mongo_mapper/cases/helper.rb +9 -0
- data/test/mongo_mapper/cases/test_base.rb +15 -0
- data/test/mongo_mapper/cases/test_middleware.rb +77 -0
- data/test/mongo_mapper/cases/test_uniqueness_validator.rb +50 -0
- data/test/mongo_mapper/models/magazine.rb +11 -0
- data/test/mongoid/cases/helper.rb +16 -0
- data/test/mongoid/cases/test_base.rb +15 -0
- data/test/mongoid/cases/test_middleware.rb +77 -0
- data/test/mongoid/cases/test_uniqueness_validator.rb +49 -0
- data/test/mongoid/models/book.rb +12 -0
- data/test/simple_form/cases/helper.rb +5 -0
- data/test/simple_form/cases/test_form_builder.rb +14 -0
- data/test/simple_form/cases/test_form_helper.rb +24 -0
- data/test/test_loader.rb +6 -0
- data/vendor/assets/javascripts/rails.validations.js +418 -0
- metadata +436 -0
@@ -0,0 +1,34 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "client_side_validations/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "af-client_side_validations"
|
7
|
+
s.version = ClientSideValidations::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Paul Kmiec"]
|
10
|
+
s.email = ["paul.kmiec@appfolio.com"]
|
11
|
+
s.homepage = "https://github.com/appfolio/client_side_validations"
|
12
|
+
s.summary = %q{Client Side Validations}
|
13
|
+
s.description = %q{Client Side Validations}
|
14
|
+
|
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
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_development_dependency 'rails', '~> 3.1.0'
|
21
|
+
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
|
+
s.add_development_dependency 'mocha'
|
26
|
+
s.add_development_dependency 'simple_form', '~> 1.5.0'
|
27
|
+
s.add_development_dependency 'formtastic', '~> 2.0.0'
|
28
|
+
|
29
|
+
# For QUnit testing
|
30
|
+
s.add_development_dependency 'sinatra', '~> 1.0'
|
31
|
+
s.add_development_dependency 'shotgun'
|
32
|
+
s.add_development_dependency 'thin'
|
33
|
+
s.add_development_dependency 'json'
|
34
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module ClientSideValidations::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_client_side_validations(method, options = {})
|
8
|
+
apply_client_side_validators(method, options)
|
9
|
+
options.delete(:validate)
|
10
|
+
#{selector}_without_client_side_validations(method, options)
|
11
|
+
end
|
12
|
+
RUBY_EVAL
|
13
|
+
|
14
|
+
base.class_eval { alias_method_chain selector, :client_side_validations }
|
15
|
+
end
|
16
|
+
|
17
|
+
base.class_eval do
|
18
|
+
alias_method_chain :initialize, :client_side_validations
|
19
|
+
alias_method_chain :fields_for, :client_side_validations
|
20
|
+
alias_method_chain :check_box, :client_side_validations
|
21
|
+
alias_method_chain :radio_button, :client_side_validations
|
22
|
+
alias_method_chain :select, :client_side_validations
|
23
|
+
alias_method_chain :collection_select, :client_side_validations
|
24
|
+
alias_method_chain :grouped_collection_select, :client_side_validations
|
25
|
+
alias_method_chain :time_zone_select, :client_side_validations
|
26
|
+
|
27
|
+
def self.client_side_form_settings(options, form_helper)
|
28
|
+
{
|
29
|
+
:type => self.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 initialize_with_client_side_validations(object_name, object, template, options, proc)
|
38
|
+
initialize_without_client_side_validations(object_name, object, template, options, proc)
|
39
|
+
@options[:validators] = {}
|
40
|
+
end
|
41
|
+
|
42
|
+
def fields_for_with_client_side_validations(record_or_name_or_array, *args, &block)
|
43
|
+
options = args.extract_options!
|
44
|
+
options[:validate] ||= @options[:validate] if @options[:validate] && !options.key?(:validate)
|
45
|
+
fields_for_without_client_side_validations(record_or_name_or_array, *(args << options), &block)
|
46
|
+
end
|
47
|
+
|
48
|
+
def check_box_with_client_side_validations(method, options = {}, checked_value = "1", unchecked_value = "0")
|
49
|
+
apply_client_side_validators(method, options)
|
50
|
+
check_box_without_client_side_validations(method, options, checked_value, unchecked_value)
|
51
|
+
end
|
52
|
+
|
53
|
+
def radio_button_with_client_side_validations(method, tag_value, options = {})
|
54
|
+
apply_client_side_validators(method, options)
|
55
|
+
radio_button_without_client_side_validations(method, tag_value, options)
|
56
|
+
end
|
57
|
+
|
58
|
+
def select_with_client_side_validations(method, choices, options = {}, html_options = {})
|
59
|
+
apply_client_side_validators(method, html_options)
|
60
|
+
select_without_client_side_validations(method, choices, options, html_options)
|
61
|
+
end
|
62
|
+
|
63
|
+
def collection_select_with_client_side_validations(method, collection, value_method, text_method, options = {}, html_options = {})
|
64
|
+
apply_client_side_validators(method, html_options)
|
65
|
+
collection_select_without_client_side_validations(method, collection, value_method, text_method, options, html_options)
|
66
|
+
end
|
67
|
+
|
68
|
+
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)
|
70
|
+
grouped_collection_select_without_client_side_validations(method, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
71
|
+
end
|
72
|
+
|
73
|
+
def time_zone_select_with_client_side_validations(method, priority_zones = nil, options = {}, html_options = {})
|
74
|
+
apply_client_side_validators(method, html_options)
|
75
|
+
time_zone_select_without_client_side_validations(method, priority_zones = nil, options, html_options)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
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)
|
83
|
+
name = options[:name] || "#{@object_name}[#{method}]"
|
84
|
+
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)
|
87
|
+
end
|
88
|
+
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
|
+
unfiltered_validators[validator.first] = validator.last
|
94
|
+
if has_filter_for_validator?(validator, filters)
|
95
|
+
if filter_validator?(validator, filters)
|
96
|
+
unfiltered_validators.delete(validator.first)
|
97
|
+
elsif force_validator_despite_conditional?(validator, filters) && !can_run_validator?(validator, method)
|
98
|
+
unfiltered_validators.delete(validator.first)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
if (conditional = (validator.last[:if] || validator.last[:unless])) && conditional.is_a?(Symbol) && !conditional_method_is_change_method?(conditional, method)
|
102
|
+
unfiltered_validators.delete(validator.first)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
unfiltered_validators[validator.first].delete(:if) if unfiltered_validators[validator.first]
|
106
|
+
unfiltered_validators[validator.first].delete(:unless) if unfiltered_validators[validator.first]
|
107
|
+
unfiltered_validators
|
108
|
+
end
|
109
|
+
|
110
|
+
unfiltered_validators.empty? ? nil : unfiltered_validators
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def has_filter_for_validator?(validator, filters)
|
115
|
+
filters && (filters == true || filters.key?(validator.first))
|
116
|
+
end
|
117
|
+
|
118
|
+
def filter_validator?(validator, filters)
|
119
|
+
filters != true && filters[validator.first] == false
|
120
|
+
end
|
121
|
+
|
122
|
+
def force_validator_despite_conditional?(validator, filters)
|
123
|
+
filters == true || filters[validator.first] == true
|
124
|
+
end
|
125
|
+
|
126
|
+
def can_run_validator?(validator, method)
|
127
|
+
result = true
|
128
|
+
if_result = run_if_validator(validator.last[:if], method)
|
129
|
+
unless_result = run_unless_validator(validator.last[:unless], method)
|
130
|
+
result = result && if_result unless if_result.nil?
|
131
|
+
result = result && unless_result unless unless_result.nil?
|
132
|
+
result
|
133
|
+
end
|
134
|
+
|
135
|
+
def run_if_validator(conditional, method)
|
136
|
+
if conditional
|
137
|
+
if conditional.is_a?(Symbol)
|
138
|
+
conditional_method_is_change_method?(conditional, method) ? true : !!@object.send(conditional)
|
139
|
+
else
|
140
|
+
!!conditional.call(@object)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def run_unless_validator(conditional, method)
|
146
|
+
if conditional
|
147
|
+
if conditional.is_a?(Symbol)
|
148
|
+
conditional_method_is_change_method?(conditional, method) ? true : !@object.send(conditional)
|
149
|
+
else
|
150
|
+
!conditional.call(@object)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def conditional_method_is_change_method?(conditional, method)
|
156
|
+
conditional.to_sym == "#{method}_changed?".to_sym
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module ClientSideValidations::ActionView::Helpers
|
2
|
+
module FormHelper
|
3
|
+
class Error < StandardError; end
|
4
|
+
|
5
|
+
def form_for(record_or_name_or_array, *args, &proc)
|
6
|
+
options = args.extract_options!
|
7
|
+
if options[:validate]
|
8
|
+
|
9
|
+
content_for_name = options[:validate] unless options[:validate] == true
|
10
|
+
|
11
|
+
# Always turn off HTML5 Validations
|
12
|
+
options[:html] ||= {}
|
13
|
+
options[:html][:novalidate] = 'novalidate'
|
14
|
+
|
15
|
+
case record_or_name_or_array
|
16
|
+
when String, Symbol
|
17
|
+
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.'
|
18
|
+
when Array
|
19
|
+
object = record_or_name_or_array.last
|
20
|
+
else
|
21
|
+
object = record_or_name_or_array
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@validators = {}
|
26
|
+
# Order matters here. Rails mutates the options object
|
27
|
+
script = client_side_form_settings(object, options)
|
28
|
+
form = super(record_or_name_or_array, *(args << options), &proc)
|
29
|
+
# Because of the load order requirement above this sub is necessary
|
30
|
+
# Would be nice to not do this
|
31
|
+
script = insert_validators_into_script(script)
|
32
|
+
if content_for_name
|
33
|
+
content_for(content_for_name) { script.html_safe }
|
34
|
+
script = nil
|
35
|
+
end
|
36
|
+
"#{form}#{script}".html_safe
|
37
|
+
end
|
38
|
+
|
39
|
+
def apply_form_for_options!(object_or_array, options)
|
40
|
+
super
|
41
|
+
options[:html][:validate] = true if options[:validate]
|
42
|
+
end
|
43
|
+
|
44
|
+
def fields_for(record_or_name_or_array, *args, &block)
|
45
|
+
output = super
|
46
|
+
|
47
|
+
if @validators
|
48
|
+
options = args.last || {}
|
49
|
+
if validators = options[:validators]
|
50
|
+
@validators.merge!(validators)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
output
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def insert_validators_into_script(script)
|
59
|
+
# There is probably a more performant way of doing this
|
60
|
+
# But using String#sub has some issues. Undocumented "features"
|
61
|
+
if script
|
62
|
+
script = script.split(/"validator_hash"/)
|
63
|
+
script = "#{script[0]}#{@validators.to_json}#{script[1]}"
|
64
|
+
end
|
65
|
+
|
66
|
+
script
|
67
|
+
end
|
68
|
+
|
69
|
+
def client_side_form_settings(object, options)
|
70
|
+
if options[:validate]
|
71
|
+
builder = options[:builder] || ActionView::Base.default_form_builder
|
72
|
+
|
73
|
+
if options[:html] && options[:html][:id]
|
74
|
+
var_name = options[:html][:id]
|
75
|
+
else
|
76
|
+
var_name = if object.respond_to?(:persisted?) && object.persisted?
|
77
|
+
options[:as] ? "#{options[:as]}_edit" : dom_id(object, :edit)
|
78
|
+
else
|
79
|
+
options[:as] ? "#{options[:as]}_new" : dom_id(object)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
content_tag(:script) do
|
84
|
+
"window['#{var_name}'] = #{builder.client_side_form_settings(options, self).merge(:validators => 'validator_hash').to_json};".html_safe
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module ClientSideValidations::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,14 @@
|
|
1
|
+
module ClientSideValidations::ActionView
|
2
|
+
module Helpers
|
3
|
+
end
|
4
|
+
end
|
5
|
+
|
6
|
+
require 'client_side_validations/core_ext'
|
7
|
+
require 'client_side_validations/action_view/form_helper'
|
8
|
+
require 'client_side_validations/action_view/form_tag_helper'
|
9
|
+
require 'client_side_validations/action_view/form_builder'
|
10
|
+
|
11
|
+
ActionView::Base.send(:include, ClientSideValidations::ActionView::Helpers::FormHelper)
|
12
|
+
ActionView::Base.send(:include, ClientSideValidations::ActionView::Helpers::FormTagHelper)
|
13
|
+
ActionView::Helpers::FormBuilder.send(:include, ClientSideValidations::ActionView::Helpers::FormBuilder)
|
14
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ClientSideValidations::ActiveModel
|
2
|
+
module Length
|
3
|
+
|
4
|
+
def client_side_hash(model, attribute)
|
5
|
+
options = self.options.dup
|
6
|
+
hash = { :messages => {} }
|
7
|
+
hash[:js_tokenizer] = options[:js_tokenizer] if options[:js_tokenizer]
|
8
|
+
hash[:allow_blank] = true if options[:allow_blank]
|
9
|
+
|
10
|
+
self.class::MESSAGES.each do |option, message_type|
|
11
|
+
if count = options[option]
|
12
|
+
options[:message] = options[message_type]
|
13
|
+
options.delete(:message) if options[:message].nil?
|
14
|
+
hash[:messages][option] = model.errors.generate_message(attribute, message_type, options.merge(:count => count))
|
15
|
+
hash[option] = count
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
hash
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ClientSideValidations::ActiveModel
|
2
|
+
module Numericality
|
3
|
+
|
4
|
+
OPTION_MAP = {}
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
OPTION_MAP.merge!(base::CHECKS.keys.inject({}) { |hash, key| hash.merge!(key => key) })
|
8
|
+
end
|
9
|
+
|
10
|
+
def client_side_hash(model, attribute)
|
11
|
+
options = self.options.dup
|
12
|
+
hash = { :messages => { :numericality => model.errors.generate_message(attribute, :not_a_number, options) } }
|
13
|
+
|
14
|
+
if options[:only_integer]
|
15
|
+
hash[:messages][:only_integer] = model.errors.generate_message(attribute, :not_an_integer, options)
|
16
|
+
hash[:only_integer] = true
|
17
|
+
end
|
18
|
+
|
19
|
+
OPTION_MAP.each do |option, message_type|
|
20
|
+
if count = options[option]
|
21
|
+
hash[:messages][option] = model.errors.generate_message(attribute, message_type, options.merge(:count => count))
|
22
|
+
hash[option] = count
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
hash
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'client_side_validations/core_ext'
|
2
|
+
|
3
|
+
module ClientSideValidations::ActiveModel
|
4
|
+
module Validator
|
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]))
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def message_type
|
14
|
+
kind
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module Validations
|
19
|
+
def client_side_validation_hash
|
20
|
+
@client_side_validation_hash ||= _validators.inject({}) do |attr_hash, attr|
|
21
|
+
unless [nil, :block].include?(attr[0])
|
22
|
+
|
23
|
+
validator_hash = attr[1].inject({}) do |kind_hash, validator|
|
24
|
+
client_side_hash = validator.client_side_hash(self, attr[0])
|
25
|
+
# Yeah yeah, #new_record? is not part of ActiveModel :p
|
26
|
+
if (can_use_for_client_side_validation?(client_side_hash, validator))
|
27
|
+
kind_hash.merge!(validator.kind => client_side_hash.except(:on))
|
28
|
+
else
|
29
|
+
kind_hash.merge!({})
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if validator_hash.present?
|
34
|
+
attr_hash.merge!(attr[0] => validator_hash)
|
35
|
+
else
|
36
|
+
attr_hash
|
37
|
+
end
|
38
|
+
else
|
39
|
+
attr_hash
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def can_use_for_client_side_validation?(client_side_hash, validator)
|
47
|
+
((self.respond_to?(:new_record?) && validator.options[:on] == (self.new_record? ? :create : :update)) || validator.options[:on].nil?) && validator.kind != :block
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
ActiveModel::Validator.send(:include, ClientSideValidations::ActiveModel::Validator)
|
53
|
+
ActiveModel::Validations.send(:include, ClientSideValidations::ActiveModel::Validations)
|
54
|
+
|
55
|
+
%w{acceptance exclusion inclusion length format numericality presence}.each do |validator|
|
56
|
+
require "client_side_validations/active_model/#{validator}"
|
57
|
+
validator.capitalize!
|
58
|
+
eval "ActiveModel::Validations::#{validator}Validator.send(:include, ClientSideValidations::ActiveModel::#{validator})"
|
59
|
+
end
|
60
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module ClientSideValidations::ActiveRecord
|
2
|
+
class Middleware
|
3
|
+
|
4
|
+
def self.is_unique?(klass, attribute, value, params)
|
5
|
+
column = klass.columns_hash[attribute.to_s]
|
6
|
+
value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?
|
7
|
+
|
8
|
+
t = klass.arel_table
|
9
|
+
|
10
|
+
if params[:case_sensitive] == 'true'
|
11
|
+
if t.engine.connection.instance_variable_get("@config")[:adapter] == 'mysql'
|
12
|
+
relation = Arel::Nodes::SqlLiteral.new("BINARY #{t[attribute].eq(value).to_sql}")
|
13
|
+
else
|
14
|
+
relation = t[attribute].eq(value)
|
15
|
+
end
|
16
|
+
else
|
17
|
+
relation = t[attribute].matches(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
if relation.is_a?(Arel::Nodes::SqlLiteral)
|
21
|
+
relation = Arel::Nodes::SqlLiteral.new("BINARY #{t[attribute].eq(value).to_sql} AND #{t.primary_key.not_eq(params[:id]).to_sql}")
|
22
|
+
else
|
23
|
+
relation = relation.and(t.primary_key.not_eq(params[:id])) if params[:id]
|
24
|
+
end
|
25
|
+
|
26
|
+
(params[:scope] || {}).each do |key, value|
|
27
|
+
relation = relation.and(t[key].eq(value))
|
28
|
+
end
|
29
|
+
|
30
|
+
!klass.where(relation).exists?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module ClientSideValidations::ActiveRecord
|
2
|
+
module Uniqueness
|
3
|
+
def client_side_hash(model, attribute)
|
4
|
+
hash = {}
|
5
|
+
hash[:message] = model.errors.generate_message(attribute, message_type, options.except(:scope))
|
6
|
+
hash[:case_sensitive] = options[:case_sensitive]
|
7
|
+
hash[:id] = model.id unless model.new_record?
|
8
|
+
if options.key?(:scope) && options[:scope].present?
|
9
|
+
hash[:scope] = Array.wrap(options[:scope]).inject({}) do |scope_hash, scope_item|
|
10
|
+
scope_hash.merge!(scope_item => model.send(scope_item))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
unless model.class.name.demodulize == model.class.name
|
15
|
+
hash[:class] = model.class.name.underscore
|
16
|
+
end
|
17
|
+
|
18
|
+
hash
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def message_type
|
24
|
+
:taken
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'client_side_validations/active_model'
|
2
|
+
require 'client_side_validations/active_record/middleware'
|
3
|
+
|
4
|
+
%w{uniqueness}.each do |validator|
|
5
|
+
require "client_side_validations/active_record/#{validator}"
|
6
|
+
validator.capitalize!
|
7
|
+
eval "ActiveRecord::Validations::#{validator}Validator.send(:include, ClientSideValidations::ActiveRecord::#{validator})"
|
8
|
+
end
|
9
|
+
|
10
|
+
ActiveRecord::Base.send(:include, ClientSideValidations::ActiveModel::Validations)
|
11
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Regexp
|
2
|
+
def as_json(options = nil)
|
3
|
+
Regexp.new inspect.sub("\\A","^").sub("\\Z","$").sub("\\z","$").sub(/^\//,"").sub(/\/[a-z]*$/,""), self.options
|
4
|
+
end
|
5
|
+
|
6
|
+
def to_json(options = nil)
|
7
|
+
as_json(options).inspect
|
8
|
+
end
|
9
|
+
|
10
|
+
def encode_json(encoder)
|
11
|
+
inspect
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
# This is only used by dependant libraries that need to find the files
|
2
|
+
|
3
|
+
module ClientSideValidations
|
4
|
+
module Files
|
5
|
+
Initializer = File.expand_path(File.dirname(__FILE__) + '/../generators/templates/client_side_validations/initializer.rb')
|
6
|
+
Javascript = File.expand_path(File.dirname(__FILE__) + '/../../vendor/assets/javascripts/rails.validations.js')
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ClientSideValidations
|
2
|
+
module Formtastic
|
3
|
+
module FormBuilder
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
def self.client_side_form_settings(options, form_helper)
|
8
|
+
{
|
9
|
+
:type => self.to_s,
|
10
|
+
:inline_error_class => ::Formtastic::FormBuilder.default_inline_error_class
|
11
|
+
}
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
Formtastic::FormBuilder.send(:include, ClientSideValidations::Formtastic::FormBuilder)
|
21
|
+
|