client_side_validations 3.2.8 → 4.2.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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/HISTORY.md +46 -0
  3. data/README.md +540 -0
  4. data/lib/client_side_validations.rb +0 -1
  5. data/lib/client_side_validations/action_view.rb +4 -3
  6. data/lib/client_side_validations/action_view/form_builder.rb +101 -84
  7. data/lib/client_side_validations/action_view/form_helper.rb +99 -110
  8. data/lib/client_side_validations/action_view/form_tag_helper.rb +13 -9
  9. data/lib/client_side_validations/active_model.rb +95 -80
  10. data/lib/client_side_validations/active_model/absence.rb +11 -0
  11. data/lib/client_side_validations/active_model/acceptance.rb +7 -6
  12. data/lib/client_side_validations/active_model/exclusion.rb +4 -24
  13. data/lib/client_side_validations/active_model/format.rb +24 -23
  14. data/lib/client_side_validations/active_model/inclusion.rb +4 -23
  15. data/lib/client_side_validations/active_model/length.rb +15 -15
  16. data/lib/client_side_validations/active_model/numericality.rb +23 -22
  17. data/lib/client_side_validations/active_model/presence.rb +7 -6
  18. data/lib/client_side_validations/active_record.rb +2 -2
  19. data/lib/client_side_validations/active_record/middleware.rb +41 -39
  20. data/lib/client_side_validations/active_record/uniqueness.rb +24 -23
  21. data/lib/client_side_validations/config.rb +1 -1
  22. data/lib/client_side_validations/core_ext/range.rb +1 -2
  23. data/lib/client_side_validations/core_ext/regexp.rb +6 -4
  24. data/lib/client_side_validations/engine.rb +0 -1
  25. data/lib/client_side_validations/generators.rb +2 -3
  26. data/lib/client_side_validations/generators/rails_validations.rb +2 -3
  27. data/lib/client_side_validations/middleware.rb +17 -24
  28. data/lib/client_side_validations/version.rb +1 -1
  29. data/lib/generators/client_side_validations/copy_assets_generator.rb +7 -11
  30. data/lib/generators/client_side_validations/install_generator.rb +0 -2
  31. data/lib/generators/templates/client_side_validations/initializer.rb +1 -2
  32. data/vendor/assets/javascripts/rails.validations.js +26 -20
  33. metadata +173 -48
  34. data/client_side_validations.gemspec +0 -30
@@ -1,25 +1,27 @@
1
- module ClientSideValidations::ActiveModel
2
- module Numericality
1
+ module ClientSideValidations
2
+ module ActiveModel
3
+ module Numericality
4
+ OPTION_MAP = {}
3
5
 
4
- OPTION_MAP = {}
6
+ def self.included(base)
7
+ OPTION_MAP.merge!(base::CHECKS.keys.inject({}) { |a, e| a.merge!(e => e) })
8
+ end
5
9
 
6
- def self.included(base)
7
- OPTION_MAP.merge!(base::CHECKS.keys.inject({}) { |hash, key| hash.merge!(key => key) })
8
- end
10
+ def client_side_hash(model, attribute, force = nil)
11
+ options = self.options.dup
12
+ hash = { messages: { numericality: model.errors.generate_message(attribute, :not_a_number, options) } }
9
13
 
10
- def client_side_hash(model, attribute, force = nil)
11
- options = self.options.dup
12
- hash = { :messages => { :numericality => model.errors.generate_message(attribute, :not_a_number, options) } }
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
13
18
 
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
19
+ hash[:allow_blank] = true if options[:allow_nil] || options[:allow_blank]
18
20
 
19
- hash[:allow_blank] = true if options[:allow_nil] || options[:allow_blank]
21
+ OPTION_MAP.each do |option, message_type|
22
+ count = options[option]
23
+ next unless count
20
24
 
21
- OPTION_MAP.each do |option, message_type|
22
- if count = options[option]
23
25
  if count.respond_to?(:call)
24
26
  if force
25
27
  count = count.call(model)
@@ -27,16 +29,15 @@ module ClientSideValidations::ActiveModel
27
29
  next
28
30
  end
29
31
  end
30
- hash[:messages][option] = model.errors.generate_message(attribute, message_type, options.merge(:count => count))
32
+
33
+ hash[:messages][option] = model.errors.generate_message(attribute, message_type, options.merge(count: count))
31
34
  hash[option] = count
32
35
  end
33
- end
34
36
 
35
- copy_conditional_attributes(hash, options)
37
+ copy_conditional_attributes(hash, options)
36
38
 
37
- hash
39
+ hash
40
+ end
38
41
  end
39
-
40
42
  end
41
43
  end
42
-
@@ -1,10 +1,11 @@
1
- module ClientSideValidations::ActiveModel
2
- module Presence
3
- private
1
+ module ClientSideValidations
2
+ module ActiveModel
3
+ module Presence
4
+ private
4
5
 
