Module: Mack::ViewHelpers::FormHelpers

Defined in:
lib/mack/view_helpers/form_helpers.rb,
lib/mack/view_helpers/date_time_helpers.rb

Overview

A useful collection of helpers for forms.

Defined Under Namespace

Classes: FormElement

Constant Summary collapse

MONTHS =
[["January", 1], ["February", 2], ["March", 3], ["April", 4], ["May", 5], ["June", 6], ["July", 7], ["August", 8], 
["September", 9], ["October", 10], ["November", 11], ["December", 12]]
DAYS =
[]
HOURS =
[]
MINUTES =
[]

Instance Method Summary collapse

Instance Method Details

#check_box(name, *args) ⇒ Object

Examples:

@user = User.new(:accepted_tos => true)
<%= check_box :user, :accepted_tos %> # => <input checked="checked" id="user_accepted_tos" name="user[accepted_tos]" type="checkbox" />
<%= check_box :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="checkbox" />


112
113
114
115
116
117
118
119
# File 'lib/mack/view_helpers/form_helpers.rb', line 112

def check_box(name, *args)
  build_form_element(name, {:type => :checkbox}, *args) do |var, fe, options|
    if options[:value]
      options.merge!(:checked => "checked")
    end
    options.delete(:value)
  end
end

#date_select(name, *args) ⇒ Object

Used just like date_time_select, but it has hours, minutes, and seconds turned off. See date_time_select for more options.

@user = User.new
<%= :user.date_select :birth_date %>
@some_time = Time.new
<%= :date_select :some_time, :label => true %>


99
100
101
102
# File 'lib/mack/view_helpers/date_time_helpers.rb', line 99

def date_select(name, *args)
  fe = FormElement.new(*args)
  date_time_select(name, fe.calling_method, {:hours => false, :minutes => false, :seconds => false}.merge(fe.options))
end

#date_time_select(name, *args) ⇒ Object

This will create a series of select boxes that compromise a time object. By default boxes will be created for day/month/year hour:minute. You can optionally turn on or off any of these boxes, including seconds by setting them to true/false. For example:

<%= date_time_select :some_time, :days => false %>

will not produce a select box for days, and so on…

You can pass in an array of arrays to represent the options for any of the boxes like such:

<%= date_time_select :some_time, :day_options => [[1,"one"], [2,"two"]] %>

Will produce a day select box with only two options. Alternatively you can pass in an array of values and the options will be done for you. Like such:

<%= date_time_select :some_time, :day_values => 1..60 %>

Will produce a day select box with 60 options whose values and keys will be the same.

The separators for dates and times can be set with the date_separator and time_separator options. By default they are:

:date_separator => '/'
:time_separator => ':'


43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'lib/mack/view_helpers/date_time_helpers.rb', line 43

def date_time_select(name, *args)
  var = instance_variable_get("@#{name}")
  fe = FormElement.new(*args)
  
  time = var if var.is_a?(Time) || var.is_a?(Date)
  time = var.nil? ? Time.now : (var.send(fe.calling_method) || Time.now) if time.nil?
  
  years = []
  (time.year - 5).upto(time.year + 5) { |y| years << [y, y]}
  
  options = {:years => true, :months => true, :days => true, :hours => true, :minutes => true, :seconds => false, :year_options => years, :month_options => MONTHS, :day_options => DAYS, :hour_options => HOURS, :minute_options => MINUTES, :second_options => MINUTES, :date_separator => '/', :time_separator => ':', :date_order => [:month, :day, :year], :time_order => [:hour, :minute, :second], :date_time_order => [:date, :time]}.merge(fe.options)
  
  [:year, :month, :day, :hour, :minute, :second].each do |v|
    if options["#{v}_values".to_sym]
      options["#{v}_options".to_sym] = []
      options["#{v}_values".to_sym].each {|i| options["#{v}_options".to_sym] << [i, i]}
    end
  end
  
  fe.options - [:years, :months, :days, :hours, :minutes, :seconds, :year_options, :month_options, :day_options, :hour_options, :minute_options, :second_options, :year_values, :month_values, :day_values, :hour_values, :minute_values, :second_values, :date_order, :time_order, :date_time_order]
  
  label = label_parameter_tag(name, (fe.calling_method == :to_s ? name : "#{name}_#{fe.calling_method}"), var, fe)

  
  date_boxes = []
  options[:date_order].collect! { |s| s.to_sym }
  date_boxes[options[:date_order].index(:month)] = dt_select(:month, name, fe, time.month, options[:month_options]) if options[:months]
  date_boxes[options[:date_order].index(:day)] = dt_select(:day, name, fe, time.day, options[:day_options]) if options[:days]
  date_boxes[options[:date_order].index(:year)] = dt_select(:year, name, fe, time.year, options[:year_options]) if options[:years]
  date_boxes.compact!
  
  time_boxes = []
  options[:time_order].collect! { |s| s.to_sym }
  time_boxes[options[:time_order].index(:hour)] = dt_select(:hour, name, fe, time.hour, options[:hour_options]) if options[:hours]
  time_boxes[options[:time_order].index(:minute)] = dt_select(:minute, name, fe, time.min, options[:minute_options]) if options[:minutes]
  time_boxes[options[:time_order].index(:second)] = dt_select(:second, name, fe, time.sec, options[:second_options]) if options[:seconds]
  time_boxes.compact!
  
  elts = []
  elts[options[:date_time_order].index(:date)] = date_boxes.join(options[:date_separator])
  unless elts.empty?
    elts[options[:date_time_order].index(:time)] = time_boxes.join(options[:time_separator])
  end
  elts.compact!
  
  boxes = elts.join(" ").strip
  return label + boxes
