nimboids-client_side_validations 3.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.
Files changed (101) hide show
  1. data/client_side_validations.gemspec +45 -0
  2. data/javascript/rails.validations.js +393 -0
  3. data/lib/client_side_validations.rb +8 -0
  4. data/lib/client_side_validations/action_view.rb +13 -0
  5. data/lib/client_side_validations/action_view/form_builder.rb +64 -0
  6. data/lib/client_side_validations/action_view/form_helper.rb +69 -0
  7. data/lib/client_side_validations/action_view/form_tag_helper.rb +12 -0
  8. data/lib/client_side_validations/active_model.rb +52 -0
  9. data/lib/client_side_validations/active_model/acceptance.rb +10 -0
  10. data/lib/client_side_validations/active_model/exclusion.rb +15 -0
  11. data/lib/client_side_validations/active_model/format.rb +10 -0
  12. data/lib/client_side_validations/active_model/inclusion.rb +15 -0
  13. data/lib/client_side_validations/active_model/length.rb +22 -0
  14. data/lib/client_side_validations/active_model/numericality.rb +26 -0
  15. data/lib/client_side_validations/active_model/presence.rb +10 -0
  16. data/lib/client_side_validations/active_record.rb +11 -0
  17. data/lib/client_side_validations/active_record/middleware.rb +25 -0
  18. data/lib/client_side_validations/active_record/uniqueness.rb +26 -0
  19. data/lib/client_side_validations/core_ext.rb +3 -0
  20. data/lib/client_side_validations/core_ext/range.rb +10 -0
  21. data/lib/client_side_validations/core_ext/regexp.rb +14 -0
  22. data/lib/client_side_validations/formtastic.rb +21 -0
  23. data/lib/client_side_validations/middleware.rb +83 -0
  24. data/lib/client_side_validations/mongoid.rb +9 -0
  25. data/lib/client_side_validations/mongoid/middleware.rb +20 -0
  26. data/lib/client_side_validations/mongoid/uniqueness.rb +26 -0
  27. data/lib/client_side_validations/simple_form.rb +24 -0
  28. data/lib/client_side_validations/version.rb +3 -0
  29. data/lib/generators/client_side_validations/install_generator.rb +22 -0
  30. data/lib/generators/templates/README +7 -0
  31. data/lib/generators/templates/client_side_validations.rb +14 -0
  32. data/test/action_view/cases/helper.rb +152 -0
  33. data/test/action_view/cases/test_helpers.rb +237 -0
  34. data/test/action_view/cases/test_legacy_helpers.rb +150 -0
  35. data/test/action_view/models.rb +3 -0
  36. data/test/action_view/models/comment.rb +35 -0
  37. data/test/action_view/models/post.rb +35 -0
  38. data/test/active_model/cases/helper.rb +4 -0
  39. data/test/active_model/cases/test_acceptance_validator.rb +16 -0
  40. data/test/active_model/cases/test_base.rb +11 -0
  41. data/test/active_model/cases/test_confirmation_validator.rb +16 -0
  42. data/test/active_model/cases/test_exclusion_validator.rb +20 -0
  43. data/test/active_model/cases/test_format_validator.rb +21 -0
  44. data/test/active_model/cases/test_inclusion_validator.rb +21 -0
  45. data/test/active_model/cases/test_length_validator.rb +61 -0
  46. data/test/active_model/cases/test_numericality_validator.rb +46 -0
  47. data/test/active_model/cases/test_presence_validator.rb +16 -0
  48. data/test/active_model/cases/test_validations.rb +151 -0
  49. data/test/active_model/models/person.rb +17 -0
  50. data/test/active_record/cases/helper.rb +12 -0
  51. data/test/active_record/cases/test_base.rb +11 -0
  52. data/test/active_record/cases/test_middleware.rb +150 -0
  53. data/test/active_record/cases/test_uniqueness_validator.rb +45 -0
  54. data/test/active_record/models/guid.rb +7 -0
  55. data/test/active_record/models/user.rb +10 -0
  56. data/test/base_helper.rb +6 -0
  57. data/test/core_ext/cases/test_core_ext.rb +45 -0
  58. data/test/formtastic/cases/helper.rb +2 -0
  59. data/test/formtastic/cases/test_form_builder.rb +11 -0
  60. data/test/formtastic/cases/test_form_helper.rb +22 -0
  61. data/test/generators/cases/test_install_generator.rb +15 -0
  62. data/test/javascript/config.ru +3 -0
  63. data/test/javascript/public/test/callbacks/elementAfter.js +54 -0
  64. data/test/javascript/public/test/callbacks/elementBefore.js +54 -0
  65. data/test/javascript/public/test/callbacks/elementFail.js +70 -0
  66. data/test/javascript/public/test/callbacks/elementPass.js +70 -0
  67. data/test/javascript/public/test/callbacks/formAfter.js +45 -0
  68. data/test/javascript/public/test/callbacks/formBefore.js +45 -0
  69. data/test/javascript/public/test/callbacks/formFail.js +51 -0
  70. data/test/javascript/public/test/callbacks/formPass.js +50 -0
  71. data/test/javascript/public/test/form_builders/validateForm.js +66 -0
  72. data/test/javascript/public/test/form_builders/validateFormtastic.js +54 -0
  73. data/test/javascript/public/test/form_builders/validateSimpleForm.js +57 -0
  74. data/test/javascript/public/test/settings.js +15 -0
  75. data/test/javascript/public/test/validateElement.js +144 -0
  76. data/test/javascript/public/test/validators/acceptance.js +42 -0
  77. data/test/javascript/public/test/validators/confirmation.js +25 -0
  78. data/test/javascript/public/test/validators/exclusion.js +41 -0
  79. data/test/javascript/public/test/validators/format.js +27 -0
  80. data/test/javascript/public/test/validators/inclusion.js +42 -0
  81. data/test/javascript/public/test/validators/length.js +70 -0
  82. data/test/javascript/public/test/validators/numericality.js +140 -0
  83. data/test/javascript/public/test/validators/presence.js +15 -0
  84. data/test/javascript/public/test/validators/uniqueness.js +89 -0
  85. data/test/javascript/public/vendor/jquery.metadata.js +122 -0
  86. data/test/javascript/public/vendor/qunit.css +196 -0
  87. data/test/javascript/public/vendor/qunit.js +1374 -0
  88. data/test/javascript/server.rb +78 -0
  89. data/test/javascript/views/index.erb +20 -0
  90. data/test/javascript/views/layout.erb +21 -0
  91. data/test/middleware/cases/helper.rb +15 -0
  92. data/test/middleware/cases/test_middleware.rb +8 -0
  93. data/test/mongoid/cases/helper.rb +16 -0
  94. data/test/mongoid/cases/test_base.rb +15 -0
  95. data/test/mongoid/cases/test_middleware.rb +68 -0
  96. data/test/mongoid/cases/test_uniqueness_validator.rb +44 -0
  97. data/test/mongoid/models/book.rb +8 -0
  98. data/test/simple_form/cases/helper.rb +2 -0
  99. data/test/simple_form/cases/test_form_builder.rb +14 -0
  100. data/test/simple_form/cases/test_form_helper.rb +22 -0
  101. metadata +435 -0
