Fix #5136: Regular tags are now case-sensitive.
* Fix `AST.tag` to downcase the tag name. * Change PostQuery::Parser to use build nodes using `AST.tag`, `AST.metatag`, `AST.wildcard`, etc methods instead of building nodes directly. This way all the normalization happens in the node constructor methods instead of in the parser.
This commit is contained in:
@@ -68,11 +68,37 @@ class PostQuery
|
||||
AST.new(:all, [])
|
||||
end
|
||||
|
||||
def none
|
||||
AST.new(:none, [])
|
||||
end
|
||||
|
||||
def not(ast)
|
||||
AST.new(:not, [ast])
|
||||
end
|
||||
|
||||
def opt(ast)
|
||||
AST.new(:opt, [ast])
|
||||
end
|
||||
|
||||
def tag(name)
|
||||
AST.new(:tag, [name])
|
||||
AST.new(:tag, [name.downcase])
|
||||
end
|
||||
|
||||
def wildcard(name)
|
||||
AST.new(:wildcard, [name.downcase])
|
||||
end
|
||||
|
||||
def metatag(name, value, quoted = false)
|
||||
name = name.downcase
|
||||
name = name.singularize + "_count" if name.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS)
|
||||
|
||||
if name == "order"
|
||||
attribute, direction, _tail = value.to_s.downcase.partition(/_(asc|desc)\z/i)
|
||||
if attribute.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS)
|
||||
value = attribute.singularize + "_count" + direction
|
||||
end
|
||||
end
|
||||
|
||||
AST.new(:metatag, [name, value, quoted])
|
||||
end
|
||||
end
|
||||
|
||||
@@ -65,7 +65,7 @@ class PostQuery
|
||||
def parse
|
||||
parse!
|
||||
rescue Error
|
||||
node(:none)
|
||||
AST.none
|
||||
end
|
||||
|
||||
# Parse the search and return the AST, or raise an error if the parse failed.
|
||||
@@ -86,11 +86,11 @@ class PostQuery
|
||||
space
|
||||
|
||||
if a.empty?
|
||||
node(:all)
|
||||
AST.all
|
||||
elsif a.size == 1
|
||||
a.first
|
||||
else
|
||||
node(:and, *a)
|
||||
AST.new(:and, a)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -101,7 +101,7 @@ class PostQuery
|
||||
space
|
||||
if accept(/or +/i)
|
||||
b = or_clause
|
||||
node(:or, a, b)
|
||||
AST.new(:or, [a, b])
|
||||
else
|
||||
a
|
||||
end
|
||||
@@ -114,7 +114,7 @@ class PostQuery
|
||||
space
|
||||
if accept(/and +/i)
|
||||
b = and_clause
|
||||
node(:and, a, b)
|
||||
AST.new(:and, [a, b])
|
||||
else
|
||||
a
|
||||
end
|
||||
@@ -123,7 +123,7 @@ class PostQuery
|
||||
# factor_list = factor [factor_list]
|
||||
def factor_list
|
||||
a = one_or_more { factor }
|
||||
node(:and, *a)
|
||||
AST.new(:and, a)
|
||||
end
|
||||
|
||||
# factor = "-" expr | "~" expr | expr
|
||||
@@ -131,9 +131,9 @@ class PostQuery
|
||||
space
|
||||
|
||||
if accept("-")
|
||||
node(:not, expr)
|
||||
AST.not(expr)
|
||||
elsif accept("~")
|
||||
node(:opt, expr)
|
||||
AST.opt(expr)
|
||||
else
|
||||
expr
|
||||
end
|
||||
@@ -166,20 +166,10 @@ class PostQuery
|
||||
# metatag = metatag_name ":" quoted_string
|
||||
# metatag_name = "user" | "fav" | "pool" | "order" | ...
|
||||
def metatag
|
||||
name = expect(METATAG_NAME_REGEX)
|
||||
name = expect(METATAG_NAME_REGEX).delete_suffix(":")
|
||||
quoted, value = quoted_string
|
||||
|
||||
name = name.delete_suffix(":").downcase
|
||||
name = name.singularize + "_count" if name.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS)
|
||||
|
||||
if name == "order"
|
||||
attribute, direction, _tail = value.to_s.downcase.partition(/_(asc|desc)\z/i)
|
||||
if attribute.in?(PostQueryBuilder::COUNT_METATAG_SYNONYMS)
|
||||
value = attribute.singularize + "_count" + direction
|
||||
end
|
||||
end
|
||||
|
||||
node(:metatag, name, value, quoted)
|
||||
AST.metatag(name, value, quoted)
|
||||
end
|
||||
|
||||
def quoted_string
|
||||
@@ -201,7 +191,7 @@ class PostQuery
|
||||
t = string(/(?=[^ ]*\*)[^ \)~-][^ ]*/, skip_balanced_parens: true)
|
||||
raise Error if t.match?(/\A#{METATAG_NAME_REGEX}/)
|
||||
space
|
||||
node(:wildcard, t.downcase)
|
||||
AST.wildcard(t)
|
||||
end
|
||||
|
||||
# A tag is a string that begins with a nonspace, non-')', non-'~', or non-'-' character, followed by nonspace characters.
|
||||
@@ -209,7 +199,7 @@ class PostQuery
|
||||
t = string(/[^ \)~-][^ ]*/, skip_balanced_parens: true)
|
||||
raise Error if t.downcase.in?(%w[and or]) || t.include?("*") || t.match?(/\A#{METATAG_NAME_REGEX}/)
|
||||
space
|
||||
node(:tag, t.downcase)
|
||||
AST.tag(t)
|
||||
end
|
||||
|
||||
def string(pattern, skip_balanced_parens: false)
|
||||
@@ -292,11 +282,6 @@ class PostQuery
|
||||
|
||||
raise Error, "expected one of: #{parsers}"
|
||||
end
|
||||
|
||||
# Build an AST node of the given type.
|
||||
def node(type, *args)
|
||||
AST.new(type, args)
|
||||
end
|
||||
end
|
||||
|
||||
memoize :parse, :parse!
|
||||
|
||||
@@ -85,6 +85,7 @@ class PostQueryBuilderTest < ActiveSupport::TestCase
|
||||
post3 = create(:post, tag_string: "bbb ccc")
|
||||
|
||||
assert_tag_match([post2, post1], "aaa")
|
||||
assert_tag_match([post2, post1], "AAA")
|
||||
assert_tag_match([post2, post1], " aaa ")
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user