Class: RoundhouseUi::FilterQuery::Pattern
- Inherits:
-
Object
- Object
- RoundhouseUi::FilterQuery::Pattern
- Defined in:
- lib/roundhouse_ui/filter_query.rb
Overview
class=Roundhouse%, class=%oundhouse%, class=%Worker, class=A%B%C.
% only. NOT _: SQL's single-character wildcard would silently turn
queue=default_low into a pattern matching defaultXlow, and every Ruby name
in this console is underscore-dense. A wildcard nobody typed is the one thing
that must never happen here, because the same predicate scopes a Delete.
An index walk rather than a Regexp. The pattern comes from a URL and is matched against every entry in a set that can hold fifty thousand of them, so a backtracking engine is both a performance and a ReDoS question. This is linear in the value and provably terminates.
Constant Summary collapse
- MAX_WILDCARDS =
6
Instance Attribute Summary collapse
-
#source ⇒ Object
readonly
Returns the value of attribute source.
Class Method Summary collapse
Instance Method Summary collapse
-
#initialize(value) ⇒ Pattern
constructor
A new instance of Pattern.
-
#literals ⇒ Object
Literal characters the pattern insists on.
- #match?(value) ⇒ Boolean
- #too_many? ⇒ Boolean
- #wildcards ⇒ Object
Constructor Details
#initialize(value) ⇒ Pattern
Returns a new instance of Pattern.
82 83 84 85 86 87 88 |
# File 'lib/roundhouse_ui/filter_query.rb', line 82 def initialize(value) @source = value.to_s @segments = @source.split("%", -1) @lead = @segments.first @tail = @segments.last @mids = @segments[1..-2] || [] end |
Instance Attribute Details
#source ⇒ Object (readonly)
Returns the value of attribute source.
80 81 82 |
# File 'lib/roundhouse_ui/filter_query.rb', line 80 def source @source end |
Class Method Details
.for(value) ⇒ Object
76 77 78 |
# File 'lib/roundhouse_ui/filter_query.rb', line 76 def self.for(value) value.to_s.include?("%") ? new(value) : nil end |
Instance Method Details
#literals ⇒ Object
Literal characters the pattern insists on. Zero means it matches every possible value, which is not a filter — see the refusal in the parser.
92 |
# File 'lib/roundhouse_ui/filter_query.rb', line 92 def literals = @segments.sum(&:length) |
#match?(value) ⇒ Boolean
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
# File 'lib/roundhouse_ui/filter_query.rb', line 96 def match?(value) v = value.to_s return false unless v.start_with?(@lead) # The tail must not reuse characters the lead already consumed, or `A%A` # would match "A" — one literal doing double duty. return false unless v.end_with?(@tail) && v.length >= @lead.length + @tail.length cursor = @lead.length stop = v.length - @tail.length @mids.each do |mid| next if mid.empty? at = v.index(mid, cursor) return false if at.nil? || at + mid.length > stop cursor = at + mid.length end true end |
#too_many? ⇒ Boolean
94 |
# File 'lib/roundhouse_ui/filter_query.rb', line 94 def too_many? = wildcards > MAX_WILDCARDS |
#wildcards ⇒ Object
93 |
# File 'lib/roundhouse_ui/filter_query.rb', line 93 def wildcards = @segments.length - 1 |