Skip to content

Commit 5373bf2

Browse files
committed
Merge pull request #21057 from schneems/schneems/journey-formatter-objects
Beyond Ludicrous Speed
2 parents 70009e3 + 22f5924 commit 5373bf2

18 files changed

Lines changed: 157 additions & 68 deletions

File tree

actionpack/lib/action_controller/metal/url_for.rb

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,11 @@ def url_options
4141
if original_script_name
4242
options[:original_script_name] = original_script_name
4343
else
44-
options[:script_name] = same_origin ? request.script_name.dup : script_name
44+
if same_origin
45+
options[:script_name] = request.script_name.empty? ? "".freeze : request.script_name.dup
46+
else
47+
options[:script_name] = script_name
48+
end
4549
end
4650
options.freeze
4751
else

actionpack/lib/action_dispatch/journey/formatter.rb

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def initialize(routes)
1414

1515
def generate(name, options, path_parameters, parameterize = nil)
1616
constraints = path_parameters.merge(options)
17-
missing_keys = []
17+
missing_keys = nil # need for variable scope
1818

1919
match_route(name, constraints) do |route|
2020
parameterized_parts = extract_parameterized_parts(route, options, path_parameters, parameterize)
@@ -25,22 +25,22 @@ def generate(name, options, path_parameters, parameterize = nil)
2525
next unless name || route.dispatcher?
2626

2727
missing_keys = missing_keys(route, parameterized_parts)
28-
next unless missing_keys.empty?
28+
next if missing_keys && missing_keys.any?
2929
params = options.dup.delete_if do |key, _|
3030
parameterized_parts.key?(key) || route.defaults.key?(key)
3131
end
3232

3333
defaults = route.defaults
3434
required_parts = route.required_parts
35-
parameterized_parts.delete_if do |key, value|
36-
value.to_s == defaults[key].to_s && !required_parts.include?(key)
35+
parameterized_parts.keep_if do |key, value|
36+
defaults[key].nil? || value.to_s != defaults[key].to_s || required_parts.include?(key)
3737
end
3838

3939
return [route.format(parameterized_parts), params]
4040
end
4141

4242
message = "No route matches #{Hash[constraints.sort_by{|k,v| k.to_s}].inspect}"
43-
message << " missing required keys: #{missing_keys.sort.inspect}" unless missing_keys.empty?
43+
message << " missing required keys: #{missing_keys.sort.inspect}" if missing_keys && missing_keys.any?
4444

4545
raise ActionController::UrlGenerationError, message
4646
end
@@ -54,12 +54,12 @@ def clear
5454
def extract_parameterized_parts(route, options, recall, parameterize = nil)
5555
parameterized_parts = recall.merge(options)
5656

