ramaze 2010.06.18 → 2011.01

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. data/.gitignore +1 -0
  2. data/MANIFEST +9 -16
  3. data/README.md +37 -30
  4. data/Rakefile +5 -1
  5. data/TODO.md +19 -0
  6. data/doc/AUTHORS +5 -1
  7. data/doc/CHANGELOG +3553 -3272
  8. data/doc/tutorial/todolist.html +1512 -1512
  9. data/examples/app/blog/app.rb +2 -0
  10. data/examples/app/todolist/controller/init.rb +1 -2
  11. data/examples/app/wiktacular/mkd/main/2007-07-20_19-21-12.mkd +1 -1
  12. data/examples/app/wiktacular/mkd/main/2007-07-20_19-23-10.mkd +1 -1
  13. data/examples/app/wiktacular/mkd/main/2007-07-20_19-45-07.mkd +1 -1
  14. data/examples/app/wiktacular/mkd/main/current.mkd +1 -1
  15. data/examples/app/wiktacular/mkd/testing/2007-07-20_16-43-46.mkd +1 -1
  16. data/examples/app/wiktacular/mkd/testing/2007-07-20_19-43-50.mkd +2 -2
  17. data/examples/app/wiktacular/mkd/testing/2007-07-21_18-47-08.mkd +16 -16
  18. data/examples/app/wiktacular/mkd/testing/2007-07-21_18-47-54.mkd +16 -16
  19. data/examples/app/wiktacular/mkd/testing/current.mkd +16 -16
  20. data/lib/proto/model/init.rb +1 -1
  21. data/lib/proto/public/js/jquery.js +2034 -1095
  22. data/lib/proto/start.rb +2 -0
  23. data/lib/proto/view/index.xhtml +3 -3
  24. data/lib/ramaze.rb +1 -2
  25. data/lib/ramaze/cache.rb +1 -0
  26. data/lib/ramaze/cache/sequel.rb +131 -37
  27. data/lib/ramaze/controller.rb +1 -0
  28. data/lib/ramaze/gestalt.rb +75 -46
  29. data/lib/ramaze/helper.rb +1 -0
  30. data/lib/ramaze/helper/auth.rb +38 -4
  31. data/lib/ramaze/helper/blue_form.rb +498 -78
  32. data/lib/ramaze/helper/cache.rb +2 -2
  33. data/lib/ramaze/helper/csrf.rb +225 -0
  34. data/lib/ramaze/helper/erector.rb +67 -9
  35. data/lib/ramaze/helper/flash.rb +4 -2
  36. data/lib/ramaze/helper/gestalt.rb +2 -0
  37. data/lib/ramaze/helper/gravatar.rb +1 -1
  38. data/lib/ramaze/helper/localize.rb +4 -0
  39. data/lib/ramaze/helper/send_file.rb +30 -0
  40. data/lib/ramaze/helper/thread.rb +5 -0
  41. data/lib/ramaze/helper/user.rb +4 -3
  42. data/lib/ramaze/helper/xhtml.rb +87 -8
  43. data/lib/ramaze/log.rb +13 -0
  44. data/lib/ramaze/log/analogger.rb +15 -5
  45. data/lib/ramaze/log/growl.rb +28 -13
  46. data/lib/ramaze/log/hub.rb +12 -4
  47. data/lib/ramaze/log/informer.rb +28 -11
  48. data/lib/ramaze/log/knotify.rb +7 -2
  49. data/lib/ramaze/log/logger.rb +12 -4
  50. data/lib/ramaze/log/logging.rb +40 -14
  51. data/lib/ramaze/log/rotatinginformer.rb +47 -23
  52. data/lib/ramaze/log/syslog.rb +37 -31
  53. data/lib/ramaze/log/xosd.rb +7 -4
  54. data/lib/ramaze/middleware_compiler.rb +2 -2
  55. data/lib/ramaze/snippets/fiber.rb +63 -63
  56. data/lib/ramaze/snippets/ramaze/lru_hash.rb +1 -1
  57. data/lib/ramaze/tool/bin.rb +1 -1
  58. data/lib/ramaze/version.rb +1 -1
  59. data/lib/ramaze/view.rb +4 -4
  60. data/lib/ramaze/view/erector.rb +88 -13
  61. data/ramaze.gemspec +65 -65
  62. data/spec/ramaze/bin/ramaze.rb +1 -1
  63. data/spec/ramaze/cache/localmemcache.rb +20 -12
  64. data/spec/ramaze/cache/sequel.rb +19 -19
  65. data/spec/ramaze/helper/blue_form.rb +549 -257
  66. data/spec/ramaze/helper/csrf.rb +109 -0
  67. data/spec/ramaze/helper/httpdigest.rb +31 -29
  68. data/spec/ramaze/helper/user.rb +1 -1
  69. data/spec/ramaze/helper/xhtml.rb +17 -0
  70. data/spec/ramaze/log/growl.rb +34 -0
  71. data/spec/ramaze/log/informer.rb +1 -0
  72. data/spec/ramaze/view/erector.rb +49 -71
  73. data/spec/ramaze/view/erector/external_view.erector +5 -0
  74. data/spec/ramaze/view/erector/index.erector +5 -0
  75. data/spec/ramaze/view/erector/layout.erector +13 -3
  76. data/spec/ramaze/view/erector/tables.erector +23 -0
  77. data/spec/ramaze/view/erector/view.erector +6 -0
  78. data/tasks/git.rake +2 -2
  79. metadata +133 -176
  80. data/examples/helpers/form_with_sequel.rb +0 -24
  81. data/examples/helpers/nitro_form.rb +0 -23
  82. data/lib/ramaze/helper/form.rb +0 -133
  83. data/lib/ramaze/helper/nitroform.rb +0 -14
  84. data/lib/ramaze/helper/pager.rb +0 -367
  85. data/lib/ramaze/helper/partial.rb +0 -100
  86. data/lib/ramaze/helper/sequel.rb +0 -55
  87. data/lib/ramaze/helper/sequel_form.rb +0 -284
  88. data/lib/vendor/etag.rb +0 -22
  89. data/spec/ramaze/helper/form.rb +0 -360
  90. data/spec/ramaze/helper/pager.rb +0 -96
  91. data/spec/ramaze/helper/sequel_form.rb +0 -94
  92. data/spec/ramaze/view/erector/external.erector +0 -1
  93. data/spec/ramaze/view/erector/invoke_helper_method.erector +0 -1
  94. data/spec/ramaze/view/erector/strict_xhtml.erector +0 -3
  95. data/spec/ramaze/view/erector/sum.erector +0 -1
