andrewroth-client_side_validations 3.2.0.beta.4

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 (31) hide show
  1. data/client_side_validations.gemspec +28 -0
  2. data/lib/client_side_validations.rb +18 -0
  3. data/lib/client_side_validations/action_view.rb +14 -0
  4. data/lib/client_side_validations/action_view/form_builder.rb +176 -0
  5. data/lib/client_side_validations/action_view/form_helper.rb +105 -0
  6. data/lib/client_side_validations/action_view/form_tag_helper.rb +12 -0
  7. data/lib/client_side_validations/active_model.rb +68 -0
  8. data/lib/client_side_validations/active_model/acceptance.rb +10 -0
  9. data/lib/client_side_validations/active_model/exclusion.rb +15 -0
  10. data/lib/client_side_validations/active_model/format.rb +10 -0
  11. data/lib/client_side_validations/active_model/inclusion.rb +15 -0
  12. data/lib/client_side_validations/active_model/length.rb +26 -0
  13. data/lib/client_side_validations/active_model/numericality.rb +33 -0
  14. data/lib/client_side_validations/active_model/presence.rb +10 -0
  15. data/lib/client_side_validations/active_record.rb +12 -0
  16. data/lib/client_side_validations/active_record/middleware.rb +50 -0
  17. data/lib/client_side_validations/active_record/uniqueness.rb +28 -0
  18. data/lib/client_side_validations/core_ext.rb +3 -0
  19. data/lib/client_side_validations/core_ext/range.rb +10 -0
  20. data/lib/client_side_validations/core_ext/regexp.rb +14 -0
  21. data/lib/client_side_validations/engine.rb +6 -0
  22. data/lib/client_side_validations/files.rb +8 -0
  23. data/lib/client_side_validations/generators.rb +12 -0
  24. data/lib/client_side_validations/generators/rails_validations.rb +15 -0
  25. data/lib/client_side_validations/middleware.rb +105 -0
  26. data/lib/client_side_validations/version.rb +3 -0
  27. data/lib/generators/client_side_validations/copy_assets_generator.rb +56 -0
  28. data/lib/generators/client_side_validations/install_generator.rb +22 -0
  29. data/lib/generators/templates/client_side_validations/initializer.rb +11 -0
  30. data/vendor/assets/javascripts/rails.validations.js +424 -0
  31. metadata +213 -0
