omg-actionview 8.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +25 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +40 -0
- data/app/assets/javascripts/rails-ujs.esm.js +686 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +316 -0
- data/lib/action_view/buffers.rb +165 -0
- data/lib/action_view/cache_expiry.rb +69 -0
- data/lib/action_view/context.rb +32 -0
- data/lib/action_view/dependency_tracker/erb_tracker.rb +159 -0
- data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
- data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
- data/lib/action_view/dependency_tracker.rb +41 -0
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +130 -0
- data/lib/action_view/flows.rb +75 -0
- data/lib/action_view/gem_version.rb +17 -0
- data/lib/action_view/helpers/active_model_helper.rb +54 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +680 -0
- data/lib/action_view/helpers/asset_url_helper.rb +473 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
- data/lib/action_view/helpers/cache_helper.rb +315 -0
- data/lib/action_view/helpers/capture_helper.rb +236 -0
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +42 -0
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +35 -0
- data/lib/action_view/helpers/date_helper.rb +1266 -0
- data/lib/action_view/helpers/debug_helper.rb +38 -0
- data/lib/action_view/helpers/form_helper.rb +2765 -0
- data/lib/action_view/helpers/form_options_helper.rb +927 -0
- data/lib/action_view/helpers/form_tag_helper.rb +1088 -0
- data/lib/action_view/helpers/javascript_helper.rb +96 -0
- data/lib/action_view/helpers/number_helper.rb +165 -0
- data/lib/action_view/helpers/output_safety_helper.rb +70 -0
- data/lib/action_view/helpers/rendering_helper.rb +218 -0
- data/lib/action_view/helpers/sanitize_helper.rb +201 -0
- data/lib/action_view/helpers/tag_helper.rb +621 -0
- data/lib/action_view/helpers/tags/base.rb +138 -0
- data/lib/action_view/helpers/tags/check_box.rb +65 -0
- data/lib/action_view/helpers/tags/checkable.rb +18 -0
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +37 -0
- data/lib/action_view/helpers/tags/collection_helpers.rb +118 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
- data/lib/action_view/helpers/tags/collection_select.rb +33 -0
- data/lib/action_view/helpers/tags/color_field.rb +26 -0
- data/lib/action_view/helpers/tags/date_field.rb +14 -0
- data/lib/action_view/helpers/tags/date_select.rb +75 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +39 -0
- data/lib/action_view/helpers/tags/datetime_local_field.rb +29 -0
- data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
- data/lib/action_view/helpers/tags/email_field.rb +10 -0
- data/lib/action_view/helpers/tags/file_field.rb +26 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +34 -0
- data/lib/action_view/helpers/tags/hidden_field.rb +14 -0
- data/lib/action_view/helpers/tags/label.rb +84 -0
- data/lib/action_view/helpers/tags/month_field.rb +14 -0
- data/lib/action_view/helpers/tags/number_field.rb +20 -0
- data/lib/action_view/helpers/tags/password_field.rb +14 -0
- data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
- data/lib/action_view/helpers/tags/radio_button.rb +32 -0
- data/lib/action_view/helpers/tags/range_field.rb +10 -0
- data/lib/action_view/helpers/tags/search_field.rb +27 -0
- data/lib/action_view/helpers/tags/select.rb +45 -0
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/tel_field.rb +10 -0
- data/lib/action_view/helpers/tags/text_area.rb +24 -0
- data/lib/action_view/helpers/tags/text_field.rb +33 -0
- data/lib/action_view/helpers/tags/time_field.rb +23 -0
- data/lib/action_view/helpers/tags/time_select.rb +10 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +25 -0
- data/lib/action_view/helpers/tags/translator.rb +39 -0
- data/lib/action_view/helpers/tags/url_field.rb +10 -0
- data/lib/action_view/helpers/tags/week_field.rb +14 -0
- data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
- data/lib/action_view/helpers/tags.rb +47 -0
- data/lib/action_view/helpers/text_helper.rb +568 -0
- data/lib/action_view/helpers/translation_helper.rb +161 -0
- data/lib/action_view/helpers/url_helper.rb +812 -0
- data/lib/action_view/helpers.rb +68 -0
- data/lib/action_view/layouts.rb +434 -0
- data/lib/action_view/locale/en.yml +56 -0
- data/lib/action_view/log_subscriber.rb +132 -0
- data/lib/action_view/lookup_context.rb +299 -0
- data/lib/action_view/model_naming.rb +14 -0
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +84 -0
- data/lib/action_view/railtie.rb +132 -0
- data/lib/action_view/record_identifier.rb +118 -0
- data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
- data/lib/action_view/render_parser/ripper_render_parser.rb +350 -0
- data/lib/action_view/render_parser.rb +40 -0
- data/lib/action_view/renderer/abstract_renderer.rb +186 -0
- data/lib/action_view/renderer/collection_renderer.rb +204 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +120 -0
- data/lib/action_view/renderer/partial_renderer.rb +267 -0
- data/lib/action_view/renderer/renderer.rb +107 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +107 -0
- data/lib/action_view/renderer/template_renderer.rb +115 -0
- data/lib/action_view/rendering.rb +190 -0
- data/lib/action_view/routing_url_for.rb +149 -0
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +264 -0
- data/lib/action_view/template/handlers/builder.rb +25 -0
- data/lib/action_view/template/handlers/erb/erubi.rb +85 -0
- data/lib/action_view/template/handlers/erb.rb +157 -0
- data/lib/action_view/template/handlers/html.rb +11 -0
- data/lib/action_view/template/handlers/raw.rb +11 -0
- data/lib/action_view/template/handlers.rb +66 -0
- data/lib/action_view/template/html.rb +33 -0
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +30 -0
- data/lib/action_view/template/resolver.rb +212 -0
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +32 -0
- data/lib/action_view/template/types.rb +50 -0
- data/lib/action_view/template.rb +580 -0
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +66 -0
- data/lib/action_view/test_case.rb +449 -0
- data/lib/action_view/testing/resolvers.rb +44 -0
- data/lib/action_view/unbound_template.rb +67 -0
- data/lib/action_view/version.rb +10 -0
- data/lib/action_view/view_paths.rb +117 -0
- data/lib/action_view.rb +104 -0
- metadata +275 -0
@@ -0,0 +1,812 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/array/access"
|
4
|
+
require "active_support/core_ext/hash/keys"
|
5
|
+
require "active_support/core_ext/string/output_safety"
|
6
|
+
require "action_view/helpers/content_exfiltration_prevention_helper"
|
7
|
+
require "action_view/helpers/tag_helper"
|
8
|
+
|
9
|
+
module ActionView
|
10
|
+
module Helpers # :nodoc:
|
11
|
+
# = Action View URL \Helpers
|
12
|
+
#
|
13
|
+
# Provides a set of methods for making links and getting URLs that
|
14
|
+
# depend on the routing subsystem (see ActionDispatch::Routing).
|
15
|
+
# This allows you to use the same format for links in views
|
16
|
+
# and controllers.
|
17
|
+
module UrlHelper
|
18
|
+
# This helper may be included in any class that includes the
|
19
|
+
# URL helpers of a routes (routes.url_helpers). Some methods
|
20
|
+
# provided here will only work in the context of a request
|
21
|
+
# (link_to_unless_current, for instance), which must be provided
|
22
|
+
# as a method called #request on the context.
|
23
|
+
BUTTON_TAG_METHOD_VERBS = %w{patch put delete}
|
24
|
+
extend ActiveSupport::Concern
|
25
|
+
|
26
|
+
include TagHelper
|
27
|
+
include ContentExfiltrationPreventionHelper
|
28
|
+
|
29
|
+
module ClassMethods
|
30
|
+
def _url_for_modules
|
31
|
+
ActionView::RoutingUrlFor
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
mattr_accessor :button_to_generates_button_tag, default: false
|
36
|
+
|
37
|
+
# Basic implementation of url_for to allow use helpers without routes existence
|
38
|
+
def url_for(options = nil) # :nodoc:
|
39
|
+
case options
|
40
|
+
when String
|
41
|
+
options
|
42
|
+
when :back
|
43
|
+
_back_url
|
44
|
+
else
|
45
|
+
raise ArgumentError, "arguments passed to url_for can't be handled. Please require " \
|
46
|
+
"routes or provide your own implementation"
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def _back_url # :nodoc:
|
51
|
+
_filtered_referrer || "javascript:history.back()"
|
52
|
+
end
|
53
|
+
private :_back_url
|
54
|
+
|
55
|
+
def _filtered_referrer # :nodoc:
|
56
|
+
if controller.respond_to?(:request)
|
57
|
+
referrer = controller.request.env["HTTP_REFERER"]
|
58
|
+
if referrer && URI(referrer).scheme != "javascript"
|
59
|
+
referrer
|
60
|
+
end
|
61
|
+
end
|
62
|
+
rescue URI::InvalidURIError
|
63
|
+
end
|
64
|
+
private :_filtered_referrer
|
65
|
+
|
66
|
+
# Creates an anchor element of the given +name+ using a URL created by the set of +options+.
|
67
|
+
# See the valid options in the documentation for +url_for+. It's also possible to
|
68
|
+
# pass a \String instead of an options hash, which generates an anchor element that uses the
|
69
|
+
# value of the \String as the href for the link. Using a <tt>:back</tt> \Symbol instead
|
70
|
+
# of an options hash will generate a link to the referrer (a JavaScript back link
|
71
|
+
# will be used in place of a referrer if none exists). If +nil+ is passed as the name
|
72
|
+
# the value of the link itself will become the name.
|
73
|
+
#
|
74
|
+
# ==== Signatures
|
75
|
+
#
|
76
|
+
# link_to(body, url, html_options = {})
|
77
|
+
# # url is a String; you can use URL helpers like
|
78
|
+
# # posts_path
|
79
|
+
#
|
80
|
+
# link_to(body, url_options = {}, html_options = {})
|
81
|
+
# # url_options, except :method, is passed to url_for
|
82
|
+
#
|
83
|
+
# link_to(options = {}, html_options = {}) do
|
84
|
+
# # name
|
85
|
+
# end
|
86
|
+
#
|
87
|
+
# link_to(url, html_options = {}) do
|
88
|
+
# # name
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# link_to(active_record_model)
|
92
|
+
#
|
93
|
+
# ==== Options
|
94
|
+
# * <tt>:data</tt> - This option can be used to add custom data attributes.
|
95
|
+
#
|
96
|
+
# ==== Examples
|
97
|
+
#
|
98
|
+
# Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
|
99
|
+
# and newer RESTful routes. Current \Rails style favors RESTful routes whenever possible, so base
|
100
|
+
# your application on resources and use
|
101
|
+
#
|
102
|
+
# link_to "Profile", profile_path(@profile)
|
103
|
+
# # => <a href="/profiles/1">Profile</a>
|
104
|
+
#
|
105
|
+
# or the even pithier
|
106
|
+
#
|
107
|
+
# link_to "Profile", @profile
|
108
|
+
# # => <a href="/profiles/1">Profile</a>
|
109
|
+
#
|
110
|
+
# in place of the older more verbose, non-resource-oriented
|
111
|
+
#
|
112
|
+
# link_to "Profile", controller: "profiles", action: "show", id: @profile
|
113
|
+
# # => <a href="/profiles/show/1">Profile</a>
|
114
|
+
#
|
115
|
+
# Similarly,
|
116
|
+
#
|
117
|
+
# link_to "Profiles", profiles_path
|
118
|
+
# # => <a href="/profiles">Profiles</a>
|
119
|
+
#
|
120
|
+
# is better than
|
121
|
+
#
|
122
|
+
# link_to "Profiles", controller: "profiles"
|
123
|
+
# # => <a href="/profiles">Profiles</a>
|
124
|
+
#
|
125
|
+
# When name is +nil+ the href is presented instead
|
126
|
+
#
|
127
|
+
# link_to nil, "http://example.com"
|
128
|
+
# # => <a href="http://www.example.com">http://www.example.com</a>
|
129
|
+
#
|
130
|
+
# More concise yet, when +name+ is an Active Record model that defines a
|
131
|
+
# +to_s+ method returning a default value or a model instance attribute
|
132
|
+
#
|
133
|
+
# link_to @profile
|
134
|
+
# # => <a href="http://www.example.com/profiles/1">Eileen</a>
|
135
|
+
#
|
136
|
+
# You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
|
137
|
+
#
|
138
|
+
# <%= link_to(@profile) do %>
|
139
|
+
# <strong><%= @profile.name %></strong> -- <span>Check it out!</span>
|
140
|
+
# <% end %>
|
141
|
+
# # => <a href="/profiles/1">
|
142
|
+
# <strong>David</strong> -- <span>Check it out!</span>
|
143
|
+
# </a>
|
144
|
+
#
|
145
|
+
# Classes and ids for CSS are easy to produce:
|
146
|
+
#
|
147
|
+
# link_to "Articles", articles_path, id: "news", class: "article"
|
148
|
+
# # => <a href="/articles" class="article" id="news">Articles</a>
|
149
|
+
#
|
150
|
+
# Be careful when using the older argument style, as an extra literal hash is needed:
|
151
|
+
#
|
152
|
+
# link_to "Articles", { controller: "articles" }, id: "news", class: "article"
|
153
|
+
# # => <a href="/articles" class="article" id="news">Articles</a>
|
154
|
+
#
|
155
|
+
# Leaving the hash off gives the wrong link:
|
156
|
+
#
|
157
|
+
# link_to "WRONG!", controller: "articles", id: "news", class: "article"
|
158
|
+
# # => <a href="/articles/index/news?class=article">WRONG!</a>
|
159
|
+
#
|
160
|
+
# +link_to+ can also produce links with anchors or query strings:
|
161
|
+
#
|
162
|
+
# link_to "Comment wall", profile_path(@profile, anchor: "wall")
|
163
|
+
# # => <a href="/profiles/1#wall">Comment wall</a>
|
164
|
+
#
|
165
|
+
# link_to "Ruby on Rails search", controller: "searches", query: "ruby on rails"
|
166
|
+
# # => <a href="/searches?query=ruby+on+rails">Ruby on Rails search</a>
|
167
|
+
#
|
168
|
+
# link_to "Nonsense search", searches_path(foo: "bar", baz: "quux")
|
169
|
+
# # => <a href="/searches?foo=bar&baz=quux">Nonsense search</a>
|
170
|
+
#
|
171
|
+
# You can set any link attributes such as <tt>target</tt>, <tt>rel</tt>, <tt>type</tt>:
|
172
|
+
#
|
173
|
+
# link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
|
174
|
+
# # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>
|
175
|
+
#
|
176
|
+
# ==== Turbo
|
177
|
+
#
|
178
|
+
# Rails 7 ships with Turbo enabled by default. Turbo provides the following +:data+ options:
|
179
|
+
#
|
180
|
+
# * <tt>turbo_method: symbol of HTTP verb</tt> - Performs a Turbo link visit
|
181
|
+
# with the given HTTP verb. Forms are recommended when performing non-+GET+ requests.
|
182
|
+
# Only use <tt>data-turbo-method</tt> where a form is not possible.
|
183
|
+
#
|
184
|
+
# * <tt>turbo_confirm: "question?"</tt> - Adds a confirmation dialog to the link with the
|
185
|
+
# given value.
|
186
|
+
#
|
187
|
+
# {Consult the Turbo Handbook for more information on the options
|
188
|
+
# above.}[https://turbo.hotwired.dev/handbook/drive#performing-visits-with-a-different-method]
|
189
|
+
#
|
190
|
+
# ===== \Examples
|
191
|
+
#
|
192
|
+
# link_to "Delete profile", @profile, data: { turbo_method: :delete }
|
193
|
+
# # => <a href="/profiles/1" data-turbo-method="delete">Delete profile</a>
|
194
|
+
#
|
195
|
+
# link_to "Visit Other Site", "https://rubyonrails.org/", data: { turbo_confirm: "Are you sure?" }
|
196
|
+
# # => <a href="https://rubyonrails.org/" data-turbo-confirm="Are you sure?">Visit Other Site</a>
|
197
|
+
#
|
198
|
+
def link_to(name = nil, options = nil, html_options = nil, &block)
|
199
|
+
html_options, options, name = options, name, block if block_given?
|
200
|
+
options ||= {}
|
201
|
+
|
202
|
+
html_options = convert_options_to_data_attributes(options, html_options)
|
203
|
+
|
204
|
+
url = url_target(name, options)
|
205
|
+
html_options["href"] ||= url
|
206
|
+
|
207
|
+
content_tag("a", name || url, html_options, &block)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Generates a form containing a single button that submits to the URL created
|
211
|
+
# by the set of +options+. This is the safest method to ensure links that
|
212
|
+
# cause changes to your data are not triggered by search bots or accelerators.
|
213
|
+
#
|
214
|
+
# You can control the form and button behavior with +html_options+. Most
|
215
|
+
# values in +html_options+ are passed through to the button element. For
|
216
|
+
# example, passing a +:class+ option within +html_options+ will set the
|
217
|
+
# class attribute of the button element.
|
218
|
+
#
|
219
|
+
# The class attribute of the form element can be set by passing a
|
220
|
+
# +:form_class+ option within +html_options+. It defaults to
|
221
|
+
# <tt>"button_to"</tt> to allow styling of the form and its children.
|
222
|
+
#
|
223
|
+
# The form submits a POST request by default if the object is not persisted;
|
224
|
+
# conversely, if the object is persisted, it will submit a PATCH request.
|
225
|
+
# To specify a different HTTP verb use the +:method+ option within +html_options+.
|
226
|
+
#
|
227
|
+
# If the HTML button generated from +button_to+ does not work with your layout, you can
|
228
|
+
# consider using the +link_to+ method with the +data-turbo-method+
|
229
|
+
# attribute as described in the +link_to+ documentation.
|
230
|
+
#
|
231
|
+
# ==== Options
|
232
|
+
# The +options+ hash accepts the same options as +url_for+. To generate a
|
233
|
+
# <tt><form></tt> element without an <tt>[action]</tt> attribute, pass
|
234
|
+
# <tt>false</tt>:
|
235
|
+
#
|
236
|
+
# <%= button_to "New", false %>
|
237
|
+
# # => "<form method="post" class="button_to">
|
238
|
+
# # <button type="submit">New</button>
|
239
|
+
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
|
240
|
+
# # </form>"
|
241
|
+
#
|
242
|
+
# Most values in +html_options+ are passed through to the button element,
|
243
|
+
# but there are a few special options:
|
244
|
+
#
|
245
|
+
# * <tt>:method</tt> - \Symbol of HTTP verb. Supported verbs are <tt>:post</tt>, <tt>:get</tt>,
|
246
|
+
# <tt>:delete</tt>, <tt>:patch</tt>, and <tt>:put</tt>. By default it will be <tt>:post</tt>.
|
247
|
+
# * <tt>:disabled</tt> - If set to true, it will generate a disabled button.
|
248
|
+
# * <tt>:data</tt> - This option can be used to add custom data attributes.
|
249
|
+
# * <tt>:form</tt> - This hash will be form attributes
|
250
|
+
# * <tt>:form_class</tt> - This controls the class of the form within which the submit button will
|
251
|
+
# be placed
|
252
|
+
# * <tt>:params</tt> - \Hash of parameters to be rendered as hidden fields within the form.
|
253
|
+
#
|
254
|
+
# ==== Examples
|
255
|
+
# <%= button_to "New", action: "new" %>
|
256
|
+
# # => "<form method="post" action="/controller/new" class="button_to">
|
257
|
+
# # <button type="submit">New</button>
|
258
|
+
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
|
259
|
+
# # </form>"
|
260
|
+
#
|
261
|
+
# <%= button_to "New", new_article_path %>
|
262
|
+
# # => "<form method="post" action="/articles/new" class="button_to">
|
263
|
+
# # <button type="submit">New</button>
|
264
|
+
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
|
265
|
+
# # </form>"
|
266
|
+
#
|
267
|
+
# <%= button_to "New", new_article_path, params: { time: Time.now } %>
|
268
|
+
# # => "<form method="post" action="/articles/new" class="button_to">
|
269
|
+
# # <button type="submit">New</button>
|
270
|
+
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6"/>
|
271
|
+
# # <input type="hidden" name="time" value="2021-04-08 14:06:09 -0500" autocomplete="off">
|
272
|
+
# # </form>"
|
273
|
+
#
|
274
|
+
# <%= button_to [:make_happy, @user] do %>
|
275
|
+
# Make happy <strong><%= @user.name %></strong>
|
276
|
+
# <% end %>
|
277
|
+
# # => "<form method="post" action="/users/1/make_happy" class="button_to">
|
278
|
+
# # <button type="submit">
|
279
|
+
# # Make happy <strong><%= @user.name %></strong>
|
280
|
+
# # </button>
|
281
|
+
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
|
282
|
+
# # </form>"
|
283
|
+
#
|
284
|
+
# <%= button_to "New", { action: "new" }, form_class: "new-thing" %>
|
285
|
+
# # => "<form method="post" action="/controller/new" class="new-thing">
|
286
|
+
# # <button type="submit">New</button>
|
287
|
+
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
|
288
|
+
# # </form>"
|
289
|
+
#
|
290
|
+
# <%= button_to "Create", { action: "create" }, form: { "data-type" => "json" } %>
|
291
|
+
# # => "<form method="post" action="/images/create" class="button_to" data-type="json">
|
292
|
+
# # <button type="submit">Create</button>
|
293
|
+
# # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
|
294
|
+
# # </form>"
|
295
|
+
#
|
296
|
+
def button_to(name = nil, options = nil, html_options = nil, &block)
|
297
|
+
html_options, options = options, name if block_given?
|
298
|
+
html_options ||= {}
|
299
|
+
html_options = html_options.stringify_keys
|
300
|
+
|
301
|
+
url =
|
302
|
+
case options
|
303
|
+
when FalseClass then nil
|
304
|
+
else url_for(options)
|
305
|
+
end
|
306
|
+
|
307
|
+
remote = html_options.delete("remote")
|
308
|
+
params = html_options.delete("params")
|
309
|
+
|
310
|
+
authenticity_token = html_options.delete("authenticity_token")
|
311
|
+
|
312
|
+
method = (html_options.delete("method").presence || method_for_options(options)).to_s
|
313
|
+
method_tag = BUTTON_TAG_METHOD_VERBS.include?(method) ? method_tag(method) : "".html_safe
|
314
|
+
|
315
|
+
form_method = method == "get" ? "get" : "post"
|
316
|
+
form_options = html_options.delete("form") || {}
|
317
|
+
form_options[:class] ||= html_options.delete("form_class") || "button_to"
|
318
|
+
form_options[:method] = form_method
|
319
|
+
form_options[:action] = url
|
320
|
+
form_options[:'data-remote'] = true if remote
|
321
|
+
|
322
|
+
request_token_tag = if form_method == "post"
|
323
|
+
request_method = method.empty? ? "post" : method
|
324
|
+
token_tag(authenticity_token, form_options: { action: url, method: request_method })
|
325
|
+
else
|
326
|
+
""
|
327
|
+
end
|
328
|
+
|
329
|
+
html_options = convert_options_to_data_attributes(options, html_options)
|
330
|
+
html_options["type"] = "submit"
|
331
|
+
|
332
|
+
button = if block_given?
|
333
|
+
content_tag("button", html_options, &block)
|
334
|
+
elsif button_to_generates_button_tag
|
335
|
+
content_tag("button", name || url, html_options, &block)
|
336
|
+
else
|
337
|
+
html_options["value"] = name || url
|
338
|
+
tag("input", html_options)
|
339
|
+
end
|
340
|
+
|
341
|
+
inner_tags = method_tag.safe_concat(button).safe_concat(request_token_tag)
|
342
|
+
if params
|
343
|
+
to_form_params(params).each do |param|
|
344
|
+
inner_tags.safe_concat tag(:input, type: "hidden", name: param[:name], value: param[:value],
|
345
|
+
autocomplete: "off")
|
346
|
+
end
|
347
|
+
end
|
348
|
+
html = content_tag("form", inner_tags, form_options)
|
349
|
+
prevent_content_exfiltration(html)
|
350
|
+
end
|
351
|
+
|
352
|
+
# Creates a link tag of the given +name+ using a URL created by the set of
|
353
|
+
# +options+ unless the current request URI is the same as the links, in
|
354
|
+
# which case only the name is returned (or the given block is yielded, if
|
355
|
+
# one exists). You can give +link_to_unless_current+ a block which will
|
356
|
+
# specialize the default behavior (e.g., show a "Start Here" link rather
|
357
|
+
# than the link's text).
|
358
|
+
#
|
359
|
+
# ==== Examples
|
360
|
+
# Let's say you have a navigation menu...
|
361
|
+
#
|
362
|
+
# <ul id="navbar">
|
363
|
+
# <li><%= link_to_unless_current("Home", { action: "index" }) %></li>
|
364
|
+
# <li><%= link_to_unless_current("About Us", { action: "about" }) %></li>
|
365
|
+
# </ul>
|
366
|
+
#
|
367
|
+
# If in the "about" action, it will render...
|
368
|
+
#
|
369
|
+
# <ul id="navbar">
|
370
|
+
# <li><a href="/controller/index">Home</a></li>
|
371
|
+
# <li>About Us</li>
|
372
|
+
# </ul>
|
373
|
+
#
|
374
|
+
# ...but if in the "index" action, it will render:
|
375
|
+
#
|
376
|
+
# <ul id="navbar">
|
377
|
+
# <li>Home</li>
|
378
|
+
# <li><a href="/controller/about">About Us</a></li>
|
379
|
+
# </ul>
|
380
|
+
#
|
381
|
+
# The implicit block given to +link_to_unless_current+ is evaluated if the current
|
382
|
+
# action is the action given. So, if we had a comments page and wanted to render a
|
383
|
+
# "Go Back" link instead of a link to the comments page, we could do something like this...
|
384
|
+
#
|
385
|
+
# <%=
|
386
|
+
# link_to_unless_current("Comment", { controller: "comments", action: "new" }) do
|
387
|
+
# link_to("Go back", { controller: "posts", action: "index" })
|
388
|
+
# end
|
389
|
+
# %>
|
390
|
+
def link_to_unless_current(name, options = {}, html_options = {}, &block)
|
391
|
+
link_to_unless current_page?(options), name, options, html_options, &block
|
392
|
+
end
|
393
|
+
|
394
|
+
# Creates a link tag of the given +name+ using a URL created by the set of
|
395
|
+
# +options+ unless +condition+ is true, in which case only the name is
|
396
|
+
# returned. To specialize the default behavior (i.e., show a login link rather
|
397
|
+
# than just the plaintext link text), you can pass a block that
|
398
|
+
# accepts the name or the full argument list for +link_to_unless+.
|
399
|
+
#
|
400
|
+
# ==== Examples
|
401
|
+
# <%= link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) %>
|
402
|
+
# # If the user is logged in...
|
403
|
+
# # => <a href="/controller/reply/">Reply</a>
|
404
|
+
#
|
405
|
+
# <%=
|
406
|
+
# link_to_unless(@current_user.nil?, "Reply", { action: "reply" }) do |name|
|
407
|
+
# link_to(name, { controller: "accounts", action: "signup" })
|
408
|
+
# end
|
409
|
+
# %>
|
410
|
+
# # If the user is logged in...
|
411
|
+
# # => <a href="/controller/reply/">Reply</a>
|
412
|
+
# # If not...
|
413
|
+
# # => <a href="/accounts/signup">Reply</a>
|
414
|
+
def link_to_unless(condition, name, options = {}, html_options = {}, &block)
|
415
|
+
link_to_if !condition, name, options, html_options, &block
|
416
|
+
end
|
417
|
+
|
418
|
+
# Creates a link tag of the given +name+ using a URL created by the set of
|
419
|
+
# +options+ if +condition+ is true, otherwise only the name is
|
420
|
+
# returned. To specialize the default behavior, you can pass a block that
|
421
|
+
# accepts the name or the full argument list for +link_to_if+.
|
422
|
+
#
|
423
|
+
# ==== Examples
|
424
|
+
# <%= link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) %>
|
425
|
+
# # If the user isn't logged in...
|
426
|
+
# # => <a href="/sessions/new/">Login</a>
|
427
|
+
#
|
428
|
+
# <%=
|
429
|
+
# link_to_if(@current_user.nil?, "Login", { controller: "sessions", action: "new" }) do
|
430
|
+
# link_to(@current_user.login, { controller: "accounts", action: "show", id: @current_user })
|
431
|
+
# end
|
432
|
+
# %>
|
433
|
+
# # If the user isn't logged in...
|
434
|
+
# # => <a href="/sessions/new/">Login</a>
|
435
|
+
# # If they are logged in...
|
436
|
+
# # => <a href="/accounts/show/3">my_username</a>
|
437
|
+
def link_to_if(condition, name, options = {}, html_options = {}, &block)
|
438
|
+
if condition
|
439
|
+
link_to(name, options, html_options)
|
440
|
+
else
|
441
|
+
if block_given?
|
442
|
+
block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
|
443
|
+
else
|
444
|
+
ERB::Util.html_escape(name)
|
445
|
+
end
|
446
|
+
end
|
447
|
+
end
|
448
|
+
|
449
|
+
# Creates a mailto link tag to the specified +email_address+, which is
|
450
|
+
# also used as the name of the link unless +name+ is specified. Additional
|
451
|
+
# HTML attributes for the link can be passed in +html_options+.
|
452
|
+
#
|
453
|
+
# +mail_to+ has several methods for customizing the email itself by
|
454
|
+
# passing special keys to +html_options+.
|
455
|
+
#
|
456
|
+
# ==== Options
|
457
|
+
# * <tt>:subject</tt> - Preset the subject line of the email.
|
458
|
+
# * <tt>:body</tt> - Preset the body of the email.
|
459
|
+
# * <tt>:cc</tt> - Carbon Copy additional recipients on the email.
|
460
|
+
# * <tt>:bcc</tt> - Blind Carbon Copy additional recipients on the email.
|
461
|
+
# * <tt>:reply_to</tt> - Preset the +Reply-To+ field of the email.
|
462
|
+
#
|
463
|
+
# ==== Obfuscation
|
464
|
+
# Prior to \Rails 4.0, +mail_to+ provided options for encoding the address
|
465
|
+
# in order to hinder email harvesters. To take advantage of these options,
|
466
|
+
# install the +actionview-encoded_mail_to+ gem.
|
467
|
+
#
|
468
|
+
# ==== Examples
|
469
|
+
# mail_to "me@domain.com"
|
470
|
+
# # => <a href="mailto:me@domain.com">me@domain.com</a>
|
471
|
+
#
|
472
|
+
# mail_to "me@domain.com", "My email"
|
473
|
+
# # => <a href="mailto:me@domain.com">My email</a>
|
474
|
+
#
|
475
|
+
# mail_to "me@domain.com", cc: "ccaddress@domain.com",
|
476
|
+
# subject: "This is an example email"
|
477
|
+
# # => <a href="mailto:me@domain.com?cc=ccaddress@domain.com&subject=This%20is%20an%20example%20email">me@domain.com</a>
|
478
|
+
#
|
479
|
+
# You can use a block as well if your link target is hard to fit into the name parameter. ERB example:
|
480
|
+
#
|
481
|
+
# <%= mail_to "me@domain.com" do %>
|
482
|
+
# <strong>Email me:</strong> <span>me@domain.com</span>
|
483
|
+
# <% end %>
|
484
|
+
# # => <a href="mailto:me@domain.com">
|
485
|
+
# <strong>Email me:</strong> <span>me@domain.com</span>
|
486
|
+
# </a>
|
487
|
+
def mail_to(email_address, name = nil, html_options = {}, &block)
|
488
|
+
html_options, name = name, nil if name.is_a?(Hash)
|
489
|
+
html_options = (html_options || {}).stringify_keys
|
490
|
+
|
491
|
+
extras = %w{ cc bcc body subject reply_to }.map! { |item|
|
492
|
+
option = html_options.delete(item).presence || next
|
493
|
+
"#{item.dasherize}=#{ERB::Util.url_encode(option)}"
|
494
|
+
}.compact
|
495
|
+
extras = extras.empty? ? "" : "?" + extras.join("&")
|
496
|
+
|
497
|
+
encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
|
498
|
+
html_options["href"] = "mailto:#{encoded_email_address}#{extras}"
|
499
|
+
|
500
|
+
content_tag("a", name || email_address, html_options, &block)
|
501
|
+
end
|
502
|
+
|
503
|
+
# True if the current request URI was generated by the given +options+.
|
504
|
+
#
|
505
|
+
# ==== Examples
|
506
|
+
# Let's say we're in the <tt>http://www.example.com/shop/checkout?order=desc&page=1</tt> action.
|
507
|
+
#
|
508
|
+
# current_page?(action: 'process')
|
509
|
+
# # => false
|
510
|
+
#
|
511
|
+
# current_page?(action: 'checkout')
|
512
|
+
# # => true
|
513
|
+
#
|
514
|
+
# current_page?(controller: 'library', action: 'checkout')
|
515
|
+
# # => false
|
516
|
+
#
|
517
|
+
# current_page?(controller: 'shop', action: 'checkout')
|
518
|
+
# # => true
|
519
|
+
#
|
520
|
+
# current_page?(controller: 'shop', action: 'checkout', order: 'asc')
|
521
|
+
# # => false
|
522
|
+
#
|
523
|
+
# current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '1')
|
524
|
+
# # => true
|
525
|
+
#
|
526
|
+
# current_page?(controller: 'shop', action: 'checkout', order: 'desc', page: '2')
|
527
|
+
# # => false
|
528
|
+
#
|
529
|
+
# current_page?('http://www.example.com/shop/checkout')
|
530
|
+
# # => true
|
531
|
+
#
|
532
|
+
# current_page?('http://www.example.com/shop/checkout', check_parameters: true)
|
533
|
+
# # => false
|
534
|
+
#
|
535
|
+
# current_page?('/shop/checkout')
|
536
|
+
# # => true
|
537
|
+
#
|
538
|
+
# current_page?('http://www.example.com/shop/checkout?order=desc&page=1')
|
539
|
+
# # => true
|
540
|
+
#
|
541
|
+
# Let's say we're in the <tt>http://www.example.com/products</tt> action with method POST in case of invalid product.
|
542
|
+
#
|
543
|
+
# current_page?(controller: 'product', action: 'index')
|
544
|
+
# # => false
|
545
|
+
#
|
546
|
+
# We can also pass in the symbol arguments instead of strings.
|
547
|
+
#
|
548
|
+
def current_page?(options = nil, check_parameters: false, **options_as_kwargs)
|
549
|
+
unless request
|
550
|
+
raise "You cannot use helpers that need to determine the current " \
|
551
|
+
"page unless your view context provides a Request object " \
|
552
|
+
"in a #request method"
|
553
|
+
end
|
554
|
+
|
555
|
+
return false unless request.get? || request.head?
|
556
|
+
|
557
|
+
options ||= options_as_kwargs
|
558
|
+
check_parameters ||= options.is_a?(Hash) && options.delete(:check_parameters)
|
559
|
+
url_string = URI::RFC2396_PARSER.unescape(url_for(options)).force_encoding(Encoding::BINARY)
|
560
|
+
|
561
|
+
# We ignore any extra parameters in the request_uri if the
|
562
|
+
# submitted URL doesn't have any either. This lets the function
|
563
|
+
# work with things like ?order=asc
|
564
|
+
# the behavior can be disabled with check_parameters: true
|
565
|
+
request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
|
566
|
+
request_uri = URI::RFC2396_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY)
|
567
|
+
|
568
|
+
if %r{^\w+://}.match?(url_string)
|
569
|
+
request_uri = +"#{request.protocol}#{request.host_with_port}#{request_uri}"
|
570
|
+
end
|
571
|
+
|
572
|
+
remove_trailing_slash!(url_string)
|
573
|
+
remove_trailing_slash!(request_uri)
|
574
|
+
|
575
|
+
url_string == request_uri
|
576
|
+
end
|
577
|
+
|
578
|
+
# Creates an SMS anchor link tag to the specified +phone_number+. When the
|
579
|
+
# link is clicked, the default SMS messaging app is opened ready to send a
|
580
|
+
# message to the linked phone number. If the +body+ option is specified,
|
581
|
+
# the contents of the message will be preset to +body+.
|
582
|
+
#
|
583
|
+
# If +name+ is not specified, +phone_number+ will be used as the name of
|
584
|
+
# the link.
|
585
|
+
#
|
586
|
+
# A +country_code+ option is supported, which prepends a plus sign and the
|
587
|
+
# given country code to the linked phone number. For example,
|
588
|
+
# <tt>country_code: "01"</tt> will prepend <tt>+01</tt> to the linked
|
589
|
+
# phone number.
|
590
|
+
#
|
591
|
+
# Additional HTML attributes for the link can be passed via +html_options+.
|
592
|
+
#
|
593
|
+
# ==== Options
|
594
|
+
# * <tt>:country_code</tt> - Prepend the country code to the phone number.
|
595
|
+
# * <tt>:body</tt> - Preset the body of the message.
|
596
|
+
#
|
597
|
+
# ==== Examples
|
598
|
+
# sms_to "5155555785"
|
599
|
+
# # => <a href="sms:5155555785;">5155555785</a>
|
600
|
+
#
|
601
|
+
# sms_to "5155555785", country_code: "01"
|
602
|
+
# # => <a href="sms:+015155555785;">5155555785</a>
|
603
|
+
#
|
604
|
+
# sms_to "5155555785", "Text me"
|
605
|
+
# # => <a href="sms:5155555785;">Text me</a>
|
606
|
+
#
|
607
|
+
# sms_to "5155555785", body: "I have a question about your product."
|
608
|
+
# # => <a href="sms:5155555785;?body=I%20have%20a%20question%20about%20your%20product">5155555785</a>
|
609
|
+
#
|
610
|
+
# You can use a block as well if your link target is hard to fit into the name parameter. \ERB example:
|
611
|
+
#
|
612
|
+
# <%= sms_to "5155555785" do %>
|
613
|
+
# <strong>Text me:</strong>
|
614
|
+
# <% end %>
|
615
|
+
# # => <a href="sms:5155555785;">
|
616
|
+
# <strong>Text me:</strong>
|
617
|
+
# </a>
|
618
|
+
def sms_to(phone_number, name = nil, html_options = {}, &block)
|
619
|
+
html_options, name = name, nil if name.is_a?(Hash)
|
620
|
+
html_options = (html_options || {}).stringify_keys
|
621
|
+
|
622
|
+
country_code = html_options.delete("country_code").presence
|
623
|
+
country_code = country_code ? "+#{ERB::Util.url_encode(country_code)}" : ""
|
624
|
+
|
625
|
+
body = html_options.delete("body").presence
|
626
|
+
body = body ? "?&body=#{ERB::Util.url_encode(body)}" : ""
|
627
|
+
|
628
|
+
encoded_phone_number = ERB::Util.url_encode(phone_number)
|
629
|
+
html_options["href"] = "sms:#{country_code}#{encoded_phone_number};#{body}"
|
630
|
+
|
631
|
+
content_tag("a", name || phone_number, html_options, &block)
|
632
|
+
end
|
633
|
+
|
634
|
+
# Creates a TEL anchor link tag to the specified +phone_number+. When the
|
635
|
+
# link is clicked, the default app to make phone calls is opened and
|
636
|
+
# prepopulated with the phone number.
|
637
|
+
#
|
638
|
+
# If +name+ is not specified, +phone_number+ will be used as the name of
|
639
|
+
# the link.
|
640
|
+
#
|
641
|
+
# A +country_code+ option is supported, which prepends a plus sign and the
|
642
|
+
# given country code to the linked phone number. For example,
|
643
|
+
# <tt>country_code: "01"</tt> will prepend <tt>+01</tt> to the linked
|
644
|
+
# phone number.
|
645
|
+
#
|
646
|
+
# Additional HTML attributes for the link can be passed via +html_options+.
|
647
|
+
#
|
648
|
+
# ==== Options
|
649
|
+
# * <tt>:country_code</tt> - Prepends the country code to the phone number
|
650
|
+
#
|
651
|
+
# ==== Examples
|
652
|
+
# phone_to "1234567890"
|
653
|
+
# # => <a href="tel:1234567890">1234567890</a>
|
654
|
+
#
|
655
|
+
# phone_to "1234567890", "Phone me"
|
656
|
+
# # => <a href="tel:1234567890">Phone me</a>
|
657
|
+
#
|
658
|
+
# phone_to "1234567890", country_code: "01"
|
659
|
+
# # => <a href="tel:+011234567890">1234567890</a>
|
660
|
+
#
|
661
|
+
# You can use a block as well if your link target is hard to fit into the name parameter. \ERB example:
|
662
|
+
#
|
663
|
+
# <%= phone_to "1234567890" do %>
|
664
|
+
# <strong>Phone me:</strong>
|
665
|
+
# <% end %>
|
666
|
+
# # => <a href="tel:1234567890">
|
667
|
+
# <strong>Phone me:</strong>
|
668
|
+
# </a>
|
669
|
+
def phone_to(phone_number, name = nil, html_options = {}, &block)
|
670
|
+
html_options, name = name, nil if name.is_a?(Hash)
|
671
|
+
html_options = (html_options || {}).stringify_keys
|
672
|
+
|
673
|
+
country_code = html_options.delete("country_code").presence
|
674
|
+
country_code = country_code.nil? ? "" : "+#{ERB::Util.url_encode(country_code)}"
|
675
|
+
|
676
|
+
encoded_phone_number = ERB::Util.url_encode(phone_number)
|
677
|
+
html_options["href"] = "tel:#{country_code}#{encoded_phone_number}"
|
678
|
+
|
679
|
+
content_tag("a", name || phone_number, html_options, &block)
|
680
|
+
end
|
681
|
+
|
682
|
+
private
|
683
|
+
def convert_options_to_data_attributes(options, html_options)
|
684
|
+
if html_options
|
685
|
+
html_options = html_options.stringify_keys
|
686
|
+
html_options["data-remote"] = "true" if link_to_remote_options?(options) || link_to_remote_options?(html_options)
|
687
|
+
|
688
|
+
method = html_options.delete("method")
|
689
|
+
|
690
|
+
add_method_to_attributes!(html_options, method) if method
|
691
|
+
|
692
|
+
html_options
|
693
|
+
else
|
694
|
+
link_to_remote_options?(options) ? { "data-remote" => "true" } : {}
|
695
|
+
end
|
696
|
+
end
|
697
|
+
|
698
|
+
def url_target(name, options)
|
699
|
+
if name.respond_to?(:model_name) && options.is_a?(Hash) && options.empty?
|
700
|
+
url_for(name)
|
701
|
+
else
|
702
|
+
url_for(options)
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
def link_to_remote_options?(options)
|
707
|
+
if options.is_a?(Hash)
|
708
|
+
options.delete("remote") || options.delete(:remote)
|
709
|
+
end
|
710
|
+
end
|
711
|
+
|
712
|
+
def add_method_to_attributes!(html_options, method)
|
713
|
+
if method_not_get_method?(method) && !html_options["rel"]&.include?("nofollow")
|
714
|
+
if html_options["rel"].blank?
|
715
|
+
html_options["rel"] = "nofollow"
|
716
|
+
else
|
717
|
+
html_options["rel"] = "#{html_options["rel"]} nofollow"
|
718
|
+
end
|
719
|
+
end
|
720
|
+
html_options["data-method"] = method
|
721
|
+
end
|
722
|
+
|
723
|
+
def method_for_options(options)
|
724
|
+
if options.is_a?(Array)
|
725
|
+
method_for_options(options.last)
|
726
|
+
elsif options.respond_to?(:persisted?)
|
727
|
+
options.persisted? ? :patch : :post
|
728
|
+
elsif options.respond_to?(:to_model)
|
729
|
+
method_for_options(options.to_model)
|
730
|
+
end
|
731
|
+
end
|
732
|
+
|
733
|
+
STRINGIFIED_COMMON_METHODS = {
|
734
|
+
get: "get",
|
735
|
+
delete: "delete",
|
736
|
+
patch: "patch",
|
737
|
+
post: "post",
|
738
|
+
put: "put",
|
739
|
+
}.freeze
|
740
|
+
|
741
|
+
def method_not_get_method?(method)
|
742
|
+
return false unless method
|
743
|
+
(STRINGIFIED_COMMON_METHODS[method] || method.to_s.downcase) != "get"
|
744
|
+
end
|
745
|
+
|
746
|
+
def token_tag(token = nil, form_options: {})
|
747
|
+
if token != false && defined?(protect_against_forgery?) && protect_against_forgery?
|
748
|
+
token =
|
749
|
+
if token == true || token.nil?
|
750
|
+
form_authenticity_token(form_options: form_options.merge(authenticity_token: token))
|
751
|
+
else
|
752
|
+
token
|
753
|
+
end
|
754
|
+
tag(:input, type: "hidden", name: request_forgery_protection_token.to_s, value: token, autocomplete: "off")
|
755
|
+
else
|
756
|
+
""
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
def method_tag(method)
|
761
|
+
tag("input", type: "hidden", name: "_method", value: method.to_s, autocomplete: "off")
|
762
|
+
end
|
763
|
+
|
764
|
+
# Returns an array of hashes each containing :name and :value keys
|
765
|
+
# suitable for use as the names and values of form input fields:
|
766
|
+
#
|
767
|
+
# to_form_params(name: 'David', nationality: 'Danish')
|
768
|
+
# # => [{name: 'name', value: 'David'}, {name: 'nationality', value: 'Danish'}]
|
769
|
+
#
|
770
|
+
# to_form_params(country: { name: 'Denmark' })
|
771
|
+
# # => [{name: 'country[name]', value: 'Denmark'}]
|
772
|
+
#
|
773
|
+
# to_form_params(countries: ['Denmark', 'Sweden']})
|
774
|
+
# # => [{name: 'countries[]', value: 'Denmark'}, {name: 'countries[]', value: 'Sweden'}]
|
775
|
+
#
|
776
|
+
# An optional namespace can be passed to enclose key names:
|
777
|
+
#
|
778
|
+
# to_form_params({ name: 'Denmark' }, 'country')
|
779
|
+
# # => [{name: 'country[name]', value: 'Denmark'}]
|
780
|
+
def to_form_params(attribute, namespace = nil)
|
781
|
+
attribute = if attribute.respond_to?(:permitted?)
|
782
|
+
attribute.to_h
|
783
|
+
else
|
784
|
+
attribute
|
785
|
+
end
|
786
|
+
|
787
|
+
params = []
|
788
|
+
case attribute
|
789
|
+
when Hash
|
790
|
+
attribute.each do |key, value|
|
791
|
+
prefix = namespace ? "#{namespace}[#{key}]" : key
|
792
|
+
params.push(*to_form_params(value, prefix))
|
793
|
+
end
|
794
|
+
when Array
|
795
|
+
array_prefix = "#{namespace}[]"
|
796
|
+
attribute.each do |value|
|
797
|
+
params.push(*to_form_params(value, array_prefix))
|
798
|
+
end
|
799
|
+
else
|
800
|
+
params << { name: namespace.to_s, value: attribute.to_param }
|
801
|
+
end
|
802
|
+
|
803
|
+
params.sort_by { |pair| pair[:name] }
|
804
|
+
end
|
805
|
+
|
806
|
+
def remove_trailing_slash!(url_string)
|
807
|
+
trailing_index = (url_string.index("?") || 0) - 1
|
808
|
+
url_string[trailing_index] = "" if url_string[trailing_index] == "/"
|
809
|
+
end
|
810
|
+
end
|
811
|
+
end
|
812
|
+
end
|