end

#delete_button(url, value = "Delete", form_options = {}, button_options = {}) ⇒ Object

Generates a button with a form around it and will set the request method to delete.



55
56
57
58
59
60
# File 'lib/mack/view_helpers/form_helpers.rb', line 55

def delete_button(url, value = "Delete", form_options = {}, button_options = {})
  t = "\n" << hidden_field(:_method, :value => :delete)
  t << "\n" << submit_button(value, button_options)
  t << "\n"
  (:form, {:action => url, :method => :post}.merge(form_options), t)
end

#file_field(name, *args) ⇒ Object

Examples:

@user = User.new(:bio_file => "~/bio.doc")
<%= file_field :user, :bio_file %> # => <input id="user_bio_field" name="user[bio_field]" type="file" value="~/bio.doc" />
<%= file_field :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="file" value="" />


125
126
127
# File 'lib/mack/view_helpers/form_helpers.rb', line 125

def file_field(name, *args)
  build_form_element(name, {:type => :file}, *args)
end

#form(action, options = {}) {|form_builder| ... } ⇒ Object

Examples:

<% form(users_create_url) do -%>
  # form stuff here...
<% end -%>

<% form(users_update_url, :method => :put) do -%>
  # form stuff here...
<% end -%>

<% form(photos_create_url, :multipart => true) do -%>
  # form stuff here...
<% end -%>

Yields:

  • (form_builder)


31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/mack/view_helpers/form_helpers.rb', line 31

def form(action, options = {}, &block)
  options = {:method => :post, :action => action}.merge(options)
  form_builder = options.delete(:builder) || configatron.mack.default_form_builder.camelcase.constantize.new(Thread.current[:view_template])
  if options[:id]
    options = {:class => options[:id]}.merge(options)
  end
  if options[:multipart]
    options = {:enctype => "multipart/form-data"}.merge(options)
    options.delete(:multipart)
  end
  meth = nil
  unless options[:method] == :get || options[:method] == :post
    meth = "<input name=\"_method\" type=\"hidden\" value=\"#{options[:method]}\" />\n"
    options[:method] = :post
  end
  concat("<form#{build_options(options)}>\n", block.binding)
  concat(meth, block.binding) unless meth.blank?
  concat(form_authenticity_field, block.binding) unless configatron.mack.disable_forgery_detector
  yield form_builder
  concat("\n</form>", block.binding)
  # content_tag(:form, options, &block)
end

#form_authenticity_fieldObject

Get the secret token to be added in an HTML form. This is to ensure that your form is valid.

Only call this method if you generate the form manually. If you use the form() method to generate your form, then the authenticity token is already included in your form.



14
15
16
# File 'lib/mack/view_helpers/form_helpers.rb', line 14

def form_authenticity_field
  str = %{<input type="hidden" name="__authenticity_token" value="#{Mack::Utils::AuthenticityTokenDispenser.instance.dispense_token(request.session.id)}" />\n}
end

#hidden_field(name, *args) ⇒ Object

Examples:

@user = User.new(:email => "[email protected]")
<%= hidden_field :user, :email %> # => <input id="user_email" name="user[email]" type="hidden" value="[email protected]" />
<%= hidden_field :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="hidden" />


133
134
135
# File 'lib/mack/view_helpers/form_helpers.rb', line 133

def hidden_field(name, *args)
  build_form_element(name, {:type => :hidden}, *args)
end

#image_submit(src, options = {}) ⇒ Object

Examples:

<%= image_submit "logo.png" %> # => <input src="/images/logo.png" type="image" />


139
140
141
# File 'lib/mack/view_helpers/form_helpers.rb', line 139

def image_submit(src, options = {})
  (:input, {:type => :image, :src => "/images/#{src}"}.merge(options))
end

#label_tag(name, *args) ⇒ Object

Examples:

@user = User.new
<%= label_tag :user, :email %> # => <label for="user_email">Email</label>
<%= label_tag :i_dont_exist %> # => <label for="i_dont_exist">I don't exist</label>
<%= label_tag :i_dont_exist, :value => "Hello" %> # => <label for="i_dont_exist">Hello</label>


249
250
251
252
253
254
255
256
257
258
259
260
# File 'lib/mack/view_helpers/form_helpers.rb', line 249

def label_tag(name, *args)
  fe = FormElement.new(*args)
  unless fe.options[:for]
    fe.options[:for] = (fe.calling_method == :to_s ? name.to_s : "#{name}_#{fe.calling_method}")
  end
  unless fe.options[:value]
    fe.options[:value] = (fe.calling_method == :to_s ? name.to_s.humanize : fe.calling_method.to_s.humanize)
  end
  content = fe.options[:value]
  fe.options.delete(:value)
  (:label, fe.options, content)
end

#password_field(name, *args) ⇒ Object

Examples:

@user = User.new(:email => "[email protected]")
<%= password_field :user, :email %> # => <input id="user_email" name="user[email]" type="password" value="[email protected]" />
<%= password_field :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="password" />


223
224
225
# File 'lib/mack/view_helpers/form_helpers.rb', line 223

def password_field(name, *args)
  build_form_element(name, {:type => :password}, *args)
end

#radio_button(name, *args) ⇒ Object

Examples:

@user = User.new(:level => 1)
<%= radio_button :user, :level %> # => <input checked="checked" id="user_level" name="user[level]" type="radio" value="1" />
<%= radio_button :user, :level, :value => 2 %> # => <input id="user_level" name="user[level]" type="radio" value="2" />
<%= radio_button :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="radio" value="" />


232
233
234
235
236
237
238
239
240
241
242
# File 'lib/mack/view_helpers/form_helpers.rb', line 232

def radio_button(name, *args)
  build_form_element(name, {:type => :radio, :value => ""}, *args) do |var, fe, options|
    if fe.options[:value]
      if fe.options[:value] == options[:value]
        options.merge!(:checked => "checked")
      end
    elsif options[:value]
      options.merge!(:checked => "checked")
    end
  end
end

#select_tag(name, *args) ⇒ Object

Examples:

@user = User.new(:level => 1)
<%= select_tag :user, :level, :options => [["one", 1], ["two", 2]] %> # => <select id="user_level" name="user[level]"><option value="1" selected>one</option><option value="2" >two</option></select>
<%= select_tag :user :level, :options => {:one => 1, :two => 2} %> # => <select id="user_level" name="user[level]"><option value="1" selected>one</option><option value="2" >two</option></select>
<%= select_tag :i_dont_exist :options => [["one", 1], ["two", 2]], :selected => 1 %> # => <select id="i_dont_exist" name="i_dont_exist"><option value="1" selected>one</option><option value="2" >two</option></select>


148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/mack/view_helpers/form_helpers.rb', line 148

