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
|
||||
|
||||
%w(content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth|
|
||||
module_eval "def #{meth}(*) error_wrapping(super) end", __FILE__, __LINE__
|
||||
end
|
||||
module_eval "def content_tag(*) error_wrapping(super) end", __FILE__, __LINE__
|
||||
|
||||
def tag(type, options, *)
|
||||
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
|
||||
# all month choices are valid.
|
||||
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
|
||||
|
||||
# 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
|
||||
# all month choices are valid.
|
||||
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
|
||||
|
||||
# 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.
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
def date_select(method, 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/form_tag_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/hash/slice'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
@ -654,16 +655,7 @@ module ActionView
|
|||
# 'Accept <a href="/terms">Terms</a>.'.html_safe
|
||||
# end
|
||||
def label(object_name, method, content_or_options = nil, options = nil, &block)
|
||||
content_is_options = content_or_options.is_a?(Hash)
|
||||
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)
|
||||
Tags::Label.new(object_name, method, self, content_or_options, options).render(&block)
|
||||
end
|
||||
|
||||
# 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" />
|
||||
#
|
||||
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
|
||||
|
||||
# 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" />
|
||||
#
|
||||
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
|
||||
|
||||
# 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)
|
||||
# # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
|
||||
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
|
||||
|
||||
# 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" />
|
||||
#
|
||||
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
|
||||
|
||||
# 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}
|
||||
# # </textarea>
|
||||
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
|
||||
|
||||
# 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" />
|
||||
#
|
||||
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
|
||||
|
||||
# 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_no" name="user[receive_newsletter]" value="no" checked="checked" />
|
||||
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
|
||||
|
||||
# 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" />
|
||||
#
|
||||
def search_field(object_name, method, options = {})
|
||||
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
|
||||
|
||||
InstanceTag.new(object_name, method, self, options.delete("object")).to_input_field_tag("search", options)
|
||||
Tags::SearchField.new(object_name, method, self, options).render
|
||||
end
|
||||
|
||||
# Returns a text_field of type "tel".
|
||||
|
@ -906,7 +885,7 @@ module ActionView
|
|||
# # => <input id="user_phone" name="user[phone]" size="30" type="tel" />
|
||||
#
|
||||
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
|
||||
alias phone_field telephone_field
|
||||
|
||||
|
@ -916,7 +895,7 @@ module ActionView
|
|||
# # => <input id="user_homepage" size="30" name="user[homepage]" type="url" />
|
||||
#
|
||||
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
|
||||
|
||||
# Returns a text_field of type "email".
|
||||
|
@ -925,7 +904,7 @@ module ActionView
|
|||
# # => <input id="user_address" size="30" name="user[address]" type="email" />
|
||||
#
|
||||
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
|
||||
|
||||
# Returns an input tag of type "number".
|
||||
|
@ -933,7 +912,7 @@ module ActionView
|
|||
# ==== Options
|
||||
# * Accepts same options as number_field_tag
|
||||
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
|
||||
|
||||
# Returns an input tag of type "range".
|
||||
|
@ -941,7 +920,7 @@ module ActionView
|
|||
# ==== Options
|
||||
# * Accepts same options as range_field_tag
|
||||
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
|
||||
|
||||
private
|
||||
|
@ -961,272 +940,6 @@ module ActionView
|
|||
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
|
||||
# The methods which wrap a form helper call.
|
||||
class_attribute :field_helpers
|
||||
|
|
|
@ -154,7 +154,7 @@ module ActionView
|
|||
# key in the query string, that works for ordinary forms.
|
||||
#
|
||||
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
|
||||
|
||||
# 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>
|
||||
# </select>
|
||||
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
|
||||
|
||||
|
||||
# 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
|
||||
# be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
|
||||
|
@ -240,7 +239,7 @@ module ActionView
|
|||
# </select>
|
||||
#
|
||||
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
|
||||
|
||||
# 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)
|
||||
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
|
||||
|
||||
# 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
|
||||
|
||||
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
|
||||
def select(method, choices, options = {}, 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:" })
|
||||
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
|
||||
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
|
||||
|
|
Loading…
Reference in a new issue