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.
Files changed (115) hide show
  1. data/client_side_validations.gemspec +34 -0
  2. data/lib/client_side_validations/action_view/form_builder.rb +160 -0
  3. data/lib/client_side_validations/action_view/form_helper.rb +92 -0
  4. data/lib/client_side_validations/action_view/form_tag_helper.rb +12 -0
  5. data/lib/client_side_validations/action_view.rb +14 -0
  6. data/lib/client_side_validations/active_model/acceptance.rb +10 -0
  7. data/lib/client_side_validations/active_model/exclusion.rb +15 -0
  8. data/lib/client_side_validations/active_model/format.rb +10 -0
  9. data/lib/client_side_validations/active_model/inclusion.rb +15 -0
  10. data/lib/client_side_validations/active_model/length.rb +24 -0
  11. data/lib/client_side_validations/active_model/numericality.rb +31 -0
  12. data/lib/client_side_validations/active_model/presence.rb +10 -0
  13. data/lib/client_side_validations/active_model.rb +60 -0
  14. data/lib/client_side_validations/active_record/middleware.rb +33 -0
  15. data/lib/client_side_validations/active_record/uniqueness.rb +28 -0
  16. data/lib/client_side_validations/active_record.rb +11 -0
  17. data/lib/client_side_validations/core_ext/range.rb +10 -0
  18. data/lib/client_side_validations/core_ext/regexp.rb +14 -0
  19. data/lib/client_side_validations/core_ext.rb +3 -0
  20. data/lib/client_side_validations/engine.rb +6 -0
  21. data/lib/client_side_validations/files.rb +8 -0
  22. data/lib/client_side_validations/formtastic.rb +21 -0
  23. data/lib/client_side_validations/middleware.rb +81 -0
  24. data/lib/client_side_validations/mongo_mapper/middleware.rb +20 -0
  25. data/lib/client_side_validations/mongo_mapper/uniqueness.rb +28 -0
  26. data/lib/client_side_validations/mongo_mapper.rb +9 -0
  27. data/lib/client_side_validations/mongoid/middleware.rb +20 -0
  28. data/lib/client_side_validations/mongoid/uniqueness.rb +28 -0
  29. data/lib/client_side_validations/mongoid.rb +9 -0
  30. data/lib/client_side_validations/simple_form.rb +24 -0
  31. data/lib/client_side_validations/version.rb +3 -0
  32. data/lib/client_side_validations.rb +13 -0
  33. data/lib/generators/client_side_validations/copy_asset_generator.rb +36 -0
  34. data/lib/generators/client_side_validations/install_generator.rb +45 -0
  35. data/lib/generators/templates/client_side_validations/README.rails.3.0 +6 -0
  36. data/lib/generators/templates/client_side_validations/README.rails.3.1 +7 -0
  37. data/lib/generators/templates/client_side_validations/initializer.rb +14 -0
  38. data/test/action_view/cases/helper.rb +176 -0
  39. data/test/action_view/cases/test_helpers.rb +666 -0
  40. data/test/action_view/cases/test_legacy_helpers.rb +217 -0
  41. data/test/action_view/models/comment.rb +35 -0
  42. data/test/action_view/models/post.rb +35 -0
  43. data/test/action_view/models.rb +3 -0
  44. data/test/active_model/cases/helper.rb +4 -0
  45. data/test/active_model/cases/test_acceptance_validator.rb +16 -0
  46. data/test/active_model/cases/test_base.rb +11 -0
  47. data/test/active_model/cases/test_confirmation_validator.rb +16 -0
  48. data/test/active_model/cases/test_exclusion_validator.rb +20 -0
  49. data/test/active_model/cases/test_format_validator.rb +21 -0
  50. data/test/active_model/cases/test_inclusion_validator.rb +21 -0
  51. data/test/active_model/cases/test_length_validator.rb +61 -0
  52. data/test/active_model/cases/test_numericality_validator.rb +46 -0
  53. data/test/active_model/cases/test_presence_validator.rb +16 -0
  54. data/test/active_model/cases/test_validations.rb +175 -0
  55. data/test/active_model/models/person.rb +17 -0
  56. data/test/active_record/cases/helper.rb +12 -0
  57. data/test/active_record/cases/test_base.rb +11 -0
  58. data/test/active_record/cases/test_middleware.rb +175 -0
  59. data/test/active_record/cases/test_uniqueness_validator.rb +50 -0
  60. data/test/active_record/models/guid.rb +7 -0
  61. data/test/active_record/models/user.rb +14 -0
  62. data/test/base_helper.rb +7 -0
  63. data/test/core_ext/cases/test_core_ext.rb +46 -0
  64. data/test/formtastic/cases/helper.rb +7 -0
  65. data/test/formtastic/cases/test_form_builder.rb +11 -0
  66. data/test/formtastic/cases/test_form_helper.rb +21 -0
  67. data/test/generators/cases/test_generators.rb +70 -0
  68. data/test/javascript/config.ru +3 -0
  69. data/test/javascript/public/test/callbacks/elementAfter.js +54 -0
  70. data/test/javascript/public/test/callbacks/elementBefore.js +54 -0
  71. data/test/javascript/public/test/callbacks/elementFail.js +70 -0
  72. data/test/javascript/public/test/callbacks/elementPass.js +70 -0
  73. data/test/javascript/public/test/callbacks/formAfter.js +45 -0
  74. data/test/javascript/public/test/callbacks/formBefore.js +45 -0
  75. data/test/javascript/public/test/callbacks/formFail.js +51 -0
  76. data/test/javascript/public/test/callbacks/formPass.js +50 -0
  77. data/test/javascript/public/test/form_builders/validateForm.js +66 -0
  78. data/test/javascript/public/test/form_builders/validateFormtastic.js +54 -0
  79. data/test/javascript/public/test/form_builders/validateNestedForm.js +66 -0
  80. data/test/javascript/public/test/form_builders/validateSimpleForm.js +57 -0
  81. data/test/javascript/public/test/settings.js +15 -0
  82. data/test/javascript/public/test/validateElement.js +209 -0
  83. data/test/javascript/public/test/validators/acceptance.js +42 -0
  84. data/test/javascript/public/test/validators/confirmation.js +25 -0
  85. data/test/javascript/public/test/validators/exclusion.js +41 -0
  86. data/test/javascript/public/test/validators/format.js +27 -0
  87. data/test/javascript/public/test/validators/inclusion.js +42 -0
  88. data/test/javascript/public/test/validators/length.js +76 -0
  89. data/test/javascript/public/test/validators/numericality.js +158 -0
  90. data/test/javascript/public/test/validators/presence.js +21 -0
  91. data/test/javascript/public/test/validators/uniqueness.js +107 -0
  92. data/test/javascript/public/vendor/jquery.metadata.js +122 -0
  93. data/test/javascript/public/vendor/qunit.css +196 -0
  94. data/test/javascript/public/vendor/qunit.js +1374 -0
  95. data/test/javascript/server.rb +84 -0
  96. data/test/javascript/views/index.erb +20 -0
  97. data/test/javascript/views/layout.erb +21 -0
  98. data/test/middleware/cases/helper.rb +18 -0
  99. data/test/middleware/cases/test_middleware.rb +8 -0
  100. data/test/mongo_mapper/cases/helper.rb +9 -0
  101. data/test/mongo_mapper/cases/test_base.rb +15 -0
  102. data/test/mongo_mapper/cases/test_middleware.rb +77 -0
  103. data/test/mongo_mapper/cases/test_uniqueness_validator.rb +50 -0
  104. data/test/mongo_mapper/models/magazine.rb +11 -0
  105. data/test/mongoid/cases/helper.rb +16 -0
  106. data/test/mongoid/cases/test_base.rb +15 -0
  107. data/test/mongoid/cases/test_middleware.rb +77 -0
  108. data/test/mongoid/cases/test_uniqueness_validator.rb +49 -0
  109. data/test/mongoid/models/book.rb +12 -0
  110. data/test/simple_form/cases/helper.rb +5 -0
  111. data/test/simple_form/cases/test_form_builder.rb +14 -0
  112. data/test/simple_form/cases/test_form_helper.rb +24 -0
  113. data/test/test_loader.rb +6 -0
  114. data/vendor/assets/javascripts/rails.validations.js +418 -0
  115. 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,10 @@
1
+ module ClientSideValidations::ActiveModel
2
+ module Acceptance
3
+ private
4
+
5
+ def message_type
6
+ :accepted
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,15 @@
1
+ module ClientSideValidations::ActiveModel
2
+ module Exclusion
3
+
4
+ def client_side_hash(model, attribute)
5
+ hash = super
6
+ if hash[:in].is_a?(Range)
7
+ hash[:range] = hash[:in]
8
+ hash.delete(:in)
9
+ end
10
+ hash
11
+ end
12
+
13
+ end
14
+ end
15
+
@@ -0,0 +1,10 @@
1
+ module ClientSideValidations::ActiveModel
2
+ module Format
3
+ private
4
+
5
+ def message_type
6
+ :invalid
7
+ end
8
+ end
9
+ end
10
+
@@ -0,0 +1,15 @@
1
+ module ClientSideValidations::ActiveModel
2
+ module Inclusion
3
+
4
+ def client_side_hash(model, attribute)
5
+ hash = super
6
+ if hash[:in].is_a?(Range)
7
+ hash[:range] = hash[:in]
8
+ hash.delete(:in)
9
+ end
10
+ hash
11
+ end
12
+
13
+ end
14
+ end
15
+
@@ -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,10 @@
1
+ module ClientSideValidations::ActiveModel
2
+ module Presence
3
+ private
4
+
5
+ def message_type
6
+ :blank
7
+ end
8
+ end
9
+ end
10
+
@@ -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,10 @@
1
+ class Range
2
+ def as_json(options = nil)
3
+ [first, last]
4
+ end
5
+
6
+ def to_json(options = nil)
7
+ as_json(options).inspect
8
+ end
9
+ end
10
+
@@ -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,3 @@
1
+ require 'active_support/json'
2
+ require 'client_side_validations/core_ext/range'
3
+ require 'client_side_validations/core_ext/regexp'
@@ -0,0 +1,6 @@
1
+ module ClientSideValidations
2
+ class Engine < ::Rails::Engine
3
+ config.app_middleware.use ClientSideValidations::Middleware::Validators
4
+ end
5
+ end
6
+
@@ -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
+