@@ -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,26 @@
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] if options[message_type].present?
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
+ copy_conditional_attributes(hash, options)
20
+
21
+ hash
22
+ end
23
+
24
+ end
25
+ end
26
+
@@ -0,0 +1,33 @@
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
+ copy_conditional_attributes(hash, options)
27
+
28
+ hash
29
+ end
30
+
31
+ end
32
+ end
33
+
@@ -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,12 @@
1
+ require 'client_side_validations/active_model'
2
+ require 'client_side_validations/middleware'
3
+ require 'client_side_validations/active_record/middleware'
4
+
5
+ %w{uniqueness}.each do |validator|
6
+ require "client_side_validations/active_record/#{validator}"
7
+ validator.capitalize!
8
+ eval "ActiveRecord::Validations::#{validator}Validator.send(:include, ClientSideValidations::ActiveRecord::#{validator})"
9
+ end
10
+
11
+ ActiveRecord::Base.send(:include, ClientSideValidations::ActiveModel::Validations)
12
+ ClientSideValidations::Middleware::Uniqueness.register_orm(ClientSideValidations::ActiveRecord::Middleware)
@@ -0,0 +1,50 @@
1
+ module ClientSideValidations::ActiveRecord
2
+ class Middleware
3
+
4
+ def self.is_class?(klass)
5
+ klass < ::ActiveRecord::Base
6
+ end
7
+
8
+ def self.is_unique?(klass, attribute, value, params)
9
+ value = type_cast_value(klass, attribute, value)
10
+ column = klass.columns_hash[attribute.to_s]
11
+ value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if column.text?
12
+
13
+ t = klass.arel_table
14
+
15
+ if params[:case_sensitive] == 'true'
16
+ if t.engine.connection.instance_variable_get("@config")[:adapter] == 'mysql'
17
+ relation = Arel::Nodes::SqlLiteral.new("BINARY #{t[attribute].eq(value).to_sql}")
18
+ else
19
+ relation = t[attribute].eq(value)
20
+ end
21
+ else
22
+ relation = t[attribute].matches(value)
23
+ end
24
+
25
+ if relation.is_a?(Arel::Nodes::SqlLiteral)
26
+ relation = Arel::Nodes::SqlLiteral.new("BINARY #{t[attribute].eq(value).to_sql} AND #{t.primary_key.not_eq(params[:id]).to_sql}")
27
+ else
28
+ relation = relation.and(t.primary_key.not_eq(params[:id])) if params[:id]
29
+ end
30
+
31
+ (params[:scope] || {}).each do |attribute, value|
32
+ value = type_cast_value(klass, attribute, value)
33
+ if relation.is_a?(Arel::Nodes::SqlLiteral)
34
+ relation = Arel::Nodes::SqlLiteral.new("#{relation} AND #{t[attribute].eq(value).to_sql}")
35
+ else
36
+ relation = relation.and(t[attribute].eq(value))
37
+ end
38
+ end
39
+
40
+ !klass.where(relation).exists?
41
+ end
42
+
43
+ private
44
+
45
+ def self.type_cast_value(klass, attribute, value)
46
+ klass.columns_hash[attribute].type_cast(value)
47
+ end
48
+
49
+ end
50
+ 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,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]*$/,'').gsub(/\(\?#.+\)/, '').gsub(/\(\?-\w+:/,'('), 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,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,12 @@
1
+ module ClientSideValidations
2
+ module Generators
3
+ Assets = []
4
+
5
+ def self.register_assets(klass)
6
+ Assets.push(*klass.assets)
7
+ end
8
+ end
9
+ end
10
+
11
+ require 'client_side_validations/generators/rails_validations'
12
+
@@ -0,0 +1,15 @@
1
+ module ClientSideValidations
2
+ module Generators
3
+ class RailsValidations
4
+ def self.assets
5
+ [{
6
+ :path => File.expand_path('../../../../vendor/assets/javascripts', __FILE__),
7
+ :file => 'rails.validations.js'
8
+ }]
9
+ end
10
+
11
+ Generators.register_assets(self)
12
+ end
13
+ end
14
+ end
15
+
@@ -0,0 +1,105 @@
1
+ # encoding: utf-8
2
+
3
+ require 'client_side_validations/core_ext'
4
+
5
+ module ClientSideValidations
6
+
7
+ module Middleware
8
+ class Validators
9
+ def initialize(app)
10
+ @app = app
11
+ end
12
+
13
+ def call(env)
14
+ matches = /^\/validators\/(\w+)$/.match(env['PATH_INFO'])
15
+ if !matches || (matches[1] == 'uniqueness' && Config.uniqueness_validator_disabled)
16
+ @app.call(env)
17
+ else
18
+ "::ClientSideValidations::Middleware::#{matches[1].camelize}".constantize.new(env).response
19
+ end
20
+ end
21
+ end
22
+
23
+ class Base
24
+ attr_accessor :request, :body, :status
25
+
26
+ def initialize(env)
27
+ self.body = ''
28
+ self.status = 200
29
+ self.request = ActionDispatch::Request.new(env)
30
+ end
31
+
32
+ def response
33
+ [status, {'Content-Type' => content_type, 'Content-Length' => body.length.to_s}, [body]]
34
+ end
35
+
36
+ def content_type
37
+ 'application/json'
38
+ end
39
+ end
40
+
41
+ class Uniqueness < Base
42
+ IGNORE_PARAMS = %w{case_sensitive id scope}
43
+ REGISTERED_ORMS = []
44
+
45
+ def response
46
+ if is_unique?
47
+ self.status = 404
48
+ self.body = 'true'
49
+ else
50
+ self.status = 200
51
+ self.body = 'false'
52
+ end
53
+ super
54
+ end
55
+
56
+ def self.register_orm(orm)
57
+ registered_orms << orm
58
+ end
59
+
60
+ def self.registered_orms
61
+ REGISTERED_ORMS
62
+ end
63
+
64
+ def registered_orms
65
+ self.class.registered_orms
66
+ end
67
+
68
+ private
69
+
70
+ def is_unique?
71
+ convert_scope_value_from_null_to_nil
72
+ resource = extract_resource
73
+ klass = resource.classify.constantize
74
+ attribute = request.params[resource].keys.first
75
+ value = request.params[resource][attribute]
76
+ middleware_class = nil
77
+
78
+ registered_orms.each do |orm|
79
+ if orm.is_class?(klass)
80
+ middleware_class = orm
81
+ break
82
+ end
83
+ end
84
+
85
+ middleware_class.is_unique?(klass, attribute, value, request.params)
86
+ end
87
+
88
+ def convert_scope_value_from_null_to_nil
89
+ if request.params['scope']
90
+ request.params['scope'].each do |key, value|
91
+ if value == 'null'
92
+ request.params['scope'][key] = nil
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ def extract_resource
99
+ parent_key = (request.params.keys - IGNORE_PARAMS).first
100
+ end
101
+ end
102
+ end
103
+
104
+ end
105
+
@@ -0,0 +1,3 @@
1
+ module ClientSideValidations
2
+ VERSION = '3.2.0.beta.4'
3
+ end
@@ -0,0 +1,56 @@
1
+ module ClientSideValidations
2
+ module Generators
3
+ class CopyAssetsGenerator < Rails::Generators::Base
4
+
5
+ def copy_javascript_asset
6
+ if self.class == CopyAssetsGenerator || !asset_pipeline_enabled?
7
+ assets.each do |asset|
8
+ source_paths << asset[:path]
9
+ copy_file asset[:file], "#{asset_directory}/#{asset[:file]}"
10
+ end
11
+ end
12
+ end
13
+
14
+ private
15
+
16
+ def self.asset_directory
17
+ if asset_pipeline_enabled?
18
+ "app#{Rails.configuration.assets.prefix}/javascripts"
19
+ else
20
+ 'public/javascripts'
21
+ end
22
+ end
23
+
24
+ def asset_directory
25
+ CopyAssetsGenerator.asset_directory
26
+ end
27
+
28
+ def self.assets
29
+ Assets
30
+ end
31
+
32
+ def assets
33
+ CopyAssetsGenerator.assets
34
+ end
35
+
36
+ def self.asset_file_names
37
+ assets.map { |asset| asset[:file] }.join(', ')
38
+ end
39
+
40
+ def self.asset_pipeline_enabled?
41
+ (Rails.configuration.respond_to?(:assets) ? (Rails.configuration.assets || {}) : {})[:enabled]
42
+ end
43
+
44
+ def asset_pipeline_enabled?
45
+ self.class.asset_pipeline_enabled?
46
+ end
47
+
48
+ def self.installation_message
49
+ "Copies #{asset_file_names} to #{asset_directory}"
50
+ end
51
+
52
+ desc installation_message
53
+ end
54
+ end
55
+ end
56
+