structurematch 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/structurematch.rb +69 -26
  3. metadata +1 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c071ed38259f2735cad5b428fe364fa186e58ffb
4
- data.tar.gz: 569278eab25de5512e30d557a409830e4544568b
3
+ metadata.gz: 99e88855ccc4edb6ae03e0f7b6679d1cf40e253b
4
+ data.tar.gz: d1cb943a7475bc63a326f6d1d7d4402bffbb0185
5
5
  SHA512:
6
- metadata.gz: 210676dc6826c5bc18356469db5931ab733562829d5a64c4e1d61c76eb787d0a625cb6e83eda394c8813e5d73bdce6fd6518779d1f94810789d4d52dd5b590a8
7
- data.tar.gz: a29709b8af3237fd1a013a97ac32c71d40178d29580ebd748c85b36f5e1cf78dc57b86f7bba93cc878581be490329389c469fb8a80a7c06e4188bd3655ca8749
6
+ metadata.gz: 767925436ade0b040cdf96559a95db0e9b8eea1dda4077d7ebd8f9c8d12c4fc95cf2eeee9fd67a1b2b9408a1680bdb21ab95e5a30c7f106058d87aebd0fa63c2
7
+ data.tar.gz: 4d67cb27f30897e3997b8ed8658f7f38b3722301b04aaf601d6bea6fe6e910013c144741a88905ee26a7aa441b10829f3be4e78280c1ca934e824f631f5c3f4d
@@ -8,24 +8,31 @@
8
8
  class StructureMatch
9
9
 
10
10
  # Comparator handles the actual comparison operations that StructureMatch uses.
11
- # Comparators are initialized using a hash with the following structure:
12
- #
13
- # {
14
- # "op" => "operation"
15
- # "match" => "The value that the operation should work with"
16
- # }
17
- #
18
- # Comparator knows about the following tests:
19
- # "==","!=",">","<",">=","<=" --> The matching tests from Comparable.
20
- # "and" --> Returns true if all the submatches return true, false otherwise.
21
- # "or" --> Returns true if one of the submatches trturn true, false otherwise.
22
- # "and" and "or" require that "match" be an array of hashes that Comparator.new can process.
23
- # "not" --> Inverts its submatch. Requires that "match" be a hash that Comparator.new can process.
24
- # "range" --> Tests to see if a value is within a range of values.
25
- # Requires that "match" be a two-element array whose elements can be used to construct a Range.
26
- # "regex" --> Tests to see if a value matches a regular expression. "match" must be a regex.
27
- # "member" --> Tests to see if a value is within an array. "match" must be the array.
28
11
  class Comparator
12
+ # Comparators are initialized using a hash with the following structure:
13
+ #
14
+ # {
15
+ # "op" => "operation"
16
+ # "match" => "The value that the operation should work with"
17
+ # # Optional, defaults to 1 or the length of the matched thing (if array-ish)
18
+ # "score" => 1 # The score adjustment a match here gives.
19
+ # }
20
+ #
21
+ # Comparator knows about the following tests:
22
+ #
23
+ # "==","!=",">","<",">=","<=":: The matching tests from Comparable.
24
+ # "and":: Returns true if all the submatches return true, false otherwise.
25
+ # "or":: Returns true if one of the submatches return true, false otherwise.
26
+ # "and" and "or" require that "match" be an array of hashes that Comparator.new can process.
27
+ # "not":: Inverts its submatch. Requires that "match" be a hash that Comparator.new can process.
28
+ # "range":: Tests to see if a value is within a range of values.
29
+ # Requires that "match" be a two-element array whose elements can be used to construct a Range.
30
+ # "regex":: Tests to see if a value matches a regular expression. "match" must be a regex.
31
+ # The score on a matching regex will be the length of the MatchData by default, and the returned
32
+ # value will be matching MatchData.
33
+ # "member":: Tests to see if a value is within an array. "match" must be the array.
34
+ # If an array is passed to a member test, then scoring and the returned value will be the
35
+ # set intersection of the match array and the tested array.
29
36
  def initialize(op)
30
37
  raise "#{op.inspect} must be a Hash" unless op.kind_of?(Hash)
31
38
  unless ["==","!=",">","<",">=","<=","and","or","not","range","regex","member"].member?(op["op"])
@@ -34,6 +41,8 @@ class StructureMatch
34
41
  raise "#{op.inspect} must have a match key" unless op.has_key?("match")
