client_side_validations 3.0.3 → 3.1.0

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 (73) hide show
  1. data/client_side_validations.gemspec +4 -7
  2. data/lib/client_side_validations/action_view/form_builder.rb +85 -11
  3. data/lib/client_side_validations/action_view/form_helper.rb +21 -3
  4. data/lib/client_side_validations/action_view.rb +4 -3
  5. data/lib/client_side_validations/active_model/length.rb +12 -10
  6. data/lib/client_side_validations/active_model/numericality.rb +14 -9
  7. data/lib/client_side_validations/active_model.rb +21 -13
  8. data/lib/client_side_validations/active_record/middleware.rb +5 -1
  9. data/lib/client_side_validations/active_record/uniqueness.rb +10 -8
  10. data/lib/client_side_validations/engine.rb +6 -0
  11. data/lib/client_side_validations/files.rb +8 -0
  12. data/lib/client_side_validations/formtastic.rb +3 -3
  13. data/lib/client_side_validations/middleware.rb +5 -7
  14. data/lib/client_side_validations/mongo_mapper/middleware.rb +20 -0
  15. data/lib/client_side_validations/mongo_mapper/uniqueness.rb +28 -0
  16. data/lib/client_side_validations/mongo_mapper.rb +9 -0
  17. data/lib/client_side_validations/mongoid/uniqueness.rb +10 -8
  18. data/lib/client_side_validations/version.rb +1 -1
  19. data/lib/client_side_validations.rb +8 -3
  20. data/lib/generators/client_side_validations/copy_asset_generator.rb +23 -0
  21. data/lib/generators/client_side_validations/install_generator.rb +18 -6
  22. data/lib/generators/templates/client_side_validations/README.rails.3.0 +6 -0
  23. data/lib/generators/templates/client_side_validations/README.rails.3.1 +7 -0
  24. data/lib/generators/templates/{client_side_validations.rb → client_side_validations/initializer.rb} +1 -1
  25. data/test/action_view/cases/helper.rb +34 -12
  26. data/test/action_view/cases/test_helpers.rb +314 -1
  27. data/test/action_view/cases/test_legacy_helpers.rb +58 -1
  28. data/test/active_model/cases/test_validations.rb +21 -8
  29. data/test/active_record/cases/test_middleware.rb +41 -16
  30. data/test/active_record/cases/test_uniqueness_validator.rb +5 -0
  31. data/test/active_record/models/user.rb +4 -0
  32. data/test/base_helper.rb +2 -0
  33. data/test/core_ext/cases/test_core_ext.rb +1 -0
  34. data/test/formtastic/cases/helper.rb +5 -0
  35. data/test/formtastic/cases/test_form_builder.rb +3 -3
  36. data/test/formtastic/cases/test_form_helper.rb +3 -4
  37. data/test/generators/cases/test_generators.rb +31 -0
  38. data/test/javascript/public/test/callbacks/elementAfter.js +1 -1
  39. data/test/javascript/public/test/callbacks/elementBefore.js +1 -1
  40. data/test/javascript/public/test/callbacks/elementFail.js +1 -1
  41. data/test/javascript/public/test/callbacks/elementPass.js +1 -1
  42. data/test/javascript/public/test/callbacks/formAfter.js +1 -1
  43. data/test/javascript/public/test/callbacks/formBefore.js +1 -1
  44. data/test/javascript/public/test/callbacks/formFail.js +1 -1
  45. data/test/javascript/public/test/callbacks/formPass.js +1 -1
  46. data/test/javascript/public/test/form_builders/validateForm.js +1 -1
  47. data/test/javascript/public/test/form_builders/validateFormtastic.js +2 -2
  48. data/test/javascript/public/test/form_builders/validateNestedForm.js +66 -0
  49. data/test/javascript/public/test/form_builders/validateSimpleForm.js +1 -1
  50. data/test/javascript/public/test/validateElement.js +36 -1
  51. data/test/javascript/public/test/validators/length.js +7 -1
  52. data/test/javascript/public/test/validators/numericality.js +7 -0
  53. data/test/javascript/public/test/validators/presence.js +6 -0
  54. data/test/javascript/public/test/validators/uniqueness.js +8 -1
  55. data/test/javascript/server.rb +9 -3
  56. data/test/javascript/views/index.erb +1 -1
  57. data/test/javascript/views/layout.erb +1 -1
  58. data/test/middleware/cases/helper.rb +3 -0
  59. data/test/mongo_mapper/cases/helper.rb +9 -0
  60. data/test/mongo_mapper/cases/test_base.rb +15 -0
  61. data/test/mongo_mapper/cases/test_middleware.rb +77 -0
  62. data/test/mongo_mapper/cases/test_uniqueness_validator.rb +50 -0
  63. data/test/mongo_mapper/models/magazine.rb +11 -0
  64. data/test/mongoid/cases/test_middleware.rb +15 -6
  65. data/test/mongoid/cases/test_uniqueness_validator.rb +5 -0
  66. data/test/mongoid/models/book.rb +4 -0
  67. data/test/simple_form/cases/helper.rb +3 -0
  68. data/test/simple_form/cases/test_form_helper.rb +3 -1
  69. data/test/test_loader.rb +6 -0
  70. data/{javascript → vendor/assets/javascripts}/rails.validations.js +32 -17
  71. metadata +44 -23
  72. data/lib/generators/templates/README +0 -7
  73. data/test/generators/cases/test_install_generator.rb +0 -15
