justinfrench-formtastic 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -14,7 +14,7 @@ One day, I finally had enough, so I opened up my text editor, and wrote a DSL fo
14
14
  <%= form.input :body %>
15
15
  <%= form.input :section %>
16
16
  <%= form.input :publication_state, :as => :radio %>
17
- <%= form.input :author %>
17
+ <%= form.input :category %>
18
18
  <%= form.input :allow_comments, :label => "Allow commenting on this article" %>
19
19
  <% end %>
20
20
 
@@ -25,6 +25,11 @@ One day, I finally had enough, so I opened up my text editor, and wrote a DSL fo
25
25
  <%= form.input :url_title, :required => false %>
26
26
  <% end %>
27
27
 
28
+ <% form.inputs :for => :author do |author_form| %>
29
+ <%= form.input :first_name %>
30
+ <%= form.input :last_name %>
31
+ <% end %>
32
+
28
33
  <% form.buttons do %>
29
34
  <%= form.commit_button %>
30
35
  <% end %>
@@ -151,13 +156,26 @@ If you want to customize the label text, or render some hint text below the fiel
151
156
  <% end %>
152
157
  </pre>
153
158
 
154
- Nested forms (Rails 2.3) are also supported:
159
+ Nested forms (Rails 2.3) are also supported. You can do it in the Rails way:
155
160
 
156
161
  <pre>
157
162
  <% semantic_form_for @post do |post| %>
163
+ <%= post.inputs %>
164
+
158
165
  <%= post.semantic_fields_for :author do |author| %>
159
- <%= author.inputs %>
166
+ <%= author.inputs :first_name, :last_name %>
160
167
  <%= end %>
168
+
169
+ <%= post.buttons %>
170
+ <% end %>
171
+ </pre>
172
+
173
+ Or in the formtastic way:
174
+
175
+ <pre>
176
+ <% semantic_form_for @post do |post| %>
177
+ <%= post.inputs %>
178
+ <%= post.inputs :first_name, :last_name, :for => :author %>
161
179
  <%= post.buttons %>
162
180
  <% end %>
163
181
  </pre>
data/lib/formtastic.rb CHANGED
@@ -177,18 +177,60 @@ module Formtastic #:nodoc:
177
177
  # </ol>
178
178
  # </fieldset>
179
179
  # </form>
180
+ #
181
+ # === Nested attributes
182
+ #
183
+ # As in Rails, you can use semantic_fields_for to nest attributes:
184
+ #
185
+ # <% semantic_form_for @post do |form| %>
186
+ # <%= form.inputs :title, :body %>
187
+ #
188
+ # <% form.semantic_fields_for :author, @bob do |author_form| %>
189
+ # <% author_form.inputs do %>
190
+ # <%= author_form.input :first_name, :required => false %>
191
+ # <%= author_form.input :last_name %>
192
+ # <% end %>
193
+ # <% end %>
194
+ # <% end %>
195
+ #
196
+ # But this does not look formtastic! This is equivalent:
197
+ #
198
+ # <% semantic_form_for @post do |form| %>
199
+ # <%= form.inputs :title, :body %>
200
+ # <% form.inputs :for => [ :author, @bob ] do |author_form| %>
201
+ # <%= author_form.input :first_name, :required => false %>
202
+ # <%= author_form.input :last_name %>
203
+ # <% end %>
204
+ # <% end %>
205
+ #
206
+ # And if you don't need to give options to your input call, you could do it
207
+ # in just one line:
208
+ #
209
+ # <% semantic_form_for @post do |form| %>
210
+ # <%= form.inputs :title, :body %>
211
+ # <%= form.inputs :first_name, :last_name, :for => @bob %>
212
+ # <% end %>
213
+ #
214
+ # Just remember that calling inputs generates a new fieldset to wrap your
215
+ # inputs. If you have two separate models, but, semantically, on the page
216
+ # they are part of the same fieldset, you should use semantic_fields_for
217
+ # instead (just as you would do with Rails' form builder).
218
+ #
180
219
  def inputs(*args, &block)
181
220
  html_options = args.extract_options!
182
221
  html_options[:class] ||= "inputs"