data/lib/ramaze/helper.rb CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'innate/helper'
5
5
 
6
+ # TODO: Describe what this module does besides loading a few default helper.
6
7
  module Ramaze
7
8
  Helper = Innate::Helper
8
9
  Innate::HelpersHelper.options.paths << File.dirname(__FILE__)
@@ -8,6 +8,13 @@ module Ramaze
8
8
  # Please have a look at the docs of Auth#auth_login.
9
9
  #
10
10
  # If you want to do authentication with a model see Helper::User instead.
11
+
12
+ ##
13
+ # The Auth helper can be used for authentication without using a model.
14
+ # This can be useful when working with very basic applications that don't require database access.
15
+ #
16
+ # If you're looking for a way to do authentication using a model you should take a look at Helper::User instead.
17
+ #
11
18
  module Auth
12
19
  Helper::LOOKUP << self
13
20
  include Ramaze::Traited
@@ -19,7 +26,12 @@ module Ramaze
19
26
  def self.included(into)
20
27
  into.helper(:stack)
21
28
  end
22
-
29
+
30
+ ##
31
+ # Log a user in based on the :username and :password key in the request hash.
32
+ #
33
+ # @return [String] The login template in case the user's login data was incorrect.
34
+ #
23
35
  def login
24
36
  return auth_template if trait[:auth_post_only] and !request.post?
25
37
  @username, password = request[:username, :password]
@@ -27,6 +39,9 @@ module Ramaze
27
39
  return auth_template
28
40
  end
29
41
 
42
+ ##
43
+ # Log the user out and redirect him back to the previous page.
44
+ #
30
45
  def logout
31
46
  auth_logout
32
47
  answer(request.referer)
@@ -34,16 +49,29 @@ module Ramaze
34
49
 
35
50
  private
36
51
 
52
+ ##
53
+ # Validate the user's session and redirect him/her to the login page in case the user isn't logged in.
54
+ #
37
55
  def login_required
38
56
  call(r(:login)) unless logged_in?
39
57
  end
40
58
 
41
- # @return [true false] whether user is logged in right now
59
+ ##
60
+ # Validate the user's session and return a boolean that indicates if the user is logged in or not.
61
+ #
62
+ # @return [true false] Whether user is logged in right now
63
+ #
42
64
  def logged_in?
43
65
  !!session[:logged_in]
44
66
  end
45
67
 
