lib/cfa/matcher.rb
# frozen_string_literal: true
module CFA
# The Matcher is used as a predicate on {AugeasElement}.
#
# Being a predicate, it is passed to methods such as Enumerable#select
# or Array#index, returning a Boolean meaning whether a match was found.
#
# Acting on {AugeasElement} means it expects a Hash `e`
# containing `e[:key]` and `e[:value]`.
#
# It is used with the `&` syntax which makes the matcher
# act like a block/lambda/Proc (via {Matcher#to_proc}).
#
# @note The coupling to {AugeasTree}, {AugeasElement} is not a goal.
# Once we have more parsers it will go away.
#
# @example
# elements = [
# {key: "#comment[]", value: "\"magical\" mostly works"},
# {key: "DRIVE", value: "magical"},
# {key: "#comment[]", value: "'years' or 'centuries'"},
# {key: "PRECISION", value: "years"}
# ]
# drive_matcher = Matcher.new(key: "DRIVE")
# i = elements.index(&drive_matcher) # => 1
class Matcher
# The constructor arguments are constraints to match on an element.
# All constraints are optional.
# All supplied constraints must match, so it is a conjunction.
# @param key [Object,nil] if non-nil,
# constrain to elements with the name "*key*"
# @param collection [Object,nil] if non-nil,
# constrain to elements with the name "*collection*[]"
# @param value_matcher [Object,Regexp,nil] if non-nil,
# constrain to elements whose value is Object or matches Regexp
# @yieldparam blk_key [Object]
# @yieldparam blk_value [Object]
# @yieldreturn [Boolean] if the block is present,
# constrain to elements for which the block(*blk_key*, *blk_value*)
# returns true
def initialize(key: nil, collection: nil, value_matcher: nil, &block)
@matcher = lambda do |element|
return false unless key_match?(element, key)
return false unless collection_match?(element, collection)
return false unless value_match?(element, value_matcher)
return false unless !block || yield(element[:key], element[:value])
return true
end
end
# @return [Proc{AugeasElement=>Boolean}]
def to_proc
@matcher
end
private
def key_match?(element, key)
return true unless key
element[:key] == key
end
def collection_match?(element, collection)
return true unless collection
element[:key] == (collection + "[]")
end
def value_match?(element, matcher)
case matcher
when nil then true
when Regexp
return false unless element[:value].is_a?(String)
matcher =~ element[:value]
else
matcher == element[:value]
end
end
end
end