rubymail 0.11

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.
Files changed (60) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +64 -0
  3. data/Gemfile +7 -0
  4. data/LICENSE +201 -0
  5. data/README.md +2 -0
  6. data/Rakefile +1 -0
  7. data/lib/multimap/.gitignore +4 -0
  8. data/lib/multimap/LICENSE +20 -0
  9. data/lib/multimap/README.rdoc +16 -0
  10. data/lib/multimap/Rakefile +34 -0
  11. data/lib/multimap/benchmarks/bm_nested_multimap_construction.rb +60 -0
  12. data/lib/multimap/benchmarks/bm_nested_multimap_lookup.rb +33 -0
  13. data/lib/multimap/ext/extconf.rb +6 -0
  14. data/lib/multimap/ext/nested_multimap_ext.c +24 -0
  15. data/lib/multimap/extras/graphing.rb +83 -0
  16. data/lib/multimap/lib/multimap.rb +569 -0
  17. data/lib/multimap/lib/multiset.rb +185 -0
  18. data/lib/multimap/lib/nested_multimap.rb +158 -0
  19. data/lib/multimap/spec/enumerable_examples.rb +50 -0
  20. data/lib/multimap/spec/hash_examples.rb +264 -0
  21. data/lib/multimap/spec/multimap_spec.rb +45 -0
  22. data/lib/multimap/spec/multiset_spec.rb +184 -0
  23. data/lib/multimap/spec/nested_multimap_spec.rb +202 -0
  24. data/lib/multimap/spec/set_examples.rb +301 -0
  25. data/lib/multimap/spec/spec_helper.rb +67 -0
  26. data/lib/rubymail/address.rb +17 -0
  27. data/lib/rubymail/base.rb +118 -0
  28. data/lib/rubymail/bounce.rb +31 -0
  29. data/lib/rubymail/client.rb +87 -0
  30. data/lib/rubymail/complaint.rb +31 -0
  31. data/lib/rubymail/domain.rb +34 -0
  32. data/lib/rubymail/list.rb +37 -0
  33. data/lib/rubymail/log.rb +19 -0
  34. data/lib/rubymail/mailbox.rb +41 -0
  35. data/lib/rubymail/message.rb +16 -0
  36. data/lib/rubymail/route.rb +99 -0
  37. data/lib/rubymail/rubymail_error.rb +53 -0
  38. data/lib/rubymail/secure.rb +19 -0
  39. data/lib/rubymail/unsubscribe.rb +31 -0
  40. data/lib/rubymail/webhook.rb +43 -0
  41. data/lib/rubymail.rb +31 -0
  42. data/rubymail.gemspec +18 -0
  43. data/spec/address_spec.rb +27 -0
  44. data/spec/base_spec.rb +132 -0
  45. data/spec/bounce_spec.rb +66 -0
  46. data/spec/client_spec.rb +118 -0
  47. data/spec/complaint_spec.rb +103 -0
  48. data/spec/domain_spec.rb +80 -0
  49. data/spec/helpers/rubymail_helper.rb +9 -0
  50. data/spec/list/member_spec.rb +82 -0
  51. data/spec/list/message_spec.rb +40 -0
  52. data/spec/list_spec.rb +70 -0
  53. data/spec/log_spec.rb +27 -0
  54. data/spec/mailbox_spec.rb +63 -0
  55. data/spec/route_spec.rb +100 -0
  56. data/spec/secure_spec.rb +54 -0
  57. data/spec/spec_helper.rb +10 -0
  58. data/spec/unsubscribe_spec.rb +82 -0
  59. data/spec/webhook_spec.rb +115 -0
  60. metadata +159 -0