35
42
  @op = op["op"].dup.freeze
36
43
  @match = op["match"]
44
+ @score = op["score"].to_i
45
+ @score = nil if @score == 0
37
46
  case @op
38
47
  when "and","or"
39
48
  raise "#{op.inspect} match key must be an array of submatches" unless @match.kind_of?(Array)
@@ -47,13 +56,14 @@ class StructureMatch
47
56
  end
48
57
  end
49
58
 
50
- # test tests to see if v matches @match.
59
+ # Takes a single argument which is the value to be tested.
60
+ #
51
61
  # It returns a two-element array:
52
62
  # [score,val]
53
- # score is the score adjustment factor for this test
54
- # val is the value that test returns. It is usually the value that was passed in,
55
- # except for regular expressions (which return the MatchData) and array & array
56
- # comparisons performed by member (which returns the set intersection of the arrays)
63
+ # score:: the score adjustment factor for this test
64
+ # val:: the value that test returns. It is usually the value that was passed in,
65
+ # except for regular expressions (which return the MatchData) and array & array
66
+ # comparisons performed by member (which returns the set intersection of the arrays)
57
67
  def test(v=true)
58
68
  case @op
59
69
  when "and" then [_t(@match.all?{|m|m.test(v)[0]}),v]
@@ -62,11 +72,11 @@ class StructureMatch
62
72
  when "range" then [_t(@match === v),v]
63
73
  when "regex"
64
74
  r = @match.match(v)
65
- [r.nil? ? -1 : r.length, r]
75
+ [r.nil? ? -1 : @score || r.length, r]
66
76
  when "member"
67
77
  if v.kind_of?(Array)
68
78
  r = @match & v
69
- [r.empty? ? -1 : r.length, r]
79
+ [r.empty? ? -1 : @score || r.length, r]
70
80
  else
71
81
  [_t(@match.member?(v)),v]
72
82
  end
@@ -83,10 +93,37 @@ class StructureMatch
83
93
 
84
94
  private
85
95
  def _t(v)
86
- v ? 1 : -1
96
+ v ? @score || 1 : -1
87
97
  end
88
98
  end
89
99
 
100
+ # StructureMatchers are initialized by passing in an example nested hash that maps out what
101
+ # StructureMatch should dig through, bind matching values, and how to score the matches it found.
102
+ # As an example:
103
+ # StructureMatch.new("foo" => "bar",
104
+ # "numbermatch" => 5,
105
+ # "regexmatch" => {
106
+ # "__sm_leaf" => true,
107
+ # "op"=> "regex",
108
+ # "match" => "foo(bar)"},
109
+ # "ormatch" => {
110
+ # "__sm_leaf" => true,
111
+ # "op" => "or",
112
+ # "match" => [
113
+ # {
114
+ # "op" => ">",
115
+ # "match" => 7
116
+ # },
117
+ # {
118
+ # "op" => "<",
119
+ # "match" => 10}]})
120
+ # will create a matcher that perfectly matches the following JSON:
121
+ # { "foo": "bar",
122
+ # "numbermatch": 5,
123
+ # "regexmatch": "foobar",
124
+ # "ormatch": 8
125
+ # }
126
+ # This match will be assigned a score of 5.
90
127
  def initialize(matcher)
91
128
  raise "#{matcher.inspect} must be a Hash" unless matcher.kind_of?(Hash)
92
129
  @matchers = Hash.new
@@ -108,6 +145,12 @@ class StructureMatch
108
145
  end
109
146
  end
110
147
 
148
+ # Takes a (possibly nested) hash that is a result of parsing JSON and matches what it can,
149
+ # assigning a score in the process.
150
+ #
151
+ # Returns a two-entry array in the form of [binds,score]:
152
+ # binds:: The nested hash corresponding to the matched values from val.
153
+ # score:: The score assigned to this match.
111
154
  def bind(val)
112
155
  raise "Must pass a Hash to StructureMatch.bind" unless val.kind_of?(Hash)
113
156
  score = 0
@@ -131,7 +174,7 @@ class StructureMatch
131
174
  [binds,score]
132
175
  end
133
176
 
134
-
177
+ # Runs bind on val and returns just the score component.
135
178
  def score(val)
136
179
  bind(val)[1]
137
180
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: structurematch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Victor Lowther