@@ -12,22 +12,19 @@ Gem::Specification.new do |s|
12
12
  s.summary = %q{Client Side Validations}
13
13
  s.description = %q{Client Side Validations}
14
14
 
15
- s.rubyforge_project = "client_side_validations"
16
-
17
- s.files = `git ls-files -- {lib/*,javascript/*,*.gemspec}`.split("\n")
15
+ s.files = `git ls-files -- {lib/*,vendor/*,*.gemspec}`.split("\n")
18
16
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
17
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
18
  s.require_paths = ["lib"]
21
19
 
22
- s.add_dependency 'activesupport', '~> 3.0.0'
23
-
24
- s.add_development_dependency 'rails', '~> 3.0.0'
20
+ s.add_development_dependency 'rails', '3.1.0.rc4'
25
21
  s.add_development_dependency 'sqlite3'
26
22
  s.add_development_dependency 'bson_ext'
27
23
  s.add_development_dependency 'mongoid', '~> 2.0.0'
24
+ s.add_development_dependency 'mongo_mapper','~>0.9.0'
28
25
  s.add_development_dependency 'mocha'
29
26
  s.add_development_dependency 'simple_form'
30
- s.add_development_dependency 'formtastic'
27
+ s.add_development_dependency 'formtastic', '~> 2.0.0.rc3'
31
28
 
32
29
  # For QUnit testing
33
30
  s.add_development_dependency 'sinatra', '~> 1.0'
@@ -2,7 +2,7 @@ module ClientSideValidations::ActionView::Helpers
2
2
  module FormBuilder
3
3
 
4
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|
5
+ (base.field_helpers.map(&:to_s) - %w(apply_form_for_options! label check_box radio_button fields_for hidden_field)).each do |selector|
6
6
  base.class_eval <<-RUBY_EVAL
7
7
  def #{selector}_with_client_side_validations(method, options = {})
8
8
  apply_client_side_validators(method, options)
@@ -15,16 +15,20 @@ module ClientSideValidations::ActionView::Helpers
15
15
  end
16
16
 
17
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
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
22
26
 
23
27
  def self.client_side_form_settings(options, form_helper)
24
28
  {
25
29
  :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" />})
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([], ""))
28
32
  }
29
33
  end
30
34
  end