46
- # @return
68
+ ##
69
+ # Try to log the user in based on the username and password.
70
+ # This method is called by the login() method and shouldn't be called directly.
71
+ #
72
+ # @param [String] user The users's username.
73
+ # @param [String] pass The user's password.
74
+ #
47
75
  def auth_login(user, pass)
48
76
  return unless user and pass
49
77
  return if user.empty? or pass.empty?
@@ -62,12 +90,18 @@ module Ramaze
62
90
  session[:username] = user
63
91
  end
64
92
 
93
+ ##
94
+ # Remove the session items that specified that the user was logged in.
95
+ #
65
96
  def auth_logout
66
97
  session.delete(:logged_in)
67
98
  session.delete(:username)
68
99
  end
69
100
 
70
- # @return [String] template for auth
101
+ ##
102
+ # Method that returns a small form that can be used for logging in.
103
+ #
104
+ # @return [String] The login form.
71
105
  def auth_template
72
106
  <<-TEMPLATE.strip!
73
107
  <form method="post" action="#{r(:login)}">
@@ -3,15 +3,90 @@ require 'ramaze/gestalt'
3
3
 
4
4
  module Ramaze
5
5
  module Helper
6
- # This helper tries to be an even better way to build forms
7
- # programmatically, see the specs for lots of examples.
6
+ ##
7
+ # == Introduction
8
+ #
9
+ # The BlueForm helper tries to be an even better way to build forms programmatically.
10
+ # By using a simple block you can quickly create all the required elements for your form.
11
+ #
12
+ # Since November 2010 the BlueForm helper works different. You can now specify an object as the first
13
+ # parameter of the form_for() method. This object will be used to retrieve the values of each field.
14
+ # This means that you can directly pass a database result object to the form and no longer have
15
+ # to manually specify values. However, you can still specify your own values if you want.
16
+ #
17
+ # Old behaviour:
18
+ #
19
+ # form_for(:method => :post) do |f|
20
+ # f.input_text 'Username', :username, 'Chuck Norris'
21
+ # end
22
+ #
23
+ # New behaviour:
24
+ #
25
+ # # @data is an object that contains an instance variable named "username".
26
+ # # This variable contains the value "Chuck Norris".
27
+ # form_for(@data, :method => :post) do |f|
28
+ # f.input_text 'Username', :username
29
+ # end
30
+ #
31
+ # == Form Data
32
+ #
33
+ # As stated earlier it's possible to pass an object to the form_for() method. What kind of object this is,
34
+ # a database result object or an OpenStruct object doesn't matter as long as the attributes can be accessed
35
+ # outside of the object (this can be done using attr_readers). This makes it extremely easy to directly pass
36
+ # a result object from your favourite ORM. Example:
37
+ #
38
+ # @data = User[1]
39
+ #
40
+ # form_for(@data, :method => :post) do |f|
41
+ # f.input_text 'Username', :username
42
+ # end
43
+ #
44
+ # If you don't want to use an object you can simply set the first parameter to nil.
45
+ #
46
+ # == HTML Output
47
+ #
48
+ # The form helper uses Gestalt, Ramaze's custom HTML builder that works somewhat like Erector.
49
+ # The output is very minimalistic, elements such as legends and fieldsets have to be added manually.
50
+ # Each combination of a label and input element will be wrapped in <p> tags.
51
+ #
52
+ # When using the form helper as a block in your templates it's important to remember that the
53
+ # result is returned and not displayed in the browser directly. When using Etanni this would result in
54
+ # something like the following:
55
+ #
56
+ # #{
57
+ # form_for(@result, :method => :post) do |f| do
58
+ # f.input_text 'Text label', :textname, 'Chunky bacon!'
59
+ # end
60
+ # }
61
+ #
62
+ # @example
63
+ #
64
+ # form_for(@data, :method => :post) do |f|
65
+ # f.input_text 'Username', :username
66
+ # end
67
+ #
8
68
  module BlueForm
9
- def form(options = {}, &block)
10
- form = Form.new(options)
69
+ ##
70
+ # The form method generates the basic structure of the form. It should be called
71
+ # using a block and it's return value should be manually sent to the browser (since it does not echo the value).
72
+ #
73
+ # @param [Object] form_values Object containing the values for each form field.
74
+ # @param [Hash] options Hash containing any additional form attributes such as the method, action, enctype and so on.
75
+ # @param [Block] block Block containing the elements of the form such as password fields, textareas and so on.
76
+ #
77
+ def form_for(form_values, options = {}, &block)
78
+ form = Form.new(form_values, options)
11
79
  form.build(form_errors, &block)
