object-template 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/COPYING +22 -0
- data/README.md +4 -0
- data/Rakefile +16 -0
- data/bench/bench-match.rb +47 -0
- data/bench/lib/bench.rb +55 -0
- data/lib/object-template.rb +142 -0
- data/test/lib/assert-threequal.rb +11 -0
- data/test/lib/eq3.rb +48 -0
- data/test/test-errors.rb +28 -0
- data/test/test-key-conv.rb +44 -0
- data/test/test-match.rb +165 -0
- metadata +68 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 56e379a6cf0a215d88542bde7bdae2ffba74fbf7
|
4
|
+
data.tar.gz: 6da3bba0bf52782c243023ebdd6e544a04db5d5a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 56a120f187e1439c0a40f29d56207e72036f815180b9c86327f9e8d5b4604ddb9cbde43419bedaed9030c94a2bb1f01236cff7025d33e6d6b90bbd7dfa887c8e
|
7
|
+
data.tar.gz: e0c16129bfb20ff2bb44977e16b7fdecd8ce928e1f96efb4ab5969c4bb694fdc30edd5a3f4aaaf9027272b572e413873339daba0ea2bfad1831f5ec7c406c736
|
data/COPYING
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013, Joel VanderWerf, vjoel@users.sourceforge.net
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
10
|
+
this list of conditions and the following disclaimer in the documentation
|
11
|
+
and/or other materials provided with the distribution.
|
12
|
+
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
15
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
16
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
17
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
18
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
19
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
20
|
+
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
21
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
22
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
desc "Run tests"
|
5
|
+
Rake::TestTask.new :test do |t|
|
6
|
+
t.libs << "lib"
|
7
|
+
t.libs << "ext"
|
8
|
+
t.test_files = FileList["test/*.rb"]
|
9
|
+
end
|
10
|
+
|
11
|
+
desc "Run benchmarks"
|
12
|
+
Rake::TestTask.new :bench do |t|
|
13
|
+
t.libs << "lib"
|
14
|
+
t.libs << "ext"
|
15
|
+
t.test_files = FileList["bench/*.rb"]
|
16
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
$LOAD_PATH.unshift File.join(__dir__, "lib")
|
2
|
+
|
3
|
+
require 'bench'
|
4
|
+
require 'object-template'
|
5
|
+
|
6
|
+
ROT = RubyObjectTemplate
|
7
|
+
POT = PortableObjectTemplate
|
8
|
+
|
9
|
+
SEED = 1237
|
10
|
+
|
11
|
+
def make_dataset
|
12
|
+
strs = ("a".."z").map {|c| "fo" + c}
|
13
|
+
(1..1000).map {|i| [ i, strs.sample, rand(50) ]}
|
14
|
+
end
|
15
|
+
|
16
|
+
def run_bench_on_template template, seed: nil
|
17
|
+
srand seed
|
18
|
+
objs = make_dataset
|
19
|
+
|
20
|
+
matched = 0
|
21
|
+
rslt = bench_rate n_sec: 1.0, notify: proc {|c| print c} do
|
22
|
+
matched += 1 if template === objs.sample
|
23
|
+
end
|
24
|
+
|
25
|
+
puts
|
26
|
+
puts "seed = %8d" % seed
|
27
|
+
puts "matched = %8d" % matched
|
28
|
+
puts "n_iter = %8d" % rslt[:n_iter]
|
29
|
+
puts "n_chunk = %8d" % rslt[:n_chunk]
|
30
|
+
puts "t_warmup = %12.3f sec" % rslt[:t_warmup]
|
31
|
+
puts "t_run = %12.3f sec" % rslt[:t_run]
|
32
|
+
puts "rate = %12.3f iter/sec" % rslt[:rate]
|
33
|
+
|
34
|
+
rslt
|
35
|
+
end
|
36
|
+
|
37
|
+
template = POT.new [ {set: [0,1,2,*100..999]}, {regex: "foo"}, {value: 0} ]
|
38
|
+
puts "Unoptimized:"
|
39
|
+
r0 = run_bench_on_template template, seed: SEED
|
40
|
+
|
41
|
+
puts
|
42
|
+
puts "Optimized:"
|
43
|
+
template.optimize!
|
44
|
+
r1 = run_bench_on_template template, seed: SEED
|
45
|
+
|
46
|
+
puts
|
47
|
+
puts "Speed-up: %5.2f" % (r1[:rate] / r0[:rate])
|
data/bench/lib/bench.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
def bench
|
2
|
+
times = Process.times
|
3
|
+
t0 = times.utime + times.stime
|
4
|
+
|
5
|
+
yield
|
6
|
+
|
7
|
+
times = Process.times
|
8
|
+
t1 = times.utime + times.stime
|
9
|
+
t1 - t0
|
10
|
+
end
|
11
|
+
|
12
|
+
def bench_rate n_sec: 1.0, n_chunk: 1, notify: nil
|
13
|
+
times = Process.times
|
14
|
+
t_init = times.utime + times.stime
|
15
|
+
#puts "init at #{t_init}"
|
16
|
+
|
17
|
+
t_run = 0
|
18
|
+
n_iter = 0
|
19
|
+
t_now = nil
|
20
|
+
t_start = nil
|
21
|
+
|
22
|
+
while t_run < n_sec
|
23
|
+
n_chunk.times do
|
24
|
+
yield
|
25
|
+
end
|
26
|
+
|
27
|
+
times = Process.times
|
28
|
+
t_now = times.utime + times.stime
|
29
|
+
|
30
|
+
if t_now - t_init < n_sec/4.0 # warm up and seek stride
|
31
|
+
n_chunk *= 2
|
32
|
+
#puts "n_chunk = #{n_chunk}"
|
33
|
+
notify and notify.call "."
|
34
|
+
else
|
35
|
+
if not t_start
|
36
|
+
t_start = t_now
|
37
|
+
#puts "start at #{t_start}"
|
38
|
+
end
|
39
|
+
|
40
|
+
notify and notify.call "0"
|
41
|
+
|
42
|
+
n_iter += n_chunk
|
43
|
+
t_run = t_now - t_start
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
{
|
48
|
+
n_iter: n_iter,
|
49
|
+
t_warmup: t_start - t_init,
|
50
|
+
t_run: t_run,
|
51
|
+
rate: n_iter / t_run.to_f,
|
52
|
+
n_chunk: n_chunk
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
# Base class for classes of templates used to match somewhat arbitrary objects.
|
4
|
+
class ObjectTemplate
|
5
|
+
# A set implementation that treats the matching operator (===) as membership.
|
6
|
+
# Used internally by PortableObjectTemplate, but can also be used in
|
7
|
+
# RubyObjectTemplate or in case statements.
|
8
|
+
class MemberMatchingSet < Set
|
9
|
+
alias === member?
|
10
|
+
end
|
11
|
+
|
12
|
+
# The key_converter is for matching objects that, for example, have had symbol
|
13
|
+
# keys serialized to strings. Using a converter that round-trips through the
|
14
|
+
# same serialializer, symbols in keys will match strings. However, symbols in
|
15
|
+
# values will not.
|
16
|
+
def initialize spec, key_converter = nil
|
17
|
+
unless spec.respond_to? :size and spec.respond_to? :each
|
18
|
+
raise ArgumentError, "cannot be used as a template: #{spec.inspect}"
|
19
|
+
end
|
20
|
+
|
21
|
+
@spec = spec
|
22
|
+
@size = spec.size
|
23
|
+
@matchers = []
|
24
|
+
|
25
|
+
if spec.respond_to? :to_hash # assume hash-like
|
26
|
+
@shibboleth = :to_hash
|
27
|
+
spec.each do |k, v|
|
28
|
+
kc = key_converter ? key_converter[k]: k
|
29
|
+
# Note: cannot use key_converter[v] because v may have class, regex,
|
30
|
+
# or other non-serializable object.
|
31
|
+
fill_matchers kc, v
|
32
|
+
end
|
33
|
+
|
34
|
+
else # assume array-like
|
35
|
+
@shibboleth = :to_ary
|
36
|
+
spec.each_with_index do |v, i|
|
37
|
+
fill_matchers i, v unless v.nil?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Reorders the list of matchers so that easy ones come first.
|
43
|
+
# For example: nil, then single values (==), then patterns (===).
|
44
|
+
# Returns self.
|
45
|
+
def optimize!
|
46
|
+
@matchers.sort_by! do |k, v|
|
47
|
+
case v
|
48
|
+
when nil; 0
|
49
|
+
when Range; 2
|
50
|
+
when Module; 3
|
51
|
+
when Regexp; 4
|
52
|
+
when MemberMatchingSet; 4
|
53
|
+
when Proc; 5
|
54
|
+
else 1 # assume it is a value
|
55
|
+
end
|
56
|
+
end
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
# True if the template matches the given object.
|
61
|
+
# Adapted from rinda/rinda.rb.
|
62
|
+
def === obj
|
63
|
+
return false unless obj.respond_to?(@shibboleth)
|
64
|
+
return false unless @size == obj.size
|
65
|
+
@matchers.each do |k, v|
|
66
|
+
begin
|
67
|
+
it = obj.fetch(k)
|
68
|
+
rescue
|
69
|
+
return false
|
70
|
+
end
|
71
|
+
next if v.nil?
|
72
|
+
next if v == it
|
73
|
+
next if v === it
|
74
|
+
return false
|
75
|
+
end
|
76
|
+
return true
|
77
|
+
end
|
78
|
+
|
79
|
+
def inspect
|
80
|
+
"<#{self.class}: #{@spec}>"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Template specified by array or hash of ruby objects. The #== and #===
|
85
|
+
# methods of entry values are used in matching. Entry values may include
|
86
|
+
# classes, regexes, ranges, and so on, in addition to single values.
|
87
|
+
class RubyObjectTemplate < ObjectTemplate
|
88
|
+
def fill_matchers k, v # :nodoc:
|
89
|
+
@matchers << [k, v]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Template specified by array or hash in a portable format composed of
|
94
|
+
# strings, numbers, booleans, arrays, and hashes. Special entry values
|
95
|
+
# correspond to wildcards and matchers of several kinds. See the unit
|
96
|
+
# tests for examples.
|
97
|
+
#
|
98
|
+
# The objects matched include anything constructed out of numbers, booleans,
|
99
|
+
# including null, and strings using hashes and arrays. In other words,
|
100
|
+
# objects that can be serialized with json or msgpack.
|
101
|
+
#
|
102
|
+
class PortableObjectTemplate < ObjectTemplate
|
103
|
+
def fill_matchers k, v # :nodoc:
|
104
|
+
case v
|
105
|
+
when nil
|
106
|
+
@matchers << [k, nil]
|
107
|
+
# This must be there to ensure the key exists in the hash case.
|
108
|
+
when Hash
|
109
|
+
v.each do |kk, vv|
|
110
|
+
case kk
|
111
|
+
when :value, "value"
|
112
|
+
@matchers << [k, vv]
|
113
|
+
when :set, "set"
|
114
|
+
@matchers << [k, MemberMatchingSet.new(vv)]
|
115
|
+
when :type, "type"
|
116
|
+
@matchers << [k, CLASS_FOR[vv.to_s]]
|
117
|
+
when :range, "range"
|
118
|
+
@matchers << [k, Range.new(*vv)]
|
119
|
+
when :regex, "regex"
|
120
|
+
@matchers << [k, Regexp.new(vv)]
|
121
|
+
else
|
122
|
+
raise ArgumentError,
|
123
|
+
"unrecognized match specifier: #{kk.inspect}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
else
|
127
|
+
raise ArgumentError,
|
128
|
+
"expected nil or Hash in template, found #{v.inspect}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
CLASS_FOR = {
|
133
|
+
"number" => Numeric,
|
134
|
+
"string" => String,
|
135
|
+
"list" => Array,
|
136
|
+
"map" => Hash
|
137
|
+
}
|
138
|
+
|
139
|
+
CLASS_FOR.default_proc = proc do |h,k|
|
140
|
+
raise ArgumentError, "no known class for matching type #{k.inspect}"
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module AssertThreequal
|
2
|
+
def assert_threequal o1, o2, msg = nil
|
3
|
+
msg = message(msg) { "Expected #{mu_pp(o1)} to be === #{mu_pp(o2)}" }
|
4
|
+
assert o1.__send__(:===, o2), msg
|
5
|
+
end
|
6
|
+
|
7
|
+
def assert_not_threequal o1, o2, msg = nil
|
8
|
+
msg = message(msg) { "Expected #{mu_pp(o1)} to be !== #{mu_pp(o2)}" }
|
9
|
+
assert !(o1.__send__(:===, o2)), msg
|
10
|
+
end
|
11
|
+
end
|
data/test/lib/eq3.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'assert-threequal'
|
2
|
+
|
3
|
+
module Eq3
|
4
|
+
include AssertThreequal
|
5
|
+
|
6
|
+
ROT = RubyObjectTemplate
|
7
|
+
POT = PortableObjectTemplate
|
8
|
+
|
9
|
+
KEY_CONV = proc {|x| Symbol === x ? x.to_s : x}
|
10
|
+
|
11
|
+
FIELD_NAMES = ["zero", "one", "two", "three", "four", "five", "six"]
|
12
|
+
|
13
|
+
def mkhash ary
|
14
|
+
Hash[ FIELD_NAMES[0...ary.size].zip(ary) ]
|
15
|
+
end
|
16
|
+
|
17
|
+
def pot_for(pot)
|
18
|
+
POT.new(pot, KEY_CONV).optimize!
|
19
|
+
end
|
20
|
+
|
21
|
+
def rot_for(rot)
|
22
|
+
ROT.new(rot, KEY_CONV).optimize!
|
23
|
+
end
|
24
|
+
|
25
|
+
def eq3 literal, rot, pot
|
26
|
+
assert_threequal(rot_for(rot), literal)
|
27
|
+
assert_threequal(pot_for(pot), literal)
|
28
|
+
|
29
|
+
if literal.kind_of? Array
|
30
|
+
hlit = mkhash(literal)
|
31
|
+
hrot = mkhash(rot)
|
32
|
+
hpot = mkhash(pot)
|
33
|
+
eq3 hlit, hrot, hpot
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def ne3 literal, rot, pot
|
38
|
+
assert_not_threequal(rot_for(rot), literal)
|
39
|
+
assert_not_threequal(pot_for(pot), literal)
|
40
|
+
|
41
|
+
if literal.kind_of? Array
|
42
|
+
hlit = mkhash(literal)
|
43
|
+
hrot = mkhash(rot)
|
44
|
+
hpot = mkhash(pot)
|
45
|
+
ne3 hlit, hrot, hpot
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/test/test-errors.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$LOAD_PATH.unshift File.join(__dir__, "lib")
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'object-template'
|
5
|
+
require 'eq3'
|
6
|
+
|
7
|
+
class TestErrors < Minitest::Test
|
8
|
+
POT = PortableObjectTemplate
|
9
|
+
ROT = RubyObjectTemplate
|
10
|
+
|
11
|
+
def test_bad_type
|
12
|
+
assert_raises ArgumentError do
|
13
|
+
POT.new [ {type: "float"} ]
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_bad_entry
|
18
|
+
assert_raises ArgumentError do
|
19
|
+
POT.new [ "number" ]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_bad_match_specifier
|
24
|
+
assert_raises ArgumentError do
|
25
|
+
POT.new [ {foo: 1} ]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
$LOAD_PATH.unshift File.join(__dir__, "lib")
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'object-template'
|
5
|
+
require 'assert-threequal'
|
6
|
+
|
7
|
+
class TestKeyConv < Minitest::Test
|
8
|
+
include AssertThreequal
|
9
|
+
|
10
|
+
POT = PortableObjectTemplate
|
11
|
+
ROT = RubyObjectTemplate
|
12
|
+
|
13
|
+
def test_match_converted_key
|
14
|
+
assert_threequal(
|
15
|
+
POT.new(
|
16
|
+
{foo: {value: 1}},
|
17
|
+
proc {|x| Symbol === x ? x.to_s : x}),
|
18
|
+
{"foo" => 1}
|
19
|
+
)
|
20
|
+
|
21
|
+
assert_threequal(
|
22
|
+
ROT.new(
|
23
|
+
{foo: 1},
|
24
|
+
proc {|x| Symbol === x ? x.to_s : x}),
|
25
|
+
{"foo" => 1}
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_not_match_unconverted_key
|
30
|
+
assert_not_threequal(
|
31
|
+
POT.new(
|
32
|
+
{foo: {value: 1}},
|
33
|
+
proc {|x| x}),
|
34
|
+
{"foo" => 1}
|
35
|
+
)
|
36
|
+
|
37
|
+
assert_not_threequal(
|
38
|
+
ROT.new(
|
39
|
+
{foo: 1},
|
40
|
+
proc {|x| x}),
|
41
|
+
{"foo" => 1}
|
42
|
+
)
|
43
|
+
end
|
44
|
+
end
|
data/test/test-match.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
$LOAD_PATH.unshift File.join(__dir__, "lib")
|
2
|
+
|
3
|
+
require 'minitest/autorun'
|
4
|
+
require 'object-template'
|
5
|
+
require 'set'
|
6
|
+
require 'eq3'
|
7
|
+
|
8
|
+
Member = ObjectTemplate::MemberMatchingSet
|
9
|
+
|
10
|
+
class TestMatch < Minitest::Test
|
11
|
+
include Eq3
|
12
|
+
|
13
|
+
def test_cardinality
|
14
|
+
#=========================================================
|
15
|
+
#= LITERAL | ROT | POT
|
16
|
+
#=========================================================
|
17
|
+
#= 1. every test that can be expressed in terms of presence of column/field
|
18
|
+
#= or cardinality of columns/fields, without matching values
|
19
|
+
#=
|
20
|
+
eq3 [], [], []
|
21
|
+
eq3 ["anything"], [nil], [nil]
|
22
|
+
eq3 ["any", "thing"], [nil, nil], [nil, nil]
|
23
|
+
#=========================================================
|
24
|
+
ne3 ["anything"], [], []
|
25
|
+
ne3 [], [nil], [nil] # cardinality mismatch
|
26
|
+
ne3 ["any", "thing"], [nil], [nil] # ditto
|
27
|
+
ne3 ["anything"], [nil, nil], [nil, nil]
|
28
|
+
#=========================================================
|
29
|
+
#= 1a. specific to hashes
|
30
|
+
ne3( {foo: "anything"}, {bar: nil}, {bar: nil} ) # name mismatch
|
31
|
+
#=========================================================
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_literal
|
35
|
+
#=========================================================
|
36
|
+
#= LITERAL | ROT | POT
|
37
|
+
#=========================================================
|
38
|
+
#= 2. every test that can be expressed in terms of a literal value in the
|
39
|
+
#= template
|
40
|
+
#=
|
41
|
+
literals = [
|
42
|
+
true, false,
|
43
|
+
42, -1.2e13, "baz",
|
44
|
+
43, 2.4507, "zap",
|
45
|
+
[1,2,3], {a: 1, b: 2},
|
46
|
+
["a", "b"], {x: "y"}
|
47
|
+
]
|
48
|
+
literals.each do |x1|
|
49
|
+
literals.each do |x2|
|
50
|
+
if x1 == x2
|
51
|
+
eq3 [x1], [x1], [{value: x1}]
|
52
|
+
else
|
53
|
+
ne3 [x1], [x2], [{value: x2}]
|
54
|
+
end
|
55
|
+
eq3 [x1, x2], [x1, x2], [{value: x1}, {value: x2}]
|
56
|
+
end
|
57
|
+
end
|
58
|
+
#=========================================================
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_set
|
62
|
+
#=========================================================
|
63
|
+
#= LITERAL | ROT | POT
|
64
|
+
#=========================================================
|
65
|
+
#= 3. every test that can be expressed in terms of a set of values in the
|
66
|
+
#= template
|
67
|
+
#=
|
68
|
+
eq3 [1], [Member[0,1,2]], [{set: [0,1,2]}]
|
69
|
+
#=========================================================
|
70
|
+
ne3 [4], [Member[0,1,2]], [{set: [0,1,2]}]
|
71
|
+
#=========================================================
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_range
|
75
|
+
#=========================================================
|
76
|
+
#= LITERAL | ROT | POT
|
77
|
+
#=========================================================
|
78
|
+
#= 4. every test that can be expressed in terms of a range in the
|
79
|
+
#= template
|
80
|
+
#=
|
81
|
+
eq3 [0], [0..2], [{range: [0,2]}]
|
82
|
+
eq3 [2], [0..2], [{range: [0,2]}]
|
83
|
+
eq3 [1], [0...2], [{range: [0,2, true]}]
|
84
|
+
#=========================================================
|
85
|
+
ne3 [-1], [0..2], [{range: [0,2]}]
|
86
|
+
ne3 [3], [0..2], [{range: [0,2]}]
|
87
|
+
ne3 [2], [0...2], [{range: [0,2, true]}]
|
88
|
+
#=========================================================
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_regex
|
92
|
+
#=========================================================
|
93
|
+
#= LITERAL | ROT | POT
|
94
|
+
#=========================================================
|
95
|
+
#= 5. every test that can be expressed in terms of a regex in the
|
96
|
+
#= template
|
97
|
+
#=
|
98
|
+
eq3 ["foo"], [/oo/], [{regex: "oo"}]
|
99
|
+
eq3 ["foo"], [/[f]\w\w/], [{regex: "[f]\\w\\w"}]
|
100
|
+
#=========================================================
|
101
|
+
ne3 ["foo"], [/zz/], [{regex: "zz"}]
|
102
|
+
ne3 ["foo"], [/o{3,}/], [{regex: "o{3,}"}]
|
103
|
+
#=========================================================
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_type
|
107
|
+
#=========================================================
|
108
|
+
#= LITERAL | ROT | POT
|
109
|
+
#=========================================================
|
110
|
+
#= 6. every test that can be expressed in terms of a type in the
|
111
|
+
#= template
|
112
|
+
#=
|
113
|
+
eq3 [42], [Numeric], [{type: "number"}]
|
114
|
+
eq3 ["foo"], [String], [{type: "string"}]
|
115
|
+
eq3 [[1,2,3]], [Array], [{type: "list"}]
|
116
|
+
eq3 [{a:1, b:2}], [Hash], [{type: "map"}]
|
117
|
+
#=========================================================
|
118
|
+
ne3 ["42"], [Numeric], [{type: "number"}]
|
119
|
+
ne3 [123], [String], [{type: "string"}]
|
120
|
+
ne3 [{a:1, b:2}], [Array], [{type: "list"}]
|
121
|
+
ne3 [[1,2,3]], [Hash], [{type: "map"}]
|
122
|
+
#=========================================================
|
123
|
+
end
|
124
|
+
|
125
|
+
def test_conjunction
|
126
|
+
## note that ROT doesn't support conjunctions, though it would be easy
|
127
|
+
## to implement.
|
128
|
+
eq3 ["abc"], [nil], [{type: "string", regex: "b", set: ["abc", "def"]}]
|
129
|
+
ne3 ["abc"], [0], [{type: "list", regex: "b", set: ["abc", "def"]}]
|
130
|
+
ne3 ["abc"], [0], [{type: "string", regex: "d", set: ["abc", "def"]}]
|
131
|
+
ne3 ["abc"], [0], [{type: "string", regex: "b", set: ["xyz", "def"]}]
|
132
|
+
end
|
133
|
+
|
134
|
+
# test that a mismatch in one entry makes the template match fail
|
135
|
+
def test_all_entries
|
136
|
+
eq3 [ 5, 1.23, "bar", "baz", [1,2], {a: 1} ],
|
137
|
+
[ Numeric, 1..3, String, /az/, Array, {a: 1} ],
|
138
|
+
[ {type: "number"}, {range: [1,3]}, {type: "string"}, {regex: "az"}, {type: "list"}, {value: {a: 1}} ]
|
139
|
+
|
140
|
+
ne3 [ "foo", 1.23, "bar", "baz", [1,2], {a: 1} ],
|
141
|
+
[ Numeric, 1..3, String, /az/, Array, {a: 1} ],
|
142
|
+
[ {type: "number"}, {range: [1,3]}, {type: "string"}, {regex: "az"}, {type: "list"}, {value: {a: 1}} ]
|
143
|
+
|
144
|
+
ne3 [ 5, 4.23, "bar", "baz", [1,2], {a: 1} ],
|
145
|
+
[ Numeric, 1..3, String, /az/, Array, {a: 1} ],
|
146
|
+
[ {type: "number"}, {range: [1,3]}, {type: "string"}, {regex: "az"}, {type: "list"}, {value: {a: 1}} ]
|
147
|
+
|
148
|
+
ne3 [ 5, 1.23, ["bar"], "baz", [1,2], {a: 1} ],
|
149
|
+
[ Numeric, 1..3, String, /az/, Array, {a: 1} ],
|
150
|
+
[ {type: "number"}, {range: [1,3]}, {type: "string"}, {regex: "az"}, {type: "list"}, {value: {a: 1}} ]
|
151
|
+
|
152
|
+
ne3 [ 5, 1.23, "bar", "bAz", [1,2], {a: 1} ],
|
153
|
+
[ Numeric, 1..3, String, /az/, Array, {a: 1} ],
|
154
|
+
[ {type: "number"}, {range: [1,3]}, {type: "string"}, {regex: "az"}, {type: "list"}, {value: {a: 1}} ]
|
155
|
+
|
156
|
+
ne3 [ 5, 1.23, "bar", "baz", {a:2}, {a: 1} ],
|
157
|
+
[ Numeric, 1..3, String, /az/, Array, {a: 1} ],
|
158
|
+
[ {type: "number"}, {range: [1,3]}, {type: "string"}, {regex: "az"}, {type: "list"}, {value: {a: 1}} ]
|
159
|
+
|
160
|
+
ne3 [ 5, 1.23, "bar", "baz", [1,2], {a: 2} ],
|
161
|
+
[ Numeric, 1..3, String, /az/, Array, {a: 1} ],
|
162
|
+
[ {type: "number"}, {range: [1,3]}, {type: "string"}, {regex: "az"}, {type: "list"}, {value: {a: 1}} ]
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
metadata
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: object-template
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joel VanderWerf
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-12 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Templates for matching objects.
|
14
|
+
email: vjoel@users.sourceforge.net
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files:
|
18
|
+
- README.md
|
19
|
+
- COPYING
|
20
|
+
files:
|
21
|
+
- README.md
|
22
|
+
- COPYING
|
23
|
+
- Rakefile
|
24
|
+
- lib/object-template.rb
|
25
|
+
- bench/lib/bench.rb
|
26
|
+
- bench/bench-match.rb
|
27
|
+
- test/lib/assert-threequal.rb
|
28
|
+
- test/lib/eq3.rb
|
29
|
+
- test/test-match.rb
|
30
|
+
- test/test-key-conv.rb
|
31
|
+
- test/test-errors.rb
|
32
|
+
homepage: https://github.com/vjoel/object-template
|
33
|
+
licenses:
|
34
|
+
- BSD
|
35
|
+
metadata: {}
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options:
|
38
|
+
- --quiet
|
39
|
+
- --line-numbers
|
40
|
+
- --inline-source
|
41
|
+
- --title
|
42
|
+
- object-template
|
43
|
+
- --main
|
44
|
+
- README.md
|
45
|
+
require_paths:
|
46
|
+
- lib
|
47
|
+
- ext
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
54
|
+
requirements:
|
55
|
+
- - '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
requirements: []
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 2.0.4
|
61
|
+
signing_key:
|
62
|
+
specification_version: 4
|
63
|
+
summary: Templates for matching objects
|
64
|
+
test_files:
|
65
|
+
- test/test-match.rb
|
66
|
+
- test/test-key-conv.rb
|
67
|
+
- test/test-errors.rb
|
68
|
+
has_rdoc:
|