@@ -51,22 +55,52 @@ module ClientSideValidations::ActionView::Helpers
51
55
  radio_button_without_client_side_validations(method, tag_value, options)
52
56
  end
53
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
+
54
78
  private
55
79
 
56
80
  def apply_client_side_validators(method, options = {})
57
81
  if @options[:validate] && options[:validate] != false && validators = filter_validators(@object.client_side_validation_hash[method], options[:validate])
58
82
  options.merge!("data-validate" => true)
59
- @options[:validators].merge!("#{@object_name}[#{method}]" => validators)
83
+ @options[:validators].merge!("#{@object_name}[#{method}]#{options[:multiple] ? "[]" : nil}" => validators)
60
84
  end
61
85
  end
62
86
 
63
87
  def filter_validators(validators, filters)
64
88
  if validators
65
89
  filtered_validators = validators.inject({}) do |filtered_validators, validator|
66
- unless filters && filters.key?(validator.first) && !filters[validator.first]
67
- filtered_validators[validator.first] = validator.last
90
+ filtered_validators[validator.first] = validator.last
91
+ if has_filter_for_validator?(validator, filters)
92
+ if filter_validator?(validator, filters)
93
+ filtered_validators.delete(validator.first)
94
+ elsif force_validator_despite_conditional?(validator, filters) && !can_run_validator?(validator)
95
+ filtered_validators.delete(validator.first)
96
+ end
97
+ else
98
+ if validator.last.key?(:if) || validator.last.key?(:unless)
99
+ filtered_validators.delete(validator.first)
100
+ end
68
101
  end
69
-
102
+ filtered_validators[validator.first].delete(:if) if filtered_validators[validator.first]
103
+ filtered_validators[validator.first].delete(:unless) if filtered_validators[validator.first]
70
104
  filtered_validators
71
105
  end
72
106
 
@@ -74,5 +108,45 @@ module ClientSideValidations::ActionView::Helpers
74
108
  end
75
109
  end
76
110
 
111
+ def has_filter_for_validator?(validator, filters)
112
+ filters && (filters == true || filters.key?(validator.first))
113
+ end
114
+
115
+ def filter_validator?(validator, filters)
116
+ filters != true && filters[validator.first] == false
117
+ end
118
+
119
+ def force_validator_despite_conditional?(validator, filters)
120
+ filters == true || filters[validator.first] == true
121
+ end
122
+
123
+ def can_run_validator?(validator)
124
+ result = true
125
+ if_result = can_run_if_validator?(validator.last[:if])
126
+ unless_result = can_run_unless_validator?(validator.last[:unless])
127
+ result = result && if_result unless if_result.nil?
128
+ result = result && unless_result unless unless_result.nil?
129
+ result
130
+ end
131
+
132
+ def can_run_if_validator?(conditional)
133
+ if conditional
134
+ if conditional.is_a?(Symbol)
135
+ !!@object.send(conditional)
136
+ else
137
+ !!conditional.call(@object)
138
+ end
139
+ end
140
+ end
141
+
142
+ def can_run_unless_validator?(conditional)
143
+ if conditional
144
+ if conditional.is_a?(Symbol)
145
+ !@object.send(conditional)
146
+ else
147
+ !conditional.call(@object)
148
+ end
149
+ end
150
+ end
77
151
  end
78
152
  end
@@ -6,9 +6,11 @@ module ClientSideValidations::ActionView::Helpers
6
6
  options = args.extract_options!
7
7
  if options[:validate]
8
8
 
9
+ content_for_name = options[:validate] unless options[:validate] == true
10
+
9
11
  # Always turn off HTML5 Validations
10
12
  options[:html] ||= {}
11
- options[:html][:novalidate] = true
13
+ options[:html][:novalidate] = 'novalidate'
12
14
 
13
15
  case record_or_name_or_array
14
16
  when String, Symbol
