actionview 4.2.11 → 5.0.7
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of actionview might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/CHANGELOG.md +304 -184
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -3
- data/lib/action_view.rb +1 -1
- data/lib/action_view/base.rb +14 -2
- data/lib/action_view/dependency_tracker.rb +51 -18
- data/lib/action_view/digestor.rb +83 -81
- data/lib/action_view/flows.rb +4 -5
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +15 -5
- data/lib/action_view/helpers/asset_url_helper.rb +51 -12
- data/lib/action_view/helpers/atom_feed_helper.rb +6 -5
- data/lib/action_view/helpers/cache_helper.rb +62 -21
- data/lib/action_view/helpers/capture_helper.rb +5 -4
- data/lib/action_view/helpers/controller_helper.rb +11 -2
- data/lib/action_view/helpers/date_helper.rb +59 -13
- data/lib/action_view/helpers/debug_helper.rb +1 -1
- data/lib/action_view/helpers/form_helper.rb +74 -72
- data/lib/action_view/helpers/form_options_helper.rb +79 -39
- data/lib/action_view/helpers/form_tag_helper.rb +74 -44
- data/lib/action_view/helpers/javascript_helper.rb +4 -4
- data/lib/action_view/helpers/number_helper.rb +28 -13
- data/lib/action_view/helpers/output_safety_helper.rb +32 -2
- data/lib/action_view/helpers/record_tag_helper.rb +12 -99
- data/lib/action_view/helpers/rendering_helper.rb +2 -2
- data/lib/action_view/helpers/sanitize_helper.rb +1 -2
- data/lib/action_view/helpers/tag_helper.rb +19 -11
- data/lib/action_view/helpers/tags/base.rb +45 -29
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -28
- data/lib/action_view/helpers/tags/collection_helpers.rb +32 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -9
- data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
- data/lib/action_view/helpers/tags/label.rb +1 -1
- data/lib/action_view/helpers/tags/placeholderable.rb +1 -1
- data/lib/action_view/helpers/tags/search_field.rb +12 -9
- data/lib/action_view/helpers/tags/text_field.rb +0 -1
- data/lib/action_view/helpers/tags/translator.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +27 -11
- data/lib/action_view/helpers/translation_helper.rb +56 -26
- data/lib/action_view/helpers/url_helper.rb +108 -79
- data/lib/action_view/layouts.rb +11 -10
- data/lib/action_view/log_subscriber.rb +35 -1
- data/lib/action_view/lookup_context.rb +69 -48
- data/lib/action_view/model_naming.rb +1 -1
- data/lib/action_view/path_set.rb +9 -0
- data/lib/action_view/railtie.rb +18 -3
- data/lib/action_view/record_identifier.rb +45 -19
- data/lib/action_view/renderer/abstract_renderer.rb +7 -3
- data/lib/action_view/renderer/partial_renderer.rb +38 -37
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +49 -0
- data/lib/action_view/renderer/renderer.rb +2 -6
- data/lib/action_view/renderer/streaming_template_renderer.rb +1 -1
- data/lib/action_view/renderer/template_renderer.rb +11 -10
- data/lib/action_view/rendering.rb +15 -7
- data/lib/action_view/routing_url_for.rb +18 -6
- data/lib/action_view/tasks/{dependencies.rake → cache_digests.rake} +2 -2
- data/lib/action_view/template.rb +36 -12
- data/lib/action_view/template/error.rb +20 -9
- data/lib/action_view/template/handlers.rb +6 -4
- data/lib/action_view/template/handlers/html.rb +9 -0
- data/lib/action_view/template/handlers/raw.rb +1 -3
- data/lib/action_view/template/resolver.rb +49 -42
- data/lib/action_view/template/types.rb +14 -16
- data/lib/action_view/test_case.rb +15 -9
- data/lib/action_view/testing/resolvers.rb +1 -2
- data/lib/action_view/view_paths.rb +6 -24
- metadata +16 -20
@@ -26,7 +26,7 @@ module ActionView
|
|
26
26
|
Marshal::dump(object)
|
27
27
|
object = ERB::Util.html_escape(object.to_yaml)
|
28
28
|
content_tag(:pre, object, :class => "debug_dump")
|
29
|
-
rescue
|
29
|
+
rescue # errors from Marshal or YAML
|
30
30
|
# Object couldn't be dumped, perhaps because of singleton methods -- this is the fallback
|
31
31
|
content_tag(:code, object.inspect, :class => "debug_dump")
|
32
32
|
end
|
@@ -4,6 +4,7 @@ require 'action_view/helpers/tag_helper'
|
|
4
4
|
require 'action_view/helpers/form_tag_helper'
|
5
5
|
require 'action_view/helpers/active_model_helper'
|
6
6
|
require 'action_view/model_naming'
|
7
|
+
require 'action_view/record_identifier'
|
7
8
|
require 'active_support/core_ext/module/attribute_accessors'
|
8
9
|
require 'active_support/core_ext/hash/slice'
|
9
10
|
require 'active_support/core_ext/string/output_safety'
|
@@ -66,9 +67,10 @@ module ActionView
|
|
66
67
|
#
|
67
68
|
# In particular, thanks to the conventions followed in the generated field names, the
|
68
69
|
# controller gets a nested hash <tt>params[:person]</tt> with the person attributes
|
69
|
-
# set in the form. That hash is ready to be passed to <tt>Person.
|
70
|
+
# set in the form. That hash is ready to be passed to <tt>Person.new</tt>:
|
70
71
|
#
|
71
|
-
#
|
72
|
+
# @person = Person.new(params[:person])
|
73
|
+
# if @person.save
|
72
74
|
# # success
|
73
75
|
# else
|
74
76
|
# # error handling
|
@@ -110,6 +112,9 @@ module ActionView
|
|
110
112
|
include FormTagHelper
|
111
113
|
include UrlHelper
|
112
114
|
include ModelNaming
|
115
|
+
include RecordIdentifier
|
116
|
+
|
117
|
+
attr_internal :default_form_builder
|
113
118
|
|
114
119
|
# Creates a form that allows the user to create or update the attributes
|
115
120
|
# of a specific model object.
|
@@ -138,6 +143,7 @@ module ActionView
|
|
138
143
|
# will get expanded to
|
139
144
|
#
|
140
145
|
# <%= text_field :person, :first_name %>
|
146
|
+
#
|
141
147
|
# which results in an HTML <tt><input></tt> tag whose +name+ attribute is
|
142
148
|
# <tt>person[first_name]</tt>. This means that when the form is submitted,
|
143
149
|
# the value entered by the user will be available in the controller as
|
@@ -759,7 +765,7 @@ module ActionView
|
|
759
765
|
# # => <label for="post_privacy_public">Public Post</label>
|
760
766
|
#
|
761
767
|
# label(:post, :terms) do
|
762
|
-
# 'Accept <a href="/terms">Terms</a>.'
|
768
|
+
# raw('Accept <a href="/terms">Terms</a>.')
|
763
769
|
# end
|
764
770
|
# # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
|
765
771
|
def label(object_name, method, content_or_options = nil, options = nil, &block)
|
@@ -843,8 +849,8 @@ module ActionView
|
|
843
849
|
# file_field(:user, :avatar)
|
844
850
|
# # => <input type="file" id="user_avatar" name="user[avatar]" />
|
845
851
|
#
|
846
|
-
# file_field(:post, :image, :
|
847
|
-
# # => <input type="file" id="post_image" name="post[image]" multiple="
|
852
|
+
# file_field(:post, :image, multiple: true)
|
853
|
+
# # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
|
848
854
|
#
|
849
855
|
# file_field(:post, :attached, accept: 'text/html')
|
850
856
|
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
|
@@ -1014,7 +1020,7 @@ module ActionView
|
|
1014
1020
|
# date_field("user", "born_on")
|
1015
1021
|
# # => <input id="user_born_on" name="user[born_on]" type="date" />
|
1016
1022
|
#
|
1017
|
-
# The default value is generated by trying to call "
|
1023
|
+
# The default value is generated by trying to call +strftime+ with "%Y-%m-%d"
|
1018
1024
|
# on the object's value, which makes it behave as expected for instances
|
1019
1025
|
# of DateTime and ActiveSupport::TimeWithZone. You can still override that
|
1020
1026
|
# by passing the "value" option explicitly, e.g.
|
@@ -1068,38 +1074,9 @@ module ActionView
|
|
1068
1074
|
Tags::TimeField.new(object_name, method, self, options).render
|
1069
1075
|
end
|
1070
1076
|
|
1071
|
-
# Returns a text_field of type "datetime".
|
1072
|
-
#
|
1073
|
-
# datetime_field("user", "born_on")
|
1074
|
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" />
|
1075
|
-
#
|
1076
|
-
# The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T.%L%z"
|
1077
|
-
# on the object's value, which makes it behave as expected for instances
|
1078
|
-
# of DateTime and ActiveSupport::TimeWithZone.
|
1079
|
-
#
|
1080
|
-
# @user.born_on = Date.new(1984, 1, 12)
|
1081
|
-
# datetime_field("user", "born_on")
|
1082
|
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" value="1984-01-12T00:00:00.000+0000" />
|
1083
|
-
#
|
1084
|
-
# You can create values for the "min" and "max" attributes by passing
|
1085
|
-
# instances of Date or Time to the options hash.
|
1086
|
-
#
|
1087
|
-
# datetime_field("user", "born_on", min: Date.today)
|
1088
|
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" />
|
1089
|
-
#
|
1090
|
-
# Alternatively, you can pass a String formatted as an ISO8601 datetime
|
1091
|
-
# with UTC offset as the values for "min" and "max."
|
1092
|
-
#
|
1093
|
-
# datetime_field("user", "born_on", min: "2014-05-20T00:00:00+0000")
|
1094
|
-
# # => <input id="user_born_on" name="user[born_on]" type="datetime" min="2014-05-20T00:00:00.000+0000" />
|
1095
|
-
#
|
1096
|
-
def datetime_field(object_name, method, options = {})
|
1097
|
-
Tags::DatetimeField.new(object_name, method, self, options).render
|
1098
|
-
end
|
1099
|
-
|
1100
1077
|
# Returns a text_field of type "datetime-local".
|
1101
1078
|
#
|
1102
|
-
#
|
1079
|
+
# datetime_field("user", "born_on")
|
1103
1080
|
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" />
|
1104
1081
|
#
|
1105
1082
|
# The default value is generated by trying to call +strftime+ with "%Y-%m-%dT%T"
|
@@ -1107,25 +1084,27 @@ module ActionView
|
|
1107
1084
|
# of DateTime and ActiveSupport::TimeWithZone.
|
1108
1085
|
#
|
1109
1086
|
# @user.born_on = Date.new(1984, 1, 12)
|
1110
|
-
#
|
1087
|
+
# datetime_field("user", "born_on")
|
1111
1088
|
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="1984-01-12T00:00:00" />
|
1112
1089
|
#
|
1113
1090
|
# You can create values for the "min" and "max" attributes by passing
|
1114
1091
|
# instances of Date or Time to the options hash.
|
1115
1092
|
#
|
1116
|
-
#
|
1093
|
+
# datetime_field("user", "born_on", min: Date.today)
|
1117
1094
|
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" />
|
1118
1095
|
#
|
1119
1096
|
# Alternatively, you can pass a String formatted as an ISO8601 datetime as
|
1120
1097
|
# the values for "min" and "max."
|
1121
1098
|
#
|
1122
|
-
#
|
1099
|
+
# datetime_field("user", "born_on", min: "2014-05-20T00:00:00")
|
1123
1100
|
# # => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" />
|
1124
1101
|
#
|
1125
|
-
def
|
1102
|
+
def datetime_field(object_name, method, options = {})
|
1126
1103
|
Tags::DatetimeLocalField.new(object_name, method, self, options).render
|
1127
1104
|
end
|
1128
1105
|
|
1106
|
+
alias datetime_local_field datetime_field
|
1107
|
+
|
1129
1108
|
# Returns a text_field of type "month".
|
1130
1109
|
#
|
1131
1110
|
# month_field("user", "born_on")
|
@@ -1211,7 +1190,7 @@ module ActionView
|
|
1211
1190
|
end
|
1212
1191
|
|
1213
1192
|
def default_form_builder_class
|
1214
|
-
builder = ActionView::Base.default_form_builder
|
1193
|
+
builder = default_form_builder || ActionView::Base.default_form_builder
|
1215
1194
|
builder.respond_to?(:constantize) ? builder.constantize : builder
|
1216
1195
|
end
|
1217
1196
|
end
|
@@ -1586,14 +1565,22 @@ module ActionView
|
|
1586
1565
|
record_name = model_name_from_record_or_class(record_object).param_key
|
1587
1566
|
end
|
1588
1567
|
|
1568
|
+
object_name = @object_name
|
1589
1569
|
index = if options.has_key?(:index)
|
1590
1570
|
options[:index]
|
1591
1571
|
elsif defined?(@auto_index)
|
1592
|
-
|
1572
|
+
object_name = object_name.to_s.sub(/\[\]$/, "")
|
1593
1573
|
@auto_index
|
1594
1574
|
end
|
1595
1575
|
|
1596
|
-
record_name =
|
1576
|
+
record_name = if index
|
1577
|
+
"#{object_name}[#{index}][#{record_name}]"
|
1578
|
+
elsif record_name.to_s.end_with?('[]')
|
1579
|
+
record_name = record_name.to_s.sub(/(.*)\[\]$/, "[\\1][#{record_object.id}]")
|
1580
|
+
"#{object_name}#{record_name}"
|
1581
|
+
else
|
1582
|
+
"#{object_name}[#{record_name}]"
|
1583
|
+
end
|
1597
1584
|
fields_options[:child_index] = index
|
1598
1585
|
|
1599
1586
|
@template.fields_for(record_name, record_object, fields_options, &block)
|
@@ -1607,7 +1594,7 @@ module ActionView
|
|
1607
1594
|
# target labels for radio_button tags (where the value is used in the ID of the input tag).
|
1608
1595
|
#
|
1609
1596
|
# ==== Examples
|
1610
|
-
# label(:
|
1597
|
+
# label(:title)
|
1611
1598
|
# # => <label for="post_title">Title</label>
|
1612
1599
|
#
|
1613
1600
|
# You can localize your labels based on model and attribute names.
|
@@ -1620,7 +1607,7 @@ module ActionView
|
|
1620
1607
|
#
|
1621
1608
|
# Which then will result in
|
1622
1609
|
#
|
1623
|
-
# label(:
|
1610
|
+
# label(:body)
|
1624
1611
|
# # => <label for="post_body">Write your entire text here</label>
|
1625
1612
|
#
|
1626
1613
|
# Localization can also be based purely on the translation of the attribute-name
|
@@ -1631,21 +1618,22 @@ module ActionView
|
|
1631
1618
|
# post:
|
1632
1619
|
# cost: "Total cost"
|
1633
1620
|
#
|
1634
|
-
# label(:
|
1621
|
+
# label(:cost)
|
1635
1622
|
# # => <label for="post_cost">Total cost</label>
|
1636
1623
|
#
|
1637
|
-
# label(:
|
1624
|
+
# label(:title, "A short title")
|
1638
1625
|
# # => <label for="post_title">A short title</label>
|
1639
1626
|
#
|
1640
|
-
# label(:
|
1627
|
+
# label(:title, "A short title", class: "title_label")
|
1641
1628
|
# # => <label for="post_title" class="title_label">A short title</label>
|
1642
1629
|
#
|
1643
|
-
# label(:
|
1630
|
+
# label(:privacy, "Public Post", value: "public")
|
1644
1631
|
# # => <label for="post_privacy_public">Public Post</label>
|
1645
1632
|
#
|
1646
|
-
# label(:
|
1647
|
-
# 'Accept <a href="/terms">Terms</a>.'
|
1633
|
+
# label(:terms) do
|
1634
|
+
# raw('Accept <a href="/terms">Terms</a>.')
|
1648
1635
|
# end
|
1636
|
+
# # => <label for="post_terms">Accept <a href="/terms">Terms</a>.</label>
|
1649
1637
|
def label(method, text = nil, options = {}, &block)
|
1650
1638
|
@template.label(@object_name, method, text, objectify_options(options), &block)
|
1651
1639
|
end
|
@@ -1694,16 +1682,17 @@ module ActionView
|
|
1694
1682
|
# hashes instead of arrays.
|
1695
1683
|
#
|
1696
1684
|
# # Let's say that @post.validated? is 1:
|
1697
|
-
# check_box("
|
1685
|
+
# check_box("validated")
|
1698
1686
|
# # => <input name="post[validated]" type="hidden" value="0" />
|
1699
1687
|
# # <input checked="checked" type="checkbox" id="post_validated" name="post[validated]" value="1" />
|
1700
1688
|
#
|
1701
1689
|
# # Let's say that @puppy.gooddog is "no":
|
1702
|
-
# check_box("
|
1690
|
+
# check_box("gooddog", {}, "yes", "no")
|
1703
1691
|
# # => <input name="puppy[gooddog]" type="hidden" value="no" />
|
1704
1692
|
# # <input type="checkbox" id="puppy_gooddog" name="puppy[gooddog]" value="yes" />
|
1705
1693
|
#
|
1706
|
-
#
|
1694
|
+
# # Let's say that @eula.accepted is "no":
|
1695
|
+
# check_box("accepted", { class: 'eula_check' }, "yes", "no")
|
1707
1696
|
# # => <input name="eula[accepted]" type="hidden" value="no" />
|
1708
1697
|
# # <input type="checkbox" class="eula_check" id="eula_accepted" name="eula[accepted]" value="yes" />
|
1709
1698
|
def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
|
@@ -1718,13 +1707,14 @@ module ActionView
|
|
1718
1707
|
# +options+ hash. You may pass HTML options there as well.
|
1719
1708
|
#
|
1720
1709
|
# # Let's say that @post.category returns "rails":
|
1721
|
-
# radio_button("
|
1722
|
-
# radio_button("
|
1710
|
+
# radio_button("category", "rails")
|
1711
|
+
# radio_button("category", "java")
|
1723
1712
|
# # => <input type="radio" id="post_category_rails" name="post[category]" value="rails" checked="checked" />
|
1724
1713
|
# # <input type="radio" id="post_category_java" name="post[category]" value="java" />
|
1725
1714
|
#
|
1726
|
-
#
|
1727
|
-
# radio_button("
|
1715
|
+
# # Let's say that @user.category returns "no":
|
1716
|
+
# radio_button("receive_newsletter", "yes")
|
1717
|
+
# radio_button("receive_newsletter", "no")
|
1728
1718
|
# # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
|
1729
1719
|
# # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
|
1730
1720
|
def radio_button(method, tag_value, options = {})
|
@@ -1737,14 +1727,17 @@ module ActionView
|
|
1737
1727
|
# shown.
|
1738
1728
|
#
|
1739
1729
|
# ==== Examples
|
1740
|
-
#
|
1741
|
-
#
|
1730
|
+
# # Let's say that @signup.pass_confirm returns true:
|
1731
|
+
# hidden_field(:pass_confirm)
|
1732
|
+
# # => <input type="hidden" id="signup_pass_confirm" name="signup[pass_confirm]" value="true" />
|
1742
1733
|
#
|
1743
|
-
#
|
1744
|
-
#
|
1734
|
+
# # Let's say that @post.tag_list returns "blog, ruby":
|
1735
|
+
# hidden_field(:tag_list)
|
1736
|
+
# # => <input type="hidden" id="post_tag_list" name="post[tag_list]" value="blog, ruby" />
|
1745
1737
|
#
|
1746
|
-
#
|
1747
|
-
#
|
1738
|
+
# # Let's say that @user.token returns "abcde":
|
1739
|
+
# hidden_field(:token)
|
1740
|
+
# # => <input type="hidden" id="user_token" name="user[token]" value="abcde" />
|
1748
1741
|
#
|
1749
1742
|
def hidden_field(method, options = {})
|
1750
1743
|
@emitted_hidden_id = true if method == :id
|
@@ -1765,19 +1758,24 @@ module ActionView
|
|
1765
1758
|
# * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
|
1766
1759
|
#
|
1767
1760
|
# ==== Examples
|
1768
|
-
#
|
1761
|
+
# # Let's say that @user has avatar:
|
1762
|
+
# file_field(:avatar)
|
1769
1763
|
# # => <input type="file" id="user_avatar" name="user[avatar]" />
|
1770
1764
|
#
|
1771
|
-
#
|
1772
|
-
#
|
1765
|
+
# # Let's say that @post has image:
|
1766
|
+
# file_field(:image, :multiple => true)
|
1767
|
+
# # => <input type="file" id="post_image" name="post[image][]" multiple="multiple" />
|
1773
1768
|
#
|
1774
|
-
#
|
1769
|
+
# # Let's say that @post has attached:
|
1770
|
+
# file_field(:attached, accept: 'text/html')
|
1775
1771
|
# # => <input accept="text/html" type="file" id="post_attached" name="post[attached]" />
|
1776
1772
|
#
|
1777
|
-
#
|
1773
|
+
# # Let's say that @post has image:
|
1774
|
+
# file_field(:image, accept: 'image/png,image/gif,image/jpeg')
|
1778
1775
|
# # => <input type="file" id="post_image" name="post[image]" accept="image/png,image/gif,image/jpeg" />
|
1779
1776
|
#
|
1780
|
-
#
|
1777
|
+
# # Let's say that @attachment has file:
|
1778
|
+
# file_field(:file, class: 'file_input')
|
1781
1779
|
# # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
|
1782
1780
|
def file_field(method, options = {})
|
1783
1781
|
self.multipart = true
|
@@ -1845,7 +1843,7 @@ module ActionView
|
|
1845
1843
|
# create: "Add %{model}"
|
1846
1844
|
#
|
1847
1845
|
# ==== Examples
|
1848
|
-
# button("Create
|
1846
|
+
# button("Create post")
|
1849
1847
|
# # => <button name='button' type='submit'>Create post</button>
|
1850
1848
|
#
|
1851
1849
|
# button do
|
@@ -1906,7 +1904,11 @@ module ActionView
|
|
1906
1904
|
explicit_child_index = options[:child_index]
|
1907
1905
|
output = ActiveSupport::SafeBuffer.new
|
1908
1906
|
association.each do |child|
|
1909
|
-
|
1907
|
+
if explicit_child_index
|
1908
|
+
options[:child_index] = explicit_child_index.call if explicit_child_index.respond_to?(:call)
|
1909
|
+
else
|
1910
|
+
options[:child_index] = nested_child_index(name)
|
1911
|
+
end
|
1910
1912
|
output << fields_for_nested_model("#{name}[#{options[:child_index]}]", child, options, block)
|
1911
1913
|
end
|
1912
1914
|
output
|
@@ -18,10 +18,10 @@ module ActionView
|
|
18
18
|
#
|
19
19
|
# could become:
|
20
20
|
#
|
21
|
-
# <select name="post[category]">
|
22
|
-
# <option></option>
|
23
|
-
# <option>joke</option>
|
24
|
-
# <option>poem</option>
|
21
|
+
# <select name="post[category]" id="post_category">
|
22
|
+
# <option value=""></option>
|
23
|
+
# <option value="joke">joke</option>
|
24
|
+
# <option value="poem">poem</option>
|
25
25
|
# </select>
|
26
26
|
#
|
27
27
|
# Another common case is a select tag for a <tt>belongs_to</tt>-associated object.
|
@@ -32,11 +32,11 @@ module ActionView
|
|
32
32
|
#
|
33
33
|
# could become:
|
34
34
|
#
|
35
|
-
# <select name="post[person_id]">
|
35
|
+
# <select name="post[person_id]" id="post_person_id">
|
36
36
|
# <option value="">None</option>
|
37
37
|
# <option value="1">David</option>
|
38
|
-
# <option value="2" selected="selected">
|
39
|
-
# <option value="3">
|
38
|
+
# <option value="2" selected="selected">Eileen</option>
|
39
|
+
# <option value="3">Rafael</option>
|
40
40
|
# </select>
|
41
41
|
#
|
42
42
|
# * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
|
@@ -45,11 +45,11 @@ module ActionView
|
|
45
45
|
#
|
46
46
|
# could become:
|
47
47
|
#
|
48
|
-
# <select name="post[person_id]">
|
48
|
+
# <select name="post[person_id]" id="post_person_id">
|
49
49
|
# <option value="">Select Person</option>
|
50
50
|
# <option value="1">David</option>
|
51
|
-
# <option value="2">
|
52
|
-
# <option value="3">
|
51
|
+
# <option value="2">Eileen</option>
|
52
|
+
# <option value="3">Rafael</option>
|
53
53
|
# </select>
|
54
54
|
#
|
55
55
|
# * <tt>:index</tt> - like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
|
@@ -71,19 +71,19 @@ module ActionView
|
|
71
71
|
#
|
72
72
|
# could become:
|
73
73
|
#
|
74
|
-
# <select name="post[category]">
|
75
|
-
# <option></option>
|
76
|
-
# <option>joke</option>
|
77
|
-
# <option>poem</option>
|
78
|
-
# <option disabled="disabled">restricted</option>
|
74
|
+
# <select name="post[category]" id="post_category">
|
75
|
+
# <option value=""></option>
|
76
|
+
# <option value="joke">joke</option>
|
77
|
+
# <option value="poem">poem</option>
|
78
|
+
# <option disabled="disabled" value="restricted">restricted</option>
|
79
79
|
# </select>
|
80
80
|
#
|
81
81
|
# When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
|
82
82
|
#
|
83
|
-
# collection_select(:post, :category_id, Category.all, :id, :name, {disabled:
|
83
|
+
# collection_select(:post, :category_id, Category.all, :id, :name, {disabled: -> (category) { category.archived? }})
|
84
84
|
#
|
85
85
|
# If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
|
86
|
-
# <select name="post[category_id]">
|
86
|
+
# <select name="post[category_id]" id="post_category_id">
|
87
87
|
# <option value="1" disabled="disabled">2008 stuff</option>
|
88
88
|
# <option value="2" disabled="disabled">Christmas</option>
|
89
89
|
# <option value="3">Jokes</option>
|
@@ -109,11 +109,11 @@ module ActionView
|
|
109
109
|
#
|
110
110
|
# would become:
|
111
111
|
#
|
112
|
-
# <select name="post[person_id]">
|
112
|
+
# <select name="post[person_id]" id="post_person_id">
|
113
113
|
# <option value=""></option>
|
114
114
|
# <option value="1" selected="selected">David</option>
|
115
|
-
# <option value="2">
|
116
|
-
# <option value="3">
|
115
|
+
# <option value="2">Eileen</option>
|
116
|
+
# <option value="3">Rafael</option>
|
117
117
|
# </select>
|
118
118
|
#
|
119
119
|
# assuming the associated person has ID 1.
|
@@ -192,7 +192,7 @@ module ActionView
|
|
192
192
|
# collection_select(:post, :author_id, Author.all, :id, :name_with_initial, prompt: true)
|
193
193
|
#
|
194
194
|
# If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
|
195
|
-
# <select name="post[author_id]">
|
195
|
+
# <select name="post[author_id]" id="post_author_id">
|
196
196
|
# <option value="">Please select</option>
|
197
197
|
# <option value="1" selected="selected">D. Heinemeier Hansson</option>
|
198
198
|
# <option value="2">D. Thomas</option>
|
@@ -243,7 +243,7 @@ module ActionView
|
|
243
243
|
#
|
244
244
|
# Possible output:
|
245
245
|
#
|
246
|
-
# <select name="city[country_id]">
|
246
|
+
# <select name="city[country_id]" id="city_country_id">
|
247
247
|
# <optgroup label="Africa">
|
248
248
|
# <option value="1">South Africa</option>
|
249
249
|
# <option value="3">Somalia</option>
|
@@ -268,10 +268,11 @@ module ActionView
|
|
268
268
|
# for more information.)
|
269
269
|
#
|
270
270
|
# You can also supply an array of ActiveSupport::TimeZone objects
|
271
|
-
# as +priority_zones
|
272
|
-
# (long) list.
|
273
|
-
#
|
274
|
-
#
|
271
|
+
# as +priority_zones+ so that they will be listed above the rest of the
|
272
|
+
# (long) list. You can use ActiveSupport::TimeZone.us_zones for a list
|
273
|
+
# of US time zones, ActiveSupport::TimeZone.country_zones(country_code)
|
274
|
+
# for another country's time zones, or a Regexp to select the zones of
|
275
|
+
# your choice.
|
275
276
|
#
|
276
277
|
# Finally, this method supports a <tt>:default</tt> option, which selects
|
277
278
|
# a default ActiveSupport::TimeZone if the object's time zone is +nil+.
|
@@ -302,17 +303,17 @@ module ActionView
|
|
302
303
|
# # => <option value="DKK">Kroner</option>
|
303
304
|
#
|
304
305
|
# options_for_select([ "VISA", "MasterCard" ], "MasterCard")
|
305
|
-
# # => <option>VISA</option>
|
306
|
-
# # => <option selected="selected">MasterCard</option>
|
306
|
+
# # => <option value="VISA">VISA</option>
|
307
|
+
# # => <option selected="selected" value="MasterCard">MasterCard</option>
|
307
308
|
#
|
308
309
|
# options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
|
309
310
|
# # => <option value="$20">Basic</option>
|
310
311
|
# # => <option value="$40" selected="selected">Plus</option>
|
311
312
|
#
|
312
313
|
# options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
|
313
|
-
# # => <option selected="selected">VISA</option>
|
314
|
-
# # => <option>MasterCard</option>
|
315
|
-
# # => <option selected="selected">Discover</option>
|
314
|
+
# # => <option selected="selected" value="VISA">VISA</option>
|
315
|
+
# # => <option value="MasterCard">MasterCard</option>
|
316
|
+
# # => <option selected="selected" value="Discover">Discover</option>
|
316
317
|
#
|
317
318
|
# You can optionally provide HTML attributes as the last element of the array.
|
318
319
|
#
|
@@ -351,12 +352,12 @@ module ActionView
|
|
351
352
|
return container if String === container
|
352
353
|
|
353
354
|
selected, disabled = extract_selected_and_disabled(selected).map do |r|
|
354
|
-
Array(r).map
|
355
|
+
Array(r).map(&:to_s)
|
355
356
|
end
|
356
357
|
|
357
358
|
container.map do |element|
|
358
359
|
html_attributes = option_html_attributes(element)
|
359
|
-
text, value = option_text_and_value(element).map
|
360
|
+
text, value = option_text_and_value(element).map(&:to_s)
|
360
361
|
|
361
362
|
html_attributes[:selected] ||= option_value_selected?(value, selected)
|
362
363
|
html_attributes[:disabled] ||= disabled && option_value_selected?(value, disabled)
|
@@ -456,7 +457,7 @@ module ActionView
|
|
456
457
|
option_tags = options_from_collection_for_select(
|
457
458
|
group.send(group_method), option_key_method, option_value_method, selected_key)
|
458
459
|
|
459
|
-
content_tag(
|
460
|
+
content_tag("optgroup".freeze, option_tags, label: group.send(group_label_method))
|
460
461
|
end.join.html_safe
|
461
462
|
end
|
462
463
|
|
@@ -528,7 +529,7 @@ module ActionView
|
|
528
529
|
body = "".html_safe
|
529
530
|
|
530
531
|
if prompt
|
531
|
-
body.safe_concat content_tag(
|
532
|
+
body.safe_concat content_tag("option".freeze, prompt_text(prompt), value: "")
|
532
533
|
end
|
533
534
|
|
534
535
|
grouped_options.each do |container|
|
@@ -541,14 +542,14 @@ module ActionView
|
|
541
542
|
end
|
542
543
|
|
543
544
|
html_attributes = { label: label }.merge!(html_attributes)
|
544
|
-
body.safe_concat content_tag(
|
545
|
+
body.safe_concat content_tag("optgroup".freeze, options_for_select(container, selected_key), html_attributes)
|
545
546
|
end
|
546
547
|
|
547
548
|
body
|
548
549
|
end
|
549
550
|
|
550
551
|
# Returns a string of option tags for pretty much any time zone in the
|
551
|
-
# world. Supply
|
552
|
+
# world. Supply an ActiveSupport::TimeZone name as +selected+ to have it
|
552
553
|
# marked as the selected option tag. You can also supply an array of
|
553
554
|
# ActiveSupport::TimeZone objects as +priority_zones+, so that they will
|
554
555
|
# be listed above the rest of the (long) list. (You can use
|
@@ -556,7 +557,7 @@ module ActionView
|
|
556
557
|
# of the US time zones, or a Regexp to select the zones of your choice)
|
557
558
|
#
|
558
559
|
# The +selected+ parameter must be either +nil+, or a string that names
|
559
|
-
#
|
560
|
+
# an ActiveSupport::TimeZone.
|
560
561
|
#
|
561
562
|
# By default, +model+ is the ActiveSupport::TimeZone constant (which can
|
562
563
|
# be obtained in Active Record as a value object). The only requirement
|
@@ -577,7 +578,7 @@ module ActionView
|
|
577
578
|
end
|
578
579
|
|
579
580
|
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
|
580
|
-
zone_options.safe_concat content_tag(
|
581
|
+
zone_options.safe_concat content_tag("option".freeze, '-------------', value: '', disabled: true)
|
581
582
|
zone_options.safe_concat "\n"
|
582
583
|
|
583
584
|
zones = zones - priority_zones
|
@@ -644,6 +645,24 @@ module ActionView
|
|
644
645
|
# collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
|
645
646
|
# b.label(:"data-value" => b.value) { b.radio_button + b.text }
|
646
647
|
# end
|
648
|
+
#
|
649
|
+
# ==== Gotcha
|
650
|
+
#
|
651
|
+
# The HTML specification says when nothing is select on a collection of radio buttons
|
652
|
+
# web browsers do not send any value to server.
|
653
|
+
# Unfortunately this introduces a gotcha:
|
654
|
+
# if a +User+ model has a +category_id+ field, and in the form none category is selected no +category_id+ parameter is sent. So,
|
655
|
+
# any strong parameters idiom like
|
656
|
+
#
|
657
|
+
# params.require(:user).permit(...)
|
658
|
+
#
|
659
|
+
# will raise an error since no +{user: ...}+ will be present.
|
660
|
+
#
|
661
|
+
# To prevent this the helper generates an auxiliary hidden field before
|
662
|
+
# every collection of radio buttons. The hidden field has the same name as collection radio button and blank value.
|
663
|
+
#
|
664
|
+
# In case if you don't want the helper to generate this hidden field you can specify
|
665
|
+
# <tt>include_hidden: false</tt> option.
|
647
666
|
def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
648
667
|
Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
|
649
668
|
end
|
@@ -707,6 +726,27 @@ module ActionView
|
|
707
726
|
# collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
|
708
727
|
# b.label(:"data-value" => b.value) { b.check_box + b.text }
|
709
728
|
# end
|
729
|
+
#
|
730
|
+
# ==== Gotcha
|
731
|
+
#
|
732
|
+
# When no selection is made for a collection of checkboxes most
|
733
|
+
# web browsers will not send any value.
|
734
|
+
#
|
735
|
+
# For example, if we have a +User+ model with +category_ids+ field and we
|
736
|
+
# have the following code in our update action:
|
737
|
+
#
|
738
|
+
# @user.update(params[:user])
|
739
|
+
#
|
740
|
+
# If no +category_ids+ are selected then we can safely assume this field
|
741
|
+
# will not be updated.
|
742
|
+
#
|
743
|
+
# This is possible thanks to a hidden field generated by the helper method
|
744
|
+
# for every collection of checkboxes.
|
745
|
+
# This hidden field is given the same field name as the checkboxes with a
|
746
|
+
# blank value.
|
747
|
+
#
|
748
|
+
# In the rare case you don't want this hidden field, you can pass the
|
749
|
+
# <tt>include_hidden: false</tt> option to the helper method.
|
710
750
|
def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
711
751
|
Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
|
712
752
|
end
|