mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Merge pull request #4488 from rafaelfranca/av-refactor
ActionView::Helpers::FormHelper refactoring
This commit is contained in:
commit
57aaaa6197
29 changed files with 688 additions and 436 deletions
|
@ -16,9 +16,7 @@ module ActionView
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
%w(content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth|
|
module_eval "def content_tag(*) error_wrapping(super) end", __FILE__, __LINE__
|
||||||
module_eval "def #{meth}(*) error_wrapping(super) end", __FILE__, __LINE__
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag(type, options, *)
|
def tag(type, options, *)
|
||||||
tag_generate_errors?(options) ? error_wrapping(super) : super
|
tag_generate_errors?(options) ? error_wrapping(super) : super
|
||||||
|
|
|
@ -213,7 +213,7 @@ module ActionView
|
||||||
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
|
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
|
||||||
# all month choices are valid.
|
# all month choices are valid.
|
||||||
def date_select(object_name, method, options = {}, html_options = {})
|
def date_select(object_name, method, options = {}, html_options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_date_select_tag(options, html_options)
|
Tags::DateSelect.new(object_name, method, self, options, html_options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
|
# Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a
|
||||||
|
@ -251,7 +251,7 @@ module ActionView
|
||||||
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
|
# Note: If the day is not included as an option but the month is, the day will be set to the 1st to ensure that
|
||||||
# all month choices are valid.
|
# all month choices are valid.
|
||||||
def time_select(object_name, method, options = {}, html_options = {})
|
def time_select(object_name, method, options = {}, html_options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_time_select_tag(options, html_options)
|
Tags::TimeSelect.new(object_name, method, self, options, html_options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
|
# Returns a set of select tags (one for year, month, day, hour, and minute) pre-selected for accessing a
|
||||||
|
@ -287,7 +287,7 @@ module ActionView
|
||||||
#
|
#
|
||||||
# The selects are prepared for multi-parameter assignment to an Active Record object.
|
# The selects are prepared for multi-parameter assignment to an Active Record object.
|
||||||
def datetime_select(object_name, method, options = {}, html_options = {})
|
def datetime_select(object_name, method, options = {}, html_options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_datetime_select_tag(options, html_options)
|
Tags::DatetimeSelect.new(object_name, method, self, options, html_options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a set of html select-tags (one for year, month, day, hour, minute, and second) pre-selected with the
|
# Returns a set of html select-tags (one for year, month, day, hour, minute, and second) pre-selected with the
|
||||||
|
@ -974,66 +974,6 @@ module ActionView
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
module DateHelperInstanceTag
|
|
||||||
def to_date_select_tag(options = {}, html_options = {})
|
|
||||||
datetime_selector(options, html_options).select_date.html_safe
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_time_select_tag(options = {}, html_options = {})
|
|
||||||
datetime_selector(options, html_options).select_time.html_safe
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_datetime_select_tag(options = {}, html_options = {})
|
|
||||||
datetime_selector(options, html_options).select_datetime.html_safe
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def datetime_selector(options, html_options)
|
|
||||||
datetime = value(object) || default_datetime(options)
|
|
||||||
@auto_index ||= nil
|
|
||||||
|
|
||||||
options = options.dup
|
|
||||||
options[:field_name] = @method_name
|
|
||||||
options[:include_position] = true
|
|
||||||
options[:prefix] ||= @object_name
|
|
||||||
options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
|
|
||||||
|
|
||||||
DateTimeSelector.new(datetime, options, html_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def default_datetime(options)
|
|
||||||
return if options[:include_blank] || options[:prompt]
|
|
||||||
|
|
||||||
case options[:default]
|
|
||||||
when nil
|
|
||||||
Time.current
|
|
||||||
when Date, Time
|
|
||||||
options[:default]
|
|
||||||
else
|
|
||||||
default = options[:default].dup
|
|
||||||
|
|
||||||
# Rename :minute and :second to :min and :sec
|
|
||||||
default[:min] ||= default[:minute]
|
|
||||||
default[:sec] ||= default[:second]
|
|
||||||
|
|
||||||
time = Time.current
|
|
||||||
|
|
||||||
[:year, :month, :day, :hour, :min, :sec].each do |key|
|
|
||||||
default[key] ||= time.send(key)
|
|
||||||
end
|
|
||||||
|
|
||||||
Time.utc_time(
|
|
||||||
default[:year], default[:month], default[:day],
|
|
||||||
default[:hour], default[:min], default[:sec]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class InstanceTag #:nodoc:
|
|
||||||
include DateHelperInstanceTag
|
|
||||||
end
|
|
||||||
|
|
||||||
class FormBuilder
|
class FormBuilder
|
||||||
def date_select(method, options = {}, html_options = {})
|
def date_select(method, options = {}, html_options = {})
|
||||||
@template.date_select(@object_name, method, objectify_options(options), html_options)
|
@template.date_select(@object_name, method, objectify_options(options), html_options)
|
||||||
|
|
|
@ -3,6 +3,7 @@ require 'action_view/helpers/date_helper'
|
||||||
require 'action_view/helpers/tag_helper'
|
require 'action_view/helpers/tag_helper'
|
||||||
require 'action_view/helpers/form_tag_helper'
|
require 'action_view/helpers/form_tag_helper'
|
||||||
require 'action_view/helpers/active_model_helper'
|
require 'action_view/helpers/active_model_helper'
|
||||||
|
require 'action_view/helpers/tags'
|
||||||
require 'active_support/core_ext/class/attribute'
|
require 'active_support/core_ext/class/attribute'
|
||||||
require 'active_support/core_ext/hash/slice'
|
require 'active_support/core_ext/hash/slice'
|
||||||
require 'active_support/core_ext/object/blank'
|
require 'active_support/core_ext/object/blank'
|
||||||
|
@ -654,16 +655,7 @@ module ActionView
|
||||||
# 'Accept <a href="/terms">Terms</a>.'.html_safe
|
# 'Accept <a href="/terms">Terms</a>.'.html_safe
|
||||||
# end
|
# end
|
||||||
def label(object_name, method, content_or_options = nil, options = nil, &block)
|
def label(object_name, method, content_or_options = nil, options = nil, &block)
|
||||||
content_is_options = content_or_options.is_a?(Hash)
|
Tags::Label.new(object_name, method, self, content_or_options, options).render(&block)
|
||||||
if content_is_options || block_given?
|
|
||||||
options = content_or_options if content_is_options
|
|
||||||
text = nil
|
|
||||||
else
|
|
||||||
text = content_or_options
|
|
||||||
end
|
|
||||||
|
|
||||||
options ||= {}
|
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options, &block)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
|
# Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
|
||||||
|
@ -685,7 +677,7 @@ module ActionView
|
||||||
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
|
# # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
|
||||||
#
|
#
|
||||||
def text_field(object_name, method, options = {})
|
def text_field(object_name, method, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("text", options)
|
Tags::TextField.new(object_name, method, self, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
|
# Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
|
||||||
|
@ -707,7 +699,7 @@ module ActionView
|
||||||
# # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
|
# # => <input type="password" id="account_pin" name="account[pin]" size="20" class="form_input" />
|
||||||
#
|
#
|
||||||
def password_field(object_name, method, options = {})
|
def password_field(object_name, method, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("password", { :value => nil }.merge!(options))
|
Tags::PasswordField.new(object_name, method, self, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
|
# Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
|
||||||
|
@ -725,7 +717,7 @@ module ActionView
|
||||||
# hidden_field(:user, :token)
|
# hidden_field(:user, :token)
|
||||||
# # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
|
# # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
|
||||||
def hidden_field(object_name, method, options = {})
|
def hidden_field(object_name, method, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("hidden", options)
|
Tags::HiddenField.new(object_name, method, self, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
|
# Returns a file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
|
||||||
|
@ -746,7 +738,7 @@ module ActionView
|
||||||
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
|
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
|
||||||
#
|
#
|
||||||
def file_field(object_name, method, options = {})
|
def file_field(object_name, method, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("file", options.update({:size => nil}))
|
Tags::FileField.new(object_name, method, self, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
|
# Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
|
||||||
|
@ -774,7 +766,7 @@ module ActionView
|
||||||
# # #{@entry.body}
|
# # #{@entry.body}
|
||||||
# # </textarea>
|
# # </textarea>
|
||||||
def text_area(object_name, method, options = {})
|
def text_area(object_name, method, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_text_area_tag(options)
|
Tags::TextArea.new(object_name, method, self, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
|
# Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
|
||||||
|
@ -836,7 +828,7 @@ module ActionView
|
||||||
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
|
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
|
||||||
#
|
#
|
||||||
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
|
def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
|
Tags::CheckBox.new(object_name, method, self, checked_value, unchecked_value, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
|
# Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
|
||||||
|
@ -858,7 +850,7 @@ module ActionView
|
||||||
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
|
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
|
||||||
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
|
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
|
||||||
def radio_button(object_name, method, tag_value, options = {})
|
def radio_button(object_name, method, tag_value, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options)
|
Tags::RadioButton.new(object_name, method, self, tag_value, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
|
# Returns an input of type "search" for accessing a specified attribute (identified by +method+) on an object
|
||||||
|
@ -884,20 +876,7 @@ module ActionView
|
||||||
# # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" size="30" type="search" />
|
# # => <input autosave="com.example.www" id="user_name" incremental="true" name="user[name]" onsearch="true" results="10" size="30" type="search" />
|
||||||
#
|
#
|
||||||
def search_field(object_name, method, options = {})
|
def search_field(object_name, method, options = {})
|
||||||
options = options.stringify_keys
|
Tags::SearchField.new(object_name, method, self, options).render
|
||||||
|
|
||||||
if options["autosave"]
|
|
||||||
if options["autosave"] == true
|
|
||||||
options["autosave"] = request.host.split(".").reverse.join(".")
|
|
||||||
end
|
|
||||||
options["results"] ||= 10
|
|
||||||
end
|
|
||||||
|
|
||||||
if options["onsearch"]
|
|
||||||
options["incremental"] = true unless options.has_key?("incremental")
|
|
||||||
end
|
|
||||||
|
|
||||||
InstanceTag.new(object_name, method, self, options.delete("object")).to_input_field_tag("search", options)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a text_field of type "tel".
|
# Returns a text_field of type "tel".
|
||||||
|
@ -906,7 +885,7 @@ module ActionView
|
||||||
# # => <input id="user_phone" name="user[phone]" size="30" type="tel" />
|
# # => <input id="user_phone" name="user[phone]" size="30" type="tel" />
|
||||||
#
|
#
|
||||||
def telephone_field(object_name, method, options = {})
|
def telephone_field(object_name, method, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("tel", options)
|
Tags::TelField.new(object_name, method, self, options).render
|
||||||
end
|
end
|
||||||
alias phone_field telephone_field
|
alias phone_field telephone_field
|
||||||
|
|
||||||
|
@ -916,7 +895,7 @@ module ActionView
|
||||||
# # => <input id="user_homepage" size="30" name="user[homepage]" type="url" />
|
# # => <input id="user_homepage" size="30" name="user[homepage]" type="url" />
|
||||||
#
|
#
|
||||||
def url_field(object_name, method, options = {})
|
def url_field(object_name, method, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("url", options)
|
Tags::UrlField.new(object_name, method, self, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a text_field of type "email".
|
# Returns a text_field of type "email".
|
||||||
|
@ -925,7 +904,7 @@ module ActionView
|
||||||
# # => <input id="user_address" size="30" name="user[address]" type="email" />
|
# # => <input id="user_address" size="30" name="user[address]" type="email" />
|
||||||
#
|
#
|
||||||
def email_field(object_name, method, options = {})
|
def email_field(object_name, method, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("email", options)
|
Tags::EmailField.new(object_name, method, self, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an input tag of type "number".
|
# Returns an input tag of type "number".
|
||||||
|
@ -933,7 +912,7 @@ module ActionView
|
||||||
# ==== Options
|
# ==== Options
|
||||||
# * Accepts same options as number_field_tag
|
# * Accepts same options as number_field_tag
|
||||||
def number_field(object_name, method, options = {})
|
def number_field(object_name, method, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_number_field_tag("number", options)
|
Tags::NumberField.new(object_name, method, self, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns an input tag of type "range".
|
# Returns an input tag of type "range".
|
||||||
|
@ -941,7 +920,7 @@ module ActionView
|
||||||
# ==== Options
|
# ==== Options
|
||||||
# * Accepts same options as range_field_tag
|
# * Accepts same options as range_field_tag
|
||||||
def range_field(object_name, method, options = {})
|
def range_field(object_name, method, options = {})
|
||||||
InstanceTag.new(object_name, method, self, options.delete(:object)).to_number_field_tag("range", options)
|
Tags::RangeField.new(object_name, method, self, options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
@ -961,272 +940,6 @@ module ActionView
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class InstanceTag
|
|
||||||
include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
|
|
||||||
|
|
||||||
attr_reader :object, :method_name, :object_name
|
|
||||||
|
|
||||||
DEFAULT_FIELD_OPTIONS = { "size" => 30 }
|
|
||||||
DEFAULT_RADIO_OPTIONS = { }
|
|
||||||
DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }
|
|
||||||
|
|
||||||
def initialize(object_name, method_name, template_object, object = nil)
|
|
||||||
@object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
|
|
||||||
@template_object = template_object
|
|
||||||
|
|
||||||
@object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
|
|
||||||
@object = retrieve_object(object)
|
|
||||||
@auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_label_tag(text = nil, options = {}, &block)
|
|
||||||
options = options.stringify_keys
|
|
||||||
tag_value = options.delete("value")
|
|
||||||
name_and_id = options.dup
|
|
||||||
|
|
||||||
if name_and_id["for"]
|
|
||||||
name_and_id["id"] = name_and_id["for"]
|
|
||||||
else
|
|
||||||
name_and_id.delete("id")
|
|
||||||
end
|
|
||||||
|
|
||||||
add_default_name_and_id_for_value(tag_value, name_and_id)
|
|
||||||
options.delete("index")
|
|
||||||
options.delete("namespace")
|
|
||||||
options["for"] ||= name_and_id["id"]
|
|
||||||
|
|
||||||
if block_given?
|
|
||||||
@template_object.label_tag(name_and_id["id"], options, &block)
|
|
||||||
else
|
|
||||||
content = if text.blank?
|
|
||||||
object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1')
|
|
||||||
method_and_value = tag_value.present? ? "#{method_name}.#{tag_value}" : method_name
|
|
||||||
|
|
||||||
if object.respond_to?(:to_model)
|
|
||||||
key = object.class.model_name.i18n_key
|
|
||||||
i18n_default = ["#{key}.#{method_and_value}".to_sym, ""]
|
|
||||||
end
|
|
||||||
|
|
||||||
i18n_default ||= ""
|
|
||||||
I18n.t("#{object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence
|
|
||||||
else
|
|
||||||
text.to_s
|
|
||||||
end
|
|
||||||
|
|
||||||
content ||= if object && object.class.respond_to?(:human_attribute_name)
|
|
||||||
object.class.human_attribute_name(method_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
content ||= method_name.humanize
|
|
||||||
|
|
||||||
label_tag(name_and_id["id"], content, options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_input_field_tag(field_type, options = {})
|
|
||||||
options = options.stringify_keys
|
|
||||||
options["size"] = options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"] unless options.key?("size")
|
|
||||||
options = DEFAULT_FIELD_OPTIONS.merge(options)
|
|
||||||
if field_type == "hidden"
|
|
||||||
options.delete("size")
|
|
||||||
end
|
|
||||||
options["type"] ||= field_type
|
|
||||||
options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file"
|
|
||||||
options["value"] &&= ERB::Util.html_escape(options["value"])
|
|
||||||
add_default_name_and_id(options)
|
|
||||||
tag("input", options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_number_field_tag(field_type, options = {})
|
|
||||||
options = options.stringify_keys
|
|
||||||
options['size'] ||= nil
|
|
||||||
|
|
||||||
if range = options.delete("in") || options.delete("within")
|
|
||||||
options.update("min" => range.min, "max" => range.max)
|
|
||||||
end
|
|
||||||
to_input_field_tag(field_type, options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_radio_button_tag(tag_value, options = {})
|
|
||||||
options = DEFAULT_RADIO_OPTIONS.merge(options.stringify_keys)
|
|
||||||
options["type"] = "radio"
|
|
||||||
options["value"] = tag_value
|
|
||||||
if options.has_key?("checked")
|
|
||||||
cv = options.delete "checked"
|
|
||||||
checked = cv == true || cv == "checked"
|
|
||||||
else
|
|
||||||
checked = self.class.radio_button_checked?(value(object), tag_value)
|
|
||||||
end
|
|
||||||
options["checked"] = "checked" if checked
|
|
||||||
add_default_name_and_id_for_value(tag_value, options)
|
|
||||||
tag("input", options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_text_area_tag(options = {})
|
|
||||||
options = DEFAULT_TEXT_AREA_OPTIONS.merge(options.stringify_keys)
|
|
||||||
add_default_name_and_id(options)
|
|
||||||
|
|
||||||
if size = options.delete("size")
|
|
||||||
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
|
|
||||||
end
|
|
||||||
|
|
||||||
content_tag("textarea", ERB::Util.html_escape(options.delete('value') || value_before_type_cast(object)), options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_check_box_tag(options = {}, checked_value = "1", unchecked_value = "0")
|
|
||||||
options = options.stringify_keys
|
|
||||||
options["type"] = "checkbox"
|
|
||||||
options["value"] = checked_value
|
|
||||||
if options.has_key?("checked")
|
|
||||||
cv = options.delete "checked"
|
|
||||||
checked = cv == true || cv == "checked"
|
|
||||||
else
|
|
||||||
checked = self.class.check_box_checked?(value(object), checked_value)
|
|
||||||
end
|
|
||||||
options["checked"] = "checked" if checked
|
|
||||||
if options["multiple"]
|
|
||||||
add_default_name_and_id_for_value(checked_value, options)
|
|
||||||
options.delete("multiple")
|
|
||||||
else
|
|
||||||
add_default_name_and_id(options)
|
|
||||||
end
|
|
||||||
hidden = unchecked_value ? tag("input", "name" => options["name"], "type" => "hidden", "value" => unchecked_value, "disabled" => options["disabled"]) : ""
|
|
||||||
checkbox = tag("input", options)
|
|
||||||
hidden + checkbox
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_boolean_select_tag(options = {})
|
|
||||||
options = options.stringify_keys
|
|
||||||
add_default_name_and_id(options)
|
|
||||||
value = value(object)
|
|
||||||
tag_text = "<select"
|
|
||||||
tag_text << tag_options(options)
|
|
||||||
tag_text << "><option value=\"false\""
|
|
||||||
tag_text << " selected" if value == false
|
|
||||||
tag_text << ">False</option><option value=\"true\""
|
|
||||||
tag_text << " selected" if value
|
|
||||||
tag_text << ">True</option></select>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_content_tag(tag_name, options = {})
|
|
||||||
content_tag(tag_name, value(object), options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def retrieve_object(object)
|
|
||||||
if object
|
|
||||||
object
|
|
||||||
elsif @template_object.instance_variable_defined?("@#{@object_name}")
|
|
||||||
@template_object.instance_variable_get("@#{@object_name}")
|
|
||||||
end
|
|
||||||
rescue NameError
|
|
||||||
# As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
def retrieve_autoindex(pre_match)
|
|
||||||
object = self.object || @template_object.instance_variable_get("@#{pre_match}")
|
|
||||||
if object && object.respond_to?(:to_param)
|
|
||||||
object.to_param
|
|
||||||
else
|
|
||||||
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def value(object)
|
|
||||||
self.class.value(object, @method_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def value_before_type_cast(object)
|
|
||||||
self.class.value_before_type_cast(object, @method_name)
|
|
||||||
end
|
|
||||||
|
|
||||||
class << self
|
|
||||||
def value(object, method_name)
|
|
||||||
object.send method_name if object
|
|
||||||
end
|
|
||||||
|
|
||||||
def value_before_type_cast(object, method_name)
|
|
||||||
unless object.nil?
|
|
||||||
object.respond_to?(method_name + "_before_type_cast") ?
|
|
||||||
object.send(method_name + "_before_type_cast") :
|
|
||||||
object.send(method_name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def check_box_checked?(value, checked_value)
|
|
||||||
case value
|
|
||||||
when TrueClass, FalseClass
|
|
||||||
value
|
|
||||||
when NilClass
|
|
||||||
false
|
|
||||||
when Integer
|
|
||||||
value != 0
|
|
||||||
when String
|
|
||||||
value == checked_value
|
|
||||||
when Array
|
|
||||||
value.include?(checked_value)
|
|
||||||
else
|
|
||||||
value.to_i != 0
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def radio_button_checked?(value, checked_value)
|
|
||||||
value.to_s == checked_value.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def add_default_name_and_id_for_value(tag_value, options)
|
|
||||||
unless tag_value.nil?
|
|
||||||
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase
|
|
||||||
specified_id = options["id"]
|
|
||||||
add_default_name_and_id(options)
|
|
||||||
options["id"] += "_#{pretty_tag_value}" if specified_id.blank? && options["id"].present?
|
|
||||||
else
|
|
||||||
add_default_name_and_id(options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def add_default_name_and_id(options)
|
|
||||||
if options.has_key?("index")
|
|
||||||
options["name"] ||= tag_name_with_index(options["index"])
|
|
||||||
options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
|
|
||||||
options.delete("index")
|
|
||||||
elsif defined?(@auto_index)
|
|
||||||
options["name"] ||= tag_name_with_index(@auto_index)
|
|
||||||
options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
|
|
||||||
else
|
|
||||||
options["name"] ||= tag_name + (options['multiple'] ? '[]' : '')
|
|
||||||
options["id"] = options.fetch("id"){ tag_id }
|
|
||||||
end
|
|
||||||
options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_name
|
|
||||||
"#{@object_name}[#{sanitized_method_name}]"
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_name_with_index(index)
|
|
||||||
"#{@object_name}[#{index}][#{sanitized_method_name}]"
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_id
|
|
||||||
"#{sanitized_object_name}_#{sanitized_method_name}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def tag_id_with_index(index)
|
|
||||||
"#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def sanitized_object_name
|
|
||||||
@sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
|
||||||
end
|
|
||||||
|
|
||||||
def sanitized_method_name
|
|
||||||
@sanitized_method_name ||= @method_name.sub(/\?$/,"")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class FormBuilder
|
class FormBuilder
|
||||||
# The methods which wrap a form helper call.
|
# The methods which wrap a form helper call.
|
||||||
class_attribute :field_helpers
|
class_attribute :field_helpers
|
||||||
|
|
|
@ -154,7 +154,7 @@ module ActionView
|
||||||
# key in the query string, that works for ordinary forms.
|
# key in the query string, that works for ordinary forms.
|
||||||
#
|
#
|
||||||
def select(object, method, choices, options = {}, html_options = {})
|
def select(object, method, choices, options = {}, html_options = {})
|
||||||
InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options)
|
Tags::Select.new(object, method, self, choices, options, html_options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
|
# Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
|
||||||
|
@ -188,10 +188,9 @@ module ActionView
|
||||||
# <option value="3">M. Clark</option>
|
# <option value="3">M. Clark</option>
|
||||||
# </select>
|
# </select>
|
||||||
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
|
def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
|
||||||
InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
|
Tags::CollectionSelect.new(object, method, self, collection, value_method, text_method, options, html_options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
|
# Returns <tt><select></tt>, <tt><optgroup></tt> and <tt><option></tt> tags for the collection of existing return values of
|
||||||
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
|
# +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
|
||||||
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
|
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
|
||||||
|
@ -240,7 +239,7 @@ module ActionView
|
||||||
# </select>
|
# </select>
|
||||||
#
|
#
|
||||||
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
def grouped_collection_select(object, method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
|
||||||
InstanceTag.new(object, method, self, options.delete(:object)).to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
Tags::GroupedCollectionSelect.new(object, method, self, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Return select and option tags for the given object and method, using
|
# Return select and option tags for the given object and method, using
|
||||||
|
@ -274,7 +273,7 @@ module ActionView
|
||||||
#
|
#
|
||||||
# time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, :model => ActiveSupport::TimeZone)
|
# time_zone_select( "user", "time_zone", ActiveSupport::TimeZone.all.sort, :model => ActiveSupport::TimeZone)
|
||||||
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
|
def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
|
||||||
InstanceTag.new(object, method, self, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
|
Tags::TimeZoneSelect.new(object, method, self, priority_zones, options, html_options).render
|
||||||
end
|
end
|
||||||
|
|
||||||
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
|
# Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
|
||||||
|
@ -573,69 +572,6 @@ module ActionView
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
class InstanceTag #:nodoc:
|
|
||||||
include FormOptionsHelper
|
|
||||||
|
|
||||||
def to_select_tag(choices, options, html_options)
|
|
||||||
selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
|
|
||||||
|
|
||||||
# Grouped choices look like this:
|
|
||||||
#
|
|
||||||
# [nil, []]
|
|
||||||
# { nil => [] }
|
|
||||||
#
|
|
||||||
if !choices.empty? && choices.first.respond_to?(:last) && Array === choices.first.last
|
|
||||||
option_tags = grouped_options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
|
|
||||||
else
|
|
||||||
option_tags = options_for_select(choices, :selected => selected_value, :disabled => options[:disabled])
|
|
||||||
end
|
|
||||||
|
|
||||||
select_content_tag(option_tags, options, html_options)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_collection_select_tag(collection, value_method, text_method, options, html_options)
|
|
||||||
selected_value = options.has_key?(:selected) ? options[:selected] : value(object)
|
|
||||||
select_content_tag(
|
|
||||||
options_from_collection_for_select(collection, value_method, text_method, :selected => selected_value, :disabled => options[:disabled]), options, html_options
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_grouped_collection_select_tag(collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
|
||||||
select_content_tag(
|
|
||||||
option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, value(object)), options, html_options
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def to_time_zone_select_tag(priority_zones, options, html_options)
|
|
||||||
select_content_tag(
|
|
||||||
time_zone_options_for_select(value(object) || options[:default], priority_zones, options[:model] || ActiveSupport::TimeZone), options, html_options
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
def add_options(option_tags, options, value = nil)
|
|
||||||
if options[:include_blank]
|
|
||||||
option_tags = "<option value=\"\">#{ERB::Util.html_escape(options[:include_blank]) if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
|
|
||||||
end
|
|
||||||
if value.blank? && options[:prompt]
|
|
||||||
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
|
|
||||||
option_tags = "<option value=\"\">#{ERB::Util.html_escape(prompt)}</option>\n" + option_tags
|
|
||||||
end
|
|
||||||
option_tags.html_safe
|
|
||||||
end
|
|
||||||
|
|
||||||
def select_content_tag(option_tags, options, html_options)
|
|
||||||
html_options = html_options.stringify_keys
|
|
||||||
add_default_name_and_id(html_options)
|
|
||||||
select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
|
|
||||||
if html_options["multiple"]
|
|
||||||
tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
|
|
||||||
else
|
|
||||||
select
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class FormBuilder
|
class FormBuilder
|
||||||
def select(method, choices, options = {}, html_options = {})
|
def select(method, choices, options = {}, html_options = {})
|
||||||
@template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options))
|
@template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options))
|
||||||
|
|
28
actionpack/lib/action_view/helpers/tags.rb
Normal file
28
actionpack/lib/action_view/helpers/tags.rb
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
autoload :Base, 'action_view/helpers/tags/base'
|
||||||
|
autoload :Label, 'action_view/helpers/tags/label'
|
||||||
|
autoload :TextField, 'action_view/helpers/tags/text_field'
|
||||||
|
autoload :PasswordField, 'action_view/helpers/tags/password_field'
|
||||||
|
autoload :HiddenField, 'action_view/helpers/tags/hidden_field'
|
||||||
|
autoload :FileField, 'action_view/helpers/tags/file_field'
|
||||||
|
autoload :SearchField, 'action_view/helpers/tags/search_field'
|
||||||
|
autoload :TelField, 'action_view/helpers/tags/tel_field'
|
||||||
|
autoload :UrlField, 'action_view/helpers/tags/url_field'
|
||||||
|
autoload :EmailField, 'action_view/helpers/tags/email_field'
|
||||||
|
autoload :NumberField, 'action_view/helpers/tags/number_field'
|
||||||
|
autoload :RangeField, 'action_view/helpers/tags/range_field'
|
||||||
|
autoload :TextArea, 'action_view/helpers/tags/text_area'
|
||||||
|
autoload :CheckBox, 'action_view/helpers/tags/check_box'
|
||||||
|
autoload :RadioButton, 'action_view/helpers/tags/radio_button'
|
||||||
|
autoload :Select, 'action_view/helpers/tags/select'
|
||||||
|
autoload :CollectionSelect, 'action_view/helpers/tags/collection_select'
|
||||||
|
autoload :GroupedCollectionSelect, 'action_view/helpers/tags/grouped_collection_select'
|
||||||
|
autoload :TimeZoneSelect, 'action_view/helpers/tags/time_zone_select'
|
||||||
|
autoload :DateSelect, 'action_view/helpers/tags/date_select'
|
||||||
|
autoload :TimeSelect, 'action_view/helpers/tags/time_select'
|
||||||
|
autoload :DatetimeSelect, 'action_view/helpers/tags/datetime_select'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
134
actionpack/lib/action_view/helpers/tags/base.rb
Normal file
134
actionpack/lib/action_view/helpers/tags/base.rb
Normal file
|
@ -0,0 +1,134 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class Base #:nodoc:
|
||||||
|
include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
|
||||||
|
include FormOptionsHelper
|
||||||
|
|
||||||
|
DEFAULT_FIELD_OPTIONS = { "size" => 30 }
|
||||||
|
|
||||||
|
attr_reader :object
|
||||||
|
|
||||||
|
def initialize(object_name, method_name, template_object, options = {})
|
||||||
|
@object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
|
||||||
|
@template_object = template_object
|
||||||
|
|
||||||
|
@object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
|
||||||
|
@object = retrieve_object(options.delete(:object))
|
||||||
|
@options = options
|
||||||
|
@auto_index = retrieve_autoindex(Regexp.last_match.pre_match) if Regexp.last_match
|
||||||
|
end
|
||||||
|
|
||||||
|
def render(&block)
|
||||||
|
raise "Abstract Method called"
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def value(object)
|
||||||
|
object.send @method_name if object
|
||||||
|
end
|
||||||
|
|
||||||
|
def value_before_type_cast(object)
|
||||||
|
unless object.nil?
|
||||||
|
object.respond_to?(@method_name + "_before_type_cast") ?
|
||||||
|
object.send(@method_name + "_before_type_cast") :
|
||||||
|
object.send(@method_name)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def retrieve_object(object)
|
||||||
|
if object
|
||||||
|
object
|
||||||
|
elsif @template_object.instance_variable_defined?("@#{@object_name}")
|
||||||
|
@template_object.instance_variable_get("@#{@object_name}")
|
||||||
|
end
|
||||||
|
rescue NameError
|
||||||
|
# As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def retrieve_autoindex(pre_match)
|
||||||
|
object = self.object || @template_object.instance_variable_get("@#{pre_match}")
|
||||||
|
if object && object.respond_to?(:to_param)
|
||||||
|
object.to_param
|
||||||
|
else
|
||||||
|
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_default_name_and_id_for_value(tag_value, options)
|
||||||
|
unless tag_value.nil?
|
||||||
|
pretty_tag_value = tag_value.to_s.gsub(/\s/, "_").gsub(/[^-\w]/, "").downcase
|
||||||
|
specified_id = options["id"]
|
||||||
|
add_default_name_and_id(options)
|
||||||
|
options["id"] += "_#{pretty_tag_value}" if specified_id.blank? && options["id"].present?
|
||||||
|
else
|
||||||
|
add_default_name_and_id(options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_default_name_and_id(options)
|
||||||
|
if options.has_key?("index")
|
||||||
|
options["name"] ||= tag_name_with_index(options["index"])
|
||||||
|
options["id"] = options.fetch("id"){ tag_id_with_index(options["index"]) }
|
||||||
|
options.delete("index")
|
||||||
|
elsif defined?(@auto_index)
|
||||||
|
options["name"] ||= tag_name_with_index(@auto_index)
|
||||||
|
options["id"] = options.fetch("id"){ tag_id_with_index(@auto_index) }
|
||||||
|
else
|
||||||
|
options["name"] ||= tag_name + (options['multiple'] ? '[]' : '')
|
||||||
|
options["id"] = options.fetch("id"){ tag_id }
|
||||||
|
end
|
||||||
|
options["id"] = [options.delete('namespace'), options["id"]].compact.join("_").presence
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_name
|
||||||
|
"#{@object_name}[#{sanitized_method_name}]"
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_name_with_index(index)
|
||||||
|
"#{@object_name}[#{index}][#{sanitized_method_name}]"
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_id
|
||||||
|
"#{sanitized_object_name}_#{sanitized_method_name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_id_with_index(index)
|
||||||
|
"#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
|
||||||
|
end
|
||||||
|
|
||||||
|
def sanitized_object_name
|
||||||
|
@sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
|
||||||
|
end
|
||||||
|
|
||||||
|
def sanitized_method_name
|
||||||
|
@sanitized_method_name ||= @method_name.sub(/\?$/,"")
|
||||||
|
end
|
||||||
|
|
||||||
|
def select_content_tag(option_tags, options, html_options)
|
||||||
|
html_options = html_options.stringify_keys
|
||||||
|
add_default_name_and_id(html_options)
|
||||||
|
select = content_tag("select", add_options(option_tags, options, value(object)), html_options)
|
||||||
|
if html_options["multiple"]
|
||||||
|
tag("input", :disabled => html_options["disabled"], :name => html_options["name"], :type => "hidden", :value => "") + select
|
||||||
|
else
|
||||||
|
select
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def add_options(option_tags, options, value = nil)
|
||||||
|
if options[:include_blank]
|
||||||
|
option_tags = "<option value=\"\">#{ERB::Util.html_escape(options[:include_blank]) if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
|
||||||
|
end
|
||||||
|
if value.blank? && options[:prompt]
|
||||||
|
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
|
||||||
|
option_tags = "<option value=\"\">#{ERB::Util.html_escape(prompt)}</option>\n" + option_tags
|
||||||
|
end
|
||||||
|
option_tags.html_safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
54
actionpack/lib/action_view/helpers/tags/check_box.rb
Normal file
54
actionpack/lib/action_view/helpers/tags/check_box.rb
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
require 'action_view/helpers/tags/checkable'
|
||||||
|
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class CheckBox < Base #:nodoc:
|
||||||
|
include Checkable
|
||||||
|
|
||||||
|
def initialize(object_name, method_name, template_object, checked_value, unchecked_value, options)
|
||||||
|
@checked_value = checked_value
|
||||||
|
@unchecked_value = unchecked_value
|
||||||
|
super(object_name, method_name, template_object, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
options = @options.stringify_keys
|
||||||
|
options["type"] = "checkbox"
|
||||||
|
options["value"] = @checked_value
|
||||||
|
options["checked"] = "checked" if input_checked?(object, options)
|
||||||
|
|
||||||
|
if options["multiple"]
|
||||||
|
add_default_name_and_id_for_value(@checked_value, options)
|
||||||
|
options.delete("multiple")
|
||||||
|
else
|
||||||
|
add_default_name_and_id(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
hidden = @unchecked_value ? tag("input", "name" => options["name"], "type" => "hidden", "value" => @unchecked_value, "disabled" => options["disabled"]) : ""
|
||||||
|
checkbox = tag("input", options)
|
||||||
|
hidden + checkbox
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def checked?(value)
|
||||||
|
case value
|
||||||
|
when TrueClass, FalseClass
|
||||||
|
value
|
||||||
|
when NilClass
|
||||||
|
false
|
||||||
|
when Integer
|
||||||
|
value != 0
|
||||||
|
when String
|
||||||
|
value == @checked_value
|
||||||
|
when Array
|
||||||
|
value.include?(@checked_value)
|
||||||
|
else
|
||||||
|
value.to_i != 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
16
actionpack/lib/action_view/helpers/tags/checkable.rb
Normal file
16
actionpack/lib/action_view/helpers/tags/checkable.rb
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
module Checkable
|
||||||
|
def input_checked?(object, options)
|
||||||
|
if options.has_key?("checked")
|
||||||
|
checked = options.delete "checked"
|
||||||
|
checked == true || checked == "checked"
|
||||||
|
else
|
||||||
|
checked?(value(object))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
23
actionpack/lib/action_view/helpers/tags/collection_select.rb
Normal file
23
actionpack/lib/action_view/helpers/tags/collection_select.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class CollectionSelect < Base #:nodoc:
|
||||||
|
def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
|
||||||
|
@collection = collection
|
||||||
|
@value_method = value_method
|
||||||
|
@text_method = text_method
|
||||||
|
@html_options = html_options
|
||||||
|
|
||||||
|
super(object_name, method_name, template_object, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
selected_value = @options.has_key?(:selected) ? @options[:selected] : value(@object)
|
||||||
|
select_content_tag(
|
||||||
|
options_from_collection_for_select(@collection, @value_method, @text_method, :selected => selected_value, :disabled => @options[:disabled]), @options, @html_options
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
64
actionpack/lib/action_view/helpers/tags/date_select.rb
Normal file
64
actionpack/lib/action_view/helpers/tags/date_select.rb
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class DateSelect < Base #:nodoc:
|
||||||
|
def initialize(object_name, method_name, template_object, options, html_options)
|
||||||
|
@html_options = html_options
|
||||||
|
|
||||||
|
super(object_name, method_name, template_object, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
error_wrapping(datetime_selector(@options, @html_options).send("select_#{select_type}").html_safe)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def select_type
|
||||||
|
self.class.name.split("::").last.sub("Select", "").downcase
|
||||||
|
end
|
||||||
|
|
||||||
|
def datetime_selector(options, html_options)
|
||||||
|
datetime = value(object) || default_datetime(options)
|
||||||
|
@auto_index ||= nil
|
||||||
|
|
||||||
|
options = options.dup
|
||||||
|
options[:field_name] = @method_name
|
||||||
|
options[:include_position] = true
|
||||||
|
options[:prefix] ||= @object_name
|
||||||
|
options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
|
||||||
|
|
||||||
|
DateTimeSelector.new(datetime, options, html_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def default_datetime(options)
|
||||||
|
return if options[:include_blank] || options[:prompt]
|
||||||
|
|
||||||
|
case options[:default]
|
||||||
|
when nil
|
||||||
|
Time.current
|
||||||
|
when Date, Time
|
||||||
|
options[:default]
|
||||||
|
else
|
||||||
|
default = options[:default].dup
|
||||||
|
|
||||||
|
# Rename :minute and :second to :min and :sec
|
||||||
|
default[:min] ||= default[:minute]
|
||||||
|
default[:sec] ||= default[:second]
|
||||||
|
|
||||||
|
time = Time.current
|
||||||
|
|
||||||
|
[:year, :month, :day, :hour, :min, :sec].each do |key|
|
||||||
|
default[key] ||= time.send(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
Time.utc_time(
|
||||||
|
default[:year], default[:month], default[:day],
|
||||||
|
default[:hour], default[:min], default[:sec]
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,8 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class DatetimeSelect < DateSelect #:nodoc:
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
actionpack/lib/action_view/helpers/tags/email_field.rb
Normal file
8
actionpack/lib/action_view/helpers/tags/email_field.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class EmailField < TextField #:nodoc:
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
actionpack/lib/action_view/helpers/tags/file_field.rb
Normal file
12
actionpack/lib/action_view/helpers/tags/file_field.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class FileField < TextField #:nodoc:
|
||||||
|
def render
|
||||||
|
@options.update(:size => nil)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,24 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class GroupedCollectionSelect < Base #:nodoc:
|
||||||
|
def initialize(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
|
||||||
|
@collection = collection
|
||||||
|
@group_method = group_method
|
||||||
|
@group_label_method = group_label_method
|
||||||
|
@option_key_method = option_key_method
|
||||||
|
@option_value_method = option_value_method
|
||||||
|
@html_options = html_options
|
||||||
|
|
||||||
|
super(object_name, method_name, template_object, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
select_content_tag(
|
||||||
|
option_groups_from_collection_for_select(@collection, @group_method, @group_label_method, @option_key_method, @option_value_method, value(@object)), @options, @html_options
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
actionpack/lib/action_view/helpers/tags/hidden_field.rb
Normal file
12
actionpack/lib/action_view/helpers/tags/hidden_field.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class HiddenField < TextField #:nodoc:
|
||||||
|
def render
|
||||||
|
@options.update(:size => nil)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
65
actionpack/lib/action_view/helpers/tags/label.rb
Normal file
65
actionpack/lib/action_view/helpers/tags/label.rb
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class Label < Base #:nodoc:
|
||||||
|
def initialize(object_name, method_name, template_object, content_or_options = nil, options = nil)
|
||||||
|
content_is_options = content_or_options.is_a?(Hash)
|
||||||
|
if content_is_options
|
||||||
|
options = content_or_options
|
||||||
|
@content = nil
|
||||||
|
else
|
||||||
|
@content = content_or_options
|
||||||
|
end
|
||||||
|
|
||||||
|
options ||= {}
|
||||||
|
|
||||||
|
super(object_name, method_name, template_object, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render(&block)
|
||||||
|
options = @options.stringify_keys
|
||||||
|
tag_value = options.delete("value")
|
||||||
|
name_and_id = options.dup
|
||||||
|
|
||||||
|
if name_and_id["for"]
|
||||||
|
name_and_id["id"] = name_and_id["for"]
|
||||||
|
else
|
||||||
|
name_and_id.delete("id")
|
||||||
|
end
|
||||||
|
|
||||||
|
add_default_name_and_id_for_value(tag_value, name_and_id)
|
||||||
|
options.delete("index")
|
||||||
|
options.delete("namespace")
|
||||||
|
options["for"] ||= name_and_id["id"]
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
@template_object.label_tag(name_and_id["id"], options, &block)
|
||||||
|
else
|
||||||
|
content = if @content.blank?
|
||||||
|
@object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1')
|
||||||
|
method_and_value = tag_value.present? ? "#{@method_name}.#{tag_value}" : @method_name
|
||||||
|
|
||||||
|
if object.respond_to?(:to_model)
|
||||||
|
key = object.class.model_name.i18n_key
|
||||||
|
i18n_default = ["#{key}.#{method_and_value}".to_sym, ""]
|
||||||
|
end
|
||||||
|
|
||||||
|
i18n_default ||= ""
|
||||||
|
I18n.t("#{@object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence
|
||||||
|
else
|
||||||
|
@content.to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
content ||= if object && object.class.respond_to?(:human_attribute_name)
|
||||||
|
object.class.human_attribute_name(@method_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
content ||= @method_name.humanize
|
||||||
|
|
||||||
|
label_tag(name_and_id["id"], content, options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
19
actionpack/lib/action_view/helpers/tags/number_field.rb
Normal file
19
actionpack/lib/action_view/helpers/tags/number_field.rb
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class NumberField < TextField #:nodoc:
|
||||||
|
def render
|
||||||
|
options = @options.stringify_keys
|
||||||
|
options['size'] ||= nil
|
||||||
|
|
||||||
|
if range = options.delete("in") || options.delete("within")
|
||||||
|
options.update("min" => range.min, "max" => range.max)
|
||||||
|
end
|
||||||
|
|
||||||
|
@options = options
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
12
actionpack/lib/action_view/helpers/tags/password_field.rb
Normal file
12
actionpack/lib/action_view/helpers/tags/password_field.rb
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class PasswordField < TextField #:nodoc:
|
||||||
|
def render
|
||||||
|
@options = {:value => nil}.merge!(@options)
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
31
actionpack/lib/action_view/helpers/tags/radio_button.rb
Normal file
31
actionpack/lib/action_view/helpers/tags/radio_button.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
require 'action_view/helpers/tags/checkable'
|
||||||
|
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class RadioButton < Base #:nodoc:
|
||||||
|
include Checkable
|
||||||
|
|
||||||
|
def initialize(object_name, method_name, template_object, tag_value, options)
|
||||||
|
@tag_value = tag_value
|
||||||
|
super(object_name, method_name, template_object, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
options = @options.stringify_keys
|
||||||
|
options["type"] = "radio"
|
||||||
|
options["value"] = @tag_value
|
||||||
|
options["checked"] = "checked" if input_checked?(object, options)
|
||||||
|
add_default_name_and_id_for_value(@tag_value, options)
|
||||||
|
tag("input", options)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def checked?(value)
|
||||||
|
value.to_s == @tag_value.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
actionpack/lib/action_view/helpers/tags/range_field.rb
Normal file
8
actionpack/lib/action_view/helpers/tags/range_field.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class RangeField < NumberField #:nodoc:
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
24
actionpack/lib/action_view/helpers/tags/search_field.rb
Normal file
24
actionpack/lib/action_view/helpers/tags/search_field.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class SearchField < TextField #:nodoc:
|
||||||
|
def render
|
||||||
|
options = @options.stringify_keys
|
||||||
|
|
||||||
|
if options["autosave"]
|
||||||
|
if options["autosave"] == true
|
||||||
|
options["autosave"] = request.host.split(".").reverse.join(".")
|
||||||
|
end
|
||||||
|
options["results"] ||= 10
|
||||||
|
end
|
||||||
|
|
||||||
|
if options["onsearch"]
|
||||||
|
options["incremental"] = true unless options.has_key?("incremental")
|
||||||
|
end
|
||||||
|
|
||||||
|
super
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
31
actionpack/lib/action_view/helpers/tags/select.rb
Normal file
31
actionpack/lib/action_view/helpers/tags/select.rb
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class Select < Base #:nodoc:
|
||||||
|
def initialize(object_name, method_name, template_object, choices, options, html_options)
|
||||||
|
@choices = choices
|
||||||
|
@html_options = html_options
|
||||||
|
|
||||||
|
super(object_name, method_name, template_object, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
selected_value = @options.has_key?(:selected) ? @options[:selected] : value(@object)
|
||||||
|
|
||||||
|
# Grouped choices look like this:
|
||||||
|
#
|
||||||
|
# [nil, []]
|
||||||
|
# { nil => [] }
|
||||||
|
#
|
||||||
|
if !@choices.empty? && @choices.first.respond_to?(:last) && Array === @choices.first.last
|
||||||
|
option_tags = grouped_options_for_select(@choices, :selected => selected_value, :disabled => @options[:disabled])
|
||||||
|
else
|
||||||
|
option_tags = options_for_select(@choices, :selected => selected_value, :disabled => @options[:disabled])
|
||||||
|
end
|
||||||
|
|
||||||
|
select_content_tag(option_tags, @options, @html_options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
actionpack/lib/action_view/helpers/tags/tel_field.rb
Normal file
8
actionpack/lib/action_view/helpers/tags/tel_field.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class TelField < TextField #:nodoc:
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
20
actionpack/lib/action_view/helpers/tags/text_area.rb
Normal file
20
actionpack/lib/action_view/helpers/tags/text_area.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class TextArea < Base #:nodoc:
|
||||||
|
DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }
|
||||||
|
|
||||||
|
def render
|
||||||
|
options = DEFAULT_TEXT_AREA_OPTIONS.merge(@options.stringify_keys)
|
||||||
|
add_default_name_and_id(options)
|
||||||
|
|
||||||
|
if size = options.delete("size")
|
||||||
|
options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
|
||||||
|
end
|
||||||
|
|
||||||
|
content_tag("textarea", ERB::Util.html_escape(options.delete('value') || value_before_type_cast(object)), options)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
24
actionpack/lib/action_view/helpers/tags/text_field.rb
Normal file
24
actionpack/lib/action_view/helpers/tags/text_field.rb
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class TextField < Base #:nodoc:
|
||||||
|
def render
|
||||||
|
options = @options.stringify_keys
|
||||||
|
options["size"] = options["maxlength"] || DEFAULT_FIELD_OPTIONS["size"] unless options.key?("size")
|
||||||
|
options = DEFAULT_FIELD_OPTIONS.merge(options)
|
||||||
|
options["type"] ||= field_type
|
||||||
|
options["value"] = options.fetch("value"){ value_before_type_cast(object) } unless field_type == "file"
|
||||||
|
options["value"] &&= ERB::Util.html_escape(options["value"])
|
||||||
|
add_default_name_and_id(options)
|
||||||
|
tag("input", options)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def field_type
|
||||||
|
@field_type ||= self.class.name.split("::").last.sub("Field", "").downcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
actionpack/lib/action_view/helpers/tags/time_select.rb
Normal file
8
actionpack/lib/action_view/helpers/tags/time_select.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class TimeSelect < DateSelect #:nodoc:
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
20
actionpack/lib/action_view/helpers/tags/time_zone_select.rb
Normal file
20
actionpack/lib/action_view/helpers/tags/time_zone_select.rb
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class TimeZoneSelect < Base #:nodoc:
|
||||||
|
def initialize(object_name, method_name, template_object, priority_zones, options, html_options)
|
||||||
|
@priority_zones = priority_zones
|
||||||
|
@html_options = html_options
|
||||||
|
|
||||||
|
super(object_name, method_name, template_object, options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def render
|
||||||
|
select_content_tag(
|
||||||
|
time_zone_options_for_select(value(@object) || @options[:default], @priority_zones, @options[:model] || ActiveSupport::TimeZone), @options, @html_options
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
8
actionpack/lib/action_view/helpers/tags/url_field.rb
Normal file
8
actionpack/lib/action_view/helpers/tags/url_field.rb
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
module ActionView
|
||||||
|
module Helpers
|
||||||
|
module Tags
|
||||||
|
class UrlField < TextField #:nodoc:
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -232,6 +232,10 @@ class FormHelperTest < ActionView::TestCase
|
||||||
assert_dom_equal('<label for="post_title">The title, please:</label>', label(:post, :title) { "The title, please:" })
|
assert_dom_equal('<label for="post_title">The title, please:</label>', label(:post, :title) { "The title, please:" })
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_label_with_block_and_options
|
||||||
|
assert_dom_equal('<label for="my_for">The title, please:</label>', label(:post, :title, "for" => "my_for") { "The title, please:" })
|
||||||
|
end
|
||||||
|
|
||||||
def test_label_with_block_in_erb
|
def test_label_with_block_in_erb
|
||||||
assert_equal "<label for=\"post_message\">\n Message\n <input id=\"post_message\" name=\"post[message]\" size=\"30\" type=\"text\" />\n</label>", view.render("test/label_with_block")
|
assert_equal "<label for=\"post_message\">\n Message\n <input id=\"post_message\" name=\"post[message]\" size=\"30\" type=\"text\" />\n</label>", view.render("test/label_with_block")
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue