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, [])
|
AST.new(:all, [])
|
||||||
end
|
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)
|
def tag(name)
|
||||||
AST.new(:tag, [name])
|
AST.new(:tag, [name.downcase])
|
||||||
|
end
|
||||||
|
|
||||||
|
def wildcard(name)
|
||||||
|
AST.new(:wildcard, [name.downcase])
|
||||||
end
|
end
|
||||||
|
|
||||||
def metatag(name, value, quoted = false)
|
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])
|
AST.new(:metatag, [name, value, quoted])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ class PostQuery
|
|||||||
def parse
|
def parse
|
||||||
parse!
|
parse!
|
||||||
rescue Error
|
rescue Error
|
||||||
node(:none)
|
AST.none
|
||||||
end
|
end
|
||||||
|
|
||||||
# Parse the search and return the AST, or raise an error if the parse failed.
|
# Parse the search and return the AST, or raise an error if the parse failed.
|
||||||
@@ -86,11 +86,11 @@ class PostQuery
|
|||||||
space
|
space
|
||||||
|
|
||||||
if a.empty?
|
if a.empty?
|
||||||
node(:all)
|
AST.all
|
||||||
elsif a.size == 1
|
elsif a.size == 1
|
||||||
a.first
|
a.first
|
||||||
else
|
else
|
||||||
node(:and, *a)
|
AST.new(:and, a)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -101,7 +101,7 @@ class PostQuery
|
|||||||
space
|
space
|
||||||
if accept(/or +/i)
|
if accept(/or +/i)
|
||||||
b = or_clause
|
b = or_clause
|
||||||
node(:or, a, b)
|
AST.new(:or, [a, b])
|
||||||
else
|
else
|
||||||
a
|
a
|
||||||
end
|
end
|
||||||
@@ -114,7 +114,7 @@ class PostQuery
|
|||||||
space
|
space
|
||||||
if accept(/and +/i)
|
if accept(/and +/i)
|
||||||
b = and_clause
|
b = and_clause
|
||||||
node(:and, a, b)
|
AST.new(:and, [a, b])
|
||||||
else
|
else
|
||||||
a
|
a
|
||||||
end
|
end
|
||||||
@@ -123,7 +123,7 @@ class PostQuery
|
|||||||
# factor_list = factor [factor_list]
|
# factor_list = factor [factor_list]
|
||||||
def factor_list
|
def factor_list
|
||||||
a = one_or_more { factor }
|
a = one_or_more { factor }
|
||||||
node(:and, *a)
|
AST.new(:and, a)
|
||||||
end
|
end
|
||||||
|
|
||||||
# factor = "-" expr | "~" expr | expr
|
# factor = "-" expr | "~" expr | expr
|
||||||
@@ -131,9 +131,9 @@ class PostQuery
|
|||||||
space
|
space
|
||||||
|
|
||||||
if accept("-")
|
if accept("-")
|
||||||
node(:not, expr)
|
AST.not(expr)
|
||||||
elsif accept("~")
|
elsif accept("~")
|
||||||
node(:opt, expr)
|
AST.opt(expr)
|
||||||
else
|
else
|
||||||
expr
|
expr
|
||||||
end
|
end
|
||||||
@@ -166,20 +166,10 @@ class PostQuery
|
|||||||
# metatag = metatag_name ":" quoted_string
|
# metatag = metatag_name ":" quoted_string
|
||||||
# metatag_name = "user" | "fav" | "pool" | "order" | ...
|
# metatag_name = "user" | "fav" | "pool" | "order" | ...
|
||||||
def metatag
|
def metatag
|
||||||
name = expect(METATAG_NAME_REGEX)
|
name = expect(METATAG_NAME_REGEX).delete_suffix(":")
|
||||||
quoted, value = quoted_string
|
quoted, value = quoted_string
|
||||||
|
|
||||||
name = name.delete_suffix(":").downcase
|
AST.metatag(name, value, quoted)
|
||||||
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)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def quoted_string
|
def quoted_string
|
||||||
@@ -201,7 +191,7 @@ class PostQuery
|
|||||||
t = string(/(?=[^ ]*\*)[^ \)~-][^ ]*/, skip_balanced_parens: true)
|
t = string(/(?=[^ ]*\*)[^ \)~-][^ ]*/, skip_balanced_parens: true)
|
||||||
raise Error if t.match?(/\A#{METATAG_NAME_REGEX}/)
|
raise Error if t.match?(/\A#{METATAG_NAME_REGEX}/)
|
||||||
space
|
space
|
||||||
node(:wildcard, t.downcase)
|
AST.wildcard(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
# A tag is a string that begins with a nonspace, non-')', non-'~', or non-'-' character, followed by nonspace characters.
|
# 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)
|
t = string(/[^ \)~-][^ ]*/, skip_balanced_parens: true)
|
||||||
raise Error if t.downcase.in?(%w[and or]) || t.include?("*") || t.match?(/\A#{METATAG_NAME_REGEX}/)
|
raise Error if t.downcase.in?(%w[and or]) || t.include?("*") || t.match?(/\A#{METATAG_NAME_REGEX}/)
|
||||||
space
|
space
|
||||||
node(:tag, t.downcase)
|
AST.tag(t)
|
||||||
end
|
end
|
||||||
|
|
||||||
def string(pattern, skip_balanced_parens: false)
|
def string(pattern, skip_balanced_parens: false)
|
||||||
@@ -292,11 +282,6 @@ class PostQuery
|
|||||||
|
|
||||||
raise Error, "expected one of: #{parsers}"
|
raise Error, "expected one of: #{parsers}"
|
||||||
end
|
end
|
||||||
|
|
||||||
# Build an AST node of the given type.
|
|
||||||
def node(type, *args)
|
|
||||||
AST.new(type, args)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
memoize :parse, :parse!
|
memoize :parse, :parse!
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ class PostQueryBuilderTest < ActiveSupport::TestCase
|
|||||||
post3 = create(:post, tag_string: "bbb ccc")
|
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")
|
||||||
assert_tag_match([post2, post1], " aaa ")
|
assert_tag_match([post2, post1], " aaa ")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user