@@ -0,0 +1,64 @@
1
+ module ClientSideValidations::ActionView::Helpers
2
+ module FormBuilder
3
+
4
+ def self.included(base)
5
+ (base.field_helpers - %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
+
23
+ def self.client_side_form_settings(options, form_helper)
24
+ {
25
+ :type => self.to_s,
26
+ :input_tag => form_helper.class.field_error_proc.call(%{<span id="input_tag" />}, Struct.new(:error_message, :tag_id).new([], "")),
27
+ :label_tag => form_helper.class.field_error_proc.call(%{<label id="label_tag" />})
28
+ }
29
+ end
30
+ end
31
+ end
32
+
33
+ def initialize_with_client_side_validations(object_name, object, template, options, proc)
34
+ initialize_without_client_side_validations(object_name, object, template, options, proc)
35
+ @options[:validators] = {}
36
+ end
37
+
38
+ def fields_for_with_client_side_validations(record_or_name_or_array, *args, &block)
39
+ options = args.extract_options!
40
+ options[:validate] ||= @options[:validate] if @options[:validate] && !options.key?(:validate)
41
+ fields_for_without_client_side_validations(record_or_name_or_array, *(args << options), &block)
42
+ end
43
+
44
+ def check_box_with_client_side_validations(method, options = {}, checked_value = "1", unchecked_value = "0")
45
+ apply_client_side_validators(method, options)
46
+ check_box_without_client_side_validations(method, options, checked_value, unchecked_value)
47
+ end
48
+
49
+ def radio_button_with_client_side_validations(method, tag_value, options = {})
50
+ apply_client_side_validators(method, options)
51
+ radio_button_without_client_side_validations(method, tag_value, options)
52
+ end
53
+
54
+ private
55
+
56
+ def apply_client_side_validators(method, options = {})
57
+ if @options[:validate] && options[:validate] != false && validators = @object.client_side_validation_hash[method]
58
+ options.merge!("data-validate" => true)
59
+ @options[:validators].merge!("#{@object_name}[#{method}]" => validators)
60
+ end
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,69 @@
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
+ # Turn off SimpleForm's HTML5 Form Validations
10
+ if options[:builder].to_s == 'SimpleForm::FormBuilder'
11
+ options[:html][:novalidate] = true
12
+ end
13
+
14
+ case record_or_name_or_array
15
+ when String, Symbol
16
+ 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.'
17
+ when Array
18
+ object = record_or_name_or_array.last
19
+ else
20
+ object = record_or_name_or_array
21
+ end
22
+ end
23
+
24
+ @validators = {}
25
+ # Order matters here. Rails mutates the options object
26
+ script = client_side_form_settings(object, options)
27
+ form = super(record_or_name_or_array, *(args << options), &proc)
28
+ # Because of the load order requirement above this sub is necessary
29
+ # Would be nice to not do this
30
+ "#{form}#{script ? script.sub('"validator_hash"', @validators.to_json) : nil}".html_safe
31
+ end
32
+
33
+ def apply_form_for_options!(object_or_array, options)
34
+ super
35
+ options[:html][:validate] = true if options[:validate]
36
+ end
37
+
38
+ def fields_for(record_or_name_or_array, *args, &block)
39
+ output = super
40
+ @validators.merge!(args.last[:validators])
41
+ output
42
+ end
43
+
44
+ private
45
+
46
+ def client_side_form_settings(object, options)
47
+ if options[:validate]
48
+ builder = options[:builder] || ActionView::Base.default_form_builder
49
+
50
+ if options[:html] && options[:html][:id]
51
+ var_name = options[:html][:id]
52
+ else
53
+ var_name = if object.respond_to?(:persisted?) && object.persisted?
54
+ options[:as] ? "#{options[:as]}_edit" : dom_id(object, :edit)
55
+ else
56
+ options[:as] ? "#{options[:as]}_new" : dom_id(object)
57
+ end
58
+ end
59
+
60
+ content_tag(:script) do
61
+ "var #{var_name} = #{builder.client_side_form_settings(options, self).merge(:validators => 'validator_hash').to_json};".html_safe
62
+ end
63
+
64
+ end
65
+ end
66
+
67
+ end
68
+ end
69
+
@@ -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,52 @@
1
+ require 'client_side_validations/core_ext'
2
+
3
+ module ClientSideValidations::ActiveModel
4
+ module Validator
5
+
6
+ def client_side_hash(model, attribute)
7
+ extra_options = options.except(*::ActiveModel::Errors::CALLBACKS_OPTIONS - [:on, :allow_blank])
8
+ { :message => model.errors.generate_message(attribute, message_type, extra_options) }.merge(extra_options)
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
+ _validators.except(nil).inject({}) do |attr_hash, attr|
21
+
22
+ validator_hash = attr[1].inject({}) do |kind_hash, validator|
23
+ client_side_hash = validator.client_side_hash(self, attr[0])
24
+ # Yeah yeah, #new_record? is not part of ActiveModel :p
25
+ if (can_use_for_client_side_validation?(client_side_hash, validator))
26
+ kind_hash.merge!(validator.kind => client_side_hash.except(:on))
27
+ else
28
+ kind_hash.merge!({})
29
+ end
30
+ end
31
+
32
+ attr_hash.merge!(attr[0] => validator_hash)
33
+ end.delete_if { |key, value| value.blank? }
34
+ end
35
+
36
+ private
37
+
38
+ def can_use_for_client_side_validation?(client_side_hash, validator)
39
+ ((self.respond_to?(:new_record?) && client_side_hash[:on] == (self.new_record? ? :create : :update)) || client_side_hash[:on].nil?) && !validator.options.key?(:if) && !validator.options.key?(:unless)
40
+ end
41
+ end
42
+ end
43
+
44
+ ActiveModel::Validator.send(:include, ClientSideValidations::ActiveModel::Validator)
45
+ ActiveModel::Validations.send(:include, ClientSideValidations::ActiveModel::Validations)
46
+
47
+ %w{acceptance exclusion inclusion length format numericality presence}.each do |validator|
48
+ require "client_side_validations/active_model/#{validator}"
49
+ validator.capitalize!
50
+ eval "ActiveModel::Validations::#{validator}Validator.send(:include, ClientSideValidations::ActiveModel::#{validator})"
51
+ end
52
+
@@ -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,22 @@
1
+ module ClientSideValidations::ActiveModel
2
+ module Length
3
+
4
+ def client_side_hash(model, attribute)
5
+ extra_options = options.except(*::ActiveModel::Errors::CALLBACKS_OPTIONS - [:allow_blank, :on]).except(:tokenizer, :too_long, :too_short, :wrong_length)
6
+
7
+ errors_options = options.except(*self.class::RESERVED_OPTIONS)
8
+ messages = extra_options.except(:js_tokenizer, :allow_blank, :on).keys.inject({}) do |hash, key|
9
+ errors_options[:count] = extra_options[key]
10
+ count = extra_options[key]
11
+ default_message = options[self.class::MESSAGES[key]]
12
+ errors_options[:message] ||= default_message if default_message
13
+
14
+ hash.merge!(key => model.errors.generate_message(attribute, self.class::MESSAGES[key], errors_options))
15
+ end
16
+
17
+ { :messages => messages }.merge(extra_options)
18
+ end
19
+
20
+ end
21
+ end
22
+
@@ -0,0 +1,26 @@
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
+ OPTION_MAP.merge!(:numericality => :not_a_number, :only_integer => :not_an_integer)
9
+ end
10
+
11
+ def client_side_hash(model, attribute)
12
+ except_options = ::ActiveModel::Errors::CALLBACKS_OPTIONS - [:on] + [:message]
13
+ extra_options = options.except(*except_options).reject { |key, value| key == :only_integer && !value }
14
+ keys = [:numericality] | (extra_options.keys - [:message, :on])
15
+ filtered_options = options.except(*self.class::RESERVED_OPTIONS)
16
+ messages = keys.inject({}) do |hash, key|
17
+ count = extra_options[key]
18
+ hash.merge!(key => model.errors.generate_message(attribute, OPTION_MAP[key], filtered_options.merge(:count => count)))
19
+ end
20
+
21
+ { :messages => messages }.merge(extra_options)
22
+ end
23
+
24
+ end
25
+ end
26
+
@@ -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,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,25 @@
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
+ relation = t[attribute].eq(value)
12
+ else
13
+ relation = t[attribute].matches(value)
14
+ end
15
+
16
+ relation = relation.and(t[:id].not_eq(params[:id])) if params[:id]
17
+
18
+ (params[:scope] || {}).each do |key, value|
19
+ relation = relation.and(t[key].eq(value))
20
+ end
21
+
22
+ !klass.where(relation).exists?
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,26 @@
1
+ module ClientSideValidations::ActiveRecord
2
+ module Uniqueness
3
+ def client_side_hash(model, attribute)
4
+ extra_options = options.except(*::ActiveModel::Errors::CALLBACKS_OPTIONS - [:on, :allow_blank])
5
+ hash = { :message => model.errors.generate_message(attribute, message_type, extra_options.except(:case_sensitive, :scope)) }
6
+ hash = hash.merge(extra_options).merge(model.new_record? ? {} : { :id => model.id })
7
+
8
+ if hash[:scope].present?
9
+ hash[:scope] = Array.wrap(hash[:scope]).inject({}) do |scope_hash, scope_item|
10
+ scope_hash.merge!(scope_item => model.send(scope_item))
11
+ end
12
+ else
13
+ hash.delete(:scope)
14
+ end
15
+
16
+ hash
17
+ end
18
+
19
+ private
20
+
21
+ def message_type
22
+ :taken
23
+ end
24
+ end
25
+ end
26
+
@@ -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,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,21 @@
1
+ module ClientSideValidations
2
+ module Formtastic
3
+ module SemanticFormBuilder
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::SemanticFormBuilder.default_inline_error_class
11
+ }
12
+ end
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+
20
+ Formtastic::SemanticFormBuilder.send(:include, ClientSideValidations::Formtastic::SemanticFormBuilder)
21
+