183
222
 
184
- if block_given?
223
+ if fields_for_object = html_options.delete(:for)
224
+ inputs_for_nested_attributes(fields_for_object, args << html_options, &block)
225
+ elsif block_given?
185
226
  field_set_and_list_wrapping(html_options, &block)
186
227
  else
187
228
  if args.empty?
188
- args = @object.class.reflections.map { |n,_| n }
229
+ args = @object.class.reflections.map { |n,_| n }
189
230
  args += @object.class.content_columns.map(&:name)
190
231
  end
191
232
  contents = args.map { |method| input(method.to_sym) }
233
+
192
234
  field_set_and_list_wrapping(html_options, contents)
193
235
  end
194
236
  end
@@ -253,6 +295,22 @@ module Formtastic #:nodoc:
253
295
 
254
296
  protected
255
297
 
298
+ # Deals with :for option when it's supplied to inputs methods.
299
+ # It should raise an error if a block with arity zero is given.
300
+ #
301
+ def inputs_for_nested_attributes(fields_for_object, inputs_args, &block)
302
+ fields_for_block = if block_given?
303
+ raise ArgumentError, 'You gave :for option with a block to inputs method, ' <<
304
+ 'but the block does not accept any argument.' if block.arity <= 0
305
+
306
+ proc { |f| f.inputs(*inputs_args){ block.call(f) } }
307
+ else
308
+ proc { |f| f.inputs(*inputs_args) }
309
+ end
310
+
311
+ semantic_fields_for(*Array(fields_for_object), &fields_for_block)
312
+ end
313
+
256
314
  # Ensure :object => @object is set before sending the options down to the Rails layer.
257
315
  # Also remove any Formtastic-specific options
258
316
  def set_options(options)
@@ -650,12 +708,12 @@ module Formtastic #:nodoc:
650
708
  # <li class="boolean_radio required" id="post_public_input">
651
709
  # <fieldset><legend><span>make this sucker public?<abbr title="required">*</abbr></span></legend>
652
710
  # <ol>
653
- # <li>
711
+ # <li class="true">
654
712
  # <label for="post_public_true">
655
713
  # <input id="post_public_true" name="post[public]" type="radio" value="true" /> Yeah!
656
714
  # </label>
657
715
  # </li>
658
- # <li>
716
+ # <li class="false">
659
717
  # <label for="post_public_false">
660
718
  # <input id="post_public_false" name="post[public]" type="radio" checked="checked" /> Nah!
661
719
  # </label>
@@ -663,6 +721,7 @@ module Formtastic #:nodoc:
663
721
  # </ol>
664
722
  # </fieldset>
665
723
  # </li>
724
+ #
666
725
  def boolean_radio_input(method, options)
667
726
  options[:true] ||= I18n.t('yes', :default => 'Yes', :scope => [:formtastic]).send(@@label_str_method)
668
727
  options[:false] ||= I18n.t('no', :default => 'No', :scope => [:formtastic]).send(@@label_str_method)
@@ -2180,20 +2180,50 @@ describe 'Formtastic' do
2180
2180
  end
2181
2181
  end
2182
2182
  end
2183
+
2183
2184
  it 'should render a fieldset inside the form, with a class of "inputs"' do
2184
2185
  output_buffer.should have_tag("form fieldset.inputs")
2185
2186
  end
2187
+
2186
2188
  it 'should render an ol inside the fieldset' do
2187
2189
  output_buffer.should have_tag("form fieldset.inputs ol")
2188
2190
  end
2191
+
2189
2192
  it 'should render the contents of the block inside the ol' do
2190
2193
  output_buffer.should have_tag("form fieldset.inputs ol", /hello/)
2191
2194
  end
2195
+
2192
2196
  it 'should not render a legend inside the fieldset' do
2193
2197
  output_buffer.should_not have_tag("form fieldset.inputs legend")
2194
2198
  end
2195
2199
  end
2196
2200
 