5
- def message_type
6
- :blank
6
+ def message_type
7
+ :blank
8
+ end
7
9
  end
8
10
  end
9
11
  end
10
-
@@ -2,10 +2,10 @@ require 'client_side_validations/active_model'
2
2
  require 'client_side_validations/middleware'
3
3
  require 'client_side_validations/active_record/middleware'
4
4
 
5
- %w{uniqueness}.each do |validator|
5
+ %w(uniqueness).each do |validator|
6
6
  require "client_side_validations/active_record/#{validator}"
7
7
  validator.capitalize!
8
- eval "ActiveRecord::Validations::#{validator}Validator.send(:include, ClientSideValidations::ActiveRecord::#{validator})"
8
+ ActiveRecord::Validations.const_get("#{validator}Validator").send :include, ClientSideValidations::ActiveRecord.const_get(validator)
9
9
  end
10
10
 
11
11
  ActiveRecord::Base.send(:include, ClientSideValidations::ActiveModel::Validations)
@@ -1,51 +1,53 @@
1
- module ClientSideValidations::ActiveRecord
2
- class Middleware
1
+ module ClientSideValidations
2
+ module ActiveRecord
3
+ class Middleware
4
+ def self.class?(klass)
5
+ klass.abstract_class.blank? && klass < ::ActiveRecord::Base
6
+ end
3
7
 
4
- def self.is_class?(klass)
5
- klass.abstract_class.blank? && klass < ::ActiveRecord::Base
6
- end
8
+ def self.unique?(klass, attribute, value, params)
9
+ klass = find_topmost_superclass(klass)
10
+ column = klass.columns_hash[attribute.to_s]
11
+ value = type_cast_value(column, value)
12
+ value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if value.is_a?(String)
7
13
 
8
- def self.is_unique?(klass, attribute, value, params)
9
- klass = find_topmost_superclass(klass)
10
- value = type_cast_value(klass, attribute, value)
11
- column = klass.columns_hash[attribute.to_s]
12
- value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?
13
-
14
- t = klass.arel_table
15
-
16
- sql = []
17
- if params[:case_sensitive] == 'true'
18
- sql << 'BINARY' if t.engine.connection.adapter_name =~ /^mysql/i
19
- sql << t[attribute].eq(value).to_sql
20
- else
21
- escaped_value = value.gsub(/[%_]/, '\\\\\0')
22
- sql << "#{t[attribute].matches(escaped_value).to_sql} ESCAPE '\\'"
23
- end
14
+ t = klass.arel_table
24
15
 
25
- sql << "AND #{t[klass.primary_key].not_eq(params[:id]).to_sql}" if params[:id]
16
+ sql = []
17
+ if params[:case_sensitive] == 'true'
18
+ sql << 'BINARY' if t.engine.connection.adapter_name =~ /^mysql/i
19
+ sql << t[attribute].eq(value).to_sql
20
+ else
21
+ escaped_value = value.gsub(/[%_]/, '\\\\\0')
22
+ sql << "#{t[attribute].matches(escaped_value).to_sql} ESCAPE '\\'"
23
+ end
26
24
 
27
- (params[:scope] || {}).each do |attribute, value|
28
- value = type_cast_value(klass, attribute, value)
29
- sql << "AND #{t[attribute].eq(value).to_sql}"
30
- end
25
+ sql << "AND #{t[klass.primary_key].not_eq(params[:id]).to_sql}" if params[:id]
31
26
 
32
- relation = Arel::Nodes::SqlLiteral.new(sql.join(' '))
33
- !klass.where(relation).exists?
34
- end
27
+ (params[:scope] || {}).each do |scope_attribute, scope_value|
28
+ scope_value = type_cast_value(klass.columns_hash[scope_attribute], scope_value)
29
+ sql << "AND #{t[scope_attribute].eq(scope_value).to_sql}"
30
+ end
35
31
 
36
- private
32
+ relation = Arel::Nodes::SqlLiteral.new(sql.join(' '))
33
+ !klass.where(relation).exists?
34
+ end
37
35
 
38
- def self.type_cast_value(klass, attribute, value)
39
- klass.columns_hash[attribute].type_cast(value)
40
- end
36
+ def self.type_cast_value(column, value)
37
+ if column.respond_to?(:type_cast)
38
+ column.type_cast(value)
39
+ else
40
+ column.type_cast_from_database(value)
41
+ end
42
+ end
41
43
 
42
- def self.find_topmost_superclass(klass)
43
- if is_class?(klass.superclass)
44
- find_topmost_superclass(klass.superclass)
45
- else
46
- klass
44
+ def self.find_topmost_superclass(klass)
45
+ if class?(klass.superclass)
46
+ find_topmost_superclass(klass.superclass)
47
+ else
48
+ klass
49
+ end
47
50
  end
