client_validations 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ **/*/log/*
2
+ **/*/tmp/*
3
+ *~
4
+ coverage/*
5
+ *.sqlite3
6
+ rdoc/*
7
+ *.gemspec
8
+ pkg
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,63 @@
1
+ h2. Dependencies
2
+
3
+ To use this plugin, you first need to download the Validation Reflection plugin:
4
+
5
+ <pre><code>script/plugin install git://github.com/redinger/validation_reflection.git</code></pre>
6
+
7
+ And you need to download the jQuery and jQuery Validator:
8
+
9
+ * http://code.jquery.com/jquery-latest.min.js
10
+ * http://ajax.microsoft.com/ajax/jquery.validate/1.6/jquery.validate.min.js
11
+
12
+ h2. Installation
13
+
14
+ h3. as a Gem
15
+
16
+ <pre><code>sudo gem install client-validations</code></pre>
17
+
18
+ h3. as a Plugin
19
+
20
+ <pre><code>script/plugin install git://github.com/willian/client_validations.git</code></pre>
21
+
22
+ h2. Usage
23
+
24
+ All you have to do is use the client_validations form_helper. Example:
25
+
26
+ <pre><code><% form_for(@post) do |f| %>
27
+ <%= f.label :title %>
28
+ <%= f.text_field :title %>
29
+
30
+ <%= f.label :content %>
31
+ <%= f.text_area :content %>
32
+
33
+ <%= f.submit 'Create' %>
34
+
35
+ <%= f.client_validations %>
36
+ <% end %></code></pre>
37
+
38
+ h2. Maintainer
39
+
40
+ Willian Fernandes (http://willianfernandes.com.br)
41
+
42
+ h2. License
43
+
44
+ Copyright (c) 2010 Willian Fernandes
45
+
46
+ Permission is hereby granted, free of charge, to any person obtaining
47
+ a copy of this software and associated documentation files (the
48
+ "Software"), to deal in the Software without restriction, including
49
+ without limitation the rights to use, copy, modify, merge, publish,
50
+ distribute, sublicense, and/or sell copies of the Software, and to
51
+ permit persons to whom the Software is furnished to do so, subject to
52
+ the following conditions:
53
+
54
+ The above copyright notice and this permission notice shall be
55
+ included in all copies or substantial portions of the Software.
56
+
57
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
58
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
59
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
60
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
61
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
62
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
63
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the client_validations plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.pattern = 'test/**/*_test.rb'
13
+ t.verbose = true
14
+ end
15
+
16
+ desc 'Generate documentation for the client_validations plugin.'
17
+ Rake::RDocTask.new(:rdoc) do |rdoc|
18
+ rdoc.rdoc_dir = 'rdoc'
19
+ rdoc.title = 'ClientValidations'
20
+ rdoc.options << '--line-numbers' << '--inline-source'
21
+ rdoc.rdoc_files.include('README')
22
+ rdoc.rdoc_files.include('lib/**/*.rb')
23
+ end
24
+
25
+ begin
26
+ require 'jeweler'
27
+ Jeweler::Tasks.new do |gemspec|
28
+ gemspec.name = "client_validations"
29
+ gemspec.summary = "Translates the validations of model to the jQuery Validator"
30
+ gemspec.description = "Translates the validations of model to the jQuery Validator"
31
+ gemspec.email = "willian@willianfernandes.com.br"
32
+ gemspec.homepage = "http://github.com/willian/client_validations"
33
+ gemspec.authors = ["Willian Fernandes"]
34
+ end
35
+ Jeweler::GemcutterTasks.new
36
+ rescue LoadError
37
+ puts "Jeweler not available. Install it with: gem install jeweler"
38
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,8 @@
1
+ class ClientValidationsController < ApplicationController
2
+ # Poll with /client_validations/uniqueness?model_class=User&column=username&value=theusername. Returns
3
+ # either 'true' or 'false'.
4
+ def uniqueness
5
+ responder = ClientValidation.current_adapter.validation_responses[:uniqueness]
6
+ render :text => responder.respond(params)
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ ActionController::Routing::Routes.draw do |map|
2
+ map.connect 'client_validations/:action', :controller => 'client_validations'
3
+ end
data/init.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "client_validations"
2
+ require "client_validations/form_builder"
3
+ require "client_validations/util"
4
+ require "client_validations/adapter_base"
5
+ require "client_validations/adapter_base/validation_hook"
6
+ require "client_validations/adapter_base/validation_response"
7
+ require "client_validations/adapters/jquery_validations"
8
+
9
+ # Hook into the default form builder
10
+ # ActionView::Helpers::FormBuilder.class_eval { include ClientValidation::FormBuilder }
11
+ ActionView::Helpers::FormBuilder.send :include, ClientValidation::FormBuilder
@@ -0,0 +1,14 @@
1
+ module ClientValidation
2
+ def current_adapter
3
+ self.current_adapter = ClientValidation::Adapters::JqueryValidations
4
+ adapter = @current_adapter
5
+
6
+ return adapter
7
+ end
8
+
9
+ def current_adapter=(adapter)
10
+ @current_adapter = adapter
11
+ end
12
+
13
+ extend self
14
+ end
@@ -0,0 +1,13 @@
1
+ module ClientValidation
2
+ # The base class of an adapter.
3
+ class AdapterBase
4
+ def self.response(name, &block)
5
+ self.validation_responses[name] = ValidationResponse.new(&block)
6
+ end
7
+
8
+ private
9
+ def self.validation_responses
10
+ @validation_responses ||= {}
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,66 @@
1
+ module LiveValidations
2
+ class AdapterBase
3
+ # A ValidationHook is this plugins representation of a validation, and is created with
4
+ # the adapter DSL through the Validations::AdapterBase.validates method.
5
+ class ValidationHook
6
+ attr_reader :data, :callback, :prefix, :adapter_instance
7
+
8
+ def initialize(&block)
9
+ @proc = block
10
+ @data = {}
11
+ end
12
+
13
+ def [](key)
14
+ data[key]
15
+ end
16
+
17
+ def []=(key, value)
18
+ data[key] = value
19
+ end
20
+
21
+ def run_validation(adapter_instance, callback)
22
+ @adapter_instance = adapter_instance
23
+ @callback = callback
24
+ @prefix = @adapter_instance.prefix
25
+
26
+ # The @proc is called once for each of the attributes in the @callback,
27
+ # passing the attribute to the proc much like validates_each.
28
+ @callback.options[:attributes].each {|attribute| @proc.call(self, attribute) }
29
+
30
+
31
+ @data.each do |key, value|
32
+ case value
33
+ when Hash
34
+ recursively_merge_hashes(@adapter_instance[key], value)
35
+ when Array
36
+ @adapter_instance[key] += value
37
+ end
38
+ end
39
+ end
40
+
41
+ # Returns either the :message specified, or the default I18n error message.
42
+ def message_for(attribute, key,options={})
43
+ handwritten_message_for(attribute) || I18n.translate(key, {:scope => 'activerecord.errors.messages'}.merge(options))
44
+ end
45
+
46
+ def handwritten_message_for(attribute)
47
+ return unless callback.options[:message]
48
+
49
+ I18n.backend.send(:interpolate, I18n.locale, callback.options[:message], {
50
+ :model => adapter_instance.active_record_instance.class.human_name,
51
+ :attribute => attribute
52
+ })
53
+ end
54
+
55
+ def regex
56
+ callback.options[:live_validator] || callback.options[:with]
57
+ end
58
+
59
+ private
60
+
61
+ def recursively_merge_hashes(h1, h2)
62
+ h1.merge!(h2) {|key, _old, _new| if _old.class == Hash then recursively_merge_hashes(_old, _new) else _new end }
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,19 @@
1
+ module ClientValidation
2
+ class AdapterBase
3
+ # Currently this is used in the validates_uniqueness_of controller callback.
4
+ class ValidationResponse
5
+ attr_reader :params
6
+ def initialize(&block)
7
+ @proc = block
8
+ end
9
+
10
+ def respond(params)
11
+ @params = params
12
+
13
+ return @proc.call(self)
14
+ rescue
15
+ nil
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,156 @@
1
+ module ClientValidation
2
+ module Adapters
3
+ # Adapter for jQuery Validation
4
+ class JqueryValidations < ClientValidation::AdapterBase
5
+
6
+ response :uniqueness do |r|
7
+ column = r.params[r.params[:model_class].downcase].keys.first
8
+ value = r.params[r.params[:model_class].downcase][column]
9
+ r.params[:model_class].constantize.count(:conditions => {column => value}) == 0
10
+ end
11
+
12
+ def self.render_script(object)
13
+ @declarations = []
14
+ @validators = {}
15
+ @messages = {}
16
+
17
+ @klasses = [object.class]
18
+
19
+ # Discovering all models
20
+ object.class.reflections.keys.uniq.each do |assoc_object|
21
+ @klasses << eval(assoc_object.to_s.classify)
22
+ end
23
+
24
+ # Gererating the jQuery Validator code
25
+ @klasses.each do |klass|
26
+ @klass = klass
27
+
28
+ @klass.reflect_on_all_validations.each do |@v|
29
+ prefix = @klass.to_s.downcase
30
+
31
+ attribute_name = @v.name unless @v.macro == :validates_confirmation_of
32
+ attribute_name = "#{@v.name}_confirmation" if @v.macro == :validates_confirmation_of
33
+
34
+ @translate_message_key = "#{prefix}.#{@v.name}"
35
+
36
+ # debugger
37
+ if object.class.to_s.downcase == prefix
38
+ @field_name = "#{prefix}[#{attribute_name}]"
39
+ @field_id = "#{prefix}_#{attribute_name}"
40
+ else
41
+ prefix = "#{prefix}_attributes"
42
+
43
+ @field_name = "#{object.class.to_s.downcase}[#{prefix}][#{attribute_name}]"
44
+ @field_id = "#{object.class.to_s.downcase}_#{prefix}_#{attribute_name}"
45
+ end
46
+ @validators[@field_name] = {} unless @validators[@field_name]
47
+ @messages[@field_name] = {} unless @messages[@field_name]
48
+
49
+ # Set validation
50
+ unless @v.options[:allow_nil] && @v.options[:allow_blank]
51
+ @validators[@field_name]['required'] = true
52
+ @messages[@field_name]['required'] = ClientValidation::Util.message_for(@translate_message_key, :blank)
53
+ end
54
+ eval("self.set_#{@v.macro.to_s}")
55
+ end
56
+ end
57
+ [@declarations, {:rules => @validators, :messages => @messages}]
58
+ end
59
+
60
+ def self.set_validates_acceptance_of
61
+ @validators[@field_name]['required'] = true
62
+ @messages[@field_name]['required'] = ClientValidation::Util.message_for(@translate_message_key, :accepted)
63
+ end
64
+
65
+ def self.set_validates_associated
66
+ # Nothing to do here
67
+ end
68
+
69
+ def self.set_validates_confirmation_of
70
+ @validators[@field_name]['equalTo'] = "##{@field_id.gsub('_confirmation', '')}"
71
+ @validators[@field_name]['required'] = true
72
+
73
+ message = ClientValidation::Util.message_for(@translate_message_key, :confirmation)
74
+ @messages[@field_name]['equalTo'] = message
75
+ @messages[@field_name]['required'] = message
76
+ end
77
+
78
+ def self.set_validates_exclusion_of
79
+ enum = @v.options[:in]
80
+ message = ClientValidation::Util.message_for(@translate_message_key, :exclusion)
81
+ add_custom_rule(@field_name, Digest::SHA1.hexdigest(enum.inspect), "var list = #{enum.to_json}; for (var i=0; i<list.length; i++){if(list[i] == value) { return false; }} return true;", message)
82
+ end
83
+
84
+ def self.set_validates_inclusion_of
85
+ enum = @v.options[:in] || @v.options[:within]
86
+ message = ClientValidation::Util.message_for(@translate_message_key, :inclusion)
87
+
88
+ case enum
89
+ when Range
90
+ @validators[@field_name]['range'] = [enum.first, enum.last]
91
+ @messages[@field_name]['range'] = message
92
+ when Array
93
+ add_custom_rule(@field_name, Digest::SHA1.hexdigest(enum.inspect), "var list = #{enum.to_json}; for (var i=0; i<list.length; i++){if(list[i] == value) { return true; }}", message)
94
+ end
95
+ end
96
+
97
+ def self.set_validates_format_of
98
+ regex = @v.options[:with].inspect.gsub(/(.*)\/.*$/, '\1/')
99
+ message = ClientValidation::Util.message_for(@translate_message_key, :invalid)
100
+ add_custom_rule(@field_name, Digest::SHA1.hexdigest(regex.inspect), "return #{regex}.test(value)", message)
101
+ end
102
+
103
+ def self.set_validates_length_of
104
+ if @v.options[:minimum]
105
+ @validators[@field_name]['minlength'] = @v.options[:minimum]
106
+ @messages[@field_name]['minlength'] = ClientValidation::Util.message_for(@translate_message_key, :too_short)
107
+ end
108
+
109
+ if @v.options[:maximum]
110
+ @validators[@field_name]['maxlength'] = @v.options[:maximum]
111
+ @messages[@field_name]['maxlength'] = ClientValidation::Util.message_for(@translate_message_key, :too_long)
112
+ end
113
+
114
+ if @v.options[:within] || @v.options[:in]
115
+ r = @v.options[:within] || @v.options[:in]
116
+ @validators[@field_name]['rangelength'] = [r.first, r.last]
117
+ @messages[@field_name]['rangelength'] = "#{ClientValidation::Util.message_for(@translate_message_key, :inclusion)} (min: #{r.first}, max: #{r.last})"
118
+ end
119
+
120
+ if @v.options[:is]
121
+ length = @v.options[:is]
122
+ add_custom_rule(@field_name, "lengthIs#{length}", "return value.length == #{length}", ClientValidation::Util.message_for(@translate_message_key, :wrong_length))
123
+ end
124
+ end
125
+
126
+ def self.set_validates_numericality_of
127
+ @validators[@field_name]['digits'] = true
128
+ @validators[@field_name]['required'] = true
129
+
130
+ message = ClientValidation::Util.message_for(@translate_message_key, :not_a_number)
131
+ @messages[@field_name]['digits'] = message
132
+ @messages[@field_name]['required'] = message
133
+ end
134
+
135
+ def self.set_validates_uniqueness_of
136
+ @validators[@field_name]['remote'] = "/client_validations/uniqueness?model_class=#{@klass.to_s}"
137
+ @messages[@field_name]['remote'] = ClientValidation::Util.message_for(@translate_message_key, :taken)
138
+ end
139
+
140
+ def self.set_validates_presence_of
141
+ @validators[@field_name]['required'] = true
142
+ @messages[@field_name]['required'] = ClientValidation::Util.message_for(@translate_message_key, :blank)
143
+ end
144
+
145
+ def self.add_custom_rule(attribute, identifier, validation, message)
146
+ @declarations << <<-EOF
147
+ jQuery.validator.addMethod('#{identifier}', function(value){
148
+ #{validation}
149
+ }, '#{message}');
150
+ EOF
151
+ @validators[@field_name][identifier] = true
152
+ end
153
+
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,22 @@
1
+ module ClientValidation
2
+ module FormBuilder
3
+ def client_validations
4
+ if object.new_record?
5
+ form_id = "new_#{object.class.to_s.downcase}"
6
+ else
7
+ form_id = "edit_#{object.class.to_s.downcase}"
8
+ end
9
+ declarations, validations = ClientValidation.current_adapter.render_script(object)
10
+ js = <<-EOF
11
+ #{declarations}
12
+ $('##{form_id}').validate({
13
+ rules: #{validations[:rules].to_json},
14
+ messages: #{validations[:messages].to_json}
15
+ });
16
+ EOF
17
+
18
+ html = template.content_tag(:script, js, :type => "text/javascript")
19
+ html.respond_to?(:html_safe!) ? html.html_safe! : html
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ module ClientValidation
2
+ class Util
3
+ def self.message_for(field_scope, key)
4
+ field_name = I18n.translate(field_scope, {:scope => "activerecord.attributes"})
5
+ message_text = I18n.translate(key, {:scope => 'activerecord.errors.messages'})
6
+ "#{field_name} #{message_text}"
7
+ end
8
+ end
9
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: client_validations
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Willian Fernandes
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-27 00:00:00 -02:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Translates the validations of model to the jQuery Validator
17
+ email: willian@willianfernandes.com.br
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.textile
24
+ files:
25
+ - .gitignore
26
+ - MIT-LICENSE
27
+ - README.textile
28
+ - Rakefile
29
+ - VERSION
30
+ - app/controllers/client_validations_controller.rb
31
+ - config/routes.rb
32
+ - init.rb
33
+ - lib/client_validations.rb
34
+ - lib/client_validations/adapter_base.rb
35
+ - lib/client_validations/adapter_base/validation_hook.rb
36
+ - lib/client_validations/adapter_base/validation_response.rb
37
+ - lib/client_validations/adapters/jquery_validations.rb
38
+ - lib/client_validations/form_builder.rb
39
+ - lib/client_validations/util.rb
40
+ has_rdoc: true
41
+ homepage: http://github.com/willian/client_validations
42
+ licenses: []
43
+
44
+ post_install_message:
45
+ rdoc_options:
46
+ - --charset=UTF-8
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ required_rubygems_version: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: "0"
60
+ version:
61
+ requirements: []
62
+
63
+ rubyforge_project:
64
+ rubygems_version: 1.3.5
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Translates the validations of model to the jQuery Validator
68
+ test_files: []
69
+