merb_helpers 0.4.0 → 0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +36 -1
- data/Rakefile +40 -6
- data/lib/merb_helpers/date_time_helpers.rb +7 -0
- data/lib/merb_helpers/form_helpers.rb +596 -0
- data/lib/{form_model.rb → merb_helpers/form_model.rb} +0 -0
- data/lib/merb_helpers.rb +33 -10
- data/lib/{merb_helpers/merbtasks.rb → tasks/merb_tasks.rb} +0 -0
- metadata +11 -11
- data/lib/form_helpers.rb +0 -183
- data/specs/merb_helpers_spec.rb +0 -412
- data/specs/spec_helper.rb +0 -99
@@ -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
|
-
|
2
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
28
|
+
Merb::Plugins.add_rakefiles "tasks/merb_tasks"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
8
32
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Merb::Helpers.load if defined?(Merb::Plugins)
|
File without changes
|