12
80
  form
13
81
  end
14
82
 
83
+ ##
84
+ # Manually add a new error to the form_errors key in the flash hash.
85
+ # The first parameter is the name of the form field and the second parameter is the custom message.
86
+ #
87
+ # @param [String] name The name of the form field to which the error belongs.
88
+ # @param [String] message The custom error message to show.
89
+ #
15
90
  def form_error(name, message)
16
91
  if respond_to?(:flash)
17
92
  old = flash[:form_errors] || {}
@@ -21,6 +96,12 @@ module Ramaze
21
96
  end
22
97
  end
23
98
 
99
+ ##
100
+ # Returns the hash containing all existing errors and allows other methods to set
101
+ # new errors by using this method as if it were a hash.
102
+ #
103
+ # @return [Array] All form errors.
104
+ #
24
105
  def form_errors
25
106
  if respond_to?(:flash)
26
107
  flash[:form_errors] ||= {}
@@ -29,42 +110,122 @@ module Ramaze
29
110
  end
30
111
  end
31
112
 
113
+ ##
114
+ # Retrieve all the form errors for the specified model and add them to the flash hash.
115
+ #
116
+ # @param [Object] obj An object of a model that contains form errors.
117
+ #
32
118
  def form_errors_from_model(obj)
33
- obj.errors.each do |key, value|
34
- form_error(key.to_s, value.first % key)
119
+ if obj.respond_to?(:errors)
120
+ obj.errors.each do |key, value|
121
+ if value.respond_to?(:first)
122
+ value = value.first
123
+ end
124
+
125
+ form_error(key.to_s, value % key)
126
+ end
35
127
  end
36
128
  end
37
129
 
38
- # Note that an instance of this class is not thread-safe, so you should
39
- # modify it only within one thread of execution
130
+ ##
131
+ # Main form class that contains all the required methods to generate form specific tags,
132
+ # such as textareas and select boxes. Do note that this class is not thread-safe so you should
133
+ # modify it only within one thread of execution.
134
+ #
40
135
  class Form
41
136
  attr_reader :g
42
-
43
- def initialize(options)
44
- @form_args = options.dup
45
- @g = Gestalt.new
137
+ attr_reader :form_values
138
+
139
+ ##
140
+ # Constructor method that generates an instance of the Form class.
141
+ #
142
+ # @param [Object] form_values Object containing the values for each form field.
143
+ # @param [Hash] options A hash containing any additional form attributes.
144
+ # @return [Object] An instance of the Form class.
145
+ #
146
+ def initialize(form_values, options)
147
+ @form_values = form_values
148
+ @form_args = options.dup
149
+ @g = Gestalt.new
46
150
  end
47
151
 
152
+ ##
153
+ # Builds the form by generating the opening/closing tags and executing
154
+ # the methods in the block.
155
+ #
156
+ # @param [Hash] form_errors Hash containing all form errors (if any).
157
+ #
48
158
  def build(form_errors = {})
49
- @form_errors = form_errors
50
-
159
+ # Convert all the keys in form_errors to strings and
160
+ # retrieve the correct values in case
161
+ @form_errors = {}
162
+
163
+ form_errors.each do |key, value|
164
+ if value.respond_to?(:first)
165
+ value = value.first
166
+ end
167
+
168
+ @form_errors[key.to_s] = value
169
+ end
170
+
51
171
  @g.form(@form_args) do
52
172
  if block_given?
53
- @g.fieldset do
54
- yield self
55
- end
173
+ yield self
56
174
  end
57
175
  end
58
176
  end
59
177
 
178
+ ##
179
+ # Generate a <legend> tag.
180
+ #
181
+ # @param [String] text The text to display inside the legend tag.
182
+ # @example
183
+ #
184
+ # form_for(@data, :method => :post) do |f|
185
+ # f.legend 'Ramaze rocks!'
186
+ # end
187
+ #
60
188
  def legend(text)
61
189
  @g.legend(text)
62
190
  end
191
+
192
+ ##
193
+ # Generate a fieldset tag.
194
+ #
195
+ # @param [Block] &block The form elements to display inside the fieldset.
196
+ # @example
197
+ #
198
+ # form_for(@data, :method => :post) do |f|
199
+ # f.fieldset do
200
+ # f.legend 'Hello, world!'
201
+ # end
202
+ # end
203
+ #
204
+ def fieldset(&block)
205
+ @g.fieldset(block)
206
+ end
63
207
 
