rails4_client_side_validations 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/README.md +1 -0
  2. data/lib/generators/rails4_client_side_validations/copy_assets_generator.rb +58 -0
  3. data/lib/generators/rails4_client_side_validations/install_generator.rb +22 -0
  4. data/lib/generators/templates/client_side_validations/initializer.rb +20 -0
  5. data/lib/rails4_client_side_validations.rb +13 -0
  6. data/lib/rails4_client_side_validations/action_view.rb +14 -0
  7. data/lib/rails4_client_side_validations/action_view/form_builder.rb +105 -0
  8. data/lib/rails4_client_side_validations/action_view/form_helper.rb +139 -0
  9. data/lib/rails4_client_side_validations/action_view/form_tag_helper.rb +12 -0
  10. data/lib/rails4_client_side_validations/active_model.rb +143 -0
  11. data/lib/rails4_client_side_validations/active_model/absence.rb +10 -0
  12. data/lib/rails4_client_side_validations/active_model/acceptance.rb +10 -0
  13. data/lib/rails4_client_side_validations/active_model/exclusion.rb +27 -0
  14. data/lib/rails4_client_side_validations/active_model/format.rb +31 -0
  15. data/lib/rails4_client_side_validations/active_model/inclusion.rb +26 -0
  16. data/lib/rails4_client_side_validations/active_model/length.rb +26 -0
  17. data/lib/rails4_client_side_validations/active_model/numericality.rb +42 -0
  18. data/lib/rails4_client_side_validations/active_model/presence.rb +10 -0
  19. data/lib/rails4_client_side_validations/active_record.rb +12 -0
  20. data/lib/rails4_client_side_validations/active_record/middleware.rb +59 -0
  21. data/lib/rails4_client_side_validations/active_record/uniqueness.rb +29 -0
  22. data/lib/rails4_client_side_validations/config.rb +13 -0
  23. data/lib/rails4_client_side_validations/core_ext.rb +3 -0
  24. data/lib/rails4_client_side_validations/core_ext/range.rb +10 -0
  25. data/lib/rails4_client_side_validations/core_ext/regexp.rb +13 -0
  26. data/lib/rails4_client_side_validations/engine.rb +6 -0
  27. data/lib/rails4_client_side_validations/files.rb +8 -0
  28. data/lib/rails4_client_side_validations/generators.rb +12 -0
  29. data/lib/rails4_client_side_validations/generators/rails_validations.rb +15 -0
  30. data/lib/rails4_client_side_validations/middleware.rb +120 -0
  31. data/lib/rails4_client_side_validations/version.rb +3 -0
  32. data/vendor/assets/javascripts/rails.validations.js +639 -0
  33. metadata +40 -9
