merb_helpers 0.4.0 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,596 @@
1
+ module Merb #:nodoc:
2
+
3
+ # Merb helpers include several helpers used for simplifying view creation.
4
+ # The available helpers currently include form tag helpers for both resource based and generic HTML form tag creation
5
+ module Helpers
6
+ # Provides a number of methods for creating form tags which may be used either with or without the presence of ORM specific models.
7
+ # There are two types of form helpers: those that specifically work with model attributes and those that don't.
8
+ # This helper deals with both model attributes and generic form tags. Model attributes generally end in "_control" such as +text_control+,
9
+ # and generic tags end with "_field", such as +text_field+
10
+ #
11
+ # The core method of this helper, +form_for+, gives you the ability to create a form for a resource.
12
+ # For example, let's say that you have a model <tt>Person</tt> and want to create a new instance of it:
13
+ #
14
+ # <% form_for :person, :action => url(:people) do %>
15
+ # <%= text_control :first_name, :label => 'First Name' %>
16
+ # <%= text_control :last_name, :label => 'Last Name' %>
17
+ # <%= submit_button 'Create' %>
18
+ # <% end %>
19
+ #
20
+ # The HTML generated for this would be:
21
+ #
22
+ # <form action="/people/create" method="post">
23
+ # <label for="person_first_name">First Name</label>
24
+ # <input id="person_first_name" name="person[first_name]" size="30" type="text" />
25
+ # <label for="person_last_name">Last Name</label>
26
+ # <input id="person_last_name" name="person[last_name]" size="30" type="text" />
27
+ # <button type="submit">Create</button>
28
+ # </form>
29
+ #
30
+ # You may also create a normal form using form_tag
31
+ # <% form_tag({url(:controller => "foo", :action => "bar", :id => 1)} do %>
32
+ # <%= text_field :name => 'first_name', :label => 'First Name' %>
33
+ # <%= submit_button 'Create' %>
34
+ # <% end %>
35
+ #
36
+ # The HTML generated for this would be:
37
+ #
38
+ # <form action="/foo/bar/1" method="post">
39
+ # <label for="first_name">First Name</label><input id="first_name" name="first_name" size="30" type="text" />
40
+ # <button type="submit">Create</button>
41
+ # </form>
42
+ module Form
43
+
44
+ # Provides a HTML formatted display of resource errors in an unordered list with a h2 form submission error
45
+ # ==== Options
46
+ # +html_class+:: Set for custom error div class default is <tt>submittal_failed<tt>
47
+ #
48
+ # ==== Example
49
+ # <%= error_messages_for :person %>
50
+ def error_messages_for(obj, error_li = nil, html_class='submittal_failed')
51
+ return "" if !obj.respond_to?(:errors) || obj.errors.empty?
52
+ header_message = block_given? ? yield(obj.errors) : "<h2>Form submittal failed because of #{obj.errors.size} #{obj.errors.size == 1 ? 'problem' : 'problems'}</h2>"
53
+ ret = %Q{
54
+ <div class='#{html_class}'>
55
+ #{header_message}
56
+ <ul>
57
+ }
58
+ obj.errors.each {|err| ret << (error_li ? error_li.call(err) : "<li>#{err.join(" ")}</li>") }
59
+ ret << %Q{
60
+ </ul>
61
+ </div>
62
+ }
63
+ end
64
+
65
+ def obj_from_ivar_or_sym(obj) #:nodoc:
66
+ obj.is_a?(Symbol) ? instance_variable_get("@#{obj}") : obj
67
+ end
68
+
69
+ # Creates a generic HTML tag
70
+ def tag(tag_name, contents, attrs = {}) #:nodoc:
71
+ open_tag(tag_name, attrs) + contents.to_s + "</#{tag_name}>"
72
+ end
73
+
74
+ # Generates a form tag, which accepts a block that is not directly based on resource attributes
75
+ #
76
+ # <% form_tag({url(:controller => "foo", :action => "bar", :id => 1)} do %>
77
+ # <%= text_field :name => 'first_name', :label => 'First Name' %>
78
+ # <%= submit_button 'Create' %>
79
+ # <% end %>
80
+ #
81
+ # The HTML generated for this would be:
82
+ #
83
+ # <form action="/foo/bar/1" method="post">
84
+ # <label for="first_name">First Name</label><input id="first_name" name="first_name" size="30" type="text" />
85
+ # <input name="commit" type="submit" value="Create" />
86
+ # </form>
87
+ def form_tag(attrs = {}, &block)
88
+ set_multipart_attribute!(attrs)
89
+ fake_form_method = set_form_method(attrs)
90
+ concat(open_tag("form", attrs), block.binding)
91
+ concat(generate_fake_form_method(fake_form_method), block.binding) if fake_form_method
92
+ concat(capture(&block), block.binding)
93
+ concat("</form>", block.binding)
94
+ end
95
+
96
+ # Generates a resource specific form tag which accepts a block, this also provides automatic resource routing.
97
+ # <% form_for :person, :action => url(:people) do %>
98
+ # <%= text_control :first_name, :label => 'First Name' %>
99
+ # <%= text_control :last_name, :label => 'Last Name' %>
100
+ # <%= submit_button 'Create' %>
101
+ # <% end %>
102
+ #
103
+ # The HTML generated for this would be:
104
+ #
105
+ # <form action="/people/create" method="post">
106
+ # <label for="person[first_name]">First Name</label><input id="person_first_name" name="person[first_name]" size="30" type="text" />
107
+ # <label for="person[last_name]">Last Name</label><input id="person_last_name" name="person[last_name]" size="30" type="text" />
108
+ # <input name="commit" type="submit" value="Create" />
109
+ # </form>
110
+ def form_for(obj, attrs={}, &block)
111
+ set_multipart_attribute!(attrs)
112
+ obj = obj_from_ivar_or_sym(obj)
113
+ fake_form_method = set_form_method(attrs, obj)
114
+ concat(open_tag("form", attrs), block.binding)
115
+ concat(generate_fake_form_method(fake_form_method), block.binding) if fake_form_method
116
+ fields_for(obj, attrs, &block)
117
+ concat("</form>", block.binding)
118
+ end
119
+
120
+ # Creates a scope around a specific resource object like form_for, but doesnt create the form tags themselves.
121
+ # This makes fields_for suitable for specifying additional resource objects in the same form.
122
+ #
123
+ # ==== Examples
124
+ # <% form_for :person, :action => url(:people) do %>
125
+ # <%= text_control :first_name, :label => 'First Name' %>
126
+ # <%= text_control :last_name, :label => 'Last Name' %>
127
+ # <% fields_for :permission do %>
128
+ # <%= checkbox_control :is_admin, :label => 'Administrator' %>
129
+ # <% end %>
130
+ # <%= submit_button 'Create' %>
131
+ # <% end %>
132
+ def fields_for(obj, attrs=nil, &block)
133
+ @_obj ||= nil
134
+ @_block ||= nil
135
+ @_object_name ||= nil
136
+ obj = obj_from_ivar_or_sym(obj)
137
+ old_obj, @_obj = @_obj, obj
138
+ old_block, @_block = @_block, block
139
+ old_object_name, @_object_name = @_object_name, "#{@_obj.class}".snake_case
140
+
141
+ concat(capture(&block), block.binding)
142
+
143
+ @_obj, @_block, @_object_name = old_obj, old_block, old_object_name
144
+ end
145
+
146
+ def control_name(col) #:nodoc:
147
+ "#{@_object_name}[#{col}]"
148
+ end
149
+
150
+ def control_id(col) #:nodoc:
151
+ "#{@_object_name}_#{col}"
152
+ end
153
+
154
+ def control_value(col) #:nodoc:
155
+ @_obj.send(col)
156
+ end
157
+
158
+ def control_name_value(col, attrs) #:nodoc:
159
+ {:name => control_name(col), :value => control_value(col)}.merge(attrs)
160
+ end
161
+
162
+ # Provides a HTML text input tag based on a resource attribute.
163
+ #
164
+ # ==== Example
165
+ # <% form_for :person, :action => url(:people) do %>
166
+ # <%= text_control :first_name, :label => 'First Name' %>
167
+ # <% end %>
168
+ def text_control(col, attrs = {})
169
+ errorify_field(attrs, col)
170
+ attrs.merge!(:id => control_id(col))
171
+ text_field(control_name_value(col, attrs))
172
+ end
173
+
174
+ # Provides a generic HTML text input tag.
175
+ # Provides a HTML text input tag based on a resource attribute.
176
+ #
177
+ # ==== Example
178
+ # <%= text_field :fav_color, :label => 'Your Favorite Color' %>
179
+ # # => <label for="fav_color">Your Favorite Color</label><input type="text" name="fav_color" id="fav_color"/>
180
+ def text_field(attrs = {})
181
+ attrs.merge!(:type => "text")
182
+ attrs.add_html_class!("text")
183
+ optional_label(attrs) { self_closing_tag("input", attrs) }
184
+ end
185
+
186
+ # Provides a HTML password input based on a resource attribute.
187
+ # This is generally used within a resource block such as +form_for+.
188
+ #
189
+ # ==== Example
190
+ # <%= password_control :password, :label => 'New Password' %>
191
+ def password_control(col, attrs = {})
192
+ attrs.merge!(:name => control_name(col), :id => control_id(col))
193
+ errorify_field(attrs, col)
194
+ password_field(control_name_value(col, attrs))
195
+ end
196
+
197
+ # Provides a generic HTML password input tag.
198
+ #
199
+ # ==== Example
200
+ # <%= password_field :password, :label => "Password" %>
201
+ # # => <label for="password">Password</label><input type="password" name="password" id="password"/>
202
+ def password_field(attrs = {})
203
+ attrs.delete(:value)
204
+ attrs.merge!(:type => 'password')
205
+ attrs.add_html_class!("password")
206
+ optional_label(attrs) { self_closing_tag("input", attrs) }
207
+ end
208
+
209
+ # translate column values from the db to boolean
210
+ # nil, false, 0 and '0' are false. All others are true
211
+ def col_val_to_bool(val) #:nodoc:
212
+ !(val == "0" || val == 0 || !val)
213
+ end
214
+ private :col_val_to_bool
215
+
216
+ # Provides a HTML checkbox input based on a resource attribute.
217
+ # This is generally used within a resource block such as +form_for+.
218
+ #
219
+ # ==== Example
220
+ # <%= checkbox_control :is_activated, :label => "Activated?" %>
221
+ def checkbox_control(col, attrs = {}, hidden_attrs={})
222
+ errorify_field(attrs, col)
223
+ attrs.merge!(:checked => "checked") if col_val_to_bool(@_obj.send(col))
224
+ attrs.merge!(:id => control_id(col))
225
+ checkbox_field(control_name_value(col, attrs), hidden_attrs)
226
+ end
227
+
228
+ # Provides a generic HTML checkbox input tag.
229
+ # There are two ways this tag can be generated, based on the
230
+ # option :boolean. If not set to true, a "magic" input is generated.
231
+ # Otherwise, an input is created that can be easily used for passing
232
+ # an array of values to the application.
233
+ #
234
+ # ==== Example
235
+ # <% checkbox_field :name => "is_activated", :value => "1" %>
236
+ #
237
+ # <% checkbox_field :name => "choices[]", :boolean => false, :value => "dog" %>
238
+ # <% checkbox_field :name => "choices[]", :boolean => false, :value => "cat" %>
239
+ # <% checkbox_field :name => "choices[]", :boolean => false, :value => "weasle" %>
240
+ def checkbox_field(attrs = {}, hidden_attrs={})
241
+ boolbox = true
242
+ boolbox = false if ( attrs.has_key?(:boolean) and !attrs[:boolean] )
243
+ attrs.delete(:boolean)
244
+
245
+ if( boolbox )
246
+ on = attrs.delete(:on) || 1
247
+ off = attrs.delete(:off) || 0
248
+ attrs[:value] = on if ( (v = attrs[:value]).nil? || v != "" )
249
+ else
250
+ # HTML-escape the value attribute
251
+ attrs[:value] = escape_xml( attrs[:value] )
252
+ end
253
+
254
+ attrs.merge!(:type => :checkbox)
255
+ attrs.add_html_class!("checkbox")
256
+ (boolbox ? hidden_field({:name => attrs[:name], :value => off}.merge(hidden_attrs)) : '') + optional_label(attrs){self_closing_tag("input", attrs)}
257
+ end
258
+
259
+ # Returns a hidden input tag tailored for accessing a specified attribute (identified by +col+) on an object
260
+ # resource within a +form_for+ resource block. Additional options on the input tag can be passed as a
261
+ # hash with +attrs+. These options will be tagged onto the HTML as an HTML element attribute as in the example
262
+ # shown.
263
+ #
264
+ # ==== Example
265
+ # <%= hidden_control :identifier %>
266
+ # # => <input id="person_identifier" name="person[identifier]" type="hidden" value="#{@person.identifier}" />
267
+ def hidden_control(col, attrs = {})
268
+ attrs.delete(:label)
269
+ errorify_field(attrs, col)
270
+ attrs[:class] ||= "hidden"
271
+ hidden_field(control_name_value(col, attrs))
272
+ end
273
+
274
+ # Provides a generic HTML hidden input field.
275
+ #
276
+ # ==== Example
277
+ # <%= hidden_field :name => "secret", :value => "some secret value" %>
278
+ def hidden_field(attrs = {})
279
+ attrs.delete(:label)
280
+ attrs.merge!(:type => :hidden)
281
+ attrs.add_html_class!("hidden")
282
+ self_closing_tag("input", attrs)
283
+ end
284
+
285
+ # Provides a radio group based on a resource attribute.
286
+ # This is generally used within a resource block such as +form_for+.
287
+ #
288
+ # ==== Examples
289
+ # <%# the labels are the options %>
290
+ # <%= radio_group_control :my_choice, [5,6,7] %>
291
+ #
292
+ # <%# custom labels %>
293
+ # <%= radio_group_control :my_choice, [{:value => 5, :label => "five"}] %>
294
+ def radio_group_control(col, options = [], attrs = {})
295
+ errorify_field(attrs, col)
296
+ val = @_obj.send(col)
297
+ ret = ""
298
+ options.each do |opt|
299
+ value, label = opt.is_a?(Hash) ? [opt[:value], opt[:label]] : [opt, opt]
300
+ hash = {:name => "#{@_object_name}[#{col}]", :value => value, :label => label}
301
+ hash.merge!(:selected => "selected") if val.to_s == value.to_s
302
+ ret << radio_field(hash)
303
+ end
304
+ ret
305
+ end
306
+
307
+ # Provides a generic HTML radio input tag.
308
+ # Normally, you would use multipe +radio_field+.
309
+ #
310
+ # ==== Example
311
+ # <%= radio_field :name => "radio_options", :value => "1", :label => "One" %>
312
+ # <%= radio_field :name => "radio_options", :value => "2", :label => "Two" %>
313
+ def radio_field(attrs = {})
314
+ attrs.merge!(:type => "radio")
315
+ attrs.add_html_class!("radio")
316
+ optional_label(attrs){self_closing_tag("input", attrs)}
317
+ end
318
+
319
+ # Provides a HTML textarea based on a resource attribute
320
+ # This is generally used within a resource block such as +form_for+
321
+ #
322
+ # ==== Example
323
+ # <% text_area_control :comments, :label => "Comments"
324
+ def text_area_control(col, attrs = {})
325
+ attrs ||= {}
326
+ errorify_field(attrs, col)
327
+ text_area_field(control_value(col), attrs.merge(:name => control_name(col)))
328
+ end
329
+
330
+ # Provides a generic HTML textarea tag.
331
+ #
332
+ # ==== Example
333
+ # <% text_area_field "my comments", :name => "comments", :label => "Comments" %>
334
+ def text_area_field(val, attrs = {})
335
+ val ||=""
336
+ optional_label(attrs) do
337
+ open_tag("textarea", attrs) +
338
+ val +
339
+ "</textarea>"
340
+ end
341
+ end
342
+
343
+ # Provides a generic HTML submit button.
344
+ #
345
+ # ==== Example
346
+ # <% submit_button "Process" %>
347
+ def submit_button(contents, attrs = {})
348
+ contents ||= "Submit"
349
+ attrs.merge!(:type => "submit")
350
+ tag("button", contents, attrs)
351
+ end
352
+
353
+ # Provides a generic HTML label.
354
+ #
355
+ # ==== Example
356
+ # <% label "Name", "", :for => "name" %>
357
+ # # => <label for="name">Name</label>
358
+ def label(name, contents = "", attrs = {})
359
+ tag("label", name.to_s + contents, attrs)
360
+ end
361
+
362
+ # Provides a generic HTML select.
363
+ #
364
+ # ==== Options
365
+ # +prompt+:: Adds an additional option tag with the provided string with no value.
366
+ # +selected+:: The value of a selected object, which may be either a string or an array.
367
+ # +include_blank+:: Adds an additional blank option tag with no value.
368
+ # +collection+:: The collection for the select options
369
+ # +text_method+:: Method to determine text of an option (as a symbol). Ex: :text_method => :name will call .name on your record object for what text to display.
370
+ # +value_method+:: Method to determine value of an option (as a symbol).
371
+ def select_field(attrs = {})
372
+ collection = attrs.delete(:collection)
373
+ option_attrs = {
374
+ :prompt => attrs.delete(:prompt),
375
+ :selected => attrs.delete(:selected),
376
+ :include_blank => attrs.delete(:include_blank),
377
+ :text_method => attrs.delete(:text_method),
378
+ :value_method => attrs.delete(:value_method)
379
+ }
380
+ optional_label(attrs) { open_tag('select', attrs) + options_from_collection_for_select(collection, option_attrs) + "</select>"}
381
+ end
382
+
383
+ # Provides a HTML select based on a resource attribute.
384
+ # This is generally used within a resource block such as +form_for+.
385
+ #
386
+ # ==== Example
387
+ # <% select_control :name, :collection => %w(one two three four) %>
388
+ def select_control(col, attrs = {})
389
+ attrs.merge!(:name => attrs[:name] || control_name(col))
390
+ attrs.merge!(:id => attrs[:id] || control_id(col))
391
+ errorify_field(attrs, col)
392
+ optional_label(attrs) { select_field(attrs) }
393
+ end
394
+
395
+ # Accepts a collection (hash, array, enumerable, your type) and returns a string of option tags.
396
+ # Given a collection where the elements respond to first and last (such as a two-element array),
397
+ # the "lasts" serve as option values and the "firsts" as option text. Hashes are turned into
398
+ # this form automatically, so the keys become "firsts" and values become lasts. If selected is
399
+ # specified, the matching "last" or element will get the selected option-tag. Selected may also
400
+ # be an array of values to be selected when using a multiple select.
401
+ #
402
+ # ==== Examples
403
+ # <%= options_for_select( [['apple','Apple Pie'],['orange','Orange Juice']], :selected => 'orange' )
404
+ # => <option value="apple">Apple Pie</option><option value="orange" selected="selected">Orange Juice</option>
405
+ #
406
+ # <%= options_for_select( [['apple','Apple Pie'],['orange','Orange Juice']], :selected => ['orange','apple'], :prompt => 'Select One' )
407
+ # => <option value="">Select One</option><option value="apple" selected="selected">Apple Pie</option><option value="orange" selected="selected">Orange Juice</option>
408
+ #
409
+ # ==== Options
410
+ # +selected+:: The value of a selected object, which may be either a string or an array.
411
+ # +prompt+:: Adds an addtional option tag with the provided string with no value.
412
+ # +include_blank+:: Adds an additional blank option tag with no value.
413
+ def options_for_select(collection, attrs = {})
414
+ prompt = attrs.delete(:prompt)
415
+ blank = attrs.delete(:include_blank)
416
+ selected = attrs.delete(:selected)
417
+ returning '' do |ret|
418
+ ret << tag('option', prompt, :value => '') if prompt
419
+ ret << tag("option", '', :value => '') if blank
420
+ unless collection.blank?
421
+ if collection.is_a?(Hash)
422
+ collection.each do |label,group|
423
+ ret << open_tag("optgroup", :label => label.to_s.titleize) + options_for_select(group, :selected => selected) + "</optgroup>"
424
+ end
425
+ else
426
+ collection.each do |value,text|
427
+ options = selected.to_a.include?(value) ? {:selected => 'selected'} : {}
428
+ ret << tag( 'option', text, {:value => value}.merge(options) )
429
+ end
430
+ end
431
+ end
432
+ end
433
+ end
434
+
435
+ # Returns a string of option tags that have been compiled by iterating over the collection and
436
+ # assigning the the result of a call to the value_method as the option value and the text_method
437
+ # as the option text. If selected_value is specified, the element returning a match on
438
+ # the value_method option will get the selected option tag.
439
+ #
440
+ # This method also also supports the automatic generation of optgroup tags by using a hash.
441
+ # ==== Examples
442
+ # If we had a collection of people within a @project object, and want to use 'id' as the value, and 'name'
443
+ # as the option content we could do something similar to this;
444
+ #
445
+ # <%= options_from_collection_for_select(@project.people, :text_method => "id", :value_method => "name") %>
446
+ # The iteration of the collection would create options in this manner;
447
+ # => <option value="#{person.id}">#{person.name}</option>
448
+ #
449
+ # <% @people = Person.find(:all).group_by( &:state )
450
+ # <%= options_for_select(@people, :text_method => 'full_name', :value_method => 'id', :selected => 3) %>
451
+ # => <optgroup label="Washington"><option value="1">Josh Martin</option><option value="2">John Doe</option></optgroup>
452
+ # => <optgroup label="Idaho"><option value="3" selected="selected">Jane Doe</option>
453
+ #
454
+ # ==== Options
455
+ # +text_method+:: Defines the method which will be used to provide the text of the option tags (required)
456
+ # +value_method+:: Defines the method which will be used to provide the value of the option tags (required)
457
+ # +selected+:: The value of a selected object, may be either a string or an array.
458
+ def options_from_collection_for_select(collection, attrs = {})
459
+ prompt = attrs.delete(:prompt)
460
+ blank = attrs.delete(:include_blank)
461
+ if collection.is_a?(Hash)
462
+ returning '' do |ret|
463
+ ret << tag("option", prompt, :value => '') if prompt
464
+ ret << tag("option", '', :value => '') if blank
465
+ collection.each do |label, group|
466
+ ret << open_tag("optgroup", :label => label.to_s.humanize.titleize) + options_from_collection_for_select(group, attrs) + "</optgroup>"
467
+ end
468
+ end
469
+ else
470
+ text_method = attrs[:text_method]
471
+ value_method = attrs[:value_method]
472
+ selected_value = attrs[:selected]
473
+
474
+ text_method ||= :to_s
475
+ value_method ||= text_method
476
+
477
+ options_for_select((collection || []).inject([]) { |options, object|
478
+ options << [ object.send(value_method), object.send(text_method) ] },
479
+ :selected => selected_value, :include_blank => blank, :prompt => prompt
480
+ )
481
+ end
482
+ end
483
+
484
+ # Provides the ability to create quick fieldsets as blocks for your forms.
485
+ #
486
+ # ==== Example
487
+ # <% fieldset :legend => 'Customer Options' do -%>
488
+ # ...your form elements
489
+ # <% end -%>
490
+ #
491
+ # => <fieldset><legend>Customer Options</legend>...your form elements</fieldset>
492
+ #
493
+ # ==== Options
494
+ # +legend+:: The name of this fieldset which will be provided in a HTML legend tag.
495
+ def fieldset(attrs={}, &block)
496
+ legend = attrs.delete(:legend)
497
+ concat( open_tag('fieldset', attrs), block.binding )
498
+ concat( tag('legend', legend), block.binding ) if legend
499
+ concat(capture(&block), block.binding)
500
+ concat( "</fieldset>", block.binding)
501
+ end
502
+
503
+ # Provides a HTML file input for a resource attribute.
504
+ # This is generally used within a resource block such as +form_for+.
505
+ #
506
+ # ==== Example
507
+ # <% file_control :file, :label => "File" %>
508
+ def file_control(col, attrs = {})
509
+ errorify_field(attrs, col)
510
+ file_field(control_name_value(col, attrs))
511
+ end
512
+
513
+ # Provides a HTML file input
514
+ #
515
+ # ==== Example
516
+ # <% file_field :name => "file", :label => "File" %>
517
+ def file_field(attrs = {})
518
+ attrs.merge!(:type => "file")
519
+ attrs.add_html_class!("file")
520
+ optional_label(attrs) { self_closing_tag("input", attrs) }
521
+ end
522
+
523
+ def submit_field(attrs = {})
524
+ attrs.merge!(:type => :submit)
525
+ attrs[:name] ||= "submit"
526
+ self_closing_tag("input", attrs)
527
+ end
528
+
529
+ # Generates a delete button inside of a form.
530
+ #
531
+ # <%= delete_button :news_post, @news_post, 'Remove' %>
532
+ #
533
+ # The HTML generated for this would be:
534
+ #
535
+ # <form method="post" action="/news_posts/4">
536
+ # <input type="hidden" value="delete" name="_method"/>
537
+ # <button type="submit">Remove</button>
538
+ # </form>
539
+ def delete_button(symbol, obj = instance_variable_get("@#{symbol}"), contents = 'Delete', form_attrs = {}, button_attrs = {})
540
+ button_attrs[:type] = :submit
541
+
542
+ form_attrs.merge! :action => url(symbol, obj), :method => :delete
543
+
544
+ fake_form_method = set_form_method(form_attrs, obj)
545
+
546
+ button = ''
547
+ button << open_tag(:form, form_attrs)
548
+ button << generate_fake_form_method(fake_form_method)
549
+ button << tag(:button, contents, button_attrs)
550
+ button << '</form>'
551
+ button
552
+ end
553
+
554
+ private
555
+
556
+ # Fake out the browser to send back the method for RESTful stuff.
557
+ # Fall silently back to post if a method is given that is not supported here
558
+ def set_form_method(options = {}, obj = nil)
559
+ options[:method] ||= ((obj && obj.respond_to?(:new_record?) && !obj.new_record?) ? :put : :post)
560
+ if ![:get,:post].include?(options[:method])
561
+ fake_form_method = options[:method] if [:put, :delete].include?(options[:method])
562
+ options[:method] = :post
563
+ end
564
+ fake_form_method
565
+ end
566
+
567
+ def generate_fake_form_method(fake_form_method)
568
+ fake_form_method ? hidden_field(:name => "_method", :value => "#{fake_form_method}") : ""
569
+ end
570
+
571
+ def optional_label(attrs = {})
572
+ label = attrs.delete(:label) if attrs
573
+ if label
574
+ title = label.is_a?(Hash) ? label.delete(:title) : label
575
+ named = attrs[:id].blank? ? {} : {:for => attrs[:id]}
576
+ label(title, '', label.is_a?(Hash) ? label.merge(named) : named) + yield
577
+ else
578
+ yield
579
+ end
580
+ end
581
+
582
+ def errorify_field(attrs, col)
583
+ attrs.add_html_class!("error") if @_obj.respond_to?(:errors) && @_obj.errors.on(col)
584
+ end
585
+
586
+ def set_multipart_attribute!(attrs = {})
587
+ attrs.merge!( :enctype => "multipart/form-data" ) if attrs.delete(:multipart)
588
+ end
589
+
590
+ end
591
+ end
592
+ end
593
+
594
+ class Merb::ViewContext #:nodoc:
595
+ include Merb::Helpers::Form
596
+ end
File without changes
data/lib/merb_helpers.rb CHANGED
@@ -1,12 +1,35 @@
1
- # make sure we're running inside Merb
2
- if defined?(Merb::Plugins)
1
+ module Merb
2
+
3
+ module Helpers
4
+ HELPERS_DIR = File.dirname(__FILE__) / 'merb_helpers'
5
+ HELPERS_FILES = Dir["#{HELPERS_DIR}/*_helpers.rb"].collect {|h| h.match(/\/(\w+)\.rb/)[1]}
6
+
7
+ def self.load_helpers(helpers = HELPERS_FILES)
8
+ helpers.each {|h| Kernel.load(File.join(HELPERS_DIR, "#{h}.rb") )} # using load here allows specs to work
9
+ end
10
+
11
+ def self.load
12
+ if Merb::Plugins.config[:merb_helpers]
13
+ config = Merb::Plugins.config[:merb_helpers]
14
+ raise "With and Without options cannot be used with merb_helpers plugin configuration" if config[:with] && config[:without]
15
+ if config[:include]
16
+ load_helpers(config[:include])
17
+ elsif config[:exclude]
18
+ load_helpers(HELPERS_FILES.reject {|h| config[:exclude].include? h})
19
+ else
20
+ # This is in case someone defines an entry in the config,
21
+ # but doesn't put in a with or without option
22
+ load_helpers
23
+ end
24
+ else
25
+ load_helpers
26
+ end
3
27
 
4
- # Merb gives you a Merb::Plugins.config hash...feel free to put your stuff in your piece of it
5
- Merb::Plugins.config[:merb_helpers] = {
6
- :chickens => false
7
- }
28
+ Merb::Plugins.add_rakefiles "tasks/merb_tasks"
29
+ end
30
+
31
+ end
8
32
 
9
- require 'form_model'
10
- require 'form_helpers'
11
- Merb::Plugins.add_rakefiles "merb_helpers/merbtasks"
12
- end
33
+ end
34
+
35
+ Merb::Helpers.load if defined?(Merb::Plugins)