def select_tag(name, *args)
  var = instance_variable_get("@#{name}")
  fe = FormElement.new(*args)
  options = {:name => name, :id => name}
  unless fe.calling_method == :to_s
    options.merge!(:name => "#{name}[#{fe.calling_method}]", :id => "#{name}_#{fe.calling_method}")
  end

  content = ""

  opts = fe.options[:options]
  unless opts.nil?
    sopts = opts
    if opts.is_a?(Array)
    elsif opts.is_a?(Hash)
      sopts = []
      opts.sort.each do |k,v|
        sopts << [k, v]
      end
    else
      raise ArgumentError.new(":options must be either an Array of Arrays or a Hash!")
    end
    sel_value = var.send(fe.calling_method) if var
    sel_value = fe.options[:selected] if fe.options[:selected]
    sopts.each do |kv|
      selected = kv[1].to_s == sel_value.to_s ? "selected" : ""
      content << %{\n<option value="#{kv[1]}" #{selected}>#{kv[0]}</option>}
    end
    fe.options.delete(:selected)
    fe.options.delete(:options)
  end
  
  return label_parameter_tag(name, options[:id], var, fe) + (:select, options.merge(fe.options), content)
end

#submit_button(value = "Submit", options = {}, *original_args) ⇒ Object

Examples:

 <%= submit_button %> # => <input type="submit" value="Submit" />
 <%= submit_button "Login" %> # => <input type="submit" value="Login" />
 You can disable the button after clicking it. In essence, this will work as follows:
 <%= submit_button "Login", :disable_with => "Please wait..." %> 
  # => <input type="submit" value="Login" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();" />
Even though :disable_with will work on the onclick parameter, you can add your own onclick behaviour to the mix, as follows:
<%= submit_button "Login", :disable_with => "Please wait...", :onclick => "alert('test')" %> 
  # => <input type="submit" value="Login" onclick="this.disabled=true;this.value='Please wait...';alert('test');this.form.submit();" />

Please note that if the form.submit() returns false the button’s value will be restored to its initial value. This behaviour is acheived through the injection of a couple bits of JS into the onlick existing parameter. These bits are injected after the disabled value, and all existing onclick behaviour that you define in the :onlick option. The included JS bits are as follows:

"result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit())",
"if (result == false) { this.value = this.getAttribute('originalValue'); this.disabled = false }",
"return result;"


80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/mack/view_helpers/form_helpers.rb', line 80

def submit_button(value = "Submit", options = {}, *original_args)
  if options[:confirm]
    onclick = "if (confirm('#{options.delete(:confirm)}')) {submit();}; return false;"
    onclick << ";#{options.delete(:onclick)}" if options.has_key?(:onclick)
    options[:onclick] = onclick
  end
  # processing the disable with option, which will be embebed in the onclick parameter.
  if disable_with = options.delete(:disable_with)
    disable_with = "this.innerHTML='#{disable_with}'"
    
    # Making sure that we keep the content of the onclick option, should it exist.
    disable_with << ";#{options.delete(:onclick)}" if options.has_key?(:onclick)
    
    # Setting the onlick option.
    options[:onclick] = [
      "this.setAttribute('originalValue', this.innerHTML)",
      "this.disabled=true",
      disable_with,
      "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit())",
      "if (result == false) { this.innerHTML = this.getAttribute('originalValue'); this.disabled = false }",
      "return result;",
    ].join(";")
  end
  
  # non_content_tag(:input, {:type => :submit, :value => value}.merge(options))
  (:button, {:type => :submit}.merge(options), value)
end

#text_area(name, *args) ⇒ Object

Examples:

@user = User.new(:bio => "my bio here")
<%= text_area :user, :bio %> # => <textarea id="user_bio" name="user[bio]">my bio here</textarea>
<%= text_area :i_dont_exist %> # => <textarea id="i_dont_exist" name="i_dont_exist"></textarea>
<%= text_area :i_dont_exist :value => "hi there" %> # => <textarea id="i_dont_exist" name="i_dont_exist">hi there</textarea>


188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'lib/mack/view_helpers/form_helpers.rb', line 188

def text_area(name, *args)
  var = instance_variable_get("@#{name}")
  fe = FormElement.new(*args)
  options = {:name => name, :id => name, :cols => 60, :rows => 20}
  if var.nil?
    value = fe.options[:value]
    fe.options.delete(:value)
    return label_parameter_tag(name, options[:id], var, fe) + (:textarea, options.merge(fe.options), value)
  else
    unless fe.calling_method == :to_s
      options.merge!(:name => "#{name}[#{fe.calling_method}]", :id => "#{name}_#{fe.calling_method}")
    end
    options[:value] = var.send(fe.calling_method)
    
    yield var, fe, options if block_given?
    
    content = options[:value]
    options.delete(:value)
    
    return label_parameter_tag(name, options[:id], var, fe) + (:textarea, options.merge(fe.options), content)
  end
end

#text_field(name, *args) ⇒ Object

Examples:

@user = User.new(:email => "[email protected]")
<%= text_field :user, :email %> # => <input id="user_email" name="user[email]" type="text" value="[email protected]" />
<%= text_field :i_dont_exist %> # => <input id="i_dont_exist" name="i_dont_exist" type="text" />


215
216
217
# File 'lib/mack/view_helpers/form_helpers.rb', line 215

def text_field(name, *args)
  build_form_element(name, {:type => :text}, *args)
end