@@ -0,0 +1,185 @@
1
+ require 'set'
2
+
3
+ # Multiset implements a collection of unordered values and
4
+ # allows duplicates.
5
+ #
6
+ # == Example
7
+ #
8
+ # require 'multiset'
9
+ # s1 = Multiset.new [1, 2] # -> #<Multiset: {1, 2}>
10
+ # s1.add(2) # -> #<Multiset: {1, 2, 2}>
11
+ # s1.merge([2, 6]) # -> #<Multiset: {1, 2, 2, 2, 3}>
12
+ # s1.multiplicity(2) # -> 3
13
+ # s1.multiplicity(3) # -> 1
14
+ class Multiset < Set
15
+ def initialize(*args, &block) #:nodoc:
16
+ @hash = Hash.new(0)
17
+ super
18
+ end
19
+
20
+ # Returns the number of times an element belongs to the multiset.
21
+ def multiplicity(e)
22
+ @hash[e]
23
+ end
24
+
25
+ # Returns the total number of elements in a multiset, including
26
+ # repeated memberships
27
+ def cardinality
28
+ @hash.inject(0) { |s, (e, m)| s += m }
29
+ end
30
+ alias_method :size, :cardinality
31
+ alias_method :length, :cardinality
32
+
33
+ # Converts the set to an array. The order of elements is uncertain.
34
+ def to_a
35
+ inject([]) { |ary, (key, _)| ary << key }
36
+ end
37
+
38
+ # Returns true if the set is a superset of the given set.
39
+ def superset?(set)
40
+ set.is_a?(self.class) or raise ArgumentError, "value must be a set"
41
+ return false if cardinality < set.cardinality
42
+ set.all? { |o| set.multiplicity(o) <= multiplicity(o) }
43
+ end
44
+
45
+ # Returns true if the set is a proper superset of the given set.
46
+ def proper_superset?(set)
47
+ set.is_a?(self.class) or raise ArgumentError, "value must be a set"
48
+ return false if cardinality <= set.cardinality
49
+ set.all? { |o| set.multiplicity(o) <= multiplicity(o) }
50
+ end
51
+
52
+ # Returns true if the set is a subset of the given set.
53
+ def subset?(set)
54
+ set.is_a?(self.class) or raise ArgumentError, "value must be a set"
55
+ return false if set.cardinality < cardinality
56
+ all? { |o| multiplicity(o) <= set.multiplicity(o) }
57
+ end
58
+
59
+ # Returns true if the set is a proper subset of the given set.
60
+ def proper_subset?(set)
61
+ set.is_a?(self.class) or raise ArgumentError, "value must be a set"
62
+ return false if set.cardinality <= cardinality
63
+ all? { |o| multiplicity(o) <= set.multiplicity(o) }
64
+ end
65
+
66
+ # Calls the given block once for each element in the set, passing
67
+ # the element as parameter. Returns an enumerator if no block is
68
+ # given.
69
+ def each
70
+ @hash.each_pair do |key, multiplicity|
71
+ multiplicity.times do
72
+ yield(key)
73
+ end
74
+ end
75
+ self
76
+ end
77
+
78
+ # Adds the given object to the set and returns self. Use +merge+ to
79
+ # add many elements at once.
80
+ def add(o)
81
+ @hash[o] ||= 0
82
+ @hash[o] += 1
83
+ self
84
+ end
85
+ alias << add
86
+
87
+ undef :add?
88
+
89
+ # Deletes all the identical object from the set and returns self.
90
+ # If +n+ is given, it will remove that amount of identical objects
91
+ # from the set. Use +subtract+ to delete many different items at
92
+ # once.
93
+ def delete(o, n = nil)
94
+ if n
95
+ @hash[o] ||= 0
96
+ @hash[o] -= n if @hash[o] > 0
97
+ @hash.delete(o) if @hash[o] == 0
98
+ else
99
+ @hash.delete(o)
100
+ end
101
+ self
102
+ end
103
+
104
+ undef :delete?
105
+
106
+ # Deletes every element of the set for which block evaluates to
107
+ # true, and returns self.
108
+ def delete_if
109
+ each { |o| delete(o) if yield(o) }
110
+ self
111
+ end
112
+
113
+ # Merges the elements of the given enumerable object to the set and
114
+ # returns self.
115
+ def merge(enum)
116
+ enum.each { |o| add(o) }
117
+ self
118
+ end
119
+
120
+ # Deletes every element that appears in the given enumerable object
121
+ # and returns self.
122
+ def subtract(enum)
123
+ enum.each { |o| delete(o, 1) }
124
+ self
125
+ end
126
+
127
+ # Returns a new set containing elements common to the set and the
128
+ # given enumerable object.
129
+ def &(enum)
130
+ s = dup
131
+ n = self.class.new
132
+ enum.each { |o|
133
+ if s.include?(o)
134
+ s.delete(o, 1)
135
+ n.add(o)
136
+ end
137
+ }
138
+ n
139
+ end
140
+ alias intersection &
141
+
142
+ # Returns a new set containing elements exclusive between the set
143
+ # and the given enumerable object. (set ^ enum) is equivalent to
144
+ # ((set | enum) - (set & enum)).
145
+ def ^(enum)
146
+ n = self.class.new(enum)
147
+ each { |o| n.include?(o) ? n.delete(o, 1) : n.add(o) }
148
+ n
149
+ end
150
+
151
+ # Returns true if two sets are equal. Two multisets are equal if
152
+ # they have the same cardinalities and each element has the same
153
+ # multiplicity in both sets. The equality of each element inside
154
+ # the multiset is defined according to Object#eql?.
155
+ def eql?(set)
156
+ return true if equal?(set)
157
+ set = self.class.new(set) unless set.is_a?(self.class)
158
+ return false unless cardinality == set.cardinality
159
+ superset?(set) && subset?(set)
160
+ end
161
+ alias_method :==, :eql?
162
+
163
+ def marshal_dump #:nodoc:
164
+ @hash
165
+ end
166
+
167
+ def marshal_load(hash) #:nodoc:
168
+ @hash = hash
169
+ end
170
+
171
+ def to_yaml(opts = {}) #:nodoc:
172
+ YAML::quick_emit(self, opts) do |out|
173
+ out.map(taguri, to_yaml_style) do |map|
174
+ @hash.each do |k, v|
175
+ map.add(k, v)
176
+ end
177
+ end
178
+ end
179
+ end
180
+
181
+ def yaml_initialize(tag, val) #:nodoc:
182
+ @hash = val
183
+ self
184
+ end
185
+ end
@@ -0,0 +1,158 @@
1
+ require 'multimap/lib/multimap'
2
+
3
+ # NestedMultimap allows values to be assoicated with a nested
4
+ # set of keys.
5
+ class NestedMultimap < Multimap
6
+ # call-seq:
7
+ # multimap[*keys] = value => value
8
+ # multimap.store(*keys, value) => value
9
+ #
10
+ # Associates the value given by <i>value</i> with multiple key
11
+ # given by <i>keys</i>.
12
+ #
13
+ # map = NestedMultimap.new
14
+ # map["a"] = 100
15
+ # map["a", "b"] = 101
16
+ # map["a"] = 102
17
+ # map #=> {"a"=>{"b"=>[100, 101, 102], default => [100, 102]}}
18
+ def store(*args)
19
+ keys = args
20
+ value = args.pop
21
+
22
+ raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value
23
+
24
+ if keys.length > 1
25
+ update_container(keys.shift) do |container|
26
+ container = self.class.new(container) unless container.is_a?(self.class)
27
+ container[*keys] = value
28
+ container
29
+ end
30
+ elsif keys.length == 1
31
+ super(keys.first, value)
32
+ else
33
+ self << value
34
+ end
35
+ end
36
+ alias_method :[]=, :store
37
+
38
+ # call-seq:
39
+ # multimap << obj => multimap
40
+ #
41
+ # Pushes the given object on to the end of all the containers.
42
+ #
43
+ # map = NestedMultimap["a" => [100], "b" => [200, 300]]
44
+ # map << 300
45
+ # map["a"] #=> [100, 300]
46
+ # map["c"] #=> [300]
47
+ def <<(value)
48
+ @hash.each_value { |container| container << value }
49
+ self.default << value
50
+ self
51
+ end
52
+
53
+ # call-seq:
54
+ # multimap[*keys] => value
55
+ # multimap[key1, key2, key3] => value
56
+ #
57
+ # Retrieves the <i>value</i> object corresponding to the
58
+ # <i>*keys</i> object.
59
+ def [](*keys)
60
+ i, l, r, k = 0, keys.length, self, self.class
61
+ while r.is_a?(k)
62
+ r = i < l ? r._internal_hash[keys[i]] : r.default
63
+ i += 1
64
+ end
65
+ r
66
+ end
67
+
68
+ # call-seq:
69
+ # multimap.each_association { |key, container| block } => multimap
70
+ #
71
+ # Calls <i>block</i> once for each key/container in <i>map</i>, passing
72
+ # the key and container to the block as parameters.
73
+ #
74
+ # map = NestedMultimap.new
75
+ # map["a"] = 100
76
+ # map["a", "b"] = 101
77
+ # map["a"] = 102
78
+ # map["c"] = 200
79
+ # map.each_association { |key, container| puts "#{key} is #{container}" }
80
+ #
81
+ # <em>produces:</em>
82
+ #
83
+ # ["a", "b"] is [100, 101, 102]
84
+ # "c" is [200]
85
+ def each_association
86
+ super() do |key, container|
87
+ if container.respond_to?(:each_association)
88
+ container.each_association do |nested_key, value|
89
+ yield [key, nested_key].flatten, value
90
+ end
91
+ else
92
+ yield key, container
93
+ end
94
+ end
95
+ end
96
+
97
+ # call-seq:
98
+ # multimap.each_container_with_default { |container| block } => map
99
+ #
100
+ # Calls <i>block</i> for every container in <i>map</i> including
101
+ # the default, passing the container as a parameter.
102
+ #
103
+ # map = NestedMultimap.new
104
+ # map["a"] = 100
105
+ # map["a", "b"] = 101
106
+ # map["a"] = 102
107
+ # map.each_container_with_default { |container| puts container }
108
+ #
109
+ # <em>produces:</em>
110
+ #
111
+ # [100, 101, 102]
112
+ # [100, 102]
113
+ # []
114
+ def each_container_with_default(&block)
115
+ @hash.each_value do |container|
116
+ iterate_over_container(container, &block)
117
+ end
118
+ iterate_over_container(default, &block)
119
+ self
120
+ end
121
+
122
+ # call-seq:
123
+ # multimap.containers_with_default => array
124
+ #
125
+ # Returns a new array populated with all the containers from
126
+ # <i>map</i> including the default.
127
+ #
128
+ # map = NestedMultimap.new
129
+ # map["a"] = 100
130
+ # map["a", "b"] = 101
131
+ # map["a"] = 102
132
+ # map.containers_with_default #=> [[100, 101, 102], [100, 102], []]
133
+ def containers_with_default
134
+ containers = []
135
+ each_container_with_default { |container| containers << container }
136
+ containers
137
+ end
138
+
139
+ def inspect #:nodoc:
140
+ super.gsub(/\}$/, ", default => #{default.inspect}}")
141
+ end
142
+
143
+ private
144
+ def iterate_over_container(container)
145
+ if container.respond_to?(:each_container_with_default)
146
+ container.each_container_with_default do |value|
147
+ yield value
148
+ end
149
+ else
150
+ yield container
151
+ end
152
+ end
153
+ end
154
+
155
+ begin
156
+ require 'nested_multimap_ext'
157
+ rescue LoadError
158
+ end
@@ -0,0 +1,50 @@
1
+ shared_examples_for Enumerable, Multimap, "with inital values {'a' => [100], 'b' => [200, 300]}" do
2
+ it "should check all key/value pairs for condition" do
3
+ expect(@map.all? { |key, value| key =~ /\w/ }).to be_true
4
+ expect(@map.all? { |key, value| key =~ /\d/ }).to be_false
5
+ expect(@map.all? { |key, value| value > 0 }).to be_true
6
+ expect(@map.all? { |key, value| value > 200 }).to be_false
7
+ end
8
+
9
+ it "should check any key/value pairs for condition" do
10
+ expect(@map.any? { |key, value| key == "a" }).to be_true
11
+ expect(@map.any? { |key, value| key == "z" }).to be_false
12
+ expect(@map.any? { |key, value| value == 100 }).to be_true
13
+ expect(@map.any? { |key, value| value > 1000 }).to be_false
14
+ end
15
+
16
+ it "should collect key/value pairs" do
17
+ expect(@map.collect { |key, value| [key, value] }).to sorted_eql([["a", 100], ["b", 200], ["b", 300]])
18
+ expect(@map.map { |key, value| [key, value] }).to sorted_eql([["a", 100], ["b", 200], ["b", 300]])
19
+ end
20
+
21
+ it "should detect key/value pair" do
22
+ expect(@map.detect { |key, value| value > 200 }).to eql(["b", 300])
23
+ expect(@map.find { |key, value| value > 200 }).to eql(["b", 300])
24
+ end
25
+
26
+ it "should return entries" do
27
+ expect(@map.entries).to sorted_eql([["a", 100], ["b", 200], ["b", 300]])
28
+ expect(@map.to_a).to sorted_eql([["a", 100], ["b", 200], ["b", 300]])
29
+ end
30
+
31
+ it "should find all key/value pairs" do
32
+ expect(@map.find_all { |key, value| value >= 200 }).to eql([["b", 200], ["b", 300]])
33
+ expect(@map.select { |key, value| value >= 200 }).to eql(Multimap["b", [200, 300]])
34
+ end
35
+
36
+ it "should combine key/value pairs with inject" do
37
+ expect(@map.inject(0) { |sum, (key, value)| sum + value }).to eql(600)
38
+
39
+ @map.inject(0) { |memo, (key, value)|
40
+ memo > value ? memo : value
41
+ expect(}).to eql(300)
42
+ end
43
+
44
+ it "should check for key membership" do
45
+ expect(@map.member?("a")).to be_true
46
+ expect(@map.include?("a")).to be_true
47
+ expect(@map.member?("z")).to be_false
48
+ expect(@map.include?("z")).to be_false
49
+ end
50
+ end
@@ -0,0 +1,264 @@
1
+ shared_examples_for Hash, Multimap, "with inital values {'a' => [100], 'b' => [200, 300]}" do
2
+ before do
3
+ @container ||= Array
4
+ end
5
+
6
+ it "should be equal to another Multimap if they contain the same keys and values" do
7
+ map2 = Multimap.new(@container.new)
8
+ map2["a"] = 100
9
+ map2["b"] = 200
10
+ map2["b"] = 300
11
+ expect(@map).to eql(map2)
12
+ end
13
+
14
+ it "should not be equal to another Multimap if they contain different values" do
15
+ expect(@map).to_not == Multimap["a" => [100], "b" => [200]]
16
+ end
17
+
18
+ it "should retrieve container of values for key" do
19
+ expect(@map["a"]).to eql(@container.new([100]))
20
+ expect(@map["b"]).to eql(@container.new([200, 300]))
21
+ expect(@map["z"]).to eql(@container.new)
22
+ end
23
+
24
+ it "should append values to container at key" do
25
+ @map["a"] = 400
26
+ @map.store("b", 500)
27
+ expect(@map["a"]).to eql(@container.new([100, 400]))
28
+ expect(@map["b"]).to eql(@container.new([200, 300, 500]))
29
+ end
30
+
31
+ it "should clear all key/values" do
32
+ @map.clear
33
+ expect(@map).to be_empty
34
+ end
35
+
36
+ it "should be the class of the container" do
37
+ expect(@map.default.class).to eql(@container)
38
+ end
39
+
40
+ it "should delete all values at key" do
41
+ @map.delete("a")
42
+ expect(@map["a"]).to eql(@container.new)
43
+ end
44
+
45
+ it "should delete single value at key" do
46
+ @map.delete("b", 200)
47
+ expect(@map["b"]).to eql(@container.new([300]))
48
+ end
49
+
50
+ it "should delete if key condition is matched" do
51
+ expect(@map.delete_if { |key, value| key >= "b" }).to eql(@map)
52
+ expect(@map["a"]).to eql(@container.new([100]))
53
+ expect(@map["b"]).to eql(@container.new)
54
+
55
+ expect(@map.delete_if { |key, value| key > "z" }).to eql(@map)
56
+ end
57
+
58
+ it "should delete if value condition is matched" do
59
+ expect(@map.delete_if { |key, value| value >= 300 }).to eql(@map)
60
+ expect(@map["a"]).to eql(@container.new([100]))
61
+ expect(@map["b"]).to eql(@container.new([200]))
62
+ end
63
+
64
+ it "should duplicate the containers" do
65
+ map2 = @map.dup
66
+ expect(map2).to_not equal(@map)
67
+ expect(map2).to eql(@map)
68
+ expect(map2["a"]).to_not equal(@map["a"])
69
+ expect(map2["b"]).to_not equal(@map["b"])
70
+ expect(map2.default).to_not equal(@map.default)
71
+ expect(map2.default).to eql(@map.default)
72
+ end
73
+
74
+ it "should freeze containers" do
75
+ @map.freeze
76
+ expect(@map).to be_frozen
77
+ expect(@map["a"]).to be_frozen
78
+ expect(@map["b"]).to be_frozen
79
+ end
80
+
81
+ it "should iterate over each key/value pair and yield an array" do
82
+ a = []
83
+ @map.each { |pair| a << pair }
84
+ expect(a).to sorted_eql([["a", 100], ["b", 200], ["b", 300]])
85
+ end
86
+
87
+ it "should iterate over each container" do
88
+ a = []
89
+ @map.each_container { |container| a << container }
90
+ expect(a).to sorted_eql([@container.new([100]), @container.new([200, 300])])
91
+ end
92
+
93
+ it "should iterate over each key/container" do
94
+ a = []
95
+ @map.each_association { |key, container| a << [key, container] }
96
+ expect(a).to sorted_eql([["a", @container.new([100])], ["b", @container.new([200, 300])]])
97
+ end
98
+
99
+ it "should iterate over each key" do
100
+ a = []
101
+ @map.each_key { |key| a << key }
102
+ expect(a).to sorted_eql(["a", "b", "b"])
103
+ end
104
+
105
+ it "should iterate over each key/value pair and yield the pair" do
106
+ h = {}
107
+ @map.each_pair { |key, value| (h[key] ||= []) << value }
108
+ expect(h).to eql({ "a" => [100], "b" => [200, 300] })
109
+ end
110
+
111
+ it "should iterate over each value" do
112
+ a = []
113
+ @map.each_value { |value| a << value }
114
+ expect(a).to sorted_eql([100, 200, 300])
115
+ end
116
+
117
+ it "should be empty if there are no key/value pairs" do
118
+ @map.clear
119
+ expect(@map).to be_empty
120
+ end
121
+
122
+ it "should not be empty if there are any key/value pairs" do
123
+ expect(@map).to_not be_empty
124
+ end
125
+
126
+ it "should fetch container of values for key" do
127
+ expect(@map.fetch("a")).to eql(@container.new([100]))
128
+ expect(@map.fetch("b")).to eql(@container.new([200, 300]))
129
+ expect(lambda { @map.fetch("z") }).to raise_error(IndexError)
130
+ end
131
+
132
+ it "should check if key is present" do
133
+ expect(@map.has_key?("a")).to be_true
134
+ expect(@map.key?("a")).to be_true
135
+ expect(@map.has_key?("z")).to be_false
136
+ expect(@map.key?("z")).to be_false
137
+ end
138
+
139
+ it "should check containers when looking up by value" do
140
+ expect(@map.has_value?(100)).to be_true
141
+ expect(@map.value?(100)).to be_true
142
+ expect(@map.has_value?(999)).to be_false
143
+ expect(@map.value?(999)).to be_false
144
+ end
145
+
146
+ it "it should return the index for value" do
147
+ if @map.respond_to?(:index)
148
+ expect(@map.index(200)).to eql(@container.new(["b"]))
149
+ expect(@map.index(999)).to eql(@container.new)
150
+ end
151
+ end
152
+
153
+ it "should replace the contents of hash" do
154
+ @map.replace({ "c" => @container.new([300]), "d" => @container.new([400]) })
155
+ expect(@map["a"]).to eql(@container.new)
156
+ expect(@map["c"]).to eql(@container.new([300]))
157
+ end
158
+
159
+ it "should return an inverted Multimap" do
160
+ if @map.respond_to?(:invert)
161
+ map2 = Multimap.new(@container.new)
162
+ map2[100] = "a"
163
+ map2[200] = "b"
164
+ map2[300] = "b"
165
+ expect(@map.invert).to eql(map2)
166
+ end
167
+ end
168
+
169
+ it "should return array of keys" do
170
+ expect(@map.keys).to eql(["a", "b", "b"])
171
+ end
172
+
173
+ it "should return the number of key/value pairs" do
174
+ expect(@map.length).to eql(3)
175
+ expect(@map.size).to eql(3)
176
+ end
177
+
178
+ it "should duplicate map and with merged values" do
179
+ map = @map.merge("b" => 254, "c" => @container.new([300]))
180
+ expect(map["a"]).to eql(@container.new([100]))
181
+ expect(map["b"]).to eql(@container.new([200, 300, 254]))
182
+ expect(map["c"]).to eql(@container.new([300]))
183
+
184
+ expect(@map["a"]).to eql(@container.new([100]))
185
+ expect(@map["b"]).to eql(@container.new([200, 300]))
186
+ expect(@map["c"]).to eql(@container.new)
187
+ end
188
+
189
+ it "should update map" do
190
+ @map.update("b" => 254, "c" => @container.new([300]))
191
+ expect(@map["a"]).to eql(@container.new([100]))
192
+ expect(@map["b"]).to eql(@container.new([200, 300, 254]))
193
+ expect(@map["c"]).to eql(@container.new([300]))
194
+
195
+ klass = @map.class
196
+ @map.update(klass[@container.new, {"a" => @container.new([400, 500]), "c" => 600}])
197
+ expect(@map["a"]).to eql(@container.new([100, 400, 500]))
198
+ expect(@map["b"]).to eql(@container.new([200, 300, 254]))
199
+ expect(@map["c"]).to eql(@container.new([300, 600]))
200
+ end
201
+
202
+ it "should reject key pairs on copy of the map" do
203
+ map = @map.reject { |key, value| key >= "b" }
204
+ expect(map["b"]).to eql(@container.new)
205
+ expect(@map["b"]).to eql(@container.new([200, 300]))
206
+ end
207
+
208
+ it "should reject value pairs on copy of the map" do
209
+ map = @map.reject { |key, value| value >= 300 }
210
+ expect(map["b"]).to eql(@container.new([200]))
211
+ expect(@map["b"]).to eql(@container.new([200, 300]))
212
+ end
213
+
214
+ it "should reject key pairs" do
215
+ expect(@map.reject! { |key, value| key >= "b" }).to eql(@map)
216
+ expect(@map["a"]).to eql(@container.new([100]))
217
+ expect(@map["b"]).to eql(@container.new)
218
+
219
+ expect(@map.reject! { |key, value| key >= "z" }).to eql(nil)
220
+ end
221
+
222
+ it "should reject value pairs" do
223
+ expect(@map.reject! { |key, value| value >= 300 }).to eql(@map)
224
+ expect(@map["a"]).to eql(@container.new([100]))
225
+ expect(@map["b"]).to eql(@container.new([200]))
226
+
227
+ expect(@map.reject! { |key, value| key >= "z" }).to eql(nil)
228
+ end
229
+
230
+ it "should select key/value pairs" do
231
+ expect(@map.select { |k, v| k > "a" }).to eql(Multimap["b", [200, 300]])
232
+ expect(@map.select { |k, v| v < 200 }).to eql(Multimap["a", 100])
233
+ end
234
+
235
+ it "should convert to hash" do
236
+ expect(@map.to_hash["a"]).to eql(@container.new([100]))
237
+ expect(@map.to_hash["b"]).to eql(@container.new([200, 300]))
238
+ expect(@map.to_hash).to_not equal(@map)
239
+ end
240
+
241
+ it "should return all containers" do
242
+ expect(@map.containers).to sorted_eql([@container.new([100]), @container.new([200, 300])])
243
+ end
244
+
245
+ it "should return all values" do
246
+ expect(@map.values).to sorted_eql([100, 200, 300])
247
+ end
248
+
249
+ it "should return return values at keys" do
250
+ expect(@map.values_at("a", "b")).to eql([@container.new([100]), @container.new([200, 300])])
251
+ end
252
+
253
+ it "should marshal hash" do
254
+ data = Marshal.dump(@map)
255
+ expect(Marshal.load(data)).to eql(@map)
256
+ end
257
+
258
+ it "should dump yaml" do
259
+ require 'yaml'
260
+
261
+ data = YAML.dump(@map)
262
+ expect(YAML.load(data)).to eql(@map)
263
+ end
264
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Multimap, "with inital values {'a' => [100], 'b' => [200, 300]}" do
4
+ it_should_behave_like "Enumerable Multimap with inital values {'a' => [100], 'b' => [200, 300]}"
5
+ it_should_behave_like "Hash Multimap with inital values {'a' => [100], 'b' => [200, 300]}"
6
+
7
+ before do
8
+ @map = Multimap["a" => 100, "b" => [200, 300]]
9
+ end
10
+ end
11
+
12
+ describe Multimap, "with inital values {'a' => [100], 'b' => [200, 300]}" do
13
+ it_should_behave_like "Enumerable Multimap with inital values {'a' => [100], 'b' => [200, 300]}"
14
+ it_should_behave_like "Hash Multimap with inital values {'a' => [100], 'b' => [200, 300]}"
15
+
16
+ before do
17
+ @map = Multimap["a", 100, "b", [200, 300]]
18
+ end
19
+ end
20
+
21
+ describe Multimap, "with", Set do
22
+ it_should_behave_like "Enumerable Multimap with inital values {'a' => [100], 'b' => [200, 300]}"
23
+ it_should_behave_like "Hash Multimap with inital values {'a' => [100], 'b' => [200, 300]}"
24
+
25
+ before do
26
+ @container = Set
27
+ @map = Multimap.new(@container.new)
28
+ @map["a"] = 100
29
+ @map["b"] = 200
30
+ @map["b"] = 300
31
+ end
32
+ end
33
+
34
+ describe Multimap, "with", MiniArray do
35
+ it_should_behave_like "Enumerable Multimap with inital values {'a' => [100], 'b' => [200, 300]}"
36
+ it_should_behave_like "Hash Multimap with inital values {'a' => [100], 'b' => [200, 300]}"
37
+
38
+ before do
39
+ @container = MiniArray
40
+ @map = Multimap.new(@container.new)
41
+ @map["a"] = 100
42
+ @map["b"] = 200
43
+ @map["b"] = 300
44
+ end
45
+ end