48
51
  end
49
-
50
52
  end
51
53
  end
@@ -1,32 +1,33 @@
1
- module ClientSideValidations::ActiveRecord
2
- module Uniqueness
3
- def client_side_hash(model, attribute, force = nil)
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
- hash[:allow_blank] = true if options[:allow_blank]
1
+ module ClientSideValidations
2
+ module ActiveRecord
3
+ module Uniqueness
4
+ def client_side_hash(model, attribute, _force = nil)
5
+ hash = {}
6
+ hash[:message] = model.errors.generate_message(attribute, message_type, options.except(:scope))
7
+ hash[:case_sensitive] = options[:case_sensitive]
8
+ hash[:id] = model.id unless model.new_record?
9
+ hash[:allow_blank] = true if options[:allow_blank]
9
10
 
10
- if options.key?(:client_validations) && options[:client_validations].key?(:class)
11
- hash[:class] = options[:client_validations][:class].underscore
12
- elsif model.class.name.demodulize != model.class.name
13
- hash[:class] = model.class.name.underscore
14
- end
11
+ if options.key?(:client_validations) && options[:client_validations].key?(:class)
12
+ hash[:class] = options[:client_validations][:class].underscore
13
+ elsif model.class.name.demodulize != model.class.name
14
+ hash[:class] = model.class.name.underscore
15
+ end
15
16
 
16
- if options.key?(:scope) && options[:scope].present?
17
- hash[:scope] = Array.wrap(options[:scope]).inject({}) do |scope_hash, scope_item|
18
- scope_hash.merge!(scope_item => model.send(scope_item))
17
+ if options.key?(:scope) && options[:scope].present?
18
+ hash[:scope] = Array.wrap(options[:scope]).inject({}) do |scope_hash, scope_item|
19
+ scope_hash.merge!(scope_item => model.send(scope_item))
20
+ end
19
21
  end
20
- end
21
22
 
22
- hash
23
- end
23
+ hash
24
+ end
24
25
 
25
- private
26
+ private
26
27
 
27
- def message_type
28
- :taken
28
+ def message_type
29
+ :taken
30
+ end
29
31
  end
30
32
  end
31
33
  end
32
-
@@ -6,7 +6,7 @@ module ClientSideValidations
6
6
  attr_accessor :root_path
7
7
  end
8
8
 
9
- self.disabled_validators = []
9
+ self.disabled_validators = [:uniqueness]
10
10
  self.number_format_with_locale = false
11
11
  self.root_path = nil
12
12
  end
@@ -1,5 +1,5 @@
1
1
  class Range
2
- def as_json(options = nil)
2
+ def as_json(*)
3
3
  [first, last]
4
4
  end
5
5
 
@@ -7,4 +7,3 @@ class Range
7
7
  as_json(options).inspect
8
8
  end
9
9
  end
10
-
@@ -1,13 +1,15 @@
1
+ require 'js_regex'
2
+
1
3
  class Regexp