64
- def input_text(label, name, value = nil, args = {})
65
- id = id_for(name)
66
- args = args.merge(:type => :text, :name => name, :class => 'text', :id => id)
67
- args[:value] = value unless value.nil?
208
+ ##
209
+ # Generate an input tag with a type of "text" along with a label tag.
210
+ # This method also has the alias "text" so feel free to use that one instead of input_text.
211
+ #
212
+ # @param [String] label The text to display inside the label tag.
213
+ # @param [String Symbol] name The name of the text field.
214
+ # @param [Hash] args Any additional HTML attributes along with their values.
215
+ # @example
216
+ #
217
+ # form_for(@data, :method => :post) do |f|
218
+ # f.input_text 'Username', :username
219
+ # end
220
+ #
221
+ def input_text(label, name, args = {})
222
+ # The ID can come from 2 places, id_for and the args hash
223
+ id = args[:id] ? args[:id] : id_for(name)
224
+ args = args.merge(:type => :text, :name => name, :id => id)
225
+
226
+ if !args[:value] and @form_values.respond_to?(name)
227
+ args[:value] = @form_values.send(name)
228
+ end
68
229
 
69
230
  @g.p do
70
231
  label_for(id, label, name)
@@ -73,10 +234,29 @@ module Ramaze
73
234
  end
74
235
  alias text input_text
75
236
 
76
- def input_password(label, name)
77
- id = id_for(name)
78
- args = {:type => :password, :name => name, :class => 'text', :id => id}
79
-
237
+ ##
238
+ # Generate an input tag with a type of "password" along with a label.
239
+ # Password fields are pretty much the same as text fields except that the content of these fields is replaced with dots.
240
+ # This method has the following alias: "password".
241
+ #
242
+ # @param [String] label The text to display inside the label tag.
243
+ # @param [String Symbol] name The name of the password field.
244
+ # @param [Hash] args Any additional HTML attributes along with their values.
245
+ # @example
246
+ #
247
+ # form_for(@data, :method => :post) do |f|
248
+ # f.input_password 'My password', :password
249
+ # end
250
+ #
251
+ def input_password(label, name, args = {})
252
+ # The ID can come from 2 places, id_for and the args hash
253
+ id = args[:id] ? args[:id] : id_for(name)
254
+ args = args.merge(:type => :password, :name => name, :id => id)
255
+
256
+ if !args[:value] and @form_values.respond_to?(name)
257
+ args[:value] = @form_values.send(name)
258
+ end
259
+
80
260
  @g.p do
81
261
  label_for(id, label, name)
82
262
  @g.input(args)
@@ -84,8 +264,20 @@ module Ramaze
84
264
  end
85
265
  alias password input_password
86
266
 
87
- def input_submit(value = nil)
88
- args = {:type => :submit, :class => 'button submit'}
267
+ ##
268
+ # Generate a submit tag (without a label). A submit tag is a button that once it's clicked
269
+ # will send the form data to the server.
270
+ #
271
+ # @param [String] value The text to display in the button.
272
+ # @param [Hash] args Any additional HTML attributes along with their values.
273
+ # @example
274
+ #
275
+ # form_for(@data, :method => :post) do |f|
276
+ # f.input_submit 'Save'
277
+ # end
278
+ #
279
+ def input_submit(value = nil, args = {})
280
+ args = args.merge(:type => :submit)
89
281
  args[:value] = value unless value.nil?
90
282
 
91
283
  @g.p do
@@ -94,49 +286,193 @@ module Ramaze
94
286
  end
95
287
  alias submit input_submit
96
288
 
97
- def input_checkbox(label, name, checked = false)
98
- id = id_for(name)
99
- args = {:type => :checkbox, :name => name, :class => 'checkbox', :id => id}
100
- args[:checked] = 'checked' if checked
289
+ ##
290
+ # Generate an input tag with a type of "checkbox". This method will also
291
+ # generate a hidden field with the same name as the checkbox to ensure
292
+ # that the data is always submitted.
293
+ #
294
+ # If you want to have multiple checkboxes you can either use an array or a hash.
295
+ # In the case of an array the values will also be used as text for each checkbox.
296
+ # When using a hash the key will be displayed and the value will be the value of the
297
+ # checkbox. Example:
298
+ #
299
+ # @data = Class.new
300
+ # attr_reader :gender_arr
301
+ # attr_reader :gender_hash
302
+ #
303
+ # def initialize
304
+ # @gender_arr = ['male', 'female']
305
+ # @gender_hash = {"Male" => "male", "Female" => "female"}
306
+ # end
307
+ # end.new
308
+ #
309
+ # form_for(@data, :method => :post) do |f|
310
+ # f.input_checkbox "Gender", :gender_arr
311
+ # f.input_checkbox "Gender", :gender_hash
312
+ # end
313
+ #
314
+ # @param [String] label The text to display inside the label tag.
315
+ # @param [String Symbol] name The name of the checkbox.
316
+ # @param [String] checked String that indicates if (and which) checkbox should be checked.
317
+ # @param [Hash] args Any additional HTML attributes along with their values.
318
+ # @example
319
+ #
320
+ # form_for(@data, :method => :post) do |f|
321
+ # f.input_checkbox 'Remember me', :remember_user
322
+ # end
323
+ #
324
+ def input_checkbox(label, name, checked = nil, args = {})
325
+ id = args[:id] ? args[:id] : "#{id_for(name)}_0"
326
+
327
+ # Get the default value for the checkbox used for the hidden field.
328
+ if args[:default]
329
+ default = args[:default]
330
+ args.delete(:default)
331
+ else
332
+ default = 0
333
+ end
101
334
 