@@ -26,7 +28,12 @@ module ClientSideValidations::ActionView::Helpers
26
28
  form = super(record_or_name_or_array, *(args << options), &proc)
27
29
  # Because of the load order requirement above this sub is necessary
28
30
  # Would be nice to not do this
29
- "#{form}#{script ? script.sub('"validator_hash"', @validators.to_json) : nil}".html_safe
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
30
37
  end
31
38
 
32
39
  def apply_form_for_options!(object_or_array, options)
@@ -42,6 +49,17 @@ module ClientSideValidations::ActionView::Helpers
42
49
 
43
50
  private
44
51
 
52
+ def insert_validators_into_script(script)
53
+ # There is probably a more performant way of doing this
54
+ # But using String#sub has some issues. Undocumented "features"
55
+ if script
56
+ script = script.split(/"validator_hash"/)
57
+ script = "#{script[0]}#{@validators.to_json}#{script[1]}"
58
+ end
59
+
60
+ script
61
+ end
62
+
45
63
  def client_side_form_settings(object, options)
46
64
  if options[:validate]
47
65
  builder = options[:builder] || ActionView::Base.default_form_builder
@@ -57,7 +75,7 @@ module ClientSideValidations::ActionView::Helpers
57
75
  end
58
76
 
59
77
  content_tag(:script) do
60
- "var #{var_name} = #{builder.client_side_form_settings(options, self).merge(:validators => 'validator_hash').to_json};".html_safe
78
+ "window['#{var_name}'] = #{builder.client_side_form_settings(options, self).merge(:validators => 'validator_hash').to_json};".html_safe
61
79
  end
62
80
 
63
81
  end
@@ -3,9 +3,10 @@ module ClientSideValidations::ActionView
3
3
  end
4
4
  end
5
5
 
6
- require "client_side_validations/action_view/form_helper"
7
- require "client_side_validations/action_view/form_tag_helper"
8
- require "client_side_validations/action_view/form_builder"
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'
9
10
 
10
11
  ActionView::Base.send(:include, ClientSideValidations::ActionView::Helpers::FormHelper)
11
12
  ActionView::Base.send(:include, ClientSideValidations::ActionView::Helpers::FormTagHelper)
@@ -2,19 +2,21 @@ module ClientSideValidations::ActiveModel
2
2
  module Length
3
3
 
4
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)
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]
6
9
 
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))
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
15
17
  end
16
18
 
17
- { :messages => messages }.merge(extra_options)
19
+ hash
18
20
  end
19
21
 
20
22
  end
@@ -5,20 +5,25 @@ module ClientSideValidations::ActiveModel
5
5
 
6
6
  def self.included(base)
7
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
8
  end
10
9
 
11
10
  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)))
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
19
24
  end
20
25
 
21
- { :messages => messages }.merge(extra_options)
26
+ hash
22
27
  end
23
28
 
24
29
  end
@@ -4,8 +4,8 @@ module ClientSideValidations::ActiveModel
4
4
  module Validator
5
5
 
6
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)
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
9
  end
10
10
 
11
11
  private
@@ -17,26 +17,34 @@ module ClientSideValidations::ActiveModel
17
17
 
18
18
  module Validations
19
19
  def client_side_validation_hash
20
- _validators.except(nil, :block).inject({}) do |attr_hash, attr|
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
21
32
 
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))
33
+ if validator_hash.present?
34
+ attr_hash.merge!(attr[0] => validator_hash)
27
35
  else
28
- kind_hash.merge!({})
36
+ attr_hash
29
37
  end
38
+ else
39
+ attr_hash
30
40
  end
31
-
32
- attr_hash.merge!(attr[0] => validator_hash)
33
- end.delete_if { |key, value| value.blank? }
41
+ end
34
42
  end
35
43
 
36
44
  private
37
45
 
38
46
  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) && validator.kind != :block
