client_side_validations-formtastic 2.0.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/client_side_validations/formtastic/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Brian Cardarella"]
6
+ gem.email = ["bcardarella@gmail.com"]
7
+ gem.description = %q{Formtastic Plugin for ClientSideValidaitons}
8
+ gem.summary = %q{Formtastic Plugin for ClientSideValidations}
9
+ gem.homepage = 'https://github.com/dockyard/client_side_validations-formtastic'
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files -- {lib/*,vendor/*,*.gemspec}`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = 'client_side_validations-formtastic'
15
+ gem.require_paths = ['lib']
16
+ gem.version = ClientSideValidations::Formtastic::VERSION
17
+
18
+ gem.add_dependency 'client_side_validations', '~> 3.2.0.beta.1'
19
+ gem.add_dependency 'formtastic', '~> 2.0.0'
20
+
21
+ gem.add_development_dependency 'actionpack', '~> 3.2.0'
22
+
23
+ # For QUnit testing
24
+ gem.add_development_dependency 'sinatra', '~> 1.0'
25
+ gem.add_development_dependency 'shotgun'
26
+ gem.add_development_dependency 'thin'
27
+ gem.add_development_dependency 'json'
28
+ end
@@ -0,0 +1,21 @@
1
+ module ClientSideValidations
2
+ module Formtastic
3
+ module FormBuilder
4
+
5
+ def self.included(base)
6
+ base.class_eval do
7
+ def self.client_side_form_settings(options, form_helper)
8
+ {
9
+ :type => self.to_s,
10
+ :inline_error_class => ::Formtastic::FormBuilder.default_inline_error_class
11
+ }
12
+ end
13
+ end
14
+ end
15
+
16
+ end
17
+ end
18
+ end
19
+
20
+ Formtastic::FormBuilder.send(:include, ClientSideValidations::Formtastic::FormBuilder)
21
+
@@ -0,0 +1,12 @@
1
+ module ClientSideValidations::Formtastic::Helpers::InputHelper
2
+ def input(method, options = {})
3
+ if options.key?(:validate)
4
+ options[:input_html] ||= {}
5
+ options[:input_html].merge!(:validate => options[:validate])
6
+ options.delete(:validate)
7
+ end
8
+ super(method, options)
9
+ end
10
+ end
11
+
12
+ ::Formtastic::FormBuilder.send(:include, ::ClientSideValidations::Formtastic::Helpers::InputHelper)
@@ -0,0 +1,3 @@
1
+ module ClientSideValidations::Formtastic::Helpers; end
2
+
3
+ require 'client_side_validations/formtastic/helpers/input_helper'
@@ -0,0 +1,5 @@
1
+ module ClientSideValidations
2
+ module Formtastic
3
+ VERSION = "2.0.0.beta.1"
4
+ end
5
+ end
@@ -0,0 +1,3 @@
1
+ require 'formtastic'
2
+ require 'client_side_validations/formtastic/form_builder'
3
+ require 'client_side_validations/formtastic/helpers'
@@ -0,0 +1,35 @@
1
+ class Post < Struct.new(:title, :author_name, :body, :secret, :written_on, :cost)
2
+ extend ActiveModel::Naming
3
+ include ActiveModel::Conversion
4
+ extend ActiveModel::Translation
5
+
6
+ alias_method :secret?, :secret
7
+
8
+ def persisted=(boolean)
9
+ @persisted = boolean
10
+ end
11
+
12
+ def persisted?
13
+ @persisted
14
+ end
15
+
16
+ def client_side_validation_hash
17
+ {
18
+ :cost => {
19
+ :presence => {
20
+ :message => "can't be blank"
21
+ }
22
+ }
23
+ }
24
+ end
25
+
26
+ attr_accessor :author
27
+ def author_attributes=(attributes); end
28
+
29
+ attr_accessor :comments, :comment_ids
30
+ def comments_attributes=(attributes); end
31
+
32
+ attr_accessor :tags
33
+ def tags_attributes=(attributes); end
34
+ end
35
+
@@ -0,0 +1,2 @@
1
+ require 'active_model'
2
+ require 'action_view/models/post'
@@ -0,0 +1,42 @@
1
+ require 'base_helper'
2
+ require 'action_view'
3
+ require 'action_view/models'
4
+ require 'client_side_validations/action_view'
5
+
6
+ module ActionController
7
+ class Base
8
+ include ActionDispatch::Routing::RouteSet.new.url_helpers
9
+ end
10
+ end
11
+
12
+ module ActionViewTestSetup
13
+ include ::ClientSideValidations::ActionView::Helpers::FormHelper
14
+ include ::ClientSideValidations::ActionView::Helpers::FormTagHelper
15
+
16
+ def form_for(*)
17
+ @output_buffer = super
18
+ end
19
+
20
+ Routes = ActionDispatch::Routing::RouteSet.new
21
+ Routes.draw do
22
+ resources :posts
23
+ end
24
+
25
+ def _routes
26
+ Routes
27
+ end
28
+
29
+ include Routes.url_helpers
30
+
31
+ def setup
32
+ super
33
+
34
+ @post = Post.new
35
+ @post.persisted = true
36
+ def @post.id; 123; end
37
+ def @post.id_before_type_cast; 123; end
38
+ def @post.to_param; '123'; end
39
+ end
40
+
41
+ end
42
+
@@ -0,0 +1,6 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ Bundler.setup
4
+ require 'test/unit'
5
+
6
+ module ClientSideValidations; end
@@ -0,0 +1,4 @@
1
+ require 'base_helper'
2
+ require 'action_view'
3
+ require 'client_side_validations/formtastic'
4
+
@@ -0,0 +1,11 @@
1
+ require 'formtastic/cases/helper'
2
+
3
+ class ClientSideValidations::Formtastic::FormBuilderTest < Test::Unit::TestCase
4
+ def test_client_side_form_js_hash
5
+ expected = {
6
+ :type => 'Formtastic::FormBuilder',
7
+ :inline_error_class => 'inline-errors'
8
+ }
9
+ assert_equal expected, Formtastic::FormBuilder.client_side_form_settings(nil, nil)
10
+ end
11
+ end
@@ -0,0 +1,30 @@
1
+ require 'action_view/test_helper'
2
+ require 'formtastic/cases/helper'
3
+
4
+ class ClientSideValidations::Formtastic::FormHelpersTest < ActionView::TestCase
5
+ include ActionViewTestSetup
6
+ include Formtastic::Helpers::FormHelper
7
+
8
+ def client_side_form_settings_helper
9
+ ''
10
+ end
11
+
12
+ def test_semantic_form_for
13
+ semantic_form_for(@post, :validate => true) do |f|
14
+ concat f.input(:cost)
15
+ end
16
+
17
+ expected = %{<form accept-charset="UTF-8" action="/posts/123" class="formtastic post" data-validate="true" id="edit_post_123" method="post" novalidate="novalidate"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /><input name="_method" type="hidden" value="put" /></div><li class="string input required stringish" id="post_cost_input"><label class=\" label\" for="post_cost">Cost<abbr title="required">*</abbr></label><input data-validate="true" id="post_cost" name="post[cost]" type="text" />\n\n</li></form><script>window.ClientSideValidations.forms['edit_post_123'] = {"type":"Formtastic::FormBuilder","inline_error_class":"inline-errors","validators":{"post[cost]":{"presence":{"message":"can't be blank"}}}};</script>}
18
+ assert_equal expected, output_buffer, "\n\n *** If you're running Ruby 1.8 and this test fails is is most likely due to 1.8's lack of insertion order persistence with Hashes ***\n"
19
+ end
20
+
21
+ def test_input_override
22
+ semantic_form_for(@post, :validate => true) do |f|
23
+ concat f.input(:cost, :validate => false)
24
+ end
25
+
26
+ expected = %{<form accept-charset="UTF-8" action="/posts/123" class="formtastic post" data-validate="true" id="edit_post_123" method="post" novalidate="novalidate"><div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="&#x2713;" /><input name="_method" type="hidden" value="put" /></div><li class="string input required stringish" id="post_cost_input"><label class=\" label\" for="post_cost">Cost<abbr title="required">*</abbr></label><input id="post_cost" name="post[cost]" type="text" />\n\n</li></form><script>window.ClientSideValidations.forms['edit_post_123'] = {"type":"Formtastic::FormBuilder","inline_error_class":"inline-errors","validators":{}};</script>}
27
+ assert_equal expected, output_buffer, "\n\n *** If you're running Ruby 1.8 and this test fails is is most likely due to 1.8's lack of insertion order persistence with Hashes ***\n"
28
+ end
29
+
30
+ end
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift File.expand_path('..', __FILE__)
2
+ require 'server'
3
+ run Sinatra::Application
@@ -0,0 +1,54 @@
1
+ module('Validate Formtastic', {
2
+ setup: function() {
3
+ window.ClientSideValidations.forms['new_user'] = {
4
+ type: 'Formtastic::FormBuilder',
5
+ inline_error_class: 'inline-errors',
6
+ validators: {
7
+ "user[name]":{"presence":{"message": "must be present"}, "format":{"message":"is invalid","with":/\d+/}}
8
+ }
9
+ }
10
+
11
+ $('#qunit-fixture')
12
+ .append($('<form />', {
13
+ action: '/users',
14
+ 'data-validate': true,
15
+ method: 'post',
16
+ id: 'new_user'
17
+ }))
18
+ .find('form')
19
+ .append('<li />').find('li')
20
+ .append($('<input />', {
21
+ name: 'user[name]',
22
+ id: 'user_name',
23
+ 'data-validate': 'true',
24
+ type: 'text'
25
+ }))
26
+ .append($('<label for="user_name">Name</label>'));
27
+ $('form#new_user').validate();
28
+ }
29
+ });
30
+
31
+ test('Validate error attaching and detaching', function() {
32
+ var form = $('form#new_user'), input = form.find('input#user_name');
33
+ var label = $('label[for="user_name"]');
34
+
35
+ input.trigger('focusout')
36
+ ok(input.parent().hasClass('error'));
37
+ ok(label.parent().hasClass('error'));
38
+ ok(input.parent().find('p.inline-errors:contains("must be present")')[0]);
39
+
40
+ input.val('abc')
41
+ input.trigger('change')
42
+ input.trigger('focusout')
43
+ ok(input.parent().hasClass('error'));
44
+ ok(label.parent().hasClass('error'));
45
+ ok(input.parent().find('p.inline-errors:contains("is invalid")')[0]);
46
+
47
+ input.val('123')
48
+ input.trigger('change')
49
+ input.trigger('focusout')
50
+ ok(!input.parent().hasClass('error'));
51
+ ok(!label.parent().hasClass('error'));
52
+ ok(!input.parent().find('p.inline-errors')[0]);
53
+ });
54
+
@@ -0,0 +1,15 @@
1
+ // hijacks normal form submit; lets it submit to an iframe to prevent
2
+ // navigating away from the test suite
3
+ $(document).bind('submit', function(e) {
4
+ if (!e.isDefaultPrevented()) {
5
+ var form = $(e.target), action = form.attr('action'),
6
+ name = 'form-frame' + jQuery.guid++,
7
+ iframe = $('<iframe name="' + name + '" />');
8
+
9
+ if (action.indexOf('iframe') < 0) form.attr('action', action + '?iframe=true')
10
+ form.attr('target', name);
11
+ $('#qunit-fixture').append(iframe);
12
+ form.trigger('iframe:loading');
13
+ }
14
+ });
15
+
@@ -0,0 +1,122 @@
1
+ /*
2
+ * Metadata - jQuery plugin for parsing metadata from elements
3
+ *
4
+ * Copyright (c) 2006 John Resig, Yehuda Katz, J�örn Zaefferer, Paul McLanahan
5
+ *
6
+ * Dual licensed under the MIT and GPL licenses:
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ * http://www.gnu.org/licenses/gpl.html
9
+ *
10
+ * Revision: $Id: jquery.metadata.js 4187 2007-12-16 17:15:27Z joern.zaefferer $
11
+ *
12
+ */
13
+
14
+ /**
15
+ * Sets the type of metadata to use. Metadata is encoded in JSON, and each property
16
+ * in the JSON will become a property of the element itself.
17
+ *
18
+ * There are three supported types of metadata storage:
19
+ *
20
+ * attr: Inside an attribute. The name parameter indicates *which* attribute.
21
+ *
22
+ * class: Inside the class attribute, wrapped in curly braces: { }
23
+ *
24
+ * elem: Inside a child element (e.g. a script tag). The
25
+ * name parameter indicates *which* element.
26
+ *
27
+ * The metadata for an element is loaded the first time the element is accessed via jQuery.
28
+ *
29
+ * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements
30
+ * matched by expr, then redefine the metadata type and run another $(expr) for other elements.
31
+ *
32
+ * @name $.metadata.setType
33
+ *
34
+ * @example <p id="one" class="some_class {item_id: 1, item_label: 'Label'}">This is a p</p>
35
+ * @before $.metadata.setType("class")
36
+ * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
37
+ * @desc Reads metadata from the class attribute
38
+ *
39
+ * @example <p id="one" class="some_class" data="{item_id: 1, item_label: 'Label'}">This is a p</p>
40
+ * @before $.metadata.setType("attr", "data")
41
+ * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
42
+ * @desc Reads metadata from a "data" attribute
43
+ *
44
+ * @example <p id="one" class="some_class"><script>{item_id: 1, item_label: 'Label'}</script>This is a p</p>
45
+ * @before $.metadata.setType("elem", "script")
46
+ * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label"
47
+ * @desc Reads metadata from a nested script element
48
+ *
49
+ * @param String type The encoding type
50
+ * @param String name The name of the attribute to be used to get metadata (optional)
51
+ * @cat Plugins/Metadata
52
+ * @descr Sets the type of encoding to be used when loading metadata for the first time
53
+ * @type undefined
54
+ * @see metadata()
55
+ */
56
+
57
+ (function($) {
58
+
59
+ $.extend({
60
+ metadata : {
61
+ defaults : {
62
+ type: 'class',
63
+ name: 'metadata',
64
+ cre: /({.*})/,
65
+ single: 'metadata'
66
+ },
67
+ setType: function( type, name ){
68
+ this.defaults.type = type;
69
+ this.defaults.name = name;
70
+ },
71
+ get: function( elem, opts ){
72
+ var settings = $.extend({},this.defaults,opts);
73
+ // check for empty string in single property
74
+ if ( !settings.single.length ) settings.single = 'metadata';
75
+
76
+ var data = $.data(elem, settings.single);
77
+ // returned cached data if it already exists
78
+ if ( data ) return data;
79
+
80
+ data = "{}";
81
+
82
+ if ( settings.type == "class" ) {
83
+ var m = settings.cre.exec( elem.className );
84
+ if ( m )
85
+ data = m[1];
86
+ } else if ( settings.type == "elem" ) {
87
+ if( !elem.getElementsByTagName )
88
+ return undefined;
89
+ var e = elem.getElementsByTagName(settings.name);
90
+ if ( e.length )
91
+ data = $.trim(e[0].innerHTML);
92
+ } else if ( elem.getAttribute != undefined ) {
93
+ var attr = elem.getAttribute( settings.name );
94
+ if ( attr )
95
+ data = attr;
96
+ }
97
+
98
+ if ( data.indexOf( '{' ) <0 )
99
+ data = "{" + data + "}";
100
+
101
+ data = eval("(" + data + ")");
102
+
103
+ $.data( elem, settings.single, data );
104
+ return data;
105
+ }
106
+ }
107
+ });
108
+
109
+ /**
110
+ * Returns the metadata object for the first member of the jQuery object.
111
+ *
112
+ * @name metadata
113
+ * @descr Returns element's metadata object
114
+ * @param Object opts An object contianing settings to override the defaults
115
+ * @type jQuery
116
+ * @cat Plugins/Metadata
117
+ */
118
+ $.fn.metadata = function( opts ){
119
+ return $.metadata.get( this[0], opts );
120
+ };
121
+
122
+ })(jQuery);
@@ -0,0 +1,196 @@
1
+ /** Font Family and Sizes */
2
+
3
+ #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
4
+ font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
5
+ }
6
+
7
+ #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
8
+ #qunit-tests { font-size: smaller; }
9
+
10
+
11
+ /** Resets */
12
+
13
+ #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
14
+ margin: 0;
15
+ padding: 0;
16
+ }
17
+
18
+
19
+ /** Header */
20
+
21
+ #qunit-header {
22
+ padding: 0.5em 0 0.5em 1em;
23
+
24
+ color: #8699a4;
25
+ background-color: #0d3349;
26
+
27
+ font-size: 1.5em;
28
+ line-height: 1em;
29
+ font-weight: normal;
30
+
31
+ border-radius: 15px 15px 0 0;
32
+ -moz-border-radius: 15px 15px 0 0;
33
+ -webkit-border-top-right-radius: 15px;
34
+ -webkit-border-top-left-radius: 15px;
35
+ }
36
+
37
+ #qunit-header a {
38
+ text-decoration: none;
39
+ color: #c2ccd1;
40
+ }
41
+
42
+ #qunit-header a:hover,
43
+ #qunit-header a:focus {
44
+ color: #fff;
45
+ }
46
+
47
+ #qunit-banner {
48
+ height: 5px;
49
+ }
50
+
51
+ #qunit-testrunner-toolbar {
52
+ padding: 0em 0 0.5em 2em;
53
+ }
54
+
55
+ #qunit-userAgent {
56
+ padding: 0.5em 0 0.5em 2.5em;
57
+ background-color: #2b81af;
58
+ color: #fff;
59
+ text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
60
+ }
61
+
62
+
63
+ /** Tests: Pass/Fail */
64
+
65
+ #qunit-tests {
66
+ list-style-position: inside;
67
+ }
68
+
69
+ #qunit-tests li {
70
+ padding: 0.4em 0.5em 0.4em 2.5em;
71
+ border-bottom: 1px solid #fff;
72
+ list-style-position: inside;
73
+ }
74
+
75
+ #qunit-tests li strong {
76
+ cursor: pointer;
77
+ }
78
+
79
+ #qunit-tests ol {
80
+ margin-top: 0.5em;
81
+ padding: 0.5em;
82
+
83
+ background-color: #fff;
84
+
85
+ border-radius: 15px;
86
+ -moz-border-radius: 15px;
87
+ -webkit-border-radius: 15px;
88
+
89
+ box-shadow: inset 0px 2px 13px #999;
90
+ -moz-box-shadow: inset 0px 2px 13px #999;
91
+ -webkit-box-shadow: inset 0px 2px 13px #999;
92
+ }
93
+
94
+ #qunit-tests table {
95
+ border-collapse: collapse;
96
+ margin-top: .2em;
97
+ }
98
+
99
+ #qunit-tests th {
100
+ text-align: right;
101
+ vertical-align: top;
102
+ padding: 0 .5em 0 0;
103
+ }
104
+
105
+ #qunit-tests td {
106
+ vertical-align: top;
107
+ }
108
+
109
+ #qunit-tests pre {
110
+ margin: 0;
111
+ white-space: pre-wrap;
112
+ word-wrap: break-word;
113
+ }
114
+
115
+ #qunit-tests del {
116
+ background-color: #e0f2be;
117
+ color: #374e0c;
118
+ text-decoration: none;
119
+ }
120
+
121
+ #qunit-tests ins {
122
+ background-color: #ffcaca;
123
+ color: #500;
124
+ text-decoration: none;
125
+ }
126
+
127
+ /*** Test Counts */
128
+
129
+ #qunit-tests b.counts { color: black; }
130
+ #qunit-tests b.passed { color: #5E740B; }
131
+ #qunit-tests b.failed { color: #710909; }
132
+
133
+ #qunit-tests li li {
134
+ margin: 0.5em;
135
+ padding: 0.4em 0.5em 0.4em 0.5em;
136
+ background-color: #fff;
137
+ border-bottom: none;
138
+ list-style-position: inside;
139
+ }
140
+
141
+ /*** Passing Styles */
142
+
143
+ #qunit-tests li li.pass {
144
+ color: #5E740B;
145
+ background-color: #fff;
146
+ border-left: 26px solid #C6E746;
147
+ }
148
+
149
+ #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
150
+ #qunit-tests .pass .test-name { color: #366097; }
151
+
152
+ #qunit-tests .pass .test-actual,
153
+ #qunit-tests .pass .test-expected { color: #999999; }
154
+
155
+ #qunit-banner.qunit-pass { background-color: #C6E746; }
156
+
157
+ /*** Failing Styles */
158
+
159
+ #qunit-tests li li.fail {
160
+ color: #710909;
161
+ background-color: #fff;
162
+ border-left: 26px solid #EE5757;
163
+ }
164
+
165
+ #qunit-tests .fail { color: #000000; background-color: #EE5757; }
166
+ #qunit-tests .fail .test-name,
167
+ #qunit-tests .fail .module-name { color: #000000; }
168
+
169
+ #qunit-tests .fail .test-actual { color: #EE5757; }
170
+ #qunit-tests .fail .test-expected { color: green; }
171
+
172
+ #qunit-banner.qunit-fail,
173
+ #qunit-testrunner-toolbar { background-color: #EE5757; }
174
+
175
+
176
+ /** Footer */
177
+
178
+ #qunit-testresult {
179
+ padding: 0.5em 0.5em 0.5em 2.5em;
180
+
181
+ color: #2b81af;
182
+ background-color: #D2E0E6;
183
+
184
+ border-radius: 0 0 15px 15px;
185
+ -moz-border-radius: 0 0 15px 15px;
186
+ -webkit-border-bottom-right-radius: 15px;
187
+ -webkit-border-bottom-left-radius: 15px;
188
+ }
189
+
190
+ /** Fixture */
191
+
192
+ #qunit-fixture {
193
+ position: absolute;
194
+ top: -10000px;
195
+ left: -10000px;
196
+ }