102
- @g.p do
103
- label_for(id, label, name)
104
- @g.input(args)
335
+ # Get the checkbox value from either the args hash or from
336
+ # the form object (as specified in the form_for() method).
337
+ if !args[:values] and @form_values.respond_to?(name)
338
+ args[:values] = @form_values.send(name)
339
+ end
340
+
341
+ # That class for each element wrapper (a span tag) can be customized
342
+ # using :span_class => "a_class".
343
+ if args[:span_class]
344
+ span_class = args[:span_class]
345
+ args.delete(:span_class)
346
+ else
347
+ span_class = "checkbox_wrap"
348
+ end
349
+
350
+ # Get the type from the args hash instead of pre-defining it. Doing so means we can use
351
+ # this method for the input_radio method.
352
+ if !args[:type]
353
+ args[:type] = :checkbox
354
+ end
355
+
356
+ # Convert the values to an array if it's something we can't use in a loop (e.g. a string).
357
+ if args[:values].class != Hash and args[:values].class != Array
358
+ args[:values] = [args[:values]]
359
+ end
360
+
361
+ # Create a checkbox for each value
362
+ if !args[:values].empty?
363
+ @g.p do
364
+ # Let's create the label and the hidden field
365
+ label_for(id, label, name)
366
+ self.input_hidden(name, default)
367
+
368
+ # Loop through all the values. Each checkbox will have an ID of "form-NAME-INDEX".
369
+ # Each name will be NAME followed by [] to indicate it's an array (since multiple values are possible).
370
+ args[:values].each_with_index do |value, index|
371
+ id = args[:id] ? args[:id] : "#{id_for(name)}_#{index}"
372
+
373
+ if args[:type] == :checkbox
374
+ checkbox_name = "#{name}[]"
375
+ else
376
+ checkbox_name = name
377
+ end
378
+
379
+ # Copy all additional attributes and their values except the values array.
380
+ opts = args.clone
381
+ opts.delete(:values)
382
+
383
+ # Get the value and text to display for each checkbox
384
+ if value.class == Array
385
+ checkbox_text = value[0]
386
+ checkbox_value = value[1]
387
+ else
388
+ checkbox_text = checkbox_value = value
389
+ end
390
+
391
+ # Let's see if the current item is checked
392
+ if checkbox_value == checked
393
+ opts[:checked] = 'checked'
394
+ end
395
+
396
+ # And we're done, easy wasn't it?
397
+ opts = opts.merge(:name => checkbox_name, :id => id, :value => checkbox_value)
398
+
399
+ # Generate the following HTML:
400
+ #
401
+ # <span class="#{span_class}">
402
+ # <input type="checkbox" name="#{checkbox_name}" id="#{id}" value="#{value}" /> #{value}
403
+ # </span>
404
+ #
405
+ @g.span :class => span_class do
406
+ @g.input(opts)
407
+ " #{checkbox_text}"
408
+ end
409
+ end
410
+ end
105
411
  end
106
412
  end
107
413
  alias checkbox input_checkbox
108
414
 
