nofxx-formtastic 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +36 -21
- data/Rakefile +1 -1
- data/generators/formtastic_stylesheets/formtastic_stylesheets_generator.rb +21 -0
- data/generators/formtastic_stylesheets/templates/formtastic.css +119 -0
- data/generators/formtastic_stylesheets/templates/formtastic_changes.css +10 -0
- data/lib/formtastic.rb +119 -61
- data/spec/formtastic_spec.rb +134 -21
- metadata +5 -2
data/README.textile
CHANGED
@@ -1,7 +1,11 @@
|
|
1
|
-
h1. Formtastic 0.1.
|
1
|
+
h1. Formtastic 0.1.4
|
2
2
|
|
3
3
|
Formtastic is a Rails FormBuilder DSL (with some other goodies) to make it far easier to create beautiful, semantically rich, syntactically awesome, readily stylable and wonderfully accessible HTML forms in your Rails applications.
|
4
4
|
|
5
|
+
h2. Warning
|
6
|
+
|
7
|
+
This fork adds jQuery only add/remove nested partial.
|
8
|
+
|
5
9
|
h2. The Story
|
6
10
|
|
7
11
|
One day, I finally had enough, so I opened up my text editor, and wrote a DSL for how I'd like to author forms:
|
@@ -83,10 +87,10 @@ You can (and should) get it as a gem:
|
|
83
87
|
And then add it as a dependency in your environment.rb file:
|
84
88
|
|
85
89
|
<pre>
|
86
|
-
config.gem "justinfrench-formtastic",
|
87
|
-
:lib => 'formtastic',
|
88
|
-
:source => 'http://gems.github.com',
|
89
|
-
:version => '0.1.
|
90
|
+
config.gem "justinfrench-formtastic",
|
91
|
+
:lib => 'formtastic',
|
92
|
+
:source => 'http://gems.github.com',
|
93
|
+
:version => '0.1.4'
|
90
94
|
</pre>
|
91
95
|
|
92
96
|
If you're a little more old school, install it as a plugin:
|
@@ -95,6 +99,13 @@ If you're a little more old school, install it as a plugin:
|
|
95
99
|
./script/plugin install git://github.com/justinfrench/formtastic.git
|
96
100
|
</pre>
|
97
101
|
|
102
|
+
h3. jQuery
|
103
|
+
|
104
|
+
To use the cliente side add/remove partial, make sure you got:
|
105
|
+
|
106
|
+
* "jquery.template":http://stanlemon.net/projects/jquery-templates.html
|
107
|
+
|
108
|
+
Avaiable on the DOM.
|
98
109
|
|
99
110
|
h2. Usage
|
100
111
|
|
@@ -156,18 +167,20 @@ If you want to customize the label text, or render some hint text below the fiel
|
|
156
167
|
<% end %>
|
157
168
|
</pre>
|
158
169
|
|
159
|
-
If you want to customize html elements for any non button inputs
|
160
|
-
to specify the :input_html options hash.
|
170
|
+
If you want to customize html elements for any non button inputs and the li
|
171
|
+
wrapper you just need to specify the :input_html and :wrapper_html options hash.
|
161
172
|
|
162
173
|
<pre>
|
163
174
|
<% semantic_form_for @post do |form| %>
|
164
175
|
<%= form.input :title, :input_html => {:size => 60} %>
|
165
|
-
<%= form.input :body %>
|
176
|
+
<%= form.input :body, :wrapper_html => { :class => 'body_wrapper' } %>
|
166
177
|
<%= form.input :created_at, :input_html => {:disabled => true} %>
|
167
178
|
<%= form.buttons %>
|
168
179
|
<% end %>
|
169
180
|
</pre>
|
170
181
|
|
182
|
+
To customize buttons, :button_html is available.
|
183
|
+
|
171
184
|
Nested forms (Rails 2.3) are also supported. You can do it in the Rails way:
|
172
185
|
|
173
186
|
<pre>
|
@@ -237,14 +250,14 @@ If you wish, put something like this in config/initializers/formtastic_config.rb
|
|
237
250
|
# Should all fields be considered "required" by default
|
238
251
|
# Defaults to true, see ValidationReflection notes below
|
239
252
|
Formtastic::SemanticFormBuilder.all_fields_required_by_default = false
|
240
|
-
|
253
|
+
|
241
254
|
# Set the string that will be appended to the labels/fieldsets which are required
|
242
255
|
# It accepts string or procs and the default is a localized version of
|
243
256
|
# '<abbr title="required">*</abbr>'. In other words, if you configure formtastic.required
|
244
257
|
# in your locale, it will replace the abbr title properly. But if you don't want to use
|
245
258
|
# abbr tag, you can simply give a string as below
|
246
259
|
Formtastic::SemanticFormBuilder.required_string = "(required)"
|
247
|
-
|
260
|
+
|
248
261
|
# Set the string that will be appended to the labels/fieldsets which are optional
|
249
262
|
# Defaults to an empty string ("") and also accepts procs (see required_string above)
|
250
263
|
Formtastic::SemanticFormBuilder.optional_string = "(optional)"
|
@@ -329,22 +342,24 @@ h2. Contributors
|
|
329
342
|
Formtastic wouldn't be as awesome as it is today if it weren't for the wonderful contributions of these fine, fine coders. An extra huge thanks goes out to "José Valim":http://github.com/josevalim for nearly 50 patches.
|
330
343
|
|
331
344
|
* "Justin French":http://justinfrench.com
|
345
|
+
* "José Valim":http://github.com/josevalim
|
346
|
+
* "Jeff Smick":http://github.com/sprsquish
|
347
|
+
* "Tien Dung":http://github.com/tiendung
|
348
|
+
* "Mark Mansour":http://stateofflux.com
|
349
|
+
* "Andy Pearson":http://github.com/andypearson
|
350
|
+
* "negonicrac":http://github.com/negonicrac
|
332
351
|
* "Xavier Shay":http://rhnh.net
|
333
|
-
* "Bin Dong":http://github.com/dongbin
|
334
|
-
* "Ben Hamill":http://blog.benhamill.com/
|
335
352
|
* "Pat Allan":http://github.com/freelancing-god
|
336
|
-
* "negonicrac":http://github.com/negonicrac
|
337
|
-
* "Andy Pearson":http://github.com/andypearson
|
338
|
-
* "Mark Mansour":http://stateofflux.com
|
339
|
-
* "Tien Dung":http://github.com/tiendung
|
340
|
-
* "Sascha Hoellger":http://github.com/mitnal
|
341
|
-
* "Jeff Smick":http://github.com/sprsquish
|
342
|
-
* "José Valim":http://github.com/josevalim
|
343
|
-
* "Greg Fitzgerald":http://github.com/gregf/
|
344
353
|
* "Gareth Townsend":http://github.com/quamen
|
354
|
+
* "Sascha Hoellger":http://github.com/mitnal
|
355
|
+
* "Andrew Carpenter":http://github.com/andrewcarpenter
|
345
356
|
* "Jack Dempsey":http://github.com/jackdempsey/
|
357
|
+
* "Greg Fitzgerald":http://github.com/gregf/
|
358
|
+
* "Hector E. Gomez Morales":http://github.com/hectoregm
|
359
|
+
* "Ben Hamill":http://blog.benhamill.com/
|
346
360
|
* "Simon Chiu":http://github.com/tolatomeow
|
347
|
-
|
361
|
+
* "Bin Dong":http://github.com/dongbin
|
362
|
+
* "Marcos Piccinini":http://github.com/nofxx
|
348
363
|
|
349
364
|
h2. Hey, join the Google group!
|
350
365
|
|
data/Rakefile
CHANGED
@@ -20,7 +20,7 @@ begin
|
|
20
20
|
|
21
21
|
s.require_path = 'lib'
|
22
22
|
s.autorequire = GEM
|
23
|
-
s.files = %w(MIT-LICENSE README.textile Rakefile) + Dir.glob("{rails,lib,spec}/**/*")
|
23
|
+
s.files = %w(MIT-LICENSE README.textile Rakefile) + Dir.glob("{rails,lib,generators,spec}/**/*")
|
24
24
|
end
|
25
25
|
rescue LoadError
|
26
26
|
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class FormtasticStylesheetsGenerator < Rails::Generator::Base
|
2
|
+
|
3
|
+
def initialize(*runtime_args)
|
4
|
+
super
|
5
|
+
end
|
6
|
+
|
7
|
+
def manifest
|
8
|
+
record do |m|
|
9
|
+
m.directory File.join('public', 'stylesheets')
|
10
|
+
m.template 'formtastic.css', File.join('public', 'stylesheets', 'formtastic.css')
|
11
|
+
m.template 'formtastic_changes.css', File.join('public', 'stylesheets', 'formtastic_changes.css')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
def banner
|
18
|
+
%{Usage: #{$0} #{spec.name}\nCopies formtastic.css and formtastic_changes.css to public/}
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
/* -------------------------------------------------------------------------------------------------
|
2
|
+
|
3
|
+
It's *strongly* suggested that you don't modify this file. Instead, load a new stylesheet after
|
4
|
+
this one in your layouts (eg formtastic_changes.css) and override the styles to suit your needs.
|
5
|
+
This will allow you to update formtastic.css with new releases without clobbering your own changes.
|
6
|
+
|
7
|
+
This stylesheet forms part of the Formtastic Rails Plugin
|
8
|
+
(c) 2008 Justin French
|
9
|
+
|
10
|
+
--------------------------------------------------------------------------------------------------*/
|
11
|
+
|
12
|
+
|
13
|
+
/* NORMALIZE AND RESET - obviously inspired by Yahoo's reset.css, but scoped to just form.formtastic
|
14
|
+
--------------------------------------------------------------------------------------------------*/
|
15
|
+
form.formtastic, form.formtastic ul, form.formtastic ol, form.formtastic li, form.formtastic fieldset, form.formtastic legend, form.formtastic input, form.formtastic textarea, form.formtastic select, form.formtastic p { margin:0; padding:0; }
|
16
|
+
form.formtastic fieldset { border:0; }
|
17
|
+
form.formtastic em, form.formtastic strong { font-style:normal; font-weight:normal; }
|
18
|
+
form.formtastic ol, form.formtastic ul { list-style:none; }
|
19
|
+
form.formtastic abbr, form.formtastic acronym { border:0; font-variant:normal; }
|
20
|
+
form.formtastic input, form.formtastic textarea, form.formtastic select { font-family:inherit; font-size:inherit; font-weight:inherit; }
|
21
|
+
form.formtastic input, form.formtastic textarea, form.formtastic select { font-size:100%; }
|
22
|
+
form.formtastic legend { color:#000; }
|
23
|
+
|
24
|
+
|
25
|
+
/* FIELDSETS & LISTS
|
26
|
+
--------------------------------------------------------------------------------------------------*/
|
27
|
+
form.formtastic fieldset { }
|
28
|
+
form.formtastic fieldset.inputs { }
|
29
|
+
form.formtastic fieldset.buttons { padding-left:25%; }
|
30
|
+
form.formtastic fieldset ol { }
|
31
|
+
|
32
|
+
/* clearfixing the fieldsets */
|
33
|
+
form.formtastic fieldset { display: inline-block; }
|
34
|
+
form.formtastic fieldset:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
|
35
|
+
html[xmlns] form.formtastic fieldset { display: block; }
|
36
|
+
* html form.formtastic fieldset { height: 1%; }
|
37
|
+
|
38
|
+
|
39
|
+
/* INPUT LIs
|
40
|
+
--------------------------------------------------------------------------------------------------*/
|
41
|
+
form.formtastic fieldset ol li { margin-bottom:1.5em; }
|
42
|
+
|
43
|
+
/* clearfixing the li's */
|
44
|
+
form.formtastic fieldset ol li { display: inline-block; }
|
45
|
+
form.formtastic fieldset ol li:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
|
46
|
+
html[xmlns] form.formtastic fieldset ol li { display: block; }
|
47
|
+
* html form.formtastic fieldset ol li { height: 1%; }
|
48
|
+
|
49
|
+
form.formtastic fieldset ol li.required { }
|
50
|
+
form.formtastic fieldset ol li.optional { }
|
51
|
+
form.formtastic fieldset ol li.error { }
|
52
|
+
|
53
|
+
|
54
|
+
/* LABELS
|
55
|
+
--------------------------------------------------------------------------------------------------*/
|
56
|
+
form.formtastic fieldset ol li label { display:block; width:25%; float:left; padding-top:.2em; }
|
57
|
+
form.formtastic fieldset ol li li label { line-height:100%; padding-top:0; }
|
58
|
+
form.formtastic fieldset ol li li label input { line-height:100%; vertical-align:middle; margin-top:-0.1em;}
|
59
|
+
|
60
|
+
|
61
|
+
/* NESTED FIELDSETS AND LEGENDS (radio and date/time inputs use nested fieldsets)
|
62
|
+
--------------------------------------------------------------------------------------------------*/
|
63
|
+
form.formtastic fieldset ol li fieldset { position:relative; }
|
64
|
+
form.formtastic fieldset ol li fieldset legend { position:absolute; width:25%; padding-top:0.1em; }
|
65
|
+
form.formtastic fieldset ol li fieldset legend span { position:absolute; }
|
66
|
+
form.formtastic fieldset ol li fieldset ol { float:left; width:74%; margin:0; padding:0 0 0 25%; }
|
67
|
+
form.formtastic fieldset ol li fieldset ol li { padding:0; border:0; }
|
68
|
+
|
69
|
+
/* INLINE HINTS
|
70
|
+
--------------------------------------------------------------------------------------------------*/
|
71
|
+
form.formtastic fieldset ol li p.inline-hints { color:#666; margin:0.5em 0 0 25%; }
|
72
|
+
|
73
|
+
|
74
|
+
/* INLINE ERRORS
|
75
|
+
--------------------------------------------------------------------------------------------------*/
|
76
|
+
form.formtastic fieldset ol li p.inline-errors { color:#cc0000; margin:0.5em 0 0 25%; }
|
77
|
+
form.formtastic fieldset ol li ul.errors { color:#cc0000; margin:0.5em 0 0 25%; list-style:square; }
|
78
|
+
form.formtastic fieldset ol li ul.errors li { padding:0; border:none; display:list-item; }
|
79
|
+
|
80
|
+
|
81
|
+
/* STRING & NUMERIC OVERRIDES
|
82
|
+
--------------------------------------------------------------------------------------------------*/
|
83
|
+
form.formtastic fieldset ol li.string input { width:74%; }
|
84
|
+
form.formtastic fieldset ol li.numeric input { width:74%; }
|
85
|
+
|
86
|
+
|
87
|
+
/* TEXTAREA OVERRIDES
|
88
|
+
--------------------------------------------------------------------------------------------------*/
|
89
|
+
form.formtastic fieldset ol li.text textarea { width:74%; }
|
90
|
+
|
91
|
+
|
92
|
+
/* CHECKBOX OVERRIDES
|
93
|
+
--------------------------------------------------------------------------------------------------*/
|
94
|
+
form.formtastic fieldset ol li.boolean label { padding-left:25%; width:auto; }
|
95
|
+
form.formtastic fieldset ol li.boolean label input { margin:0 0.5em 0 0.2em; }
|
96
|
+
|
97
|
+
|
98
|
+
/* RADIO OVERRIDES
|
99
|
+
--------------------------------------------------------------------------------------------------*/
|
100
|
+
form.formtastic fieldset ol li.radio { }
|
101
|
+
form.formtastic fieldset ol li.radio fieldset ol { margin-bottom:-0.6em; }
|
102
|
+
form.formtastic fieldset ol li.radio fieldset ol li { margin:0.1em 0 0.5em 0; }
|
103
|
+
form.formtastic fieldset ol li.radio fieldset ol li label { float:none; width:100%; }
|
104
|
+
form.formtastic fieldset ol li.radio fieldset ol li label input { margin-right:0.2em; }
|
105
|
+
|
106
|
+
|
107
|
+
/* DATE & TIME OVERRIDES
|
108
|
+
--------------------------------------------------------------------------------------------------*/
|
109
|
+
form.formtastic fieldset ol li.date fieldset ol li,
|
110
|
+
form.formtastic fieldset ol li.time fieldset ol li,
|
111
|
+
form.formtastic fieldset ol li.datetime fieldset ol li { float:left; width:auto; margin:0 .3em 0 0; }
|
112
|
+
|
113
|
+
form.formtastic fieldset ol li.date fieldset ol li label,
|
114
|
+
form.formtastic fieldset ol li.time fieldset ol li label,
|
115
|
+
form.formtastic fieldset ol li.datetime fieldset ol li label { display:none; }
|
116
|
+
|
117
|
+
form.formtastic fieldset ol li.date fieldset ol li label input,
|
118
|
+
form.formtastic fieldset ol li.time fieldset ol li label input,
|
119
|
+
form.formtastic fieldset ol li.datetime fieldset ol li label input { display:inline; margin:0; padding:0; }
|
@@ -0,0 +1,10 @@
|
|
1
|
+
/* -------------------------------------------------------------------------------------------------
|
2
|
+
|
3
|
+
Load this stylesheet after formtastic.css in your layouts to override the CSS to suit your needs.
|
4
|
+
This will allow you to update formtastic.css with new releases without clobbering your own changes.
|
5
|
+
|
6
|
+
For example, to make the inline hint paragraphs a little darker in color than the standard #666:
|
7
|
+
|
8
|
+
form.formtastic fieldset ol li p.inline-hints { color:#333; }
|
9
|
+
|
10
|
+
--------------------------------------------------------------------------------------------------*/
|
data/lib/formtastic.rb
CHANGED
@@ -35,7 +35,6 @@ module Formtastic #:nodoc:
|
|
35
35
|
STRING_MAPPINGS = [ :string, :password, :numeric ]
|
36
36
|
|
37
37
|
attr_accessor :template
|
38
|
-
attr_writer :nested_child_index
|
39
38
|
|
40
39
|
# Returns a suitable form input for the given +method+, using the database column information
|
41
40
|
# and other factors (like the method name) to figure out what you probably want.
|
@@ -47,6 +46,7 @@ module Formtastic #:nodoc:
|
|
47
46
|
# * :required - specify if the column is required (true) or not (false)
|
48
47
|
# * :hint - provide some text to hint or help the user provide the correct information for a field
|
49
48
|
# * :input_html - provide options that will be passed down to the generated input
|
49
|
+
# * :wrapper_html - provide options that will be passed down to the li wrapper
|
50
50
|
#
|
51
51
|
# Input Types:
|
52
52
|
#
|
@@ -55,6 +55,7 @@ module Formtastic #:nodoc:
|
|
55
55
|
# columns all map to a single numeric_input, for example).
|
56
56
|
#
|
57
57
|
# * :select (a select menu for associations) - default to association names
|
58
|
+
# * :time_zone (a select menu with time zones)
|
58
59
|
# * :radio (a set of radio inputs for associations) - default to association names
|
59
60
|
# * :password (a password input) - default for :string column types with 'password' in the method name
|
60
61
|
# * :text (a textarea) - default for :text column types
|
@@ -80,26 +81,22 @@ module Formtastic #:nodoc:
|
|
80
81
|
options[:required] = method_required?(method, options[:required])
|
81
82
|
options[:as] ||= default_input_type(method)
|
82
83
|
|
83
|
-
options[:
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
84
|
+
html_class = [ options[:as], (options[:required] ? :required : :optional) ]
|
85
|
+
html_class << 'error' if @object && @object.respond_to?(:errors) && @object.errors.on(method.to_s)
|
86
|
+
|
87
|
+
wrapper_html = options.delete(:wrapper_html) || {}
|
88
|
+
wrapper_html[:id] ||= generate_html_id(method)
|
89
|
+
wrapper_html[:class] = (html_class << wrapper_html[:class]).flatten.compact.join(' ')
|
88
90
|
|
89
91
|
if [:boolean_select, :boolean_radio].include?(options[:as])
|
90
92
|
::ActiveSupport::Deprecation.warn(":as => :#{options[:as]} is deprecated, use :as => :#{options[:as].to_s[8..-1]} instead", caller[3..-1])
|
91
93
|
end
|
92
94
|
|
93
|
-
html_class = [ options[:as], (options[:required] ? :required : :optional) ].join(' ')
|
94
|
-
html_class << ' error' if @object && @object.errors.on(method.to_s)
|
95
|
-
|
96
|
-
html_id = generate_html_id(method)
|
97
|
-
|
98
95
|
list_item_content = @@inline_order.map do |type|
|
99
96
|
send(:"inline_#{type}_for", method, options)
|
100
97
|
end.compact.join("\n")
|
101
98
|
|
102
|
-
return template.content_tag(:li, list_item_content,
|
99
|
+
return template.content_tag(:li, list_item_content, wrapper_html)
|
103
100
|
end
|
104
101
|
|
105
102
|
# Creates an input fieldset and ol tag wrapping for use around a set of inputs. It can be
|
@@ -233,21 +230,15 @@ module Formtastic #:nodoc:
|
|
233
230
|
html_options = args.extract_options!
|
234
231
|
html_options[:class] ||= "inputs"
|
235
232
|
|
236
|
-
if
|
237
|
-
|
238
|
-
inputs_for_nested_attributes(fields_for_object, args << html_options,
|
239
|
-
html_options.delete(:for_options) || {}, &block)
|
233
|
+
if html_options[:for]
|
234
|
+
inputs_for_nested_attributes(args, html_options, &block)
|
240
235
|
elsif block_given?
|
241
236
|
field_set_and_list_wrapping(html_options, &block)
|
242
237
|
else
|
243
238
|
if @object && args.empty?
|
244
|
-
# Get all belongs_to association
|
245
239
|
args = @object.class.reflections.map { |n,_| n if _.macro == :belongs_to }
|
246
|
-
|
247
|
-
# Get content columns and remove timestamps columns from it
|
248
240
|
args += @object.class.content_columns.map(&:name)
|
249
241
|
args -= %w[created_at updated_at created_on updated_on]
|
250
|
-
|
251
242
|
args.compact!
|
252
243
|
end
|
253
244
|
contents = args.map { |method| input(method.to_sym) }
|
@@ -285,9 +276,10 @@ module Formtastic #:nodoc:
|
|
285
276
|
#
|
286
277
|
# <%= form.commit_button "Go" %> => <input name="commit" type="submit" value="Go" />
|
287
278
|
#
|
288
|
-
def commit_button(value=nil, options
|
289
|
-
value
|
290
|
-
|
279
|
+
def commit_button(value=nil, options={})
|
280
|
+
value ||= save_or_create_button_text
|
281
|
+
button_html = options.delete(:button_html) || {}
|
282
|
+
template.content_tag(:li, self.submit(value, button_html), :class => "commit")
|
291
283
|
end
|
292
284
|
|
293
285
|
# A thin wrapper around #fields_for to set :builder => Formtastic::SemanticFormBuilder
|
@@ -323,7 +315,14 @@ module Formtastic #:nodoc:
|
|
323
315
|
# Dinamically add and remove nested forms for a has_many relation.
|
324
316
|
#
|
325
317
|
|
326
|
-
# Add a link to remove the associated partial
|
318
|
+
# Add a link to remove the associated partial. You should add this on the partial
|
319
|
+
#
|
320
|
+
# Example:
|
321
|
+
#
|
322
|
+
# #contacts
|
323
|
+
# = f.input :phone
|
324
|
+
# = f.remove_link
|
325
|
+
#
|
327
326
|
def remove_link(name, *args)
|
328
327
|
options = args.extract_options!
|
329
328
|
css_selector = options.delete(:selector) || ".#{@object.class.name.split("::").last.underscore}"
|
@@ -336,6 +335,12 @@ module Formtastic #:nodoc:
|
|
336
335
|
end
|
337
336
|
|
338
337
|
# Add a link to add more partials
|
338
|
+
#
|
339
|
+
# Example:
|
340
|
+
#
|
341
|
+
# - f.inputs "contacts" do
|
342
|
+
# = f.add_associated_link :contacts, :partial => "other/path"
|
343
|
+
#
|
339
344
|
def add_associated_link(name, association, opts = {})
|
340
345
|
object = @object.send(association).build
|
341
346
|
associated_name = extract_option_or_class_name(opts, :name, object)
|
@@ -345,7 +350,7 @@ module Formtastic #:nodoc:
|
|
345
350
|
partial = opts.delete(:partial) || associated_name
|
346
351
|
container = opts.delete(:expression) || "'#{opts.delete(:container) || '#'+associated_name.pluralize}'"
|
347
352
|
|
348
|
-
form =
|
353
|
+
form = render_associated_form(object, :partial => partial)
|
349
354
|
form.gsub!(/attributes_(\d+)/, '__idx__')
|
350
355
|
form.gsub!(/\[(\d+)\]/, '__idxx__')
|
351
356
|
|
@@ -363,7 +368,6 @@ module Formtastic #:nodoc:
|
|
363
368
|
opts.symbolize_keys!
|
364
369
|
(opts[:new] - associated.select(&:new_record?).length).times { associated.build } if opts[:new]
|
365
370
|
|
366
|
-
|
367
371
|
unless associated.empty?
|
368
372
|
name = extract_option_or_class_name(opts, :name, associated.first)
|
369
373
|
partial = opts[:partial] || name
|
@@ -371,7 +375,7 @@ module Formtastic #:nodoc:
|
|
371
375
|
|
372
376
|
output = associated.map do |element|
|
373
377
|
fields_for(association_name(name), element, (opts[:fields_for] || {}).merge(:name => name)) do |f|
|
374
|
-
|
378
|
+
render({:partial => "#{partial}", :locals => {local_assign_name.to_sym => element, :f => f}.merge(opts[:locals] || {})}.merge(opts[:render] || {}))
|
375
379
|
end
|
376
380
|
end
|
377
381
|
output.join
|
@@ -397,17 +401,20 @@ module Formtastic #:nodoc:
|
|
397
401
|
#
|
398
402
|
# It should raise an error if a block with arity zero is given.
|
399
403
|
#
|
400
|
-
def inputs_for_nested_attributes(
|
404
|
+
def inputs_for_nested_attributes(args, options, &block)
|
405
|
+
args << options.merge!(:parent => { :builder => self, :for => options[:for] })
|
406
|
+
|
401
407
|
fields_for_block = if block_given?
|
402
408
|
raise ArgumentError, 'You gave :for option with a block to inputs method, ' <<
|
403
409
|
'but the block does not accept any argument.' if block.arity <= 0
|
404
410
|
|
405
|
-
proc { |f| f.inputs(*
|
411
|
+
proc { |f| f.inputs(*args){ block.call(f) } }
|
406
412
|
else
|
407
|
-
proc { |f| f.inputs(*
|
413
|
+
proc { |f| f.inputs(*args) }
|
408
414
|
end
|
409
415
|
|
410
|
-
|
416
|
+
fields_for_args = [options.delete(:for), options.delete(:for_options) || {}].flatten
|
417
|
+
semantic_fields_for(*fields_for_args, &fields_for_block)
|
411
418
|
end
|
412
419
|
|
413
420
|
# Remove any Formtastic-specific options before passing the down options.
|
@@ -568,6 +575,20 @@ module Formtastic #:nodoc:
|
|
568
575
|
end
|
569
576
|
alias :boolean_select_input :select_input
|
570
577
|
|
578
|
+
# Outputs a timezone select input as Rails' time_zone_select helper. You
|
579
|
+
# can give priority zones as option.
|
580
|
+
#
|
581
|
+
# Examples:
|
582
|
+
#
|
583
|
+
# f.input :time_zone, :as => :time_zone, :priority_zones => /Australia/
|
584
|
+
#
|
585
|
+
def time_zone_input(method, options)
|
586
|
+
html_options = options.delete(:input_html) || {}
|
587
|
+
|
588
|
+
input_label(method, options.delete(:label), options.slice(:required)) +
|
589
|
+
self.time_zone_select(method, options.delete(:priority_zones), set_options(options), html_options)
|
590
|
+
end
|
591
|
+
|
571
592
|
# Outputs a fieldset containing a legend for the label text, and an ordered list (ol) of list
|
572
593
|
# items, one for each possible choice in the belongs_to association. Each li contains a
|
573
594
|
# label and a radio input.
|
@@ -748,11 +769,13 @@ module Formtastic #:nodoc:
|
|
748
769
|
def boolean_input(method, options)
|
749
770
|
html_options = options.delete(:input_html) || {}
|
750
771
|
|
751
|
-
|
752
|
-
|
772
|
+
input = self.check_box(method, set_options(options).merge(html_options),
|
773
|
+
options.delete(:checked_value) || '1', options.delete(:unchecked_value) || '0')
|
753
774
|
|
754
|
-
# required does not make sense
|
755
|
-
|
775
|
+
# Generate the label by hand because required or optional does not make sense here
|
776
|
+
label = options.delete(:label) || humanized_attribute_name(method)
|
777
|
+
|
778
|
+
self.label(method, input + label, options)
|
756
779
|
end
|
757
780
|
|
758
781
|
# Generates an input for the given method using the type supplied with :as.
|
@@ -776,16 +799,32 @@ module Formtastic #:nodoc:
|
|
776
799
|
# or as sentence. If :none is set, no error is shown.
|
777
800
|
#
|
778
801
|
def inline_errors_for(method, options) #:nodoc:
|
779
|
-
return nil unless @object && [:sentence, :list].include?(@@inline_errors)
|
802
|
+
return nil unless @object && @object.respond_to?(:errors) && [:sentence, :list].include?(@@inline_errors)
|
803
|
+
|
804
|
+
# Ruby 1.9: Strings are not Enumerable, ie no String#to_a
|
805
|
+
errors = @object.errors.on(method.to_s)
|
806
|
+
unless errors.respond_to?(:to_a)
|
807
|
+
errors = [errors]
|
808
|
+
else
|
809
|
+
errors = errors.to_a
|
810
|
+
end
|
811
|
+
|
812
|
+
# Ruby 1.9: Strings are not Enumerable, ie no String#to_a
|
813
|
+
errors = @object.errors.on(method.to_s)
|
814
|
+
unless errors.respond_to?(:to_a)
|
815
|
+
errors = [errors]
|
816
|
+
else
|
817
|
+
errors = errors.to_a
|
818
|
+
end
|
780
819
|
|
781
|
-
errors = @object.errors.on(method.to_s).to_a
|
782
820
|
send("error_#{@@inline_errors}", errors) unless errors.empty?
|
783
821
|
end
|
784
822
|
|
785
823
|
# Generates hints for the given method using the text supplied in :hint.
|
786
824
|
#
|
787
825
|
def inline_hints_for(method, options) #:nodoc:
|
788
|
-
options[:hint].blank?
|
826
|
+
return if options[:hint].blank?
|
827
|
+
template.content_tag(:p, options[:hint], :class => 'inline-hints')
|
789
828
|
end
|
790
829
|
|
791
830
|
# Creates an error sentence by calling to_sentence on the errors array.
|
@@ -809,7 +848,8 @@ module Formtastic #:nodoc:
|
|
809
848
|
# with class label.
|
810
849
|
#
|
811
850
|
def input_label(method, text, options={}, as_span=false) #:nodoc:
|
812
|
-
text
|
851
|
+
text ||= humanized_attribute_name(method)
|
852
|
+
text << required_or_optional_string(options.delete(:required))
|
813
853
|
|
814
854
|
if as_span
|
815
855
|
options[:class] ||= 'label'
|
@@ -841,23 +881,18 @@ module Formtastic #:nodoc:
|
|
841
881
|
# And it will generate a fieldset for each task with legend 'Task #1', 'Task #2',
|
842
882
|
# 'Task #3' and so on.
|
843
883
|
#
|
844
|
-
|
845
|
-
|
846
|
-
|
847
|
-
|
848
|
-
# f.nested_child_index = -1
|
849
|
-
#
|
850
|
-
def field_set_and_list_wrapping(html_options, contents = '', &block) #:nodoc:
|
851
|
-
# Generates the legend text allowing nested_child_index support for interpolation
|
852
|
-
legend_text = html_options.delete(:name).to_s
|
853
|
-
legend_text %= html_options[:parent_builder].instance_variable_get('@nested_child_index').to_i + 1
|
884
|
+
def field_set_and_list_wrapping(html_options, contents='', &block) #:nodoc:
|
885
|
+
legend = html_options.delete(:name).to_s
|
886
|
+
legend %= parent_child_index(html_options[:parent]) if html_options[:parent]
|
887
|
+
legend = template.content_tag(:legend, template.content_tag(:span, legend)) unless legend.blank?
|
854
888
|
|
855
|
-
legend = legend_text.blank? ? "" : template.content_tag(:legend, template.content_tag(:span, legend_text))
|
856
889
|
contents = template.capture(&block) if block_given?
|
857
890
|
|
891
|
+
# Ruby 1.9: String#to_s behavior changed, need to make an explicit join.
|
892
|
+
contents = contents.join if contents.respond_to?(:join)
|
858
893
|
fieldset = template.content_tag(:fieldset,
|
859
894
|
legend + template.content_tag(:ol, contents),
|
860
|
-
html_options.except(:builder, :
|
895
|
+
html_options.except(:builder, :parent)
|
861
896
|
)
|
862
897
|
|
863
898
|
template.concat(fieldset) if block_given?
|
@@ -874,7 +909,7 @@ module Formtastic #:nodoc:
|
|
874
909
|
)
|
875
910
|
end
|
876
911
|
|
877
|
-
# For methods that have a database column, take a best guess as to what the
|
912
|
+
# For methods that have a database column, take a best guess as to what the input method
|
878
913
|
# should be. In most cases, it will just return the column type (eg :string), but for special
|
879
914
|
# cases it will simplify (like the case of :integer, :float & :decimal to :numeric), or do
|
880
915
|
# something different (like :password and :select).
|
@@ -885,23 +920,22 @@ module Formtastic #:nodoc:
|
|
885
920
|
def default_input_type(method) #:nodoc:
|
886
921
|
return :string if @object.nil?
|
887
922
|
|
888
|
-
# Find the column object by attribute
|
889
923
|
column = @object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
|
890
924
|
|
891
|
-
# Associations map by default to a select
|
892
|
-
return :select if column.nil? && find_reflection(method)
|
893
|
-
|
894
925
|
if column
|
895
926
|
# handle the special cases where the column type doesn't map to an input method
|
896
|
-
return :
|
897
|
-
return :
|
898
|
-
return :
|
899
|
-
return :
|
927
|
+
return :time_zone if column.type == :string && method.to_s =~ /time_?zone/
|
928
|
+
return :select if column.type == :integer && method.to_s =~ /_id$/
|
929
|
+
return :datetime if column.type == :timestamp
|
930
|
+
return :numeric if [:integer, :float, :decimal].include?(column.type)
|
931
|
+
return :password if column.type == :string && method.to_s =~ /password/
|
932
|
+
|
900
933
|
# otherwise assume the input name will be the same as the column type (eg string_input)
|
901
934
|
return column.type
|
902
935
|
else
|
903
936
|
obj = @object.send(method) if @object.respond_to?(method)
|
904
937
|
|
938
|
+
return :select if find_reflection(method)
|
905
939
|
return :file if obj && @@file_methods.any? { |m| obj.respond_to?(m) }
|
906
940
|
return :password if method.to_s =~ /password/
|
907
941
|
return :string
|
@@ -986,7 +1020,7 @@ module Formtastic #:nodoc:
|
|
986
1020
|
# reflection object.
|
987
1021
|
#
|
988
1022
|
def find_reflection(method)
|
989
|
-
object.class.reflect_on_association(method) if object.class.respond_to?(:reflect_on_association)
|
1023
|
+
@object.class.reflect_on_association(method) if @object.class.respond_to?(:reflect_on_association)
|
990
1024
|
end
|
991
1025
|
|
992
1026
|
# Generates default_string_options by retrieving column information from
|
@@ -1020,10 +1054,34 @@ module Formtastic #:nodoc:
|
|
1020
1054
|
"#{sanitized_object_name}#{index}_#{sanitized_method_name}_#{value}"
|
1021
1055
|
end
|
1022
1056
|
|
1057
|
+
# Gets the nested_child_index value from the parent builder. In Rails 2.3
|
1058
|
+
# it always returns a fixnum. In next versions it returns a hash with each
|
1059
|
+
# association that the parent builds.
|
1060
|
+
#
|
1061
|
+
def parent_child_index(parent)
|
1062
|
+
duck = parent[:builder].instance_variable_get('@nested_child_index')
|
1063
|
+
|
1064
|
+
if duck.is_a?(Hash)
|
1065
|
+
child = parent[:for]
|
1066
|
+
child = child.first if child.respond_to?(:first)
|
1067
|
+
duck[child].to_i + 1
|
1068
|
+
else
|
1069
|
+
duck.to_i + 1
|
1070
|
+
end
|
1071
|
+
end
|
1072
|
+
|
1023
1073
|
def sanitized_object_name
|
1024
1074
|
@sanitized_object_name ||= @object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
1025
1075
|
end
|
1026
1076
|
|
1077
|
+
def humanized_attribute_name(method)
|
1078
|
+
if @object && @object.class.respond_to?(:human_attribute_name)
|
1079
|
+
@object.class.human_attribute_name(method.to_s)
|
1080
|
+
else
|
1081
|
+
method.to_s.send(@@label_str_method)
|
1082
|
+
end
|
1083
|
+
end
|
1084
|
+
|
1027
1085
|
end
|
1028
1086
|
|
1029
1087
|
|
data/spec/formtastic_spec.rb
CHANGED
@@ -28,6 +28,7 @@ describe 'Formtastic' do
|
|
28
28
|
include ActionView::Helpers::CaptureHelper
|
29
29
|
include ActiveSupport
|
30
30
|
include ActionController::PolymorphicRoutes
|
31
|
+
include ActionController
|
31
32
|
|
32
33
|
include Formtastic::SemanticFormHelper
|
33
34
|
|
@@ -561,7 +562,7 @@ describe 'Formtastic' do
|
|
561
562
|
end
|
562
563
|
|
563
564
|
it 'should call the corresponding input method' do
|
564
|
-
[:select, :radio, :date, :datetime, :time, :boolean].each do |input_style|
|
565
|
+
[:select, :time_zone, :radio, :date, :datetime, :time, :boolean].each do |input_style|
|
565
566
|
@new_post.stub!(:generic_column_name)
|
566
567
|
@new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
|
567
568
|
semantic_form_for(@new_post) do |builder|
|
@@ -647,6 +648,47 @@ describe 'Formtastic' do
|
|
647
648
|
end
|
648
649
|
|
649
650
|
end
|
651
|
+
|
652
|
+
describe ':wrapper_html option' do
|
653
|
+
|
654
|
+
describe 'when provided' do
|
655
|
+
it 'should be passed down to the li tag' do
|
656
|
+
semantic_form_for(@new_post) do |builder|
|
657
|
+
concat(builder.input(:title, :wrapper_html => {:id => :another_id}))
|
658
|
+
end
|
659
|
+
output_buffer.should have_tag("form li#another_id")
|
660
|
+
end
|
661
|
+
|
662
|
+
it 'should append given classes to li default classes' do
|
663
|
+
semantic_form_for(@new_post) do |builder|
|
664
|
+
concat(builder.input(:title, :wrapper_html => {:class => :another_class}, :required => true))
|
665
|
+
end
|
666
|
+
output_buffer.should have_tag("form li.string")
|
667
|
+
output_buffer.should have_tag("form li.required")
|
668
|
+
output_buffer.should have_tag("form li.another_class")
|
669
|
+
end
|
670
|
+
|
671
|
+
it 'should allow classes to be an array' do
|
672
|
+
semantic_form_for(@new_post) do |builder|
|
673
|
+
concat(builder.input(:title, :wrapper_html => {:class => [ :my_class, :another_class ]}))
|
674
|
+
end
|
675
|
+
output_buffer.should have_tag("form li.string")
|
676
|
+
output_buffer.should have_tag("form li.my_class")
|
677
|
+
output_buffer.should have_tag("form li.another_class")
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
681
|
+
describe 'when not provided' do
|
682
|
+
it 'should use default id and class' do
|
683
|
+
semantic_form_for(@new_post) do |builder|
|
684
|
+
concat(builder.input(:title))
|
685
|
+
end
|
686
|
+
output_buffer.should have_tag("form li#post_title_input")
|
687
|
+
output_buffer.should have_tag("form li.string")
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
end
|
650
692
|
end
|
651
693
|
|
652
694
|
describe ':as any type of input' do
|
@@ -971,6 +1013,57 @@ describe 'Formtastic' do
|
|
971
1013
|
end
|
972
1014
|
end
|
973
1015
|
|
1016
|
+
describe ":as => :time_zone" do
|
1017
|
+
before do
|
1018
|
+
@new_post.stub!(:time_zone)
|
1019
|
+
@new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string))
|
1020
|
+
|
1021
|
+
semantic_form_for(@new_post) do |builder|
|
1022
|
+
concat(builder.input(:time_zone))
|
1023
|
+
end
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
it "should have a time_zone class on the wrapper" do
|
1027
|
+
output_buffer.should have_tag('form li.time_zone')
|
1028
|
+
end
|
1029
|
+
|
1030
|
+
it 'should have a post_title_input id on the wrapper' do
|
1031
|
+
output_buffer.should have_tag('form li#post_time_zone_input')
|
1032
|
+
end
|
1033
|
+
|
1034
|
+
it 'should generate a label for the input' do
|
1035
|
+
output_buffer.should have_tag('form li label')
|
1036
|
+
output_buffer.should have_tag('form li label[@for="post_time_zone"')
|
1037
|
+
output_buffer.should have_tag('form li label', /Time zone/)
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
it "should generate a select" do
|
1041
|
+
output_buffer.should have_tag("form li select")
|
1042
|
+
output_buffer.should have_tag("form li select#post_time_zone")
|
1043
|
+
output_buffer.should have_tag("form li select[@name=\"post[time_zone]\"]")
|
1044
|
+
end
|
1045
|
+
|
1046
|
+
it 'should use input_html to style inputs' do
|
1047
|
+
semantic_form_for(@new_post) do |builder|
|
1048
|
+
concat(builder.input(:time_zone, :input_html => { :class => 'myclass' }))
|
1049
|
+
end
|
1050
|
+
output_buffer.should have_tag("form li select.myclass")
|
1051
|
+
end
|
1052
|
+
|
1053
|
+
it 'should generate input and labels even if no object is given' do
|
1054
|
+
semantic_form_for(:project, :url => 'http://test.host/') do |builder|
|
1055
|
+
concat(builder.input(:time_zone, :as => :time_zone))
|
1056
|
+
end
|
1057
|
+
|
1058
|
+
output_buffer.should have_tag('form li label')
|
1059
|
+
output_buffer.should have_tag('form li label[@for="project_time_zone"')
|
1060
|
+
output_buffer.should have_tag('form li label', /Time zone/)
|
1061
|
+
|
1062
|
+
output_buffer.should have_tag("form li select")
|
1063
|
+
output_buffer.should have_tag("form li select#project_time_zone")
|
1064
|
+
output_buffer.should have_tag("form li select[@name=\"project[time_zone]\"]")
|
1065
|
+
end
|
1066
|
+
end
|
974
1067
|
|
975
1068
|
describe ':as => :radio' do
|
976
1069
|
|
@@ -1145,7 +1238,7 @@ describe 'Formtastic' do
|
|
1145
1238
|
|
1146
1239
|
it 'should have a label inside the wrapper' do
|
1147
1240
|
output_buffer.should have_tag('form li label')
|
1148
|
-
output_buffer.should have_tag('form li label', /
|
1241
|
+
output_buffer.should have_tag('form li label', /Post ids/)
|
1149
1242
|
output_buffer.should have_tag("form li label[@for='author_post_ids']")
|
1150
1243
|
end
|
1151
1244
|
|
@@ -1187,7 +1280,7 @@ describe 'Formtastic' do
|
|
1187
1280
|
|
1188
1281
|
it 'should have a label inside the wrapper' do
|
1189
1282
|
output_buffer.should have_tag('form li label')
|
1190
|
-
output_buffer.should have_tag('form li label', /
|
1283
|
+
output_buffer.should have_tag('form li label', /Author ids/)
|
1191
1284
|
output_buffer.should have_tag("form li label[@for='post_author_ids']")
|
1192
1285
|
end
|
1193
1286
|
|
@@ -1658,22 +1751,20 @@ describe 'Formtastic' do
|
|
1658
1751
|
describe 'when :discard_input => true is set' do
|
1659
1752
|
|
1660
1753
|
it 'should use default hidden value equals to 1 when attribute returns nil' do
|
1661
|
-
pending
|
1662
1754
|
semantic_form_for(@new_post) do |builder|
|
1663
1755
|
concat(builder.input(:publish_at, :as => :datetime, :discard_day => true))
|
1664
1756
|
end
|
1665
1757
|
|
1666
|
-
output_buffer.should have_tag("form li fieldset
|
1758
|
+
output_buffer.should have_tag("form li fieldset input[@type='hidden'][@value='1']")
|
1667
1759
|
end
|
1668
1760
|
|
1669
1761
|
it 'should use default attribute value when it is not nil' do
|
1670
|
-
pending
|
1671
1762
|
@new_post.stub!(:publish_at).and_return(Date.new(2007,12,27))
|
1672
1763
|
semantic_form_for(@new_post) do |builder|
|
1673
1764
|
concat(builder.input(:publish_at, :as => :datetime, :discard_day => true))
|
1674
1765
|
end
|
1675
1766
|
|
1676
|
-
output_buffer.should have_tag("form li fieldset
|
1767
|
+
output_buffer.should have_tag("form li fieldset input[@type='hidden'][@value='27']")
|
1677
1768
|
end
|
1678
1769
|
end
|
1679
1770
|
|
@@ -1787,15 +1878,13 @@ describe 'Formtastic' do
|
|
1787
1878
|
end
|
1788
1879
|
|
1789
1880
|
it 'should have five labels for hour and minute' do
|
1790
|
-
|
1791
|
-
output_buffer.should have_tag('form li.time fieldset
|
1792
|
-
output_buffer.should have_tag('form li.time fieldset
|
1793
|
-
output_buffer.should have_tag('form li.time fieldset ol li label', /minute/i)
|
1881
|
+
output_buffer.should have_tag('form li.time fieldset li label', :count => 2)
|
1882
|
+
output_buffer.should have_tag('form li.time fieldset li label', /hour/i)
|
1883
|
+
output_buffer.should have_tag('form li.time fieldset li label', /minute/i)
|
1794
1884
|
end
|
1795
1885
|
|
1796
1886
|
it 'should have five selects for hour and minute' do
|
1797
|
-
|
1798
|
-
output_buffer.should have_tag('form li.time fieldset ol li select', :count => 2)
|
1887
|
+
output_buffer.should have_tag('form li.time fieldset li select', :count => 2)
|
1799
1888
|
end
|
1800
1889
|
end
|
1801
1890
|
|
@@ -1970,7 +2059,7 @@ describe 'Formtastic' do
|
|
1970
2059
|
|
1971
2060
|
it 'should send parent_builder as an option to allow child index interpolation' do
|
1972
2061
|
semantic_form_for(@new_post) do |builder|
|
1973
|
-
builder.
|
2062
|
+
builder.instance_variable_set('@nested_child_index', 0)
|
1974
2063
|
builder.inputs :for => [:author, @bob], :name => 'Author #%i' do |bob_builder|
|
1975
2064
|
concat('input')
|
1976
2065
|
end
|
@@ -1978,6 +2067,17 @@ describe 'Formtastic' do
|
|
1978
2067
|
|
1979
2068
|
output_buffer.should have_tag('fieldset legend', 'Author #1')
|
1980
2069
|
end
|
2070
|
+
|
2071
|
+
it 'should also provide child index interpolation when nested child index is a hash' do
|
2072
|
+
semantic_form_for(@new_post) do |builder|
|
2073
|
+
builder.instance_variable_set('@nested_child_index', :author => 10)
|
2074
|
+
builder.inputs :for => [:author, @bob], :name => 'Author #%i' do |bob_builder|
|
2075
|
+
concat('input')
|
2076
|
+
end
|
2077
|
+
end
|
2078
|
+
|
2079
|
+
output_buffer.should have_tag('fieldset legend', 'Author #11')
|
2080
|
+
end
|
1981
2081
|
end
|
1982
2082
|
|
1983
2083
|
describe 'when a :name option is provided' do
|
@@ -2314,6 +2414,16 @@ describe 'Formtastic' do
|
|
2314
2414
|
output_buffer.should have_tag('li.commit input[@name="commit"]')
|
2315
2415
|
end
|
2316
2416
|
|
2417
|
+
it 'should pass options given in :button_html to the button' do
|
2418
|
+
@new_post.stub!(:new_record?).and_return(false)
|
2419
|
+
semantic_form_for(@new_post) do |builder|
|
2420
|
+
concat(builder.commit_button('text', :button_html => {:class => 'my_class', :id => 'my_id'}))
|
2421
|
+
end
|
2422
|
+
|
2423
|
+
output_buffer.should have_tag('li.commit input#my_id')
|
2424
|
+
output_buffer.should have_tag('li.commit input.my_class')
|
2425
|
+
end
|
2426
|
+
|
2317
2427
|
end
|
2318
2428
|
|
2319
2429
|
describe 'when used on an existing record' do
|
@@ -2408,18 +2518,20 @@ describe 'Formtastic' do
|
|
2408
2518
|
describe "Nested Js Helpers" do
|
2409
2519
|
|
2410
2520
|
it "should have a add method" do
|
2411
|
-
|
2521
|
+
@new_post.stub!(:authors).and_return(mock("Authors", :build => mock('Author')))
|
2412
2522
|
|
2413
2523
|
semantic_form_for(@new_post) do |builder|
|
2414
|
-
builder.
|
2524
|
+
builder.should_receive(:render).with(hash_including(:partial => "mock")).and_return('"attributes___idx___test__idxx__"')
|
2525
|
+
builder.inputs :name => "Contacts", :id => "contacts" do
|
2415
2526
|
concat(builder.add_associated_link(:name, :authors))
|
2416
2527
|
end
|
2417
2528
|
end
|
2418
2529
|
|
2419
|
-
output_buffer.should
|
2530
|
+
output_buffer.should have_tag('form fieldset a[@href=#]')
|
2531
|
+
output_buffer.should match(/\\"attributes___idx___test__idxx__\\"".replace/)
|
2420
2532
|
end
|
2421
2533
|
|
2422
|
-
it "should have a
|
2534
|
+
it "should have a remove method" do
|
2423
2535
|
semantic_form_for(@new_post) do |builder|
|
2424
2536
|
builder.semantic_fields_for(:links) do |link_builder|
|
2425
2537
|
concat(link_builder.remove_link("Remove"))
|
@@ -2430,15 +2542,16 @@ describe 'Formtastic' do
|
|
2430
2542
|
end
|
2431
2543
|
|
2432
2544
|
it "should have a show method" do
|
2433
|
-
|
2434
|
-
|
2545
|
+
@new_post.stub!(:authors).and_return([mock("Author")])
|
2546
|
+
|
2435
2547
|
semantic_form_for(@new_post) do |builder|
|
2548
|
+
builder.should_receive(:render).with(hash_including(:partial => "mock")).and_return("fields")
|
2436
2549
|
builder.inputs :name => "Contacts" do
|
2437
2550
|
concat(builder.render_associated_form @new_post.authors)
|
2438
2551
|
end
|
2439
2552
|
end
|
2440
2553
|
|
2441
|
-
output_buffer.should eql("<form action=\"/posts\" class=\"formtastic post\" id=\"new_post\" method=\"post\"><fieldset class=\"inputs\"><legend><span>Contacts</span></legend><ol
|
2554
|
+
output_buffer.should eql("<form action=\"/posts\" class=\"formtastic post\" id=\"new_post\" method=\"post\"><fieldset class=\"inputs\"><legend><span>Contacts</span></legend><ol>fields</ol></fieldset></form>")
|
2442
2555
|
end
|
2443
2556
|
|
2444
2557
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nofxx-formtastic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin French
|
@@ -9,7 +9,7 @@ autorequire: formtastic
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
12
|
+
date: 2009-04-18 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -25,6 +25,9 @@ files:
|
|
25
25
|
- MIT-LICENSE
|
26
26
|
- README.textile
|
27
27
|
- Rakefile
|
28
|
+
- generators/formtastic_stylesheets/formtastic_stylesheets_generator.rb
|
29
|
+
- generators/formtastic_stylesheets/templates/formtastic.css
|
30
|
+
- generators/formtastic_stylesheets/templates/formtastic_changes.css
|
28
31
|
- lib/formtastic.rb
|
29
32
|
- lib/justin_french/formtastic.rb
|
30
33
|
- lib/locale/en.yml
|