data/README.md ADDED
@@ -0,0 +1 @@
1
+ It's modifed version of [client_side_validations](https://github.com/bcardarella/client_side_validations.git), which supports Rails 4
@@ -0,0 +1,58 @@
1
+ module Rails4ClientSideValidations
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
+ if Rails.application
42
+ (Rails.configuration.respond_to?(:assets) ? (Rails.configuration.assets || {}) : {})[:enabled]
43
+ end
44
+ end
45
+
46
+ def asset_pipeline_enabled?
47
+ self.class.asset_pipeline_enabled?
48
+ end
49
+
50
+ def self.installation_message
51
+ "Copies #{asset_file_names} to #{asset_directory}"
52
+ end
53
+
54
+ desc installation_message
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,22 @@
1
+ require 'generators/rails4_client_side_validations/copy_assets_generator'
2
+
3
+ module Rails4ClientSideValidations
4
+ module Generators
5
+ class InstallGenerator < CopyAssetsGenerator
6
+
7
+ def copy_initializer
8
+ source_paths << File.expand_path('../../templates/rails4_client_side_validations', __FILE__)
9
+ copy_file 'initializer.rb', 'config/initializers/rails4_client_side_validations.rb'
10
+ end
11
+
12
+ private
13
+
14
+ def self.installation_message
15
+ "Copies initializer into config/initializers and #{super.downcase}"
16
+ end
17
+
18
+ desc installation_message
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,20 @@
1
+ # ClientSideValidations Initializer
2
+
3
+ # Uncomment to disable uniqueness validator, possible security issue
4
+ # ClientSideValidations::Config.disabled_validators = [:uniqueness]
5
+
6
+ # Uncomment to validate number format with current I18n locale
7
+ # ClientSideValidations::Config.number_format_with_locale = true
8
+
9
+ # Uncomment to set a custom application scope
10
+ # ClientSideValidations::Config.root_path = '/your/application/scope'
11
+
12
+ # Uncomment the following block if you want each input field to have the validation messages attached.
13
+ # ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
14
+ # unless html_tag =~ /^<label/
15
+ # %{<div class="field_with_errors">#{html_tag}<label for="#{instance.send(:tag_id)}" class="message">#{instance.error_message.first}</label></div>}.html_safe
16
+ # else
17
+ # %{<div class="field_with_errors">#{html_tag}</div>}.html_safe
18
+ # end
19
+ # end
20
+
@@ -0,0 +1,13 @@
1
+ module Rails4ClientSideValidations
2
+ end
3
+
4
+ require 'rails4_client_side_validations/config'
5
+ require 'rails4_client_side_validations/active_model' if defined?(::ActiveModel)
6
+ require 'rails4_client_side_validations/active_record' if defined?(::ActiveRecord)
7
+ require 'rails4_client_side_validations/action_view' if defined?(::ActionView)
8
+ if defined?(::Rails)
9
+ require 'rails4_client_side_validations/generators'
10
+ require 'rails4_client_side_validations/middleware'
11
+ require 'rails4_client_side_validations/engine'
12
+ end
13
+
@@ -0,0 +1,14 @@
1
+ module Rails4ClientSideValidations::ActionView
2
+ module Helpers
3
+ end
4
+ end
5
+
6
+ require 'rails4_client_side_validations/core_ext'
7
+ require 'rails4_client_side_validations/action_view/form_helper'
8
+ require 'rails4_client_side_validations/action_view/form_tag_helper'
9
+ require 'rails4_client_side_validations/action_view/form_builder'
10
+
11
+ ActionView::Base.send(:include, Rails4ClientSideValidations::ActionView::Helpers::FormHelper)
12
+ ActionView::Base.send(:include, Rails4ClientSideValidations::ActionView::Helpers::FormTagHelper)
13
+ ActionView::Helpers::FormBuilder.send(:include, Rails4ClientSideValidations::ActionView::Helpers::FormBuilder)
14
+
@@ -0,0 +1,105 @@
1
+ module Rails4ClientSideValidations::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_rails4_client_side_validations(method, options = {})
8
+ build_validation_options(method, options)
9
+ options.delete(:validate)
10
+ #{selector}_without_rails4_client_side_validations(method, options)
11
+ end
12
+ RUBY_EVAL
13
+
14
+ base.class_eval { alias_method_chain selector, :rails4_client_side_validations }
15
+ end
16
+
17
+ base.class_eval do
18
+ alias_method_chain :initialize, :rails4_client_side_validations
19
+ alias_method_chain :fields_for, :rails4_client_side_validations
20
+ alias_method_chain :check_box, :rails4_client_side_validations
21
+ alias_method_chain :radio_button, :rails4_client_side_validations
22
+ alias_method_chain :select, :rails4_client_side_validations
23
+ alias_method_chain :collection_select, :rails4_client_side_validations
24
+ alias_method_chain :grouped_collection_select, :rails4_client_side_validations
25
+ alias_method_chain :time_zone_select, :rails4_client_side_validations
26
+
27
+ def client_side_form_settings(options, form_helper)
28
+ {
29
+ :type => self.class.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 validate(*attrs)
38
+ options = attrs.pop if attrs.last.is_a?(Hash)
39
+ (attrs.present? ? attrs : @object._validators.keys).each do |attr|
40
+ build_validation_options(attr, :validate => options)
41
+ end
42
+ nil
43
+ end
44
+
45
+ def initialize_with_rails4_client_side_validations(object_name, object, template, options, proc=nil)
46
+ initialize_without_rails4_client_side_validations(object_name, object, template, options, proc)
47
+ @options[:validators] = { object => {} }
48
+ end
49
+
50
+ def fields_for_with_rails4_client_side_validations(record_or_name_or_array, *args, &block)
51
+ options = args.extract_options!
52
+ options[:validate] ||= @options[:validate] if @options[:validate] && !options.key?(:validate)
53
+ fields_for_without_rails4_client_side_validations(record_or_name_or_array, *(args << options), &block)
54
+ end
55
+
56
+ def check_box_with_rails4_client_side_validations(method, options = {}, checked_value = "1", unchecked_value = "0")
57
+ build_validation_options(method, options)
58
+ options.delete(:validate)
59
+ check_box_without_rails4_client_side_validations(method, options, checked_value, unchecked_value)
60
+ end
61
+
62
+ def radio_button_with_rails4_client_side_validations(method, tag_value, options = {})
63
+ build_validation_options(method, options)
64
+ options.delete(:validate)
65
+ radio_button_without_rails4_client_side_validations(method, tag_value, options)
66
+ end
67
+
68
+ def select_with_rails4_client_side_validations(method, choices, options = {}, html_options = {})
69
+ build_validation_options(method, html_options.merge(:name => options[:name]))
70
+ html_options.delete(:validate)
71
+ select_without_rails4_client_side_validations(method, choices, options, html_options)
72
+ end
73
+
74
+ def collection_select_with_rails4_client_side_validations(method, collection, value_method, text_method, options = {}, html_options = {})
75
+ build_validation_options(method, html_options.merge(:name => options[:name]))
76
+ html_options.delete(:validate)
77
+ collection_select_without_rails4_client_side_validations(method, collection, value_method, text_method, options, html_options)
78
+ end
79
+
80
+ def grouped_collection_select_with_rails4_client_side_validations(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
81
+ build_validation_options(method, html_options.merge(:name => options[:name]))
82
+ html_options.delete(:validate)
83
+ grouped_collection_select_without_rails4_client_side_validations(method, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
84
+ end
85
+
86
+ def time_zone_select_with_rails4_client_side_validations(method, priority_zones = nil, options = {}, html_options = {})
87
+ build_validation_options(method, html_options.merge(:name => options[:name]))
88
+ html_options.delete(:validate)
89
+ time_zone_select_without_rails4_client_side_validations(method, priority_zones, options, html_options)
90
+ end
91
+
92
+ private
93
+
94
+ def build_validation_options(method, options = {})
95
+ if @options[:validate]
96
+ index = @default_options[:index].present? ? "[#{@default_options[:index]}]" : ''
97
+ name = options[:name] || "#{@object_name}#{index}[#{method}]"
98
+ child_index = @options[:child_index] ? "(\\d+|#{Regexp.escape(@options[:child_index].to_s)})" : "\\d+"
99
+ name = name.to_s.gsub(/_attributes\]\[#{child_index}\]/, '_attributes][]')
100
+ name = "#{name}#{options[:multiple] ? "[]" : nil}"
101
+ @options[:validators][@object][method] = { :name => name, :options => options[:validate] }
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,139 @@
1
+ module Rails4ClientSideValidations::ActionView::Helpers
2
+ module FormHelper
3
+ class Error < StandardError; end
4
+
5
+ def form_for(record, *args, &block)
6
+ raise ArgumentError, "Missing block" unless block_given?
7
+ options = args.extract_options!
8
+ if options[:validate]
9
+
10
+ # Always turn off HTML5 Validations
11
+ options[:html] ||= {}
12
+ options[:html][:novalidate] = 'novalidate'
13
+
14
+ case record
15
+ when String, Symbol
16
+ raise Rails4ClientSideValidations::ActionView::Helpers::FormHelper::Error, 'Using form_for(:name, @resource) is not supported with ClientSideValidations. Please use form_for(@resource, :as => :name) instead.'
17
+ else
18
+ object = record.is_a?(Array) ? record.last : record
19
+ object_name = options[:as] || model_name_from_record_or_class(object).param_key
20
+ end
21
+ end
22
+
23
+ @validators = {}
24
+
25
+ # Order matters here. Rails mutates the options object
26
+ html_id = options[:html][:id] if options[:html]
27
+ form = super(record, *(args << options), &block)
28
+ options[:id] = html_id if html_id
29
+
30
+ process_validators options
31
+ builder = instantiate_builder(object_name, object, options) if object_name and object
32
+
33
+ script = client_side_form_settings(object, options, builder)
34
+
35
+ # Because of the load order requirement above this sub is necessary
36
+ # Would be nice to not do this
37
+ script = insert_validators_into_script(script)
38
+
39
+ if assign_script_to_content_for(options[:validate], script)
40
+ form.html_safe
41
+ else
42
+ "#{form}#{script}".html_safe
43
+ end
44
+ end
45
+
46
+ def assign_script_to_content_for(name, script)
47
+ if name && name != true
48
+ content_for(name) { script.html_safe }
49
+ true
50
+ end
51
+ end
52
+
53
+ def apply_form_for_options!(object_or_array, object, options)
54
+ super
55
+ options[:html][:validate] = true if options[:validate]
56
+ end
57
+
58
+ def fields_for(record_or_name_or_array, record_object = nil, options = {}, &block)
59
+ output = super
60
+ process_validators options
61
+ output
62
+ end
63
+
64
+ private
65
+
66
+ def process_validators(options)
67
+ if @validators
68
+ options[:validators].each do |key, value|
69
+ if @validators.key?(key)
70
+ @validators[key].merge! value
71
+ else
72
+ @validators[key] = value
73
+ end
74
+ end
75
+ end
76
+ end
77
+
78
+ def insert_validators_into_script(script)
79
+ # There is probably a more performant way of doing this
80
+ # But using String#sub has some issues. Undocumented "features"
81
+ if script
82
+ script = script.split(/"validator_hash"/)
83
+ script = "#{script[0]}#{construct_validators.to_json}#{script[1]}"
84
+ end
85
+
86
+ script
87
+ end
88
+
89
+ def construct_validators
90
+ @validators.inject({}) do |validator_hash, object_opts|
91
+ option_hash = object_opts[1].inject({}) do |option_hash, attr|
92
+ option_hash.merge!(attr[0] => attr[1][:options])
93
+ end
94
+
95
+ if object_opts[0].respond_to?(:client_side_validation_hash)
96
+ validation_hash = object_opts[0].client_side_validation_hash(option_hash)
97
+ else
98
+ validation_hash = {}
99
+ end
100
+
101
+ option_hash.each_key do |attr|
102
+ if validation_hash[attr]
103
+ validator_hash.merge!(object_opts[1][attr][:name] => validation_hash[attr])
104
+ end
105
+ end
106
+
107
+ validator_hash
108
+ end
109
+ end
110
+
111
+ def client_side_form_settings(object, options, builder)
112
+ if options[:validate]
113
+ if options[:id]
114
+ var_name = options[:id]
115
+ else
116
+ var_name = if object.respond_to?(:persisted?) && object.persisted?
117
+ options[:as] ? "edit_#{options[:as]}" : [options[:namespace], dom_id(object, :edit)].compact.join("_")
118
+ else
119
+ options[:as] ? "new_#{options[:as]}" : [options[:namespace], dom_id(object)].compact.join("_")
120
+ end
121
+ end
122
+
123
+ if Rails4ClientSideValidations::Config.number_format_with_locale and defined?(I18n)
124
+ number_format = I18n.t("number.format").slice(:separator, :delimiter)
125
+ else
126
+ number_format = {:separator=>".", :delimiter=>","}
127
+ end
128
+ patterns = {:numericality=>"/^(-|\\+)?(?:\\d+|\\d{1,3}(?:\\#{number_format[:delimiter]}\\d{3})+)(?:\\#{number_format[:separator]}\\d*)?$/"}
129
+
130
+
131
+ content_tag(:script) do
132
+ "//<![CDATA[\nif(window.Rails4ClientSideValidations===undefined)window.Rails4ClientSideValidations={};window.Rails4ClientSideValidations.disabled_validators=#{Rails4ClientSideValidations::Config.disabled_validators.to_json};window.Rails4ClientSideValidations.number_format=#{number_format.to_json};if(window.Rails4ClientSideValidations.patterns===undefined)window.Rails4ClientSideValidations.patterns = {};window.Rails4ClientSideValidations.patterns.numericality=#{patterns[:numericality]};if(window.Rails4ClientSideValidations.remote_validators_prefix===undefined)window.Rails4ClientSideValidations.remote_validators_prefix='#{(Rails4ClientSideValidations::Config.root_path||"").sub(/\/+\Z/,'')}';if(window.Rails4ClientSideValidations.forms===undefined)window.Rails4ClientSideValidations.forms={};window.Rails4ClientSideValidations.forms['#{var_name}'] = #{builder.client_side_form_settings(options, self).merge(:validators => 'validator_hash').to_json};\n//]]>".html_safe
133
+ end
134
+ end
135
+ end
136
+
137
+ end
138
+ end
139
+
@@ -0,0 +1,12 @@
1
+ module Rails4ClientSideValidations::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,143 @@
1
+ require 'rails4_client_side_validations/core_ext'
2
+
3
+ module Rails4ClientSideValidations::ActiveModel
4
+ module Validator
5
+
6
+ def client_side_hash(model, attribute, force = nil)
7
+ build_client_side_hash(model, attribute, self.options.dup)
8
+ end
9
+
10
+ def copy_conditional_attributes(to, from)
11
+ [:if, :unless].each { |key| to[key] = from[key] if from[key].present? }
12
+ end
13
+
14
+ private
15
+
16
+ def build_client_side_hash(model, attribute, options)
17
+ { :message => model.errors.generate_message(attribute, message_type, options) }.merge(options.except(*::ActiveModel::Errors::CALLBACKS_OPTIONS - [:allow_blank, :if, :unless]))
18
+ end
19
+
20
+ def message_type
21
+ kind
22
+ end
23
+ end
24
+
25
+ module Validations
26
+ def client_side_validation_hash(force = nil)
27
+ _validators.inject({}) do |attr_hash, attr|
28
+ unless [nil, :block].include?(attr[0])
29
+
30
+ validator_hash = attr[1].inject(Hash.new { |h,k| h[k] = []}) do |kind_hash, validator|
31
+ if can_use_for_client_side_validation?(attr[0], validator, force)
32
+ if client_side_hash = validator.client_side_hash(self, attr[0], extract_force_option(attr[0], force))
33
+ kind_hash[validator.kind] << client_side_hash.except(:on, :if, :unless)
34
+ end
35
+ end
36
+
37
+ kind_hash
38
+ end
39
+
40
+ if validator_hash.present?
41
+ attr_hash.merge!(attr[0] => validator_hash)
42
+ else
43
+ attr_hash
44
+ end
45
+ else
46
+ attr_hash
47
+ end
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def extract_force_option(attr, force)
54
+ case force
55
+ when FalseClass, TrueClass, NilClass
56
+ force
57
+ when Hash
58
+ extract_force_option(nil, force[attr])
59
+ else
60
+ nil
61
+ end
62
+ end
63
+
64
+ def can_use_for_client_side_validation?(attr, validator, force)
65
+ if validator_turned_off?(attr, validator, force)
66
+ result = false
67
+ else
68
+ # Yeah yeah, #new_record? is not part of ActiveModel :p
69
+ result = ((self.respond_to?(:new_record?) && validator.options[:on] == (self.new_record? ? :create : :update)) || validator.options[:on].nil?)
70
+ result = result && validator.kind != :block
71
+
72
+ if validator.options[:if] || validator.options[:unless]
73
+ if validator.options[:if] && validator.options[:if] =~ /changed\?/
74
+ result = true
75
+ else result = can_force_validator?(attr, validator, force)
76
+ if validator.options[:if]
77
+ result = result && run_conditional(validator.options[:if])
78
+ end
79
+ if validator.options[:unless]
80
+ result = result && !run_conditional(validator.options[:unless])
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ result
87
+ end
88
+
89
+ def run_conditional(method_name_value_or_proc)
90
+ if method_name_value_or_proc.respond_to?(:call)
91
+ method_name_value_or_proc.call(self)
92
+ else
93
+ self.send(method_name_value_or_proc)
94
+ end
95
+ end
96
+
97
+ def validator_turned_off?(attr, validator, force)
98
+ case force
99
+ when FalseClass
100
+ true
101
+ when Hash
102
+ case force[attr]
103
+ when FalseClass
104
+ true
105
+ when Hash
106
+ force[attr][validator.kind] == false
107
+ else
108
+ false
109
+ end
110
+ else
111
+ ::Rails4ClientSideValidations::Config.disabled_validators.include?(validator.kind)
112
+ end
113
+ end
114
+
115
+ def can_force_validator?(attr, validator, force)
116
+ case force
117
+ when TrueClass
118
+ true
119
+ when Hash
120
+ case force[attr]
121
+ when TrueClass
122
+ true
123
+ when Hash
124
+ force[attr][validator.kind]
125
+ else
126
+ false
127
+ end
128
+ else
129
+ false
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ ActiveModel::Validator.send(:include, Rails4ClientSideValidations::ActiveModel::Validator)
136
+ ActiveModel::Validations.send(:include, Rails4ClientSideValidations::ActiveModel::Validations)
137
+
138
+ %w{absence acceptance exclusion inclusion length format numericality presence}.each do |validator|
139
+ require "rails4_client_side_validations/active_model/#{validator}"
140
+ validator.capitalize!
141
+ eval "ActiveModel::Validations::#{validator}Validator.send(:include, Rails4ClientSideValidations::ActiveModel::#{validator})"
142
+ end
143
+