class StructureMatch::Comparator

Comparator handles the actual comparison operations that StructureMatch uses.

Public Class Methods

new(op) click to toggle source

Comparators are initialized using a hash with the following structure:

{
  "op" => "operation"
  "match" => "The value that the operation should work with"
  # Optional, defaults to 1 or the length of the matched thing (if array-ish)
  "score" => 1 # The score adjustment a match here gives.
}

Comparator knows about the following tests:

“==”,“!=”,“>”,“<”,“>=”,“<=”

The matching tests from Comparable.

“and”

Returns true if all the submatches return true, false otherwise.

“or”

Returns true if one of the submatches return true, false otherwise. “and” and “or” require that “match” be an array of hashes that Comparator.new can process.

“not”

Inverts its submatch. Requires that “match” be a hash that Comparator.new can process.

“range”

Tests to see if a value is within a range of values. Requires that “match” be a two-element array whose elements can be used to construct a Range.

“regex”

Tests to see if a value matches a regular expression. “match” must be a regex. The score on a matching regex will be the length of the MatchData by default, and the returned value will be matching MatchData.

“member”

Tests to see if a value is within an array. “match” must be the array. If an array is passed to a member test, then scoring and the returned value will be the set intersection of the match array and the tested array.

# File lib/structurematch.rb, line 36
def initialize(op)
  raise "#{op.inspect} must be a Hash" unless op.kind_of?(Hash)
  unless ["==","!=",">","<",">=","<=","and","or","not","range","regex","member"].member?(op["op"])
    raise "Operator #{op["op"]} is not one that Comparator knows how to use!"
  end
  raise "#{op.inspect} must have a match key" unless op.has_key?("match")
  @op = op["op"].dup.freeze
  @match = op["match"]
  @score = op["score"].to_i
  @score = nil if @score == 0
  case @op
  when "and","or"
    raise "#{op.inspect} match key must be an array of submatches" unless @match.kind_of?(Array)
    @match.map!{|m|Comparator.new(m)}
  when "not"
    @match = Comparator.new(@match)
  when "range"
    raise "#{@match.inspect} is not a two-element array" unless @match.kind_of?(Array) && @match.length == 2
    @match = Range.new(@match[0],@match[1])
  when "regex" then @match = Regexp.compile(@match)
  end
end

Public Instance Methods

test(v=true) click to toggle source

Takes a single argument which is the value to be tested.

It returns a two-element array:

[score,val]
score

the score adjustment factor for this test

val

the value that test returns. It is usually the value that was passed in, except for regular expressions (which return the MatchData) and array & array comparisons performed by member (which returns the set intersection of the arrays)

# File lib/structurematch.rb, line 67
def test(v=true)
  case @op
  when "and" then [_t(@match.all?{|m|m.test(v)[0] > 0}),v]
  when "or" then [_t(@match.any?{|m|m.test(v)[0] > 0}),v]
  when "not" then [_t(@match.test(v)[0] < 0),v]
  when "range" then [_t(@match === v),v]
  when "regex"
    r = @match.match(v)
    [r.nil? ? -1 : @score || r.length, r]
  when "member"
    if v.kind_of?(Array)
      r = @match & v
      [r.empty? ? -1 : @score || r.length, r]
    else
      [_t(@match.member?(v)),v]
    end
  when "==" then [_t(@match == v),v]
  when "!=" then [_t(@match != v),v]
  when ">" then [_t(v > @match),v]
  when "<" then [_t(v < @match),v]
  when ">=" then [_t(v >= @match),v]
  when "<=" then [_t(v <= @match),v]
  else
    raise "Comparator cannot handle #{@op}"
  end
end

Private Instance Methods

_t(v) click to toggle source
# File lib/structurematch.rb, line 95
def _t(v)
  v ? @score || 1 : -1
end