merb_helpers 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +2 -2
- data/lib/merb_helpers/core_ext.rb +2 -0
- data/lib/merb_helpers/form/builder.rb +394 -0
- data/lib/merb_helpers/form/helpers.rb +410 -0
- data/lib/merb_helpers/form_helpers.rb +10 -651
- metadata +6 -3
@@ -0,0 +1,410 @@
|
|
1
|
+
# Form helpers provide a number of methods to simplify the creation of HTML forms.
|
2
|
+
# They can work directly with models (bound) or standalone (unbound).
|
3
|
+
#
|
4
|
+
# The core method of this helper, +form_for+, gives you the ability to create a form for a resource.
|
5
|
+
# For example, let's say that you have a model <tt>Person</tt> and want to create a new instance of it:
|
6
|
+
module Merb::Helpers::Form
|
7
|
+
|
8
|
+
def _singleton_form_context
|
9
|
+
@_singleton_form_context ||=
|
10
|
+
self._form_class.new(nil, nil, self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def form_contexts
|
14
|
+
@_form_contexts ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_form_context
|
18
|
+
form_contexts.last || _singleton_form_context
|
19
|
+
end
|
20
|
+
|
21
|
+
def _new_form_context(name, builder)
|
22
|
+
if name.is_a?(String) || name.is_a?(Symbol)
|
23
|
+
ivar = instance_variable_get("@#{name}")
|
24
|
+
else
|
25
|
+
ivar, name = name, name.class.to_s.snake_case
|
26
|
+
end
|
27
|
+
builder ||= current_form_context.class if current_form_context
|
28
|
+
(builder || self._form_class).new(ivar, name, self)
|
29
|
+
end
|
30
|
+
|
31
|
+
def with_form_context(name, builder)
|
32
|
+
form_contexts.push(_new_form_context(name, builder))
|
33
|
+
ret = yield
|
34
|
+
form_contexts.pop
|
35
|
+
ret
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generates a form tag, which accepts a block that is not directly based on resource attributes
|
39
|
+
#
|
40
|
+
# ==== Parameters
|
41
|
+
# attrs<Hash>:: HTML attributes
|
42
|
+
#
|
43
|
+
# ==== Returns
|
44
|
+
# String:: HTML
|
45
|
+
#
|
46
|
+
# ==== Notes
|
47
|
+
# * Block helpers use the <%= =%> syntax
|
48
|
+
# * a multipart enctype is automatically set if the form contains a file upload field
|
49
|
+
#
|
50
|
+
# ==== Example
|
51
|
+
# <%= form :action => url(:controller => "foo", :action => "bar", :id => 1) do %>
|
52
|
+
# <%= text_field :name => "first_name", :label => "First Name" %>
|
53
|
+
# <%= submit "Create" %>
|
54
|
+
# <% end =%>
|
55
|
+
#
|
56
|
+
# Generates the HTML:
|
57
|
+
#
|
58
|
+
# <form action="/foo/bar/1" method="post">
|
59
|
+
# <label for="first_name">First Name</label>
|
60
|
+
# <input type="text" id="first_name" name="first_name" />
|
61
|
+
# <input type="submit" value="Create" />
|
62
|
+
# </form>
|
63
|
+
def form(*args, &blk)
|
64
|
+
_singleton_form_context.form(*args, &blk)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Generates a resource specific form tag which accepts a block, this also provides automatic resource routing.
|
68
|
+
#
|
69
|
+
# ==== Parameters
|
70
|
+
# name<Symbol>:: Model or Resource
|
71
|
+
# attrs<Hash>:: HTML attributes
|
72
|
+
#
|
73
|
+
# ==== Returns
|
74
|
+
# String:: HTML
|
75
|
+
#
|
76
|
+
# ==== Notes
|
77
|
+
# * Block helpers use the <%= =%> syntax
|
78
|
+
#
|
79
|
+
# ==== Example
|
80
|
+
# <%= form_for @person do %>
|
81
|
+
# <%= text_field :first_name, :label => "First Name" %>
|
82
|
+
# <%= text_field :last_name, :label => "Last Name" %>
|
83
|
+
# <%= submit "Create" %>
|
84
|
+
# <% end =%>
|
85
|
+
#
|
86
|
+
# The HTML generated for this would be:
|
87
|
+
#
|
88
|
+
# <form action="/people" method="post">
|
89
|
+
# <label for="person_first_name">First Name</label>
|
90
|
+
# <input type="text" id="person_first_name" name="person[first_name]" />
|
91
|
+
# <label for="person_last_name">Last Name</label>
|
92
|
+
# <input type="text" id="person_last_name" name="person[last_name]" />
|
93
|
+
# <input type="submit" value="Create" />
|
94
|
+
# </form>
|
95
|
+
def form_for(name, attrs = {}, &blk)
|
96
|
+
with_form_context(name, attrs.delete(:builder)) do
|
97
|
+
current_form_context.form(attrs, &blk)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
# Creates a scope around a specific resource object like form_for, but doesnt create the form tags themselves.
|
102
|
+
# This makes fields_for suitable for specifying additional resource objects in the same form.
|
103
|
+
#
|
104
|
+
# ==== Examples
|
105
|
+
# <%= form_for @person do %>
|
106
|
+
# <%= text_field :first_name, :label => "First Name" %>
|
107
|
+
# <%= text_field :last_name, :label => "Last Name" %>
|
108
|
+
# <% fields_for :permission do %>
|
109
|
+
# <%= check_box :is_admin, :label => "Administrator" %>
|
110
|
+
# <% end %>
|
111
|
+
# <%= submit "Create" %>
|
112
|
+
# <% end =%>
|
113
|
+
def fields_for(name, attrs = {}, &blk)
|
114
|
+
attrs ||= {}
|
115
|
+
with_form_context(name, attrs.delete(:builder)) do
|
116
|
+
current_form_context.concat(attrs, &blk)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Provides the ability to create quick fieldsets as blocks for your forms.
|
121
|
+
#
|
122
|
+
# ==== Parameters
|
123
|
+
# attrs<Hash>:: HTML attributes and options
|
124
|
+
#
|
125
|
+
# ==== Options
|
126
|
+
# +legend+:: Adds a legend tag within the fieldset
|
127
|
+
#
|
128
|
+
# ==== Returns
|
129
|
+
# String:: HTML
|
130
|
+
#
|
131
|
+
# ==== Notes
|
132
|
+
# Block helpers use the <%= =%> syntax
|
133
|
+
#
|
134
|
+
# ==== Example
|
135
|
+
# <%= fieldset :legend => "Customer Options" do %>
|
136
|
+
# ...your form elements
|
137
|
+
# <% end =%>
|
138
|
+
#
|
139
|
+
# Generates the HTML:
|
140
|
+
#
|
141
|
+
# <fieldset>
|
142
|
+
# <legend>Customer Options</legend>
|
143
|
+
# ...your form elements
|
144
|
+
# </fieldset>
|
145
|
+
def fieldset(attrs = {}, &blk)
|
146
|
+
_singleton_form_context.fieldset(attrs, &blk)
|
147
|
+
end
|
148
|
+
|
149
|
+
def fieldset_for(name, attrs = {}, &blk)
|
150
|
+
with_form_context(name, attrs.delete(:builder)) do
|
151
|
+
current_form_context.fieldset(attrs, &blk)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Provides a HTML text input tag
|
156
|
+
#
|
157
|
+
# ==== Parameters
|
158
|
+
# name<Symbol>:: Model or Resource
|
159
|
+
# attrs<Hash>:: HTML attributes
|
160
|
+
#
|
161
|
+
# ==== Returns
|
162
|
+
# String:: HTML
|
163
|
+
#
|
164
|
+
# ==== Example
|
165
|
+
# <%= text_field :name => :fav_color, :label => "Your Favorite Color" %>
|
166
|
+
# # => <label for="fav_color">Your Favorite Color</label><input type="text" id="fav_color" name="fav_color" />
|
167
|
+
#
|
168
|
+
# Used with a model:
|
169
|
+
#
|
170
|
+
# <%= form_for @person do %>
|
171
|
+
# <%= text_field :first_name, :label => "First Name" %>
|
172
|
+
# <% end =%>
|
173
|
+
def text_field; end
|
174
|
+
|
175
|
+
# Provides a HTML password input.
|
176
|
+
#
|
177
|
+
# ==== Parameters
|
178
|
+
# name<Symbol>:: Model or Resource
|
179
|
+
# attrs<Hash>:: HTML attributes
|
180
|
+
#
|
181
|
+
# ==== Returns
|
182
|
+
# String:: HTML
|
183
|
+
#
|
184
|
+
# ==== Example
|
185
|
+
# <%= password_field :name => :password, :label => "Password" %>
|
186
|
+
# # => <label for="password">Password</label><input type="password" id="password" name="password" />
|
187
|
+
#
|
188
|
+
# Used with a model:
|
189
|
+
#
|
190
|
+
# <%= password_field :password, :label => 'New Password' %>
|
191
|
+
def password_field; end
|
192
|
+
|
193
|
+
# Provides a HTML hidden input field
|
194
|
+
#
|
195
|
+
# ==== Parameters
|
196
|
+
# name<Symbol>:: Model or Resource
|
197
|
+
# attrs<Hash>:: HTML attributes
|
198
|
+
#
|
199
|
+
# ==== Returns
|
200
|
+
# String:: HTML
|
201
|
+
#
|
202
|
+
# ==== Example
|
203
|
+
# <%= hidden_field :name => "secret", :value => "some secret value" %>
|
204
|
+
#
|
205
|
+
# Used with a model:
|
206
|
+
#
|
207
|
+
# <%= hidden_field :identifier %>
|
208
|
+
# # => <input type="hidden" id="person_identifier" name="person[identifier]" value="#{@person.identifier}" />
|
209
|
+
def hidden_field; end
|
210
|
+
|
211
|
+
# Provides a HTML file input
|
212
|
+
#
|
213
|
+
# ==== Parameters
|
214
|
+
# name<Symbol>:: Model or Resource
|
215
|
+
# attrs<Hash>:: HTML attributes
|
216
|
+
#
|
217
|
+
# ==== Returns
|
218
|
+
# String:: HTML
|
219
|
+
#
|
220
|
+
# ==== Example
|
221
|
+
# <%= file_field :name => "file", :label => "File" %>
|
222
|
+
#
|
223
|
+
# Used with a model:
|
224
|
+
#
|
225
|
+
# <%= file_field :file, :label => "Choose a file" %>
|
226
|
+
def file_field; end
|
227
|
+
|
228
|
+
# Provides a HTML textarea tag
|
229
|
+
#
|
230
|
+
# ==== Parameters
|
231
|
+
# contents<String>:: Contents of the text area
|
232
|
+
# attrs<Hash>:: HTML attributes
|
233
|
+
#
|
234
|
+
# ==== Returns
|
235
|
+
# String:: HTML
|
236
|
+
#
|
237
|
+
# ==== Example
|
238
|
+
# <%= text_area "my comments", :name => "comments" %>
|
239
|
+
#
|
240
|
+
# Used with a model:
|
241
|
+
#
|
242
|
+
# <%= text_area :comments %>
|
243
|
+
def text_area; end
|
244
|
+
|
245
|
+
# Provides a HTML select
|
246
|
+
#
|
247
|
+
# ==== Parameters
|
248
|
+
# method<Symbol>:: Resource attribute
|
249
|
+
# attrs<Hash>:: HTML attributes and options
|
250
|
+
#
|
251
|
+
# ==== Options
|
252
|
+
# +prompt+:: Adds an additional option tag with the provided string with no value.
|
253
|
+
# +selected+:: The value of a selected object, which may be either a string or an array.
|
254
|
+
# +include_blank+:: Adds an additional blank option tag with no value.
|
255
|
+
# +collection+:: The collection for the select options
|
256
|
+
# +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.
|
257
|
+
# +value_method+:: Method to determine value of an option (as a symbol).
|
258
|
+
#
|
259
|
+
# ==== Returns
|
260
|
+
# String:: HTML
|
261
|
+
#
|
262
|
+
# ==== Example
|
263
|
+
# <%= select :name, :collection => %w(one two three) %>
|
264
|
+
def select; end
|
265
|
+
|
266
|
+
# Provides a generic HTML checkbox input tag.
|
267
|
+
# There are two ways this tag can be generated, based on the
|
268
|
+
# option :boolean. If not set to true, a "magic" input is generated.
|
269
|
+
# Otherwise, an input is created that can be easily used for passing
|
270
|
+
# an array of values to the application.
|
271
|
+
#
|
272
|
+
# ==== Parameters
|
273
|
+
# method<Symbol>:: Resource attribute
|
274
|
+
# attrs<Hash>:: HTML attributes and options
|
275
|
+
#
|
276
|
+
# ==== Returns
|
277
|
+
# String:: HTML
|
278
|
+
#
|
279
|
+
# ==== Example
|
280
|
+
# <%= check_box :name => "is_activated", :value => "1" %>
|
281
|
+
# <%= check_box :name => "choices[]", :boolean => false, :value => "dog" %>
|
282
|
+
# <%= check_box :name => "choices[]", :boolean => false, :value => "cat" %>
|
283
|
+
# <%= check_box :name => "choices[]", :boolean => false, :value => "weasle" %>
|
284
|
+
#
|
285
|
+
# Used with a model:
|
286
|
+
#
|
287
|
+
# <%= check_box :is_activated, :label => "Activated?" %>
|
288
|
+
def check_box; end
|
289
|
+
|
290
|
+
# Provides a HTML radio input tag
|
291
|
+
#
|
292
|
+
# ==== Parameters
|
293
|
+
# method<Symbol>:: Resource attribute
|
294
|
+
# attrs<Hash>:: HTML attributes and options
|
295
|
+
#
|
296
|
+
# ==== Returns
|
297
|
+
# String:: HTML
|
298
|
+
#
|
299
|
+
# ==== Example
|
300
|
+
# <%= radio_button :name => "radio_options", :value => "1", :label => "One" %>
|
301
|
+
# <%= radio_button :name => "radio_options", :value => "2", :label => "Two" %>
|
302
|
+
# <%= radio_button :name => "radio_options", :value => "3", :label => "Three", :checked => true %>
|
303
|
+
#
|
304
|
+
# Used with a model:
|
305
|
+
#
|
306
|
+
# <%= form_for @person do %>
|
307
|
+
# <%= radio_button :first_name %>
|
308
|
+
# <% end =%>
|
309
|
+
def radio_button; end
|
310
|
+
|
311
|
+
# Provides a radio group based on a resource attribute.
|
312
|
+
# This is generally used within a resource block such as +form_for+.
|
313
|
+
#
|
314
|
+
# ==== Parameters
|
315
|
+
# method<Symbol>:: Resource attribute
|
316
|
+
# arr<Array>:: Choices
|
317
|
+
#
|
318
|
+
# ==== Returns
|
319
|
+
# String:: HTML
|
320
|
+
#
|
321
|
+
# ==== Examples
|
322
|
+
# <%# the labels are the options %>
|
323
|
+
# <%= radio_group :my_choice, [5,6,7] %>
|
324
|
+
#
|
325
|
+
# <%# custom labels %>
|
326
|
+
# <%= radio_group :my_choice, [{:value => 5, :label => "five"}] %>
|
327
|
+
def radio_group; end
|
328
|
+
|
329
|
+
# @todo radio_group helper still needs to be implemented
|
330
|
+
%w(text_field password_field hidden_field file_field
|
331
|
+
text_area select check_box radio_button radio_group).each do |kind|
|
332
|
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
333
|
+
def #{kind}(*args)
|
334
|
+
if bound?(*args)
|
335
|
+
current_form_context.bound_#{kind}(*args)
|
336
|
+
else
|
337
|
+
current_form_context.unbound_#{kind}(*args)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
RUBY
|
341
|
+
end
|
342
|
+
|
343
|
+
# Generates a HTML button.
|
344
|
+
#
|
345
|
+
# ==== Parameters
|
346
|
+
# contents<String>:: HTML contained within the button tag
|
347
|
+
# attrs<Hash>:: HTML attributes
|
348
|
+
#
|
349
|
+
# ==== Returns
|
350
|
+
# String:: HTML
|
351
|
+
#
|
352
|
+
# ==== Notes
|
353
|
+
# * Buttons do not always work as planned in IE
|
354
|
+
# http://www.peterbe.com/plog/button-tag-in-IE
|
355
|
+
# * Not all mobile browsers support buttons
|
356
|
+
# http://nickcowie.com/2007/time-to-stop-using-the-button-element/
|
357
|
+
#
|
358
|
+
# ==== Example
|
359
|
+
# <%= button "Initiate Launch Sequence" %>
|
360
|
+
def button(contents, attrs = {})
|
361
|
+
current_form_context.button(contents, attrs)
|
362
|
+
end
|
363
|
+
|
364
|
+
# Generates a HTML submit button.
|
365
|
+
#
|
366
|
+
# ==== Parameters
|
367
|
+
# value<String>:: Sets the value="" attribute
|
368
|
+
# attrs<Hash>:: HTML attributes
|
369
|
+
#
|
370
|
+
# ==== Returns
|
371
|
+
# String:: HTML
|
372
|
+
#
|
373
|
+
# ==== Example
|
374
|
+
# <%= submit "Process" %>
|
375
|
+
def submit(contents, attrs = {})
|
376
|
+
current_form_context.submit(contents, attrs)
|
377
|
+
end
|
378
|
+
|
379
|
+
# Provides a HTML formatted display of resource errors in an unordered list with a h2 form submission error
|
380
|
+
#
|
381
|
+
# ==== Parameters
|
382
|
+
# obj<Symbol>:: Model or Resource
|
383
|
+
# error_class<String>:: CSS class to use for error container
|
384
|
+
# build_li<String>:: Custom li tag to wrap each error in
|
385
|
+
# header<String>:: Custom header text for the error container
|
386
|
+
# before<Boolean>:: Display the errors before or inside of the form
|
387
|
+
#
|
388
|
+
# ==== Returns
|
389
|
+
# String:: HTML
|
390
|
+
#
|
391
|
+
# ==== Examples
|
392
|
+
# <%= error_messages_for :person %>
|
393
|
+
# <%= error_messages_for :person {|errors| "You can has probs nao: #{errors.size} of em!"}
|
394
|
+
# <%= error_messages_for :person, lambda{|error| "<li class='aieeee'>#{error.join(' ')}"} %>
|
395
|
+
# <%= error_messages_for :person, nil, 'bad_mojo' %>
|
396
|
+
def error_messages_for(obj = nil, opts = {})
|
397
|
+
current_form_context.error_messages_for(obj, opts[:error_class] || "error",
|
398
|
+
opts[:build_li] || "<li>%s</li>",
|
399
|
+
opts[:header] || "<h2>Form submission failed because of %s problem%s</h2>",
|
400
|
+
opts.key?(:before) ? opts[:before] : true)
|
401
|
+
end
|
402
|
+
alias error_messages error_messages_for
|
403
|
+
|
404
|
+
private
|
405
|
+
|
406
|
+
def bound?(*args)
|
407
|
+
args.first.is_a?(Symbol)
|
408
|
+
end
|
409
|
+
|
410
|
+
end
|