2201
+ describe 'when a :for option is provided' do
2202
+ it 'should render nested inputs' do
2203
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
2204
+
2205
+ semantic_form_for(@new_post) do |builder|
2206
+ builder.inputs :for => [:author, @bob] do |bob_builder|
2207
+ concat(bob_builder.input :login)
2208
+ end
2209
+ end
2210
+
2211
+ output_buffer.should have_tag("form fieldset.inputs #post_author_login")
2212
+ output_buffer.should_not have_tag("form fieldset.inputs #author_login")
2213
+ end
2214
+
2215
+ it 'should raise an error if :for and block with no argument is given' do
2216
+ semantic_form_for(@new_post) do |builder|
2217
+ proc {
2218
+ builder.inputs(:for => [:author, @bob]) do
2219
+ #
2220
+ end
2221
+ }.should raise_error(ArgumentError, 'You gave :for option with a block to inputs method, ' <<
2222
+ 'but the block does not accept any argument.')
2223
+ end
2224
+ end
2225
+ end
2226
+
2197
2227
  describe 'when a :name option is provided' do
2198
2228
  before do
2199
2229
  @legend_text = "Advanced options"
@@ -2203,6 +2233,7 @@ describe 'Formtastic' do
2203
2233
  end
2204
2234
  end
2205
2235
  end
2236
+
2206
2237
  it 'should render a fieldset inside the form' do
2207
2238
  output_buffer.should have_tag("form fieldset legend", /#{@legend_text}/)
2208
2239
  end
@@ -2218,6 +2249,7 @@ describe 'Formtastic' do
2218
2249
  end
2219
2250
  end
2220
2251
  end
2252
+
2221
2253
  it 'should pass the options into the fieldset tag as attributes' do
2222
2254
  output_buffer.should have_tag("form fieldset##{@id_option}")
2223
2255
  output_buffer.should have_tag("form fieldset.#{@class_option}")
@@ -2243,7 +2275,6 @@ describe 'Formtastic' do
2243
2275
  end
2244
2276
 
2245
2277
  describe 'with no args' do
2246
-
2247
2278
  before do
2248
2279
  semantic_form_for(@new_post) do |builder|
2249
2280
  concat(builder.inputs)
@@ -2281,11 +2312,9 @@ describe 'Formtastic' do
2281
2312
  it 'should render a select list item for author_id' do
2282
2313
  output_buffer.should have_tag('form > fieldset.inputs > ol > li.select')
2283
2314
  end
2284
-
2285
2315
  end
2286
2316
 
2287
2317
  describe 'with column names as args' do
2288
-
2289
2318
  before do
2290
2319
  semantic_form_for(@new_post) do |builder|
2291
2320
  concat(builder.inputs(:title, :body))
@@ -2297,11 +2326,22 @@ describe 'Formtastic' do
2297
2326
  output_buffer.should have_tag('form > fieldset.inputs > ol > li.string')
2298
2327
  output_buffer.should have_tag('form > fieldset.inputs > ol > li.text')
2299
2328
  end
2329
+ end
2300
2330
 
2331
+ describe 'when a :for option is provided' do
2332
+ it 'should render nested inputs' do
2333
+ @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255))
2334
+
2335
+ semantic_form_for(@new_post) do |builder|
2336
+ concat(builder.inputs :login, :for => @bob)
2337
+ end
2338
+
2339
+ output_buffer.should have_tag("form fieldset.inputs #post_author_login")
2340
+ output_buffer.should_not have_tag("form fieldset.inputs #author_login")
2341
+ end
2301
2342
  end
2302
2343
 
2303
2344
  describe 'with column names and an options hash as args' do
2304
-
2305
2345
  before do
2306
2346
  semantic_form_for(@new_post) do |builder|
2307
2347
  concat(builder.inputs(:title, :body, :name => "Legendary Legend Text", :id => "my-id"))
@@ -2319,7 +2359,6 @@ describe 'Formtastic' do
2319
2359
  it 'should use the special :name option as a text for the legend tag' do
2320
2360
  output_buffer.should have_tag('form > fieldset#my-id.inputs > legend', /Legendary Legend Text/)
2321
2361
  end
2322
-
2323
2362
  end
2324
2363
 
2325
2364
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: justinfrench-formtastic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
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-03-09 00:00:00 -07:00
12
+ date: 2009-03-17 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15