47
+ ((self.respond_to?(:new_record?) && validator.options[:on] == (self.new_record? ? :create : :update)) || validator.options[:on].nil?) && validator.kind != :block
40
48
  end
41
49
  end
42
50
  end
@@ -17,7 +17,11 @@ module ClientSideValidations::ActiveRecord
17
17
  relation = t[attribute].matches(value)
18
18
  end
19
19
 
20
- relation = relation.and(t[:id].not_eq(params[:id])) if params[:id]
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
21
25
 
22
26
  (params[:scope] || {}).each do |key, value|
23
27
  relation = relation.and(t[key].eq(value))
@@ -1,16 +1,18 @@
1
1
  module ClientSideValidations::ActiveRecord
2
2
  module Uniqueness
3
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|
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
10
  scope_hash.merge!(scope_item => model.send(scope_item))
11
11
  end
12
- else
13
- hash.delete(:scope)
12
+ end
13
+
14
+ unless model.class.name.demodulize == model.class.name
15
+ hash[:class] = model.class.name.underscore
14
16
  end
15
17
 
16
18
  hash
@@ -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
@@ -1,13 +1,13 @@
1
1
  module ClientSideValidations
2
2
  module Formtastic
3
- module SemanticFormBuilder
3
+ module FormBuilder
4
4
 
5
5
  def self.included(base)
6
6
  base.class_eval do
7
7
  def self.client_side_form_settings(options, form_helper)
8
8
  {
9
9
  :type => self.to_s,
10
- :inline_error_class => ::Formtastic::SemanticFormBuilder.default_inline_error_class
10
+ :inline_error_class => ::Formtastic::FormBuilder.default_inline_error_class
11
11
  }
12
12
  end
13
13
  end
@@ -17,5 +17,5 @@ module ClientSideValidations
17
17
  end
18
18
  end
19
19
 
20
- Formtastic::SemanticFormBuilder.send(:include, ClientSideValidations::Formtastic::SemanticFormBuilder)
20
+ Formtastic::FormBuilder.send(:include, ClientSideValidations::Formtastic::FormBuilder)
21
21
 
@@ -1,9 +1,10 @@
1
1
  # encoding: utf-8
2
2
 
3
+ require 'client_side_validations/core_ext'
4
+
3
5
  module ClientSideValidations
4
6
 
5
7
  module Middleware
6
-
7
8
  class Validators
8
9
  def initialize(app)
9
10
  @app = app
@@ -11,7 +12,7 @@ module ClientSideValidations
11
12
 
12
13
  def call(env)
13
14
  case env['PATH_INFO']
14
- when %r{\/validators\/(\w+)\.json}
15
+ when %r{\/validators\/(\w+)}
15
16
  "::ClientSideValidations::Middleware::#{$1.camelize}".constantize.new(env).response
16
17
  else
17
18
  @app.call(env)
@@ -63,6 +64,8 @@ module ClientSideValidations
63
64
  middleware_klass = ClientSideValidations::ActiveRecord::Middleware
64
65
  elsif (defined?(::Mongoid::Document) && klass.included_modules.include?(::Mongoid::Document))
65
66
  middleware_klass = ClientSideValidations::Mongoid::Middleware
67
+ elsif (defined?(::MongoMapper::Document) && klass.included_modules.include?(::MongoMapper::Document))
68
+ middleware_klass = ClientSideValidations::MongoMapper::Middleware
66
69
  end
67
70
 
68
71
  middleware_klass.is_unique?(klass, attribute, value, request.params)
@@ -72,11 +75,6 @@ module ClientSideValidations
72
75
  parent_key = (request.params.keys - IGNORE_PARAMS).first
73
76
  end
74
77
  end
75
-
76
- end
77
-
78
- class Engine < ::Rails::Engine
79
- config.app_middleware.use ClientSideValidations::Middleware::Validators
80
78
  end
81
79
 
82
80
  end