109
- def input_radio(label, name, values, options = {})
110
- has_checked, checked = options.key?(:checked), options[:checked]
111
-
112
- @g.p do
113
- values.each_with_index do |(value, o_name), index|
114
- o_name ||= value
115
- id = id_for("#{name}-#{index}")
116
-
117
- o_args = {:type => :radio, :value => value, :id => id, :name => name}
118
- o_args[:checked] = 'checked' if has_checked && value == checked
119
-
120
- if error = @form_errors.delete(name.to_s)
121
- @g.label(:for => id){
122
- @g.span(:class => :error){ error }
123
- @g.input(o_args)
124
- @g.out << o_name
125
- }
126
- else
127
- @g.label(:for => id){
128
- @g.input(o_args)
129
- @g.out << o_name
130
- }
131
- end
132
- end
415
+ ##
416
+ # Generate an input tag with a type of "radio". This method will also
417
+ # generate a hidden field with the same name as the radio button to ensure
418
+ # that the data is always submitted.
419
+ #
420
+ # If you want to generate multiple radio buttons you can use an array just like
421
+ # you can with checkboxes. Example:
422
+ #
423
+ # @data = Class.new
424
+ # attr_reader :gender_arr
425
+ # attr_reader :gender_hash
426
+ #
427
+ # def initialize
428
+ # @gender_arr = ['male', 'female']
429
+ # @gender_hash = {"Male" => "male", "Female" => "female"}
430
+ # end
431
+ # end.new
432
+ #
433
+ # form_for(@data, :method => :post) do |f|
434
+ # f.input_radio "Gender", :gender_arr
435
+ # f.input_radio "Gender", :gender_hash
436
+ # end
437
+ #
438
+ # @param [String] label The text to display inside the label tag.
439
+ # @param [String Symbol] name The name of the radio button.
440
+ # @param [String] checked String that indicates if (and which) radio button should be checked.
441
+ # @param [Hash] args Any additional HTML attributes along with their values.
442
+ # @see input_checkbox()
443
+ # @example
444
+ #
445
+ # form_for(@data, :method => :post) do |f|
446
+ # f.input_radio 'Gender', :gender
447
+ # end
448
+ #
449
+ def input_radio(label, name, checked = nil, args = {})
450
+ # Force a type of "radio"
451
+ args[:type] = :radio
452
+
453
+ if !args[:span_class]
454
+ args[:span_class] = "radio_wrap"
133
455
  end
456
+
457
+ self.input_checkbox(label, name, checked, args)
134
458
  end
135
459
  alias radio input_radio
136
460
 
137
- def input_file(label, name)
138
- id = id_for(name)
139
- args = {:type => :file, :name => name, :class => 'file', :id => id}
461
+ ##
462
+ # Generate a field for uploading files.
463
+ #
464
+ # @param [String] label The text to display inside the label tag.
465
+ # @param [String Symbol] name The name of the radio tag.
466
+ # @param [Hash] args Any additional HTML attributes along with their values.
467
+ # @example
468
+ #
469
+ # form_for(@data, :method => :post) do |f|
470
+ # f.input_file 'Image', :image
471
+ # end
472
+ #
473
+ def input_file(label, name, args = {})
474
+ id = args[:id] ? args[:id] : id_for(name)
475
+ args = args.merge(:type => :file, :name => name, :id => id)
140
476
 
141
477
  @g.p do
142
478
  label_for(id, label, name)
@@ -145,17 +481,56 @@ module Ramaze
145
481
  end
146
482
  alias file input_file
147
483
 
148
- def input_hidden(name, value = nil)
149
- args = {:type => :hidden, :name => name}
150
- args[:value] = value.to_s unless value.nil?
484
+ ##
485
+ # Generate a hidden field. Hidden fields are essentially the same as text fields
486
+ # except that they aren't displayed in the browser.
487
+ #
488
+ # @param [String Symbol] name The name of the hidden field tag.
489
+ # @param [String] value The value of the hidden field
490
+ # @param [Hash] args Any additional HTML attributes along with their values.
491
+ # @example
492
+ #
493
+ # form_for(@data, :method => :post) do |f|
494
+ # f.input_hidden :user_id
495
+ # end
496
+ #
497
+ def input_hidden(name, value = nil, args = {})
498
+ args = args.merge(:type => :hidden, :name => name)
499
+
500
+ if !value and @form_values.respond_to?(name)
501
+ args[:value] = @form_values.send(name)
502
+ else
503
+ args[:value] = value
504
+ end
151
505
 
152
506
  @g.input(args)
153
507
  end
154
508
  alias hidden input_hidden
155
509
 
