提交 e0eff2c3 编写于 作者: A Adam Hegyi

Memoize regex when checking missing route keys

When the route definition has parameters, we can supply a regex for
validation purposes:

    get "/a/:b" => "test#index", constraints: { b: /abc/ }, as: test

This regex is going to be used to check the supplied values during
link generation:

    test_path("abc") # check "abc" against /abc/ regex

The link generation code checks each parameter. To properly validate the
parameter, it creates a new regex with start and end of string modifiers:

    /\A#{original_regex}\Z/

This means for each link generation the code stringifies the existing
regex and creates a new one. When a new regex is created, it needs to be
compiled, for large regexes this can take quite a bit of time.

This change memoizes the generated regex for each route when constrains
are given. It also removes the RegexCaseComparator class since it is not
in use anymore.
上级 b50ce8d6
......@@ -125,19 +125,10 @@ def non_recursive(cache, options)
routes
end
module RegexCaseComparator
DEFAULT_INPUT = /[-_.a-zA-Z0-9]+\/[-_.a-zA-Z0-9]+/
DEFAULT_REGEX = /\A#{DEFAULT_INPUT}\Z/
def self.===(regex)
DEFAULT_INPUT == regex
end
end
# Returns an array populated with missing keys if any are present.
def missing_keys(route, parts)
missing_keys = nil
tests = route.path.requirements
tests = route.path.requirements_for_missing_keys_check
route.required_parts.each { |key|
case tests[key]
when nil
......@@ -145,13 +136,8 @@ def missing_keys(route, parts)
missing_keys ||= []
missing_keys << key
end
when RegexCaseComparator
unless RegexCaseComparator::DEFAULT_REGEX.match?(parts[key])
missing_keys ||= []
missing_keys << key
end
else
unless /\A#{tests[key]}\Z/.match?(parts[key])
unless tests[key].match?(parts[key])
missing_keys ||= []
missing_keys << key
end
......
......@@ -177,6 +177,12 @@ def to_regexp
@re ||= regexp_visitor.new(@separators, @requirements).accept spec
end
def requirements_for_missing_keys_check
@requirements_for_missing_keys_check ||= requirements.each_with_object({}) do |(key, regex), hash|
hash[key] = /\A#{regex}\Z/
end
end
private
def regexp_visitor
@anchored ? AnchoredRegexp : UnanchoredRegexp
......
......@@ -289,6 +289,37 @@ def test_named_captures
named_captures = { "action" => "list", "format" => "rss" }
assert_equal named_captures, match.named_captures
end
def test_requirements_for_missing_keys_check
name_regex = /test/
path = Pattern.build(
"/page/:name",
{ name: name_regex },
SEPARATORS,
true
)
transformed_regex = path.requirements_for_missing_keys_check[:name]
assert_not_nil transformed_regex
assert_equal(transformed_regex, /\A#{name_regex}\Z/)
end
def test_requirements_for_missing_keys_check_memoization
name_regex = /test/
path = Pattern.build(
"/page/:name",
{ name: name_regex },
SEPARATORS,
true
)
first_call = path.requirements_for_missing_keys_check[:name]
second_call = path.requirements_for_missing_keys_check[:name]
assert_equal(first_call.object_id, second_call.object_id)
end
end
end
end
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册