Class: RoundhouseUi::FilterQuery::Parser
- Inherits:
-
Object
- Object
- RoundhouseUi::FilterQuery::Parser
- Defined in:
- lib/roundhouse_ui/filter_query.rb
Overview
An index walk. No regex over the input, so there is nothing to backtrack.
Instance Method Summary collapse
- #call ⇒ Object
-
#initialize(str, keys = FilterQuery::KEYS) ⇒ Parser
constructor
A new instance of Parser.
Constructor Details
#initialize(str, keys = FilterQuery::KEYS) ⇒ Parser
Returns a new instance of Parser.
329 330 331 332 333 334 335 |
# File 'lib/roundhouse_ui/filter_query.rb', line 329 def initialize(str, keys = FilterQuery::KEYS) @s = str @keys = keys @i = 0 @seen = {} @ignored = [] end |
Instance Method Details
#call ⇒ Object
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 |
# File 'lib/roundhouse_ui/filter_query.rb', line 337 def call loop do skip_space break if @i >= @s.length key = read_key # An unrecognised key refuses the whole query. Falling through to free # text is the one resolution that can select MORE than was typed, and a # literal substring search for "clas=Foo" would find nothing and read as # "no such jobs exist". return refuse(nil) if key == :unknown break if key.nil? value = read_value return refuse("Unclosed quote in #{key}=. Quote both ends, or drop the quotes.") if value == :unterminated return refuse((key, value)) unless value_ok?(value) # A tag is a key:value pair or it is nothing. Rather than refuse the whole # query, the facet is DROPPED and recorded — a typo should not stop you # browsing. What it does stop is a bulk action: `tag=garbage queue=default` # drops to `queue=default`, which selects the whole queue, and the Delete # beneath it would then be scoped wider than what was typed. Dropping a # facet can only ever widen, so a degraded query authorises nothing. # Both halves must be non-empty. `tag=squad:` has a colon, so a # colon-only check let it through: the pill rendered, tag_pair came back nil, # and entry_selected?'s `return true if tag.nil?` selected everything — with # bulk authorised, because any_facets? was true. if key == "tag" && value.split(":", 2).reject(&:empty?).size != 2 @ignored << [ "#{key}=#{value}", "tag= takes key:value, like tag=squad:core" ] next end if @seen.key?(key) && @seen[key] != value return refuse(%(Two different values for #{key}: "#{@seen[key]}" and "#{value}". Pick one.)) end @seen[key] = value end residue = @s[@i..].to_s.strip # text= is the escape hatch for facet-shaped free text. Having BOTH is two # ways of saying the same thing, and picking one would discard the other # silently — `text=foo bar` would have kept "foo" and thrown "bar" away. if @seen.key?("text") && !residue.empty? return refuse(%(Text is given twice: text="#{@seen['text']}" and "#{residue}". ) + %(Quote the whole thing: text="#{@seen['text']} #{residue}")) end unsupported = unsupported_in(residue) return refuse(unsupported) if unsupported FilterQuery.new(klass: @seen["class"], error: @seen["error"], queue: @seen["queue"], tag: @seen["tag"], text: @seen.fetch("text", residue), raw: @s, ignored: @ignored) end |