156
- def textarea(label, name, value = nil)
157
- id = id_for(name)
158
- args = {:name => name, :id => id}
510
+ ##
511
+ # Generate a text area.
512
+ #
513
+ # @param [String] label The text to display inside the label tag.
514
+ # @param [String Symbol] name The name of the textarea.
515
+ # @param [Hash] args Any additional HTML attributes along with their values.
516
+ # @example
517
+ #
518
+ # form_for(@data, :method => :post) do |f|
519
+ # f.textarea 'Description', :description
520
+ # end
521
+ #
522
+ def textarea(label, name, args = {})
523
+ id = args[:id] ? args[:id] : id_for(name)
524
+
525
+ # Get the value of the textarea
526
+ if !args[:value] and @form_values.respond_to?(name)
527
+ value = @form_values.send(name)
528
+ else
529
+ value = args[:value]
530
+ args.delete(:value)
531
+ end
532
+
533
+ args = args.merge(:name => name, :id => id)
159
534
 
160
535
  @g.p do
161
536
  label_for(id, label, name)
@@ -163,16 +538,39 @@ module Ramaze
163
538
  end
164
539
  end
165
540
 
166
- def select(label, name, values, options = {})
167
- id = id_for(name)
168
- multiple, size = options.values_at(:multiple, :size)
169
-
170
- args = {:id => id}
541
+ ##
542
+ # Generate a select tag along with the option tags and a label.
543
+ #
544
+ # @param [String] label The text to display inside the label tag.
545
+ # @param [String Symbol] name The name of the select tag.
546
+ # @param [Hash] args Hash containing additional HTML attributes.
547
+ # @example
548
+ #
549
+ # form_for(@data, :method => :post) do |f|
550
+ # f.select 'Country', :country_list
551
+ # end
552
+ #
553
+ def select(label, name, args = {})
554
+ id = args[:id] ? args[:id] : id_for(name)
555
+ multiple, size = args.values_at(:multiple, :size)
556
+
557
+ # Get all the values
558
+ if !args[:values] and @form_values.respond_to?(name)
559
+ values = @form_values.send(name)
560
+ else
561
+ values = args[:values]
562
+ args.delete(:values)
563
+ end
564
+
171
565
  args[:multiple] = 'multiple' if multiple
172
- args[:size] = (size || multiple || 1).to_i
173
- args[:name] = multiple ? "#{name}[]" : name
566
+ args[:size] = (size || values.count || 1).to_i
567
+ args[:name] = multiple ? "#{name}[]" : name
568
+ args = args.merge(:id => id)
174
569
 
175
- has_selected, selected = options.key?(:selected), options[:selected]
570
+ # Retrieve the selected value
571
+ has_selected, selected = args.key?(:selected), args[:selected]
572
+ selected = [selected] if !selected.is_a?(Array)
573
+ args.delete(:selected)
176
574
 
177
575
  @g.p do
178
576
  label_for(id, label, name)
@@ -180,19 +578,35 @@ module Ramaze
180
578
  values.each do |value, o_name|
181
579
  o_name ||= value
182
580
  o_args = {:value => value}
183
- o_args[:selected] = 'selected' if has_selected && value == selected
581
+
582
+ if has_selected and selected.include?(value)
583
+ o_args[:selected] = 'selected'
584
+ end
585
+
184
586
  @g.option(o_args){ o_name }
185
587
  end
186
588
  end
187
589
  end
188
590
  end
189
591
 
592
+ ##
593
+ # Method used for converting the results of the BlueForm helper to a string
594
+ #
595
+ # @return [String] The form output
596
+ #
190
597
  def to_s
191
598
  @g.to_s
192
599
  end
193
600
 
194
601
  private
195
602
 
603
+ ##
604
+ # Generate a label based on the id and value.
605
+ #
606
+ # @param [String] id The ID to which the label belongs.
607
+ # @param [String] value The text to display inside the label tag.
608
+ # @param [String] name The name of the field to which the label belongs.
609
+ #
196
610
  def label_for(id, value, name)
197
611
  if error = @form_errors.delete(name.to_s)
198
612
  @g.label("#{value} ", :for => id){ @g.span(:class => :error){ error } }
@@ -201,11 +615,17 @@ module Ramaze
201
615
  end
202
616
  end
203
617
 
618
+ ##
619
+ # Generate a value for an ID tag based on the field's name.
620
+ #
621
+ # @param [String] field_name The name of the field.
622
+ # @return [String] The ID for the specified field name.
623
+ #
204
624
  def id_for(field_name)
205
625
  if name = @form_args[:name]
206
- "#{name}-#{field_name}".downcase.gsub(/_/, '-')
626
+ "#{name}_#{field_name}".downcase.gsub(/-/, '_')
207
627
  else
208
- "form-#{field_name}".downcase.gsub(/_/, '-')
628
+ "form_#{field_name}".downcase.gsub(/-/, '_')
209
629
  end
210
630
  end
211
631
  end