57-
keys_to_keep = route.parts.reverse.drop_while { |part|
57+
keys_to_keep = route.parts.reverse_each.drop_while { |part|
5858
!options.key?(part) || (options[part] || recall[part]).nil?
5959
} | route.required_parts
6060

61-
(parameterized_parts.keys - keys_to_keep).each do |bad_key|
62-
parameterized_parts.delete(bad_key)
61+
parameterized_parts.delete_if do |bad_key, _|
62+
!keys_to_keep.include?(bad_key)
6363
end
6464

6565
if parameterize
@@ -110,15 +110,36 @@ def non_recursive(cache, options)
110110
routes
111111
end
112112

113+
module RegexCaseComparator
114+
DEFAULT_INPUT = /[-_.a-zA-Z0-9]+\/[-_.a-zA-Z0-9]+/
115+
DEFAULT_REGEX = /\A#{DEFAULT_INPUT}\Z/
116+
117+
def self.===(regex)
118+
DEFAULT_INPUT == regex
119+
end
120+
end
121+
113122
# Returns an array populated with missing keys if any are present.
114123
def missing_keys(route, parts)
115-
missing_keys = []
124+
missing_keys = nil
116125
tests = route.path.requirements
117126
route.required_parts.each { |key|
118-
if tests.key?(key)
119-
missing_keys << key unless /\A#{tests[key]}\Z/ === parts[key]
127+
case tests[key]
128+
when nil
129+
unless parts[key]
130+
missing_keys ||= []
131+
missing_keys << key
132+
end
133+
when RegexCaseComparator
134+
unless RegexCaseComparator::DEFAULT_REGEX === parts[key]
135+
missing_keys ||= []
136+
missing_keys << key
137+
end
120138
else
121-
missing_keys << key unless parts[key]
139+
unless /\A#{tests[key]}\Z/ === parts[key]
140+
missing_keys ||= []
141+
missing_keys << key
142+
end
122143
end
123144
}
124145
missing_keys

actionpack/lib/action_dispatch/routing/route_set.rb

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,13 @@ def handle_positional_args(controller_options, inner_options, args, result, path
267267
path_params -= controller_options.keys
268268
path_params -= result.keys
269269
end
270-
path_params -= inner_options.keys
271-
path_params.take(args.size).each do |param|
272-
result[param] = args.shift
270+
inner_options.each do |key, _|
271+
path_params.delete(key)
272+
end
273+
274+
args.each_with_index do |arg, index|
275+
param = path_params[index]
276+
result[param] = arg if param
273277
end
274278
end
275279

@@ -594,8 +598,8 @@ class Generator
594598

595599
def initialize(named_route, options, recall, set)
596600
@named_route = named_route
597-
@options = options.dup
598-
@recall = recall.dup
601+
@options = options
602+
@recall = recall
599603
@set = set
600604

601605
normalize_recall!
@@ -617,7 +621,7 @@ def current_controller
617621
def use_recall_for(key)
618622
if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
619623
if !named_route_exists? || segment_keys.include?(key)
620-
@options[key] = @recall.delete(key)
624+
@options[key] = @recall[key]
621625
end
622626
end
623627
end
@@ -671,12 +675,18 @@ def use_relative_controller!
671675

672676
# Remove leading slashes from controllers
673677
def normalize_controller!
674-
@options[:controller] = controller.sub(%r{^/}, ''.freeze) if controller
678+
if controller
679+
if m = controller.match(/\A\/(?<controller_without_leading_slash>.*)/)
680+
@options[:controller] = m[:controller_without_leading_slash]
681+
else
682+
@options[:controller] = controller
683+
end
684+
end
675685
end
676686

677687
# Move 'index' action from options to recall
678688
def normalize_action!
679-
if @options[:action] == 'index'
689+
if @options[:action] == 'index'.freeze
680690
@recall[:action] = @options.delete(:action)
681691
end
682692
end

actionview/lib/action_view/helpers/asset_tag_helper.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ def javascript_include_tag(*sources)
6060
tag_options = {
6161
"src" => path_to_javascript(source, path_options)
6262
}.merge!(options)
63-
content_tag(:script, "", tag_options)
63+
content_tag("script".freeze, "", tag_options)
6464
}.join("\n").html_safe
6565
end
6666

actionview/lib/action_view/helpers/date_helper.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -681,7 +681,7 @@ def time_tag(date_or_time, *args, &block)
681681
content = args.first || I18n.l(date_or_time, :format => format)
682682
datetime = date_or_time.acts_like?(:time) ? date_or_time.xmlschema : date_or_time.iso8601
683683

684-
content_tag(:time, content, options.reverse_merge(:datetime => datetime), &block)
684+
content_tag("time".freeze, content, options.reverse_merge(:datetime => datetime), &block)
685685
end
686686
end
687687

@@ -809,7 +809,7 @@ def select_month
809809
1.upto(12) do |month_number|
810810
options = { :value => month_number }
811811
options[:selected] = "selected" if month == month_number
812-
month_options << content_tag(:option, month_name(month_number), options) + "\n"
812+
month_options << content_tag("option".freeze, month_name(month_number), options) + "\n"
813813
end
814814
build_select(:month, month_options.join)
815815
end
@@ -971,7 +971,7 @@ def build_options(selected, options = {})
971971
tag_options[:selected] = "selected" if selected == i
972972
text = options[:use_two_digit_numbers] ? sprintf("%02d", i) : value
973973
text = options[:ampm] ? AMPM_TRANSLATION[i] : text
974-
select_options << content_tag(:option, text, tag_options)
974+
select_options << content_tag("option".freeze, text, tag_options)
975975
end
976976

977977
(select_options.join("\n") + "\n").html_safe
@@ -991,11 +991,11 @@ def build_select(type, select_options_as_html)
991991
select_options[:class] = [select_options[:class], type].compact.join(' ') if @options[:with_css_classes]
992992

993993
select_html = "\n"
994-
select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
994+
select_html << content_tag("option".freeze, '', :value => '') + "\n" if @options[:include_blank]
995995
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
996996
select_html << select_options_as_html
997997

998-
(content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe
998+
(content_tag("select".freeze, select_html.html_safe, select_options) + "\n").html_safe
999999
end
10001000

10011001
# Builds a prompt option tag with supplied options or from default options.
@@ -1012,7 +1012,7 @@ def prompt_option_tag(type, options)
10121012
I18n.translate(:"datetime.prompts.#{type}", :locale => @options[:locale])
10131013
end
10141014

1015-
prompt ? content_tag(:option, prompt, :value => '') : ''
1015+
prompt ? content_tag("option".freeze, prompt, :value => '') : ''
10161016
end
10171017

10181018
# Builds hidden input tag for date part and value.

actionview/lib/action_view/helpers/form_options_helper.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -456,7 +456,7 @@ def option_groups_from_collection_for_select(collection, group_method, group_lab
456456
option_tags = options_from_collection_for_select(
457457
group.send(group_method), option_key_method, option_value_method, selected_key)
458458

459-
content_tag(:optgroup, option_tags, label: group.send(group_label_method))
459+
content_tag("optgroup".freeze, option_tags, label: group.send(group_label_method))
460460
end.join.html_safe
461461
end
462462

@@ -528,7 +528,7 @@ def grouped_options_for_select(grouped_options, selected_key = nil, options = {}
528528
body = "".html_safe
529529

530530
if prompt
531-
body.safe_concat content_tag(:option, prompt_text(prompt), value: "")
531+
body.safe_concat content_tag("option".freeze, prompt_text(prompt), value: "")
532532
end
533533

534534
grouped_options.each do |container|
@@ -541,7 +541,7 @@ def grouped_options_for_select(grouped_options, selected_key = nil, options = {}
541541
end
542542

543543
html_attributes = { label: label }.merge!(html_attributes)
544-
body.safe_concat content_tag(:optgroup, options_for_select(container, selected_key), html_attributes)
544+
body.safe_concat content_tag("optgroup".freeze, options_for_select(container, selected_key), html_attributes)
545545
end
546546

547547
body
@@ -577,7 +577,7 @@ def time_zone_options_for_select(selected = nil, priority_zones = nil, model = :
577577
end
578578

579579
zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
580-
zone_options.safe_concat content_tag(:option, '-------------', value: '', disabled: true)
580+
zone_options.safe_concat content_tag("option".freeze, '-------------', value: '', disabled: true)
581581
zone_options.safe_concat "\n"
582582

583583
zones = zones - priority_zones

actionview/lib/action_view/helpers/form_tag_helper.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,15 @@ def select_tag(name, option_tags = nil, options = {})
140140
end
141141

142142
if include_blank
143-
option_tags = content_tag(:option, include_blank, value: '').safe_concat(option_tags)
143+
option_tags = content_tag("option".freeze, include_blank, value: '').safe_concat(option_tags)
144144
end
145145
end
146146

147147
if prompt = options.delete(:prompt)
148-
option_tags = content_tag(:option, prompt, value: '').safe_concat(option_tags)
148+
option_tags = content_tag("option".freeze, prompt, value: '').safe_concat(option_tags)
149149
end
150150

151-
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
151+
content_tag "select".freeze, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
152152
end
153153

154154
# Creates a standard text field; use these text fields to input smaller chunks of text like a username
@@ -568,7 +568,7 @@ def image_submit_tag(source, options = {})
568568
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
569569
def field_set_tag(legend = nil, options = nil, &block)
570570
output = tag(:fieldset, options, true)
571-
output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
571+
output.safe_concat(content_tag("legend".freeze, legend)) unless legend.blank?
572572
output.concat(capture(&block)) if block_given?
573573
output.safe_concat("</fieldset>")
574574
end

actionview/lib/action_view/helpers/javascript_helper.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,8 @@ def escape_javascript(javascript)
4747
# tag.
4848
#
4949
# javascript_tag "alert('All is good')", defer: 'defer'
50-
#
51-
# Returns:
50+
#
51+
# Returns:
5252
# <script defer="defer">
5353
# //<![CDATA[
5454
# alert('All is good')
@@ -70,7 +70,7 @@ def javascript_tag(content_or_options_with_block = nil, html_options = {}, &bloc
7070
content_or_options_with_block
7171
end
7272

73-
content_tag(:script, javascript_cdata_section(content), html_options)
73+
content_tag("script".freeze, javascript_cdata_section(content), html_options)
7474
end
7575

7676
def javascript_cdata_section(content) #:nodoc:

actionview/lib/action_view/helpers/tag_helper.rb

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,10 @@ module TagHelper
2222

2323
TAG_PREFIXES = ['aria', 'data', :aria, :data].to_set
2424

25-
PRE_CONTENT_STRINGS = {
26-
:textarea => "\n"
27-
}
25+
PRE_CONTENT_STRINGS = Hash.new { "".freeze }
26+
PRE_CONTENT_STRINGS[:textarea] = "\n"
27+
PRE_CONTENT_STRINGS["textarea"] = "\n"
28+
2829

2930
# Returns an empty HTML tag of type +name+ which by default is XHTML
3031
# compliant. Set +open+ to true to create an open tag compatible
@@ -143,24 +144,25 @@ def escape_once(html)
143144
def content_tag_string(name, content, options, escape = true)
144145
tag_options = tag_options(options, escape) if options
145146
content = ERB::Util.unwrapped_html_escape(content) if escape
146-
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name.to_sym]}#{content}</#{name}>".html_safe
147+
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
147148
end
148149

149150
def tag_options(options, escape = true)
150151
return if options.blank?
151-
attrs = []
152+
output = ""
153+
sep = " ".freeze
152154
options.each_pair do |key, value|
153155
if TAG_PREFIXES.include?(key) && value.is_a?(Hash)
154156
value.each_pair do |k, v|
155-
attrs << prefix_tag_option(key, k, v, escape)
157+
output << sep + prefix_tag_option(key, k, v, escape)
156158
end
157159
elsif BOOLEAN_ATTRIBUTES.include?(key)
158-
attrs << boolean_tag_option(key) if value
160+
output << sep + boolean_tag_option(key) if value
159161
elsif !value.nil?
160-
attrs << tag_option(key, value, escape)
162+
output << sep + tag_option(key, value, escape)
161163
end
162164
end
163-
" #{attrs * ' '}" unless attrs.empty?
165+
output unless output.empty?
164166
end
165167

166168
def prefix_tag_option(prefix, key, value, escape)
@@ -177,7 +179,7 @@ def boolean_tag_option(key)
177179

178180
def tag_option(key, value, escape)
179181
if value.is_a?(Array)
180-
value = escape ? safe_join(value, " ") : value.join(" ")
182+
value = escape ? safe_join(value, " ".freeze) : value.join(" ".freeze)
181183
else
182184
value = escape ? ERB::Util.unwrapped_html_escape(value) : value
183185
end

actionview/lib/action_view/helpers/url_helper.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,9 +184,9 @@ def link_to(name = nil, options = nil, html_options = nil, &block)
184184
html_options = convert_options_to_data_attributes(options, html_options)
185185

186186
url = url_for(options)
187-
html_options['href'] ||= url
187+
html_options["href".freeze] ||= url
188188

189-
content_tag(:a, name || url, html_options, &block)
189+
content_tag("a".freeze, name || url, html_options, &block)
190190
end
191191

192192
# Generates a form containing a single button that submits to the URL created
@@ -471,7 +471,7 @@ def mail_to(email_address, name = nil, html_options = {}, &block)
471471
encoded_email_address = ERB::Util.url_encode(email_address).gsub("%40", "@")
472472
html_options["href"] = "mailto:#{encoded_email_address}#{extras}"
473473

474-
content_tag(:a, name || email_address, html_options, &block)
474+
content_tag("a".freeze, name || email_address, html_options, &block)
475475
end
476476

477477
# True if the current request URI was generated by the given +options+.

0 commit comments

Comments
 (0)