rails4_client_side_validations 0.0.2 → 0.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 (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
+