@@ -0,0 +1,20 @@
1
+ module ClientSideValidations::MongoMapper
2
+ class Middleware
3
+
4
+ # Still need to handle embedded documents
5
+ def self.is_unique?(klass, attribute, value, params)
6
+ if params[:case_sensitive] == 'false'
7
+ value = Regexp.new("^#{Regexp.escape(value.to_s)}$", Regexp::IGNORECASE)
8
+ end
9
+
10
+ criteria = klass.where(attribute => value)
11
+ criteria = criteria.where(:_id => {'$ne' => BSON::ObjectId(params[:id])}) if params[:id]
12
+
13
+ (params[:scope] || {}).each do |key, value|
14
+ criteria = criteria.where(key => value)
15
+ end
16
+
17
+ !criteria.exists?
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ module ClientSideValidations::MongoMapper
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] if options.key?(: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,9 @@
1
+ require 'client_side_validations/active_model'
2
+ require 'client_side_validations/mongo_mapper/middleware'
3
+
4
+ %w{uniqueness}.each do |validator|
5
+ require "client_side_validations/mongo_mapper/#{validator}"
6
+ validator.capitalize!
7
+ eval "MongoMapper::Plugins::Validations::#{validator}Validator.send(:include, ClientSideValidations::MongoMapper::#{validator})"
8
+ end
9
+
@@ -1,16 +1,18 @@
1
1
  module ClientSideValidations::Mongoid
2
2
  module Uniqueness
3
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|
4
+ hash = {}
5
+ hash[:message] = model.errors.generate_message(attribute, message_type, options.except(:scope))
6
+ hash[:case_sensitive] = options[:case_sensitive] if options.key?(: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
10
  scope_hash.merge!(scope_item => model.send(scope_item))
11
11
  end
12
- else
13
- hash.delete(:scope)
12
+ end
13
+
14
+ unless model.class.name.demodulize == model.class.name
15
+ hash[:class] = model.class.name.underscore
14
16
  end
15
17
 
16
18
  hash
@@ -1,3 +1,3 @@
1
1
  module ClientSideValidations
2
- VERSION = "3.0.3"
2
+ VERSION = '3.1.0'
3
3
  end
@@ -1,8 +1,13 @@
1
1
  module ClientSideValidations
2
2
  end
3
3
 
4
+ require 'client_side_validations/active_model' if defined?(::ActiveModel)
4
5
  require 'client_side_validations/active_record' if defined?(::ActiveRecord)
5
- require 'client_side_validations/mongoid' if defined?(::Mongoid)
6
- require 'client_side_validations/action_view'
7
- require 'client_side_validations/middleware' if defined?(::Rails)
6
+ require 'client_side_validations/mongoid' if defined?(::Mongoid)
7
+ require 'client_side_validations/mongo_mapper' if defined?(::MongoMapper)
8
+ require 'client_side_validations/action_view' if defined?(::ActionView)
9
+ if defined?(::Rails)
10
+ require 'client_side_validations/middleware'
11
+ require 'client_side_validations/engine'
12
+ end
8
13
 
@@ -0,0 +1,23 @@
1
+ module ClientSideValidations
2
+ module Generators
3
+ class CopyAssetGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../../../../vendor/assets/javascripts', __FILE__)
5
+ if Rails.version >= '3.1'
6
+ desc 'Creates a ClientSideValidations initializer and copies rails.validations.js to app/assets/javascripts.'
7
+ else
8
+ desc 'Creates a ClientSideValidations initializer and copies rails.validations.js to public/javascripts.'
9
+ end
10
+
11
+ def copy_javascript_asset
12
+ if Rails.version >= '3.1'
13
+ destination = 'app/assets/javascripts'
14
+ else
15
+ destination = 'public/javascripts'
16
+ end
17
+
18
+ copy_file 'rails.validations.js', "#{destination}/rails.validations.js"
19
+ end
20
+ end
21
+ end
22
+ end
23
+