2
- def as_json(options = nil)
3
- Regexp.new inspect.sub('\\A','^').sub('\\Z','$').sub('\\z','$').sub(/^\//,'').sub(/\/[a-z]*$/,'').gsub(/\(\?#.+\)/, '').gsub(/\(\?-\w+:/,'(').gsub(/\s/,''), self.options & 5
4
+ def as_json(*)
5
+ JsRegex.new(self).to_h
4
6
  end
5
7
 
6
8
  def to_json(options = nil)
7
- as_json(options).inspect
9
+ as_json(options)
8
10
  end
9
11
 
10
- def encode_json(encoder)
12
+ def encode_json(_encoder)
11
13
  inspect
12
14
  end
13
15
  end
@@ -3,4 +3,3 @@ module ClientSideValidations
3
3
  config.app_middleware.use ClientSideValidations::Middleware::Validators
4
4
  end
5
5
  end
6
-
@@ -1,12 +1,11 @@
1
1
  module ClientSideValidations
2
2
  module Generators
3
- Assets = []
3
+ ASSETS = []
4
4
 
5
5
  def self.register_assets(klass)
6
- Assets.push(*klass.assets)
6
+ ASSETS.push(*klass.assets)
7
7
  end
8
8
  end
9
9
  end
10
10
 
11
11
  require 'client_side_validations/generators/rails_validations'
12
-
@@ -3,8 +3,8 @@ module ClientSideValidations
3
3
  class RailsValidations
4
4
  def self.assets
5
5
  [{
6
- :path => File.expand_path('../../../../vendor/assets/javascripts', __FILE__),
7
- :file => 'rails.validations.js'
6
+ path: File.expand_path('../../../../vendor/assets/javascripts', __FILE__),
7
+ file: 'rails.validations.js'
8
8
  }]
9
9
  end
10
10
 
@@ -12,4 +12,3 @@ module ClientSideValidations
12
12
  end
13
13
  end
14
14
  end
15
-
@@ -1,9 +1,6 @@
1
- # encoding: utf-8
2
-
3
1
  require 'client_side_validations/core_ext'
4
2
 
5
3
  module ClientSideValidations
6
-
7
4
  module Middleware
8
5
  class Validators
9
6
  def initialize(app)
@@ -11,7 +8,8 @@ module ClientSideValidations
11
8
  end
12
9
 
13
10
  def call(env)
14
- if matches = /\A\/validators\/(\w+)\z/.match(env['PATH_INFO'])
11
+ matches = %r{\A/validators/(\w+)\z}.match(env['PATH_INFO'])
12
+ if matches
15
13
  process_request(matches.captures.first, env)
16
14
  else
17
15
  @app.call(env)
@@ -19,23 +17,23 @@ module ClientSideValidations
19
17
  end
20
18
 
21
19
  def process_request(validation, env)
22
- if disabled_validators.include?(validation.downcase)
20
+ if disabled_validators.include?(validation)
23
21
  error_resp
24
22
  else
25
23
  klass_name = validation.camelize
26
24
  klass_name = "::ClientSideValidations::Middleware::#{klass_name}"
27
25
  klass_name.constantize.new(env).response
28
26
  end
29
- rescue => e
27
+ rescue
30
28
  error_resp
31
29
  end
32
30
 
33
31
  def disabled_validators
34
- ClientSideValidations::Config.disabled_validators.map { |v| v.to_s.downcase }
32
+ ClientSideValidations::Config.disabled_validators.map(&:to_s)
35
33
  end
36
34
 
37
35
  def error_resp
38
- [500, {'Content-Type' => 'application/json', 'Content-Length' => '0'}, ['']]
36
+ [500, { 'Content-Type' => 'application/json', 'Content-Length' => '0' }, ['']]
39
37
  end
40
38
  end
41
39
 
@@ -51,7 +49,7 @@ module ClientSideValidations
51
49
  end
52
50
 
53
51
  def response
54
- [status, {'Content-Type' => content_type, 'Content-Length' => body.length.to_s}, [body]]
52
+ [status, { 'Content-Type' => content_type, 'Content-Length' => body.length.to_s }, [body]]
55
53
  end
56
54
 
57
55
  def content_type
@@ -60,13 +58,13 @@ module ClientSideValidations
60
58
  end
61
59
 
62
60
  class Uniqueness < Base
63
- IGNORE_PARAMS = %w{case_sensitive id scope}
61
+ IGNORE_PARAMS = %w(case_sensitive id scope)
64
62
  REGISTERED_ORMS = []
65
63
  class NotValidatable < StandardError; end
66
64
 
67
65
  def response
68
66
  begin
69
- if is_unique?
67
+ if unique?
70
68
  self.status = 404
71
69
  self.body = 'true'
72
70
  else
@@ -94,32 +92,29 @@ module ClientSideValidations
94
92
 
95
93
  private
96
94
 
97
- def is_unique?
95
+ def unique?
98
96
  convert_scope_value_from_null_to_nil
99
97
  klass, attribute, value = extract_resources
100
98
  middleware_class = nil
101
99
 
102
100
  unless Array.wrap(klass._validators[attribute.to_sym]).find { |v| v.kind == :uniqueness }
103
- raise NotValidatable
101
+ fail NotValidatable
104
102
  end
105
103
 
106
104
  registered_orms.each do |orm|
107
- if orm.is_class?(klass)
105
+ if orm.class?(klass)
108
106
  middleware_class = orm
109
107
  break
110
108
  end
111
109
  end
112
110
 
113
- middleware_class.is_unique?(klass, attribute, value, request.params)
111
+ middleware_class.unique?(klass, attribute, value, request.params)
114
112
  end
115
113
 
116
114
  def convert_scope_value_from_null_to_nil
117
- if request.params['scope']
118
- request.params['scope'].each do |key, value|
119
- if value == 'null'
120
- request.params['scope'][key] = nil
121
- end
122
- end
115
+ return unless request.params['scope']
116
+ request.params['scope'].each do |key, value|
117
+ request.params['scope'][key] = nil if value == 'null'
123
118
  end
124
119
  end
125
120
 
@@ -153,14 +148,12 @@ module ClientSideValidations
153
148
 
154
149
  def nested?(hash = nil, levels = 0)
155
150
  i = 0
156
- until !(hash.respond_to? :keys)
151
+ while hash.respond_to? :keys
157
152
  hash = hash[hash.keys.first]
158
153
  i += 1
159
154
  end
160
155
  i > levels
161
156
  end
162
-
163
157
  end
164
158
  end
165
159
  end
166
-