match_map 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/CHANGES ADDED
@@ -0,0 +1,4 @@
1
+ 1.1.0 Add a different optimize method and benchmarking code; now 1.9 only
2
+ 1.0.0 Improve documentation and bump version to 1.0.0
3
+ 0.5.0 Change underlying structures so return order is guaranteed; lost optimize in the process
4
+ 0.0.0 First github release
data/Gemfile ADDED
@@ -0,0 +1,16 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+
9
+
10
+ group :development do
11
+ gem "minitest", ">= 0"
12
+ gem "yard", "~> 0.6.0"
13
+ gem "bundler", "~> 1.0.0"
14
+ gem "jeweler", "~> 1.6.4"
15
+ gem 'turn', '>=0.9.3'
16
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Bill Dueber
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,195 @@
1
+ # MatchMap -- a multimap where key matching is based on regular expressions
2
+
3
+ `match_map` defines an object with a hash-like interface that allows Regexp patterns as keys and/or multiple simultaneous lookuup arguments. Calling `mm[arg]` checks _arg_ against every key, aggregating their associated values into an array.
4
+
5
+ ```ruby
6
+ require 'match_map'
7
+
8
+ mm = MatchMap.new
9
+ mm['a'] = 'a_string'
10
+ mm[/a/] = 'apat'
11
+ mm[/b/] = ['bpat1', 'bpat2']
12
+ mm[/.+b$/] = 'bpat3'
13
+
14
+ mm['a'] #=> ['a_string', 'apat'] # order is the same as the key order
15
+ mm['aa'] #=> ['apat']
16
+ mm['b'] #=> ['bpat1', 'bpat2']
17
+ mm['cob'] #=> ['bpat1', 'bpat2', 'bpat3'] # flattened one level!!!
18
+ mm['cab'] #=> ['apat', 'bpat1', 'bpat2', 'bpat3']
19
+ mm['c'] #=> nil # no match
20
+
21
+
22
+ # Change the default miss value to ease some processing forms
23
+ mm.default = []
24
+ mm['neverGonnaMatch'].each do { #never get here}
25
+
26
+ # You can also query on multiple values at once by passing an array
27
+ mm[['a', 'aa', 'b']] #=> ['a_string', 'apat', 'bpat1', 'bpat2']
28
+
29
+ # Or use a Proc as the value; it gets the match variable as its argument
30
+
31
+ mm = MatchMap.new
32
+ mm[/a(b+)/] = Proc.new {|m| [m[1].size]}
33
+ mm['abbbb'] #=> [4]
34
+
35
+
36
+ # You can set #echo to return the argument :always or only :onmiss
37
+ mm = MatchMap.new
38
+ mm[/ab/] = "AB"
39
+
40
+ # first, without echo
41
+ mm['miss'] = nil
42
+ mm['cab'] = ['AB']
43
+
44
+ #...then with echo = :always
45
+ mm.echo = :always
46
+ mm['miss'] = ['miss']
47
+ mm['cab'] = ['cab', 'AB']
48
+
49
+ #...and again with echo = :onmiss
50
+ mm.echo = :onmiss
51
+ mm['miss'] #=> ['miss'] # because nothing else matched
52
+ mm['cab'] #=> ['AB'] # because a match was found
53
+
54
+ # Need to ditch a key?
55
+ if mm.has_key? /ab/
56
+ mm.delete /ab/
57
+ end
58
+
59
+ ```
60
+
61
+ A MatchMap is a hash-like with the following properties:
62
+
63
+ * keys can be anything that responds to '==' (e.g., strings) or regular expressions
64
+ * keys cannot be repeated (mirroring how a hash works, but see below about multiple values)
65
+ * arguments are compared to non-pattern keys based on ==
66
+ * arguments are compared to pattern keys based on pattern match against arg.to_s_
67
+ * values can be scalars, arrays (treated as multiple return values), or Proc objects
68
+ * a scalar argument to #[] is left alone for comparison to non-patterns (so a string or integer can be exactly matched), but converted to a string for comparison to patterns. (see "How are arguments compared to keys?", below)
69
+ * an array argument to #[] is treated as if you want all values for all matches for all array members
70
+ * the return value from #[] goes through #uniq and #compact (no repeated values, no nil values), which may or may not mess with what you expect the output order to be.
71
+
72
+ The idea is that you can set up a bunch of (possibly overlapping) patterns, each of which is associated with one or more values, and easily get back all the values for those patterns that match the argument. `match_map` was originally designed for transforming values for full-text indexing, but has other uses as well.
73
+
74
+
75
+ ## What is this good for?
76
+
77
+ A match_map can be useful for (among other things) values that map onto a hierarchy.
78
+
79
+ Here's part of a map for library call numbers:
80
+
81
+ ```ruby
82
+ mm = MatchMap.new
83
+ mm[/^H/] = 'Social Science'
84
+ mm[/^HA/] = 'Statistics'
85
+ mm['HA37 .P27 P16'] #=> ['Social Science', 'Statistics']
86
+ ```
87
+
88
+ Or use it as a clean way to extract semi-regular information from free-text strings
89
+
90
+ ```ruby
91
+ state = MatchMap.new
92
+ state[/\bMN\b/i] = 'Minnesota'
93
+ state[/\bMI\b/i] = 'Michigan'
94
+ state['St. Paul, MN 55117'] #=> ['Minnesota']
95
+ state['2274 Delaware Drive, Ann Arbor, MI, 48103'] #=> ['Michigan']
96
+ ```
97
+
98
+
99
+ ## How are arguments compared to keys?
100
+
101
+ There are three basic rules:
102
+
103
+ 1. If the argument is an array, each element is handled separately
104
+ 2. If the argument (`a`) is being matched against a pattern key (`pk`), check if `pk.match(a.to_s)`
105
+ 3. If the argument (`a`) is being matched against a key that is not a pattern (`npk`), check if `a == npk`
106
+
107
+ Here's a quick example to show how it works
108
+
109
+ ```ruby
110
+ mm = MatchMap.new
111
+ mm[1] = 'fixnum'
112
+ mm['1'] = 'string'
113
+ mm[/1/] = 'pattern'
114
+ mm[1] #=> ['fixnum', 'pattern']
115
+ mm['1'] #=> ['string', 'pattern']
116
+ ```
117
+
118
+
119
+ ## Using Proc objects as values
120
+
121
+ You can also use a Proc object as a value. It must:
122
+
123
+ * take a single argument; the match variable
124
+ * return a (possibly empty) _array of values_
125
+
126
+ This can be abused, of course, but can be useful. Here's a simple example that reverses the order of a comma-delimited duple.
127
+
128
+ ```ruby
129
+ mm = MatchMap.new
130
+ mm[/^(.+),\s*(.+)$/] = Proc.new {|m| "#{m[2]} #{m[1]}"}
131
+ mm['Dueber, Bill'] #=> ["Bill Dueber"]
132
+ ```
133
+ ## Using echo to always/sometimes get back the argument
134
+
135
+ There are two common requirements when doing this sort of translation for indexing:
136
+
137
+ * The raw argument should always appear in the output
138
+ * The raw argument should appear in the output only if there are no other matches.
139
+
140
+ ```ruby
141
+ mm = MatchMap.new
142
+ mm[/ab/] = "AB"
143
+
144
+ # first, without echo
145
+ mm['miss'] = []
146
+ mm['cab'] = ['AB']
147
+
148
+ #...then with echo = :always
149
+ mm.echo = :always
150
+ mm['miss'] = ['miss']
151
+ mm['cab'] = ['cab', 'AB']
152
+
153
+ #...and again with echo = :onmiss
154
+ mm.echo = :onmiss
155
+ mm['miss'] #=> ['miss'] # because nothing else matched
156
+ mm['cab'] #=> ['AB'] # because a match was found
157
+ ```
158
+
159
+ Note that the `default` value will never be added to the output if `echo` is set.
160
+
161
+ ## Optimizing
162
+
163
+ You can call `mm.optimize!` to attempt to optimize a MatchMap where none of the keys are regular expressions
164
+ and none of the values are Proc objects for a significant speed increase (run `rake bench` for an idea
165
+ of how much faster). This allows you to take advantage of all the differences between MatchMap and a regular
166
+ hash (pass multiple arguments, flatten return values, echoing, etc.) while remaining an O(1) operation (instead
167
+ of a O(n) for the standard, try-to-match-each-key-in-turn algorithm).
168
+
169
+ Note that a call to `#optimize!` actually picks the best algorithm for that particular map, so if you have a simple map,
170
+ call `#optmize!`, and add a regular-expression key, another call to `#optmize!` is required to start using the
171
+ regular algorithm again.
172
+
173
+ Obviously, only call `#optimize!` when you're sure you won't be modifying the map anymore.
174
+
175
+ ## Gotchas
176
+
177
+ * Like a hash, repeated assignment to the same key results in a replacement. So `mm[/a/] = 'a'; mm[/a/] = 'A'` will give `mm['a'] #=> ['A']`
178
+ * Return values are flattened one level. So, a => 1 and b => [2,3], the something that matches both will return [1,2,3]. If you really want to return an array, you need to do something like `m['a'] = [[1,2]]`
179
+
180
+
181
+ ## Contributing to MatchMap
182
+
183
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
184
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
185
+ * Fork the project
186
+ * Start a feature/bugfix branch
187
+ * Commit and push until you are happy with your contribution
188
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
189
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
190
+
191
+ ## Copyright
192
+
193
+ Copyright (c) 2011 Bill Dueber. See LICENSE.txt for
194
+ further details.
195
+
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.required_ruby_version = '>= 1.9.0' # due to use of define_singleton_method in optimize
18
+ gem.name = "match_map"
19
+ gem.homepage = "http://github.com/billdueber/match_map"
20
+ gem.license = "MIT"
21
+ gem.summary = "A multimap that allows keys to match regex patterns"
22
+ gem.description = %Q{MatchMap is a map representing key=>value pairs but where
23
+ (a) a query argument can match more than one key, and (b) the argument is compraed to the key
24
+ such that you can use regex patterns as keys}
25
+ gem.email = "bill@dueber.com"
26
+ gem.authors = ["Bill Dueber"]
27
+ # dependencies defined in Gemfile
28
+ end
29
+ Jeweler::RubygemsDotOrgTasks.new
30
+
31
+ require 'rake/testtask'
32
+ Rake::TestTask.new(:test) do |test|
33
+ test.libs << 'lib' << 'test'
34
+ test.pattern = 'test/**/test_*.rb'
35
+ test.verbose = true
36
+ end
37
+
38
+ desc "Run a quick benchmark"
39
+ task :bench do
40
+ $: << 'lib'
41
+ load 'bench/bench.rb'
42
+ end
43
+
44
+ task :default => :test
45
+
46
+ require 'yard'
47
+ YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.1.0
data/bench/bench.rb ADDED
@@ -0,0 +1,62 @@
1
+ require 'benchmark'
2
+ require 'match_map'
3
+
4
+ h5 = {
5
+ 'a' => 'A',
6
+ 'b' => 'B',
7
+ 'c' => 'C',
8
+ 'd' => 'D',
9
+ 'e' => 'E',
10
+ # /a/ => 'AAA'
11
+ }
12
+
13
+ @mm = MatchMap.new
14
+ h5.each_pair {|k,v| @mm[k] = v}
15
+
16
+ @mm2 = MatchMap.new()
17
+ (1..20).each do |i|
18
+ @mm2[i] = i*2
19
+ end
20
+ # @mm2[/a/] = 'AAA'
21
+
22
+ iters = 100_000
23
+ puts "Testing #{iters} accesses"
24
+
25
+ method = (defined? JRUBY_VERSION) ? :bmbm : :bm
26
+ Benchmark.send(method) do |x|
27
+
28
+ x.report('hash ') do
29
+ 1..iters.times do
30
+ y = h5['a']
31
+ end
32
+ end
33
+
34
+ x.report('straight 5 keys ') do
35
+ 1..iters.times do
36
+ y = @mm['a']
37
+ end
38
+ end
39
+
40
+ x.report('optimized 5 keys') do
41
+ @mm.optimize!
42
+ 1..iters.times do
43
+ y = @mm['a']
44
+ end
45
+ end
46
+
47
+ x.report('straight 20 keys') do
48
+ 1..iters.times do
49
+ y = @mm2['a']
50
+ end
51
+ end
52
+
53
+
54
+ x.report('optimized 20 keys') do
55
+ @mm2.optimize!
56
+ 1..iters.times do
57
+ y = @mm2['a']
58
+ end
59
+ end
60
+
61
+
62
+ end
data/lib/match_map.rb ADDED
@@ -0,0 +1,102 @@
1
+ # A hash-like object that tries to match an argument against
2
+ # *all* keys (using == for non-Regexp keys and pattern matching
3
+ # for Regexp keys)
4
+
5
+ class MatchMap
6
+
7
+ #
8
+ attr_accessor :default
9
+ attr_reader :echo
10
+
11
+
12
+ def echo= arg
13
+ raise RuntimeError.new, "echo value must be :onmiss or :always" unless [:onmiss, :always].include? arg
14
+ @echo = arg
15
+ end
16
+
17
+ def []= key, val
18
+ @map[key] = val
19
+ @keys.push key unless @keys.include? key
20
+ set_attrs key, val
21
+ end
22
+
23
+ def set_attrs key, val
24
+ @attrs[key] = {:regexkey => (key.is_a? Regexp), :procval => (val.is_a? Proc)}
25
+ end
26
+
27
+ def [] arg
28
+ rv = []
29
+ rv.push *arg if @echo == :always
30
+ if arg.is_a? Array
31
+ arg.map {|s| inner = self.inner_get(s); rv.push *inner}
32
+ else
33
+ inner = self.inner_get arg
34
+ rv.push *inner
35
+ end
36
+ rv.uniq!
37
+ rv.compact!
38
+ if rv.size == 0
39
+ if @echo == :onmiss
40
+ return [*arg]
41
+ else
42
+ return @default
43
+ end
44
+ end
45
+ return rv
46
+ end
47
+
48
+ def optimized_inner_get arg
49
+ return [@map[arg]]
50
+ end
51
+
52
+ def normal_inner_get arg
53
+ rv = []
54
+ @keys.each do |k|
55
+ if k.is_a? Regexp
56
+ m = k.match arg.to_s
57
+ else
58
+ m = (k == arg) ? arg : false
59
+ end
60
+ if m
61
+ v = @map[k]
62
+ if v.is_a? Proc
63
+ processed = v.call(m)
64
+ rv.push *processed if processed
65
+ else
66
+ rv.push *v
67
+ end
68
+ end
69
+ end
70
+ return rv
71
+ end
72
+
73
+ def has_key? key
74
+ @map.has_key? key
75
+ end
76
+
77
+ def delete key
78
+ @map.delete(key)
79
+ @keys.delete(key)
80
+ end
81
+
82
+ def initialize hash = {}
83
+ @default = nil # default miss value is nil
84
+ @echo = echo
85
+ @keys = hash.keys
86
+ @map = hash
87
+ @attrs = {}
88
+ @map.each_pair {|k, v| set_attrs k, v}
89
+ define_singleton_method :inner_get, method(:normal_inner_get)
90
+ end
91
+
92
+ def optimize!
93
+ @map.each_pair do |k,v|
94
+ if k.is_a? Regexp or v.is_a? Proc
95
+ define_singleton_method :inner_get, method(:normal_inner_get)
96
+ return
97
+ end
98
+ end
99
+ define_singleton_method :inner_get, method(:optimized_inner_get)
100
+ end
101
+
102
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'minitest/spec'
11
+ require 'minitest/benchmark'
12
+ begin; require 'turn'; rescue LoadError; end
13
+
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
+ require 'match_map'
17
+
18
+ class MiniTest::Unit::TestCase
19
+ end
20
+
21
+ MiniTest::Unit.autorun
@@ -0,0 +1,213 @@
1
+ require 'helper'
2
+
3
+ describe MatchMap do
4
+
5
+ before do
6
+ @h = MatchMap.new
7
+ end
8
+
9
+ describe "when empty" do
10
+
11
+ it "should return nil (default)" do
12
+ @h['a'].must_equal nil
13
+ end
14
+
15
+ it 'should allow set of default' do
16
+ @h.default = 'def'
17
+ @h['a'].must_equal 'def'
18
+ end
19
+
20
+ end
21
+
22
+ describe "when a single string key" do
23
+ it 'should set correctly' do
24
+ @h['a'] = 3
25
+ @h['a'].must_equal [3]
26
+ end
27
+
28
+ it 'should still return default' do
29
+ @h['a'] = 3
30
+ @h['c'].must_equal @h.default
31
+ end
32
+
33
+ it 'should reset a value' do
34
+ @h['a'] = 3
35
+ @h['a'] = 4
36
+ @h['a'].must_equal [4]
37
+ end
38
+
39
+ it 'should allow array values' do
40
+ @h['a'] = [1,2]
41
+ @h['a'].must_equal [1,2]
42
+ end
43
+
44
+ end
45
+
46
+
47
+ describe "has/delete key" do
48
+ it "detects key" do
49
+ @h['a'] = 'a'
50
+ @h['b'] = 'b'
51
+ @h.has_key?('a').must_equal true
52
+ @h.has_key?('c').must_equal false
53
+ @h.delete('a')
54
+ @h.has_key?('a').must_equal false
55
+ @h['a'].must_equal @h.default
56
+ end
57
+ end
58
+
59
+ describe "works with pattern keys" do
60
+
61
+ it 'works with an always-match pattern' do
62
+ @h[/.?/] = 100
63
+ @h['a'] = 1
64
+ @h[10].must_equal [100]
65
+ @h['a'].must_equal [100, 1]
66
+ end
67
+
68
+
69
+ it "uses a single pattern" do
70
+ @h[/.+a/] = 1
71
+ @h['b'].must_equal @h.default
72
+ @h['a'].must_equal @h.default
73
+ @h['aa'].must_equal [1]
74
+ @h['era'].must_equal [1]
75
+ end
76
+
77
+ it "works with disjoint patterns" do
78
+ @h[/.+a/] = 1
79
+ @h[/b/] = 2
80
+ @h['aa'].must_equal [1]
81
+ @h['ab'].must_equal [2]
82
+ @h['cab'].must_equal [1,2]
83
+ end
84
+ end
85
+
86
+ describe "works with non-pattern keys" do
87
+
88
+ it "is fine with strings" do
89
+ @h['a'] = 1
90
+ @h[/a/] = 2
91
+ @h['a'].must_equal [1,2]
92
+ @h['aa'].must_equal [2]
93
+ end
94
+
95
+ it 'is fine with fixnums' do
96
+ @h[1] = 1
97
+ @h[2] = 2
98
+ @h[12] = 3
99
+ @h[1].must_equal [1]
100
+ @h[2].must_equal [2]
101
+ @h[12].must_equal [3]
102
+ end
103
+ end
104
+
105
+ describe "works with a Proc" do
106
+ it 'does an echo proc' do
107
+ @h[/ab+/] = Proc.new {|m| m[0]}
108
+ @h['ab'].must_equal ['ab']
109
+ end
110
+
111
+ it 'deals with match data' do
112
+ @h[/a(b+)/] = Proc.new {|m| [m[0], m[1].size.to_s]}
113
+ @h[/abb/].must_equal ['abb', '2']
114
+ end
115
+
116
+ it "works with a more complex proc" do
117
+ mm = MatchMap.new
118
+ mm[/^(.+),\s*(.+)$/] = Proc.new {|m| "#{m[2]} #{m[1]}"}
119
+ mm['Dueber, Bill'].must_equal ["Bill Dueber"]
120
+ end
121
+
122
+ it "calls the Proc for string argument, even though that kind of an abuse" do
123
+ mm = MatchMap.new
124
+ mm['a'] = Proc.new {|m| [m[0] + 'bbb']}
125
+ mm['a'].must_equal ['abbb']
126
+ end
127
+ end
128
+
129
+ describe "works with echo" do
130
+ before do
131
+ @j = MatchMap.new
132
+ end
133
+
134
+ it "echos when empty" do
135
+ @j['miss'].must_equal @h.default # no echoing
136
+
137
+ @j.echo = :always
138
+ @j['miss'].must_equal ['miss']
139
+
140
+ @j.echo = :onmiss
141
+ @j['miss'].must_equal ['miss']
142
+ end
143
+
144
+ it "echos when not empty" do
145
+ @j[/a/] = 'hello'
146
+
147
+ @j['miss'].must_equal @j.default
148
+
149
+ @j.echo = :always
150
+ @j['miss'].must_equal ['miss']
151
+ @j['ab'].must_equal ['ab', 'hello'].sort
152
+
153
+ @j.echo = :onmiss
154
+ @j['miss'].must_equal ['miss']
155
+ @j['ab'].must_equal ['hello']
156
+
157
+ end
158
+
159
+ it "works with a Proc and echo" do
160
+ mm = MatchMap.new
161
+ mm[/^(.+),\s*(.+)$/] = Proc.new {|m| "#{m[2]} #{m[1]}"}
162
+ mm.echo = :always
163
+ mm['Dueber, Bill'].must_equal ['Dueber, Bill', "Bill Dueber"]
164
+ end
165
+
166
+ end
167
+
168
+ describe 'Multiple key arguments' do
169
+ before do
170
+ @h = MatchMap.new
171
+ @h['a'] = 1
172
+ @h['b'] = 2
173
+ @h[/c/] = 3
174
+ end
175
+
176
+ it 'works with simple array arg' do
177
+ @h[['a', 'b']].must_equal [1,2]
178
+ end
179
+
180
+ it "works with echo" do
181
+ @h.echo = :always
182
+ @h[['a', 'ac']].must_equal ['a', 'ac', 1, 3]
183
+ end
184
+
185
+ it 'misses hard' do
186
+ @h[[1,2,3]].must_equal nil
187
+ end
188
+
189
+ end
190
+
191
+ describe 'Flattens correctly' do
192
+ before do
193
+ @h = MatchMap.new
194
+ @h[/c/] = 1
195
+ @h[/cc/] = [2, 3]
196
+ @h[/ccc/] = [[4,5, 6]]
197
+ @h[/cccc/] = [7, 8, [9, 10]]
198
+ end
199
+
200
+ it 'flattens one level' do
201
+ @h['cc'].must_equal [1,2,3]
202
+ end
203
+
204
+ it "doesn't over-flatten" do
205
+ @h['ccc'].must_equal [1, 2, 3, [4,5,6]]
206
+ end
207
+
208
+ it 'allows mixed depth' do
209
+ @h['cccc'].must_equal [1, 2, 3, [4, 5, 6], 7, 8, [9, 10]]
210
+ end
211
+ end
212
+
213
+ end
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: match_map
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Bill Dueber
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-21 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: minitest
16
+ requirement: &70104080307140 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70104080307140
25
+ - !ruby/object:Gem::Dependency
26
+ name: yard
27
+ requirement: &70104080321880 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ~>
31
+ - !ruby/object:Gem::Version
32
+ version: 0.6.0
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70104080321880
36
+ - !ruby/object:Gem::Dependency
37
+ name: bundler
38
+ requirement: &70104080321120 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70104080321120
47
+ - !ruby/object:Gem::Dependency
48
+ name: jeweler
49
+ requirement: &70104080320260 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.6.4
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70104080320260
58
+ - !ruby/object:Gem::Dependency
59
+ name: turn
60
+ requirement: &70104080319220 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: 0.9.3
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70104080319220
69
+ description: ! "MatchMap is a map representing key=>value pairs but where \n (a)
70
+ a query argument can match more than one key, and (b) the argument is compraed to
71
+ the key\n such that you can use regex patterns as keys"
72
+ email: bill@dueber.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files:
76
+ - LICENSE.txt
77
+ - README.md
78
+ files:
79
+ - .document
80
+ - CHANGES
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - VERSION
86
+ - bench/bench.rb
87
+ - lib/match_map.rb
88
+ - test/helper.rb
89
+ - test/test_match_map.rb
90
+ homepage: http://github.com/billdueber/match_map
91
+ licenses:
92
+ - MIT
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: 1.9.0
103
+ required_rubygems_version: !ruby/object:Gem::Requirement
104
+ none: false
105
+ requirements:
106
+ - - ! '>='
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubyforge_project:
111
+ rubygems_version: 1.8.15
112
+ signing_key:
113
+ specification_version: 3
114
+ summary: A multimap that allows keys to match regex patterns
115
+ test_files: []