rack-mount 0.6.14 → 0.7.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.
- data/lib/rack/mount.rb +0 -1
- data/lib/rack/mount/analysis/splitting.rb +43 -43
- data/lib/rack/mount/multimap.rb +95 -11
- data/lib/rack/mount/route_set.rb +7 -37
- metadata +4 -8
- data/lib/rack/mount/analysis/frequency.rb +0 -60
- data/lib/rack/mount/vendor/multimap/multimap.rb +0 -569
- data/lib/rack/mount/vendor/multimap/multiset.rb +0 -185
- data/lib/rack/mount/vendor/multimap/nested_multimap.rb +0 -158
data/lib/rack/mount.rb
CHANGED
@@ -24,7 +24,6 @@ module Rack #:nodoc:
|
|
24
24
|
autoload :Version, 'rack/mount/version'
|
25
25
|
|
26
26
|
module Analysis #:nodoc:
|
27
|
-
autoload :Frequency, 'rack/mount/analysis/frequency'
|
28
27
|
autoload :Histogram, 'rack/mount/analysis/histogram'
|
29
28
|
autoload :Splitting, 'rack/mount/analysis/splitting'
|
30
29
|
end
|
@@ -2,8 +2,8 @@ require 'rack/mount/utils'
|
|
2
2
|
|
3
3
|
module Rack::Mount
|
4
4
|
module Analysis
|
5
|
-
class Splitting
|
6
|
-
NULL = "\0"
|
5
|
+
class Splitting
|
6
|
+
NULL = "\0"
|
7
7
|
|
8
8
|
class Key < Struct.new(:method, :index, :separators)
|
9
9
|
def self.split(value, separator_pattern)
|
@@ -26,26 +26,44 @@ module Rack::Mount
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
+
def initialize(*keys)
|
30
|
+
clear
|
31
|
+
keys.each { |key| self << key }
|
32
|
+
end
|
33
|
+
|
29
34
|
def clear
|
30
|
-
@
|
31
|
-
|
35
|
+
@raw_keys = []
|
36
|
+
@key_frequency = Analysis::Histogram.new
|
37
|
+
self
|
32
38
|
end
|
33
39
|
|
34
40
|
def <<(key)
|
35
|
-
|
36
|
-
|
37
|
-
|
41
|
+
raise ArgumentError unless key.is_a?(Hash)
|
42
|
+
@raw_keys << key
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
def possible_keys
|
47
|
+
@possible_keys ||= begin
|
48
|
+
@raw_keys.map do |key|
|
49
|
+
key.inject({}) { |requirements, (method, requirement)|
|
50
|
+
process_key(requirements, method, requirement)
|
51
|
+
requirements
|
52
|
+
}
|
53
|
+
end
|
38
54
|
end
|
39
55
|
end
|
40
56
|
|
41
|
-
def
|
42
|
-
@
|
43
|
-
|
57
|
+
def report
|
58
|
+
@report ||= begin
|
59
|
+
possible_keys.each { |keys| keys.each_pair { |key, _| @key_frequency << key } }
|
60
|
+
return [] if @key_frequency.count <= 1
|
61
|
+
@key_frequency.keys_in_upper_quartile
|
62
|
+
end
|
44
63
|
end
|
45
|
-
attr_writer :separators
|
46
64
|
|
47
|
-
def
|
48
|
-
@
|
65
|
+
def expire!
|
66
|
+
@possible_keys = @report = nil
|
49
67
|
end
|
50
68
|
|
51
69
|
def process_key(requirements, method, requirement)
|
@@ -55,40 +73,22 @@ module Rack::Mount
|
|
55
73
|
requirements[Key.new(method, index, Regexp.union(*separators))] = value
|
56
74
|
end
|
57
75
|
else
|
58
|
-
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
private
|
63
|
-
def analyze_capture_boundaries(regexp, boundaries) #:nodoc:
|
64
|
-
return boundaries unless regexp.is_a?(Regexp)
|
65
|
-
|
66
|
-
parts = Utils.parse_regexp(regexp)
|
67
|
-
parts.each_with_index do |part, index|
|
68
|
-
if part.is_a?(Regin::Group)
|
69
|
-
if index > 0
|
70
|
-
previous = parts[index-1]
|
71
|
-
if previous.is_a?(Regin::Character) && previous.literal?
|
72
|
-
boundaries << previous.to_s
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
if inside = part.expression[0]
|
77
|
-
if inside.is_a?(Regin::Character) && inside.literal?
|
78
|
-
boundaries << inside.to_s
|
79
|
-
end
|
80
|
-
end
|
76
|
+
if requirement.is_a?(Regexp)
|
77
|
+
expression = Utils.parse_regexp(requirement)
|
81
78
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
boundaries << following.to_s
|
86
|
-
end
|
87
|
-
end
|
79
|
+
if expression.is_a?(Regin::Expression) && expression.anchored_to_line?
|
80
|
+
expression = Regin::Expression.new(expression.reject { |e| e.is_a?(Regin::Anchor) })
|
81
|
+
return requirements[method] = expression.to_s if expression.literal?
|
88
82
|
end
|
89
83
|
end
|
90
84
|
|
91
|
-
|
85
|
+
requirements[method] = requirement
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
def separators(key)
|
91
|
+
key == :path_info ? ["/", "."] : []
|
92
92
|
end
|
93
93
|
|
94
94
|
def generate_split_keys(regexp, separators) #:nodoc:
|
data/lib/rack/mount/multimap.rb
CHANGED
@@ -1,12 +1,16 @@
|
|
1
|
-
begin
|
2
|
-
require 'nested_multimap'
|
3
|
-
rescue LoadError
|
4
|
-
$: << File.expand_path(File.join(File.dirname(__FILE__), 'vendor/multimap'))
|
5
|
-
require 'nested_multimap'
|
6
|
-
end
|
7
|
-
|
8
1
|
module Rack::Mount
|
9
|
-
class Multimap
|
2
|
+
class Multimap #:nodoc:
|
3
|
+
def initialize(default = [])
|
4
|
+
@hash = Hash.new(default)
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize_copy(original)
|
8
|
+
@hash = Hash.new(original.default.dup)
|
9
|
+
original.hash.each_pair do |key, container|
|
10
|
+
@hash[key] = container.dup
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
10
14
|
def store(*args)
|
11
15
|
keys = args.dup
|
12
16
|
value = keys.pop
|
@@ -26,7 +30,7 @@ module Rack::Mount
|
|
26
30
|
@hash.each_pair { |k, _|
|
27
31
|
if k =~ key
|
28
32
|
args[0] = k
|
29
|
-
|
33
|
+
_store(*args)
|
30
34
|
end
|
31
35
|
}
|
32
36
|
|
@@ -34,12 +38,19 @@ module Rack::Mount
|
|
34
38
|
default[*keys.dup] = value
|
35
39
|
end
|
36
40
|
else
|
37
|
-
|
41
|
+
_store(*args)
|
38
42
|
end
|
39
43
|
end
|
40
44
|
alias_method :[]=, :store
|
41
45
|
|
42
|
-
|
46
|
+
def [](*keys)
|
47
|
+
i, l, r, k = 0, keys.length, self, self.class
|
48
|
+
while r.is_a?(k)
|
49
|
+
r = i < l ? r.hash[keys[i]] : r.default
|
50
|
+
i += 1
|
51
|
+
end
|
52
|
+
r
|
53
|
+
end
|
43
54
|
|
44
55
|
def height
|
45
56
|
containers_with_default.max { |a, b| a.length <=> b.length }.length
|
@@ -49,5 +60,78 @@ module Rack::Mount
|
|
49
60
|
lengths = containers_with_default.map { |e| e.length }
|
50
61
|
lengths.inject(0) { |sum, len| sum += len }.to_f / lengths.size
|
51
62
|
end
|
63
|
+
|
64
|
+
def containers_with_default
|
65
|
+
containers = []
|
66
|
+
each_container_with_default { |container| containers << container }
|
67
|
+
containers
|
68
|
+
end
|
69
|
+
|
70
|
+
protected
|
71
|
+
def _store(*args)
|
72
|
+
keys = args
|
73
|
+
value = args.pop
|
74
|
+
|
75
|
+
raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value
|
76
|
+
|
77
|
+
if keys.length > 1
|
78
|
+
update_container(keys.shift) do |container|
|
79
|
+
container = self.class.new(container) unless container.is_a?(self.class)
|
80
|
+
container[*keys] = value
|
81
|
+
container
|
82
|
+
end
|
83
|
+
elsif keys.length == 1
|
84
|
+
update_container(keys.first) do |container|
|
85
|
+
container << value
|
86
|
+
container
|
87
|
+
end
|
88
|
+
else
|
89
|
+
self << value
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def <<(value)
|
94
|
+
@hash.each_value { |container| container << value }
|
95
|
+
self.default << value
|
96
|
+
self
|
97
|
+
end
|
98
|
+
|
99
|
+
def each_container_with_default(&block)
|
100
|
+
@hash.each_value do |container|
|
101
|
+
iterate_over_container(container, &block)
|
102
|
+
end
|
103
|
+
iterate_over_container(default, &block)
|
104
|
+
self
|
105
|
+
end
|
106
|
+
|
107
|
+
def default
|
108
|
+
@hash.default
|
109
|
+
end
|
110
|
+
|
111
|
+
def default=(value)
|
112
|
+
@hash.default = value
|
113
|
+
end
|
114
|
+
|
115
|
+
def hash
|
116
|
+
@hash
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
def update_container(key)
|
121
|
+
container = @hash[key]
|
122
|
+
container = container.dup if container.equal?(default)
|
123
|
+
container = yield(container)
|
124
|
+
@hash[key] = container
|
125
|
+
end
|
126
|
+
|
127
|
+
def iterate_over_container(container)
|
128
|
+
if container.respond_to?(:each_container_with_default)
|
129
|
+
container.each_container_with_default do |value|
|
130
|
+
yield value
|
131
|
+
end
|
132
|
+
else
|
133
|
+
yield container
|
134
|
+
end
|
135
|
+
end
|
52
136
|
end
|
53
137
|
end
|
data/lib/rack/mount/route_set.rb
CHANGED
@@ -25,7 +25,9 @@ module Rack::Mount
|
|
25
25
|
@named_routes = {}
|
26
26
|
|
27
27
|
@recognition_key_analyzer = Analysis::Splitting.new
|
28
|
-
|
28
|
+
|
29
|
+
@generation_keys = [:controller, :action]
|
30
|
+
@generation_route_keys = []
|
29
31
|
|
30
32
|
@request_class = options.delete(:request_class) || Rack::Request
|
31
33
|
@valid_conditions = @request_class.public_instance_methods.map! { |m| m.to_sym }
|
@@ -69,7 +71,7 @@ module Rack::Mount
|
|
69
71
|
@recognition_key_analyzer << route.conditions
|
70
72
|
|
71
73
|
@named_routes[route.name] = route if route.name
|
72
|
-
@
|
74
|
+
@generation_route_keys << route.generation_keys
|
73
75
|
|
74
76
|
expire!
|
75
77
|
route
|
@@ -249,7 +251,6 @@ module Rack::Mount
|
|
249
251
|
|
250
252
|
@recognition_keys = build_recognition_keys
|
251
253
|
@recognition_graph = build_recognition_graph
|
252
|
-
@generation_keys = build_generation_keys
|
253
254
|
@generation_graph = build_generation_graph
|
254
255
|
|
255
256
|
self
|
@@ -263,7 +264,7 @@ module Rack::Mount
|
|
263
264
|
rehash
|
264
265
|
|
265
266
|
@recognition_key_analyzer = nil
|
266
|
-
@
|
267
|
+
@generation_route_keys = nil
|
267
268
|
@valid_conditions = nil
|
268
269
|
|
269
270
|
@routes.each { |route| route.freeze }
|
@@ -273,26 +274,6 @@ module Rack::Mount
|
|
273
274
|
super
|
274
275
|
end
|
275
276
|
|
276
|
-
def marshal_dump #:nodoc:
|
277
|
-
hash = {}
|
278
|
-
|
279
|
-
instance_variables_to_serialize.each do |ivar|
|
280
|
-
hash[ivar] = instance_variable_get(ivar)
|
281
|
-
end
|
282
|
-
|
283
|
-
if graph = hash[:@recognition_graph]
|
284
|
-
hash[:@recognition_graph] = graph.dup
|
285
|
-
end
|
286
|
-
|
287
|
-
hash
|
288
|
-
end
|
289
|
-
|
290
|
-
def marshal_load(hash) #:nodoc:
|
291
|
-
hash.each do |ivar, value|
|
292
|
-
instance_variable_set(ivar, value)
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
277
|
protected
|
297
278
|
def recognition_stats
|
298
279
|
{ :keys => @recognition_keys,
|
@@ -307,12 +288,7 @@ module Rack::Mount
|
|
307
288
|
@recognition_keys = @recognition_graph = nil
|
308
289
|
@recognition_key_analyzer.expire!
|
309
290
|
|
310
|
-
@
|
311
|
-
@generation_key_analyzer.expire!
|
312
|
-
end
|
313
|
-
|
314
|
-
def instance_variables_to_serialize
|
315
|
-
instance_variables.map { |ivar| ivar.to_sym } - [:@stubbed_request_class, :@optimized_recognize_defined]
|
291
|
+
@generation_graph = nil
|
316
292
|
end
|
317
293
|
|
318
294
|
# An internal helper method for constructing a nested set from
|
@@ -350,7 +326,7 @@ module Rack::Mount
|
|
350
326
|
build_nested_route_set(@generation_keys) { |k, i|
|
351
327
|
throw :skip unless @routes[i].significant_params?
|
352
328
|
|
353
|
-
if k = @
|
329
|
+
if k = @generation_route_keys[i][k]
|
354
330
|
k.to_s
|
355
331
|
else
|
356
332
|
nil
|
@@ -358,12 +334,6 @@ module Rack::Mount
|
|
358
334
|
}
|
359
335
|
end
|
360
336
|
|
361
|
-
def build_generation_keys
|
362
|
-
keys = @generation_key_analyzer.report
|
363
|
-
Utils.debug "generation keys - #{keys.inspect}"
|
364
|
-
keys
|
365
|
-
end
|
366
|
-
|
367
337
|
def extract_params!(*args)
|
368
338
|
case args.length
|
369
339
|
when 4
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-mount
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 1
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
8
|
+
- 7
|
9
|
+
- 1
|
10
|
+
version: 0.7.1
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Joshua Peek
|
@@ -72,7 +72,6 @@ extra_rdoc_files: []
|
|
72
72
|
|
73
73
|
files:
|
74
74
|
- lib/rack/mount.rb
|
75
|
-
- lib/rack/mount/analysis/frequency.rb
|
76
75
|
- lib/rack/mount/analysis/histogram.rb
|
77
76
|
- lib/rack/mount/analysis/splitting.rb
|
78
77
|
- lib/rack/mount/code_generation.rb
|
@@ -88,9 +87,6 @@ files:
|
|
88
87
|
- lib/rack/mount/strexp/tokenizer.rb
|
89
88
|
- lib/rack/mount/strexp/tokenizer.rex
|
90
89
|
- lib/rack/mount/utils.rb
|
91
|
-
- lib/rack/mount/vendor/multimap/multimap.rb
|
92
|
-
- lib/rack/mount/vendor/multimap/multiset.rb
|
93
|
-
- lib/rack/mount/vendor/multimap/nested_multimap.rb
|
94
90
|
- lib/rack/mount/vendor/regin/regin.rb
|
95
91
|
- lib/rack/mount/vendor/regin/regin/alternation.rb
|
96
92
|
- lib/rack/mount/vendor/regin/regin/anchor.rb
|
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'rack/mount/utils'
|
2
|
-
|
3
|
-
module Rack::Mount
|
4
|
-
module Analysis
|
5
|
-
class Frequency #:nodoc:
|
6
|
-
def initialize(*keys)
|
7
|
-
clear
|
8
|
-
keys.each { |key| self << key }
|
9
|
-
end
|
10
|
-
|
11
|
-
def clear
|
12
|
-
@raw_keys = []
|
13
|
-
@key_frequency = Analysis::Histogram.new
|
14
|
-
self
|
15
|
-
end
|
16
|
-
|
17
|
-
def <<(key)
|
18
|
-
raise ArgumentError unless key.is_a?(Hash)
|
19
|
-
@raw_keys << key
|
20
|
-
nil
|
21
|
-
end
|
22
|
-
|
23
|
-
def possible_keys
|
24
|
-
@possible_keys ||= begin
|
25
|
-
@raw_keys.map do |key|
|
26
|
-
key.inject({}) { |requirements, (method, requirement)|
|
27
|
-
process_key(requirements, method, requirement)
|
28
|
-
requirements
|
29
|
-
}
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def process_key(requirements, method, requirement)
|
35
|
-
if requirement.is_a?(Regexp)
|
36
|
-
expression = Utils.parse_regexp(requirement)
|
37
|
-
|
38
|
-
if expression.is_a?(Regin::Expression) && expression.anchored_to_line?
|
39
|
-
expression = Regin::Expression.new(expression.reject { |e| e.is_a?(Regin::Anchor) })
|
40
|
-
return requirements[method] = expression.to_s if expression.literal?
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
requirements[method] = requirement
|
45
|
-
end
|
46
|
-
|
47
|
-
def report
|
48
|
-
@report ||= begin
|
49
|
-
possible_keys.each { |keys| keys.each_pair { |key, _| @key_frequency << key } }
|
50
|
-
return [] if @key_frequency.count <= 1
|
51
|
-
@key_frequency.keys_in_upper_quartile
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
def expire!
|
56
|
-
@possible_keys = @report = nil
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
@@ -1,569 +0,0 @@
|
|
1
|
-
require 'forwardable'
|
2
|
-
require 'multiset'
|
3
|
-
|
4
|
-
# Multimap is a generalization of a map or associative array
|
5
|
-
# abstract data type in which more than one value may be associated
|
6
|
-
# with and returned for a given key.
|
7
|
-
#
|
8
|
-
# == Example
|
9
|
-
#
|
10
|
-
# require 'multimap'
|
11
|
-
# map = Multimap.new
|
12
|
-
# map["a"] = 100
|
13
|
-
# map["b"] = 200
|
14
|
-
# map["a"] = 300
|
15
|
-
# map["a"] # -> [100, 300]
|
16
|
-
# map["b"] # -> [200]
|
17
|
-
# map.keys # -> #<Multiset: {a, a, b}>
|
18
|
-
class Multimap
|
19
|
-
extend Forwardable
|
20
|
-
|
21
|
-
include Enumerable
|
22
|
-
|
23
|
-
# call-seq:
|
24
|
-
# Multimap[ [key =>|, value]* ] => multimap
|
25
|
-
#
|
26
|
-
# Creates a new multimap populated with the given objects.
|
27
|
-
#
|
28
|
-
# Multimap["a", 100, "b", 200] #=> {"a"=>[100], "b"=>[200]}
|
29
|
-
# Multimap["a" => 100, "b" => 200] #=> {"a"=>[100], "b"=>[200]}
|
30
|
-
def self.[](*args)
|
31
|
-
default = []
|
32
|
-
|
33
|
-
if args.size == 2 && args.last.is_a?(Hash)
|
34
|
-
default = args.shift
|
35
|
-
elsif !args.first.is_a?(Hash) && args.size % 2 == 1
|
36
|
-
default = args.shift
|
37
|
-
end
|
38
|
-
|
39
|
-
if args.size == 1 && args.first.is_a?(Hash)
|
40
|
-
args[0] = args.first.inject({}) { |hash, (key, value)|
|
41
|
-
unless value.is_a?(default.class)
|
42
|
-
value = (default.dup << value)
|
43
|
-
end
|
44
|
-
hash[key] = value
|
45
|
-
hash
|
46
|
-
}
|
47
|
-
else
|
48
|
-
index = 0
|
49
|
-
args.map! { |value|
|
50
|
-
unless index % 2 == 0 || value.is_a?(default.class)
|
51
|
-
value = (default.dup << value)
|
52
|
-
end
|
53
|
-
index += 1
|
54
|
-
value
|
55
|
-
}
|
56
|
-
end
|
57
|
-
|
58
|
-
map = new
|
59
|
-
map.instance_variable_set(:@hash, Hash[*args])
|
60
|
-
map.default = default
|
61
|
-
map
|
62
|
-
end
|
63
|
-
|
64
|
-
# call-seq:
|
65
|
-
# Multimap.new => multimap
|
66
|
-
# Multimap.new(default) => multimap
|
67
|
-
#
|
68
|
-
# Returns a new, empty multimap.
|
69
|
-
#
|
70
|
-
# map = Multimap.new(Set.new)
|
71
|
-
# h["a"] = 100
|
72
|
-
# h["b"] = 200
|
73
|
-
# h["a"] #=> [100].to_set
|
74
|
-
# h["c"] #=> [].to_set
|
75
|
-
def initialize(default = [])
|
76
|
-
@hash = Hash.new(default)
|
77
|
-
end
|
78
|
-
|
79
|
-
def initialize_copy(original) #:nodoc:
|
80
|
-
@hash = Hash.new(original.default.dup)
|
81
|
-
original._internal_hash.each_pair do |key, container|
|
82
|
-
@hash[key] = container.dup
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def_delegators :@hash, :clear, :default, :default=, :empty?,
|
87
|
-
:fetch, :has_key?, :key?
|
88
|
-
|
89
|
-
# Retrieves the <i>value</i> object corresponding to the
|
90
|
-
# <i>*keys</i> object.
|
91
|
-
def [](key)
|
92
|
-
@hash[key]
|
93
|
-
end
|
94
|
-
|
95
|
-
# call-seq:
|
96
|
-
# map[key] = value => value
|
97
|
-
# map.store(key, value) => value
|
98
|
-
#
|
99
|
-
# Associates the value given by <i>value</i> with the key
|
100
|
-
# given by <i>key</i>. Unlike a regular hash, multiple can be
|
101
|
-
# assoicated with the same value.
|
102
|
-
#
|
103
|
-
# map = Multimap["a" => 100, "b" => 200]
|
104
|
-
# map["a"] = 9
|
105
|
-
# map["c"] = 4
|
106
|
-
# map #=> {"a" => [100, 9], "b" => [200], "c" => [4]}
|
107
|
-
def store(key, value)
|
108
|
-
update_container(key) do |container|
|
109
|
-
container << value
|
110
|
-
container
|
111
|
-
end
|
112
|
-
end
|
113
|
-
alias_method :[]=, :store
|
114
|
-
|
115
|
-
# call-seq:
|
116
|
-
# map.delete(key, value) => value
|
117
|
-
# map.delete(key) => value
|
118
|
-
#
|
119
|
-
# Deletes and returns a key-value pair from <i>map</i>. If only
|
120
|
-
# <i>key</i> is given, all the values matching that key will be
|
121
|
-
# deleted.
|
122
|
-
#
|
123
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
124
|
-
# map.delete("b", 300) #=> 300
|
125
|
-
# map.delete("a") #=> [100]
|
126
|
-
def delete(key, value = nil)
|
127
|
-
if value
|
128
|
-
@hash[key].delete(value)
|
129
|
-
else
|
130
|
-
@hash.delete(key)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
# call-seq:
|
135
|
-
# map.each { |key, value| block } => map
|
136
|
-
#
|
137
|
-
# Calls <i>block</i> for each key/value pair in <i>map</i>, passing
|
138
|
-
# the key and value to the block as a two-element array.
|
139
|
-
#
|
140
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
141
|
-
# map.each { |key, value| puts "#{key} is #{value}" }
|
142
|
-
#
|
143
|
-
# <em>produces:</em>
|
144
|
-
#
|
145
|
-
# a is 100
|
146
|
-
# b is 200
|
147
|
-
# b is 300
|
148
|
-
def each
|
149
|
-
each_pair do |key, value|
|
150
|
-
yield [key, value]
|
151
|
-
end
|
152
|
-
end
|
153
|
-
|
154
|
-
# call-seq:
|
155
|
-
# map.each_association { |key, container| block } => map
|
156
|
-
#
|
157
|
-
# Calls <i>block</i> once for each key/container in <i>map</i>, passing
|
158
|
-
# the key and container to the block as parameters.
|
159
|
-
#
|
160
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
161
|
-
# map.each_association { |key, container| puts "#{key} is #{container}" }
|
162
|
-
#
|
163
|
-
# <em>produces:</em>
|
164
|
-
#
|
165
|
-
# a is [100]
|
166
|
-
# b is [200, 300]
|
167
|
-
def each_association(&block)
|
168
|
-
@hash.each_pair(&block)
|
169
|
-
end
|
170
|
-
|
171
|
-
# call-seq:
|
172
|
-
# map.each_container { |container| block } => map
|
173
|
-
#
|
174
|
-
# Calls <i>block</i> for each container in <i>map</i>, passing the
|
175
|
-
# container as a parameter.
|
176
|
-
#
|
177
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
178
|
-
# map.each_container { |container| puts container }
|
179
|
-
#
|
180
|
-
# <em>produces:</em>
|
181
|
-
#
|
182
|
-
# [100]
|
183
|
-
# [200, 300]
|
184
|
-
def each_container
|
185
|
-
each_association do |_, container|
|
186
|
-
yield container
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
# call-seq:
|
191
|
-
# map.each_key { |key| block } => map
|
192
|
-
#
|
193
|
-
# Calls <i>block</i> for each key in <i>hsh</i>, passing the key
|
194
|
-
# as a parameter.
|
195
|
-
#
|
196
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
197
|
-
# map.each_key { |key| puts key }
|
198
|
-
#
|
199
|
-
# <em>produces:</em>
|
200
|
-
#
|
201
|
-
# a
|
202
|
-
# b
|
203
|
-
# b
|
204
|
-
def each_key
|
205
|
-
each_pair do |key, _|
|
206
|
-
yield key
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
# call-seq:
|
211
|
-
# map.each_pair { |key_value_array| block } => map
|
212
|
-
#
|
213
|
-
# Calls <i>block</i> for each key/value pair in <i>map</i>,
|
214
|
-
# passing the key and value as parameters.
|
215
|
-
#
|
216
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
217
|
-
# map.each_pair { |key, value| puts "#{key} is #{value}" }
|
218
|
-
#
|
219
|
-
# <em>produces:</em>
|
220
|
-
#
|
221
|
-
# a is 100
|
222
|
-
# b is 200
|
223
|
-
# b is 300
|
224
|
-
def each_pair
|
225
|
-
each_association do |key, values|
|
226
|
-
values.each do |value|
|
227
|
-
yield key, value
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
# call-seq:
|
233
|
-
# map.each_value { |value| block } => map
|
234
|
-
#
|
235
|
-
# Calls <i>block</i> for each key in <i>map</i>, passing the
|
236
|
-
# value as a parameter.
|
237
|
-
#
|
238
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
239
|
-
# map.each_value { |value| puts value }
|
240
|
-
#
|
241
|
-
# <em>produces:</em>
|
242
|
-
#
|
243
|
-
# 100
|
244
|
-
# 200
|
245
|
-
# 300
|
246
|
-
def each_value
|
247
|
-
each_pair do |_, value|
|
248
|
-
yield value
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
def ==(other) #:nodoc:
|
253
|
-
case other
|
254
|
-
when Multimap
|
255
|
-
@hash == other._internal_hash
|
256
|
-
else
|
257
|
-
@hash == other
|
258
|
-
end
|
259
|
-
end
|
260
|
-
|
261
|
-
def eql?(other) #:nodoc:
|
262
|
-
case other
|
263
|
-
when Multimap
|
264
|
-
@hash.eql?(other._internal_hash)
|
265
|
-
else
|
266
|
-
@hash.eql?(other)
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
def freeze #:nodoc:
|
271
|
-
each_container { |container| container.freeze }
|
272
|
-
default.freeze
|
273
|
-
super
|
274
|
-
end
|
275
|
-
|
276
|
-
# call-seq:
|
277
|
-
# map.has_value?(value) => true or false
|
278
|
-
# map.value?(value) => true or false
|
279
|
-
#
|
280
|
-
# Returns <tt>true</tt> if the given value is present for any key
|
281
|
-
# in <i>map</i>.
|
282
|
-
#
|
283
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
284
|
-
# map.has_value?(300) #=> true
|
285
|
-
# map.has_value?(999) #=> false
|
286
|
-
def has_value?(value)
|
287
|
-
values.include?(value)
|
288
|
-
end
|
289
|
-
alias_method :value?, :has_value?
|
290
|
-
|
291
|
-
# call-seq:
|
292
|
-
# map.index(value) => key
|
293
|
-
#
|
294
|
-
# Returns the key for a given value. If not found, returns
|
295
|
-
# <tt>nil</tt>.
|
296
|
-
#
|
297
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
298
|
-
# map.index(100) #=> "a"
|
299
|
-
# map.index(200) #=> "b"
|
300
|
-
# map.index(999) #=> nil
|
301
|
-
def index(value)
|
302
|
-
invert[value]
|
303
|
-
end
|
304
|
-
|
305
|
-
# call-seq:
|
306
|
-
# map.delete_if {| key, value | block } -> map
|
307
|
-
#
|
308
|
-
# Deletes every key-value pair from <i>map</i> for which <i>block</i>
|
309
|
-
# evaluates to <code>true</code>.
|
310
|
-
#
|
311
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
312
|
-
# map.delete_if {|key, value| value >= 300 }
|
313
|
-
# #=> Multimap["a" => 100, "b" => 200]
|
314
|
-
#
|
315
|
-
def delete_if
|
316
|
-
each_association do |key, container|
|
317
|
-
container.delete_if do |value|
|
318
|
-
yield [key, value]
|
319
|
-
end
|
320
|
-
end
|
321
|
-
self
|
322
|
-
end
|
323
|
-
|
324
|
-
# call-seq:
|
325
|
-
# map.reject {| key, value | block } -> map
|
326
|
-
#
|
327
|
-
# Same as <code>Multimap#delete_if</code>, but works on (and returns) a
|
328
|
-
# copy of the <i>map</i>. Equivalent to
|
329
|
-
# <code><i>map</i>.dup.delete_if</code>.
|
330
|
-
#
|
331
|
-
def reject(&block)
|
332
|
-
dup.delete_if(&block)
|
333
|
-
end
|
334
|
-
|
335
|
-
# call-seq:
|
336
|
-
# map.reject! {| key, value | block } -> map or nil
|
337
|
-
#
|
338
|
-
# Equivalent to <code>Multimap#delete_if</code>, but returns
|
339
|
-
# <code>nil</code> if no changes were made.
|
340
|
-
#
|
341
|
-
def reject!(&block)
|
342
|
-
old_size = size
|
343
|
-
delete_if(&block)
|
344
|
-
old_size == size ? nil : self
|
345
|
-
end
|
346
|
-
|
347
|
-
# call-seq:
|
348
|
-
# map.replace(other_map) => map
|
349
|
-
#
|
350
|
-
# Replaces the contents of <i>map</i> with the contents of
|
351
|
-
# <i>other_map</i>.
|
352
|
-
#
|
353
|
-
# map = Multimap["a" => 100, "b" => 200]
|
354
|
-
# map.replace({ "c" => 300, "d" => 400 })
|
355
|
-
# #=> Multimap["c" => 300, "d" => 400]
|
356
|
-
def replace(other)
|
357
|
-
case other
|
358
|
-
when Array
|
359
|
-
@hash.replace(self.class[self.default, *other])
|
360
|
-
when Hash
|
361
|
-
@hash.replace(self.class[self.default, other])
|
362
|
-
when self.class
|
363
|
-
@hash.replace(other)
|
364
|
-
else
|
365
|
-
raise ArgumentError
|
366
|
-
end
|
367
|
-
end
|
368
|
-
|
369
|
-
# call-seq:
|
370
|
-
# map.invert => multimap
|
371
|
-
#
|
372
|
-
# Returns a new multimap created by using <i>map</i>'s values as keys,
|
373
|
-
# and the keys as values.
|
374
|
-
#
|
375
|
-
# map = Multimap["n" => 100, "m" => 100, "d" => [200, 300]]
|
376
|
-
# map.invert #=> Multimap[100 => ["n", "m"], 200 => "d", 300 => "d"]
|
377
|
-
def invert
|
378
|
-
h = self.class.new(default.dup)
|
379
|
-
each_pair { |key, value| h[value] = key }
|
380
|
-
h
|
381
|
-
end
|
382
|
-
|
383
|
-
# call-seq:
|
384
|
-
# map.keys => multiset
|
385
|
-
#
|
386
|
-
# Returns a new +Multiset+ populated with the keys from this hash. See also
|
387
|
-
# <tt>Multimap#values</tt> and <tt>Multimap#containers</tt>.
|
388
|
-
#
|
389
|
-
# map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
|
390
|
-
# map.keys #=> Multiset.new(["a", "b", "b", "c"])
|
391
|
-
def keys
|
392
|
-
keys = Multiset.new
|
393
|
-
each_key { |key| keys << key }
|
394
|
-
keys
|
395
|
-
end
|
396
|
-
|
397
|
-
# Returns true if the given key is present in Multimap.
|
398
|
-
def include?(key)
|
399
|
-
keys.include?(key)
|
400
|
-
end
|
401
|
-
alias_method :member?, :include?
|
402
|
-
|
403
|
-
# call-seq:
|
404
|
-
# map.length => fixnum
|
405
|
-
# map.size => fixnum
|
406
|
-
#
|
407
|
-
# Returns the number of key-value pairs in the map.
|
408
|
-
#
|
409
|
-
# map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
|
410
|
-
# map.length #=> 4
|
411
|
-
# map.delete("a") #=> 100
|
412
|
-
# map.length #=> 3
|
413
|
-
def size
|
414
|
-
values.size
|
415
|
-
end
|
416
|
-
alias_method :length, :size
|
417
|
-
|
418
|
-
# call-seq:
|
419
|
-
# map.merge(other_map) => multimap
|
420
|
-
#
|
421
|
-
# Returns a new multimap containing the contents of <i>other_map</i> and
|
422
|
-
# the contents of <i>map</i>.
|
423
|
-
#
|
424
|
-
# map1 = Multimap["a" => 100, "b" => 200]
|
425
|
-
# map2 = Multimap["a" => 254, "c" => 300]
|
426
|
-
# map2.merge(map2) #=> Multimap["a" => 100, "b" => [200, 254], "c" => 300]
|
427
|
-
# map1 #=> Multimap["a" => 100, "b" => 200]
|
428
|
-
def merge(other)
|
429
|
-
dup.update(other)
|
430
|
-
end
|
431
|
-
|
432
|
-
# call-seq:
|
433
|
-
# map.merge!(other_map) => multimap
|
434
|
-
# map.update(other_map) => multimap
|
435
|
-
#
|
436
|
-
# Adds each pair from <i>other_map</i> to <i>map</i>.
|
437
|
-
#
|
438
|
-
# map1 = Multimap["a" => 100, "b" => 200]
|
439
|
-
# map2 = Multimap["b" => 254, "c" => 300]
|
440
|
-
#
|
441
|
-
# map1.merge!(map2)
|
442
|
-
# #=> Multimap["a" => 100, "b" => [200, 254], "c" => 300]
|
443
|
-
def update(other)
|
444
|
-
case other
|
445
|
-
when self.class
|
446
|
-
other.each_pair { |key, value| store(key, value) }
|
447
|
-
when Hash
|
448
|
-
update(self.class[self.default, other])
|
449
|
-
else
|
450
|
-
raise ArgumentError
|
451
|
-
end
|
452
|
-
self
|
453
|
-
end
|
454
|
-
alias_method :merge!, :update
|
455
|
-
|
456
|
-
# call-seq:
|
457
|
-
# map.select { |key, value| block } => multimap
|
458
|
-
#
|
459
|
-
# Returns a new Multimap consisting of the pairs for which the
|
460
|
-
# block returns true.
|
461
|
-
#
|
462
|
-
# map = Multimap["a" => 100, "b" => 200, "c" => 300]
|
463
|
-
# map.select { |k,v| k > "a" } #=> Multimap["b" => 200, "c" => 300]
|
464
|
-
# map.select { |k,v| v < 200 } #=> Multimap["a" => 100]
|
465
|
-
def select
|
466
|
-
inject(self.class.new) { |map, (key, value)|
|
467
|
-
map[key] = value if yield([key, value])
|
468
|
-
map
|
469
|
-
}
|
470
|
-
end
|
471
|
-
|
472
|
-
# call-seq:
|
473
|
-
# map.to_a => array
|
474
|
-
#
|
475
|
-
# Converts <i>map</i> to a nested array of [<i>key,
|
476
|
-
# value</i>] arrays.
|
477
|
-
#
|
478
|
-
# map = Multimap["a" => 100, "b" => [200, 300], "c" => 400]
|
479
|
-
# map.to_a #=> [["a", 100], ["b", 200], ["b", 300], ["c", 400]]
|
480
|
-
def to_a
|
481
|
-
ary = []
|
482
|
-
each_pair do |key, value|
|
483
|
-
ary << [key, value]
|
484
|
-
end
|
485
|
-
ary
|
486
|
-
end
|
487
|
-
|
488
|
-
# call-seq:
|
489
|
-
# map.to_hash => hash
|
490
|
-
#
|
491
|
-
# Converts <i>map</i> to a basic hash.
|
492
|
-
#
|
493
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
494
|
-
# map.to_hash #=> { "a" => [100], "b" => [200, 300] }
|
495
|
-
def to_hash
|
496
|
-
@hash.dup
|
497
|
-
end
|
498
|
-
|
499
|
-
# call-seq:
|
500
|
-
# map.containers => array
|
501
|
-
#
|
502
|
-
# Returns a new array populated with the containers from <i>map</i>. See
|
503
|
-
# also <tt>Multimap#keys</tt> and <tt>Multimap#values</tt>.
|
504
|
-
#
|
505
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
506
|
-
# map.containers #=> [[100], [200, 300]]
|
507
|
-
def containers
|
508
|
-
containers = []
|
509
|
-
each_container { |container| containers << container }
|
510
|
-
containers
|
511
|
-
end
|
512
|
-
|
513
|
-
# call-seq:
|
514
|
-
# map.values => array
|
515
|
-
#
|
516
|
-
# Returns a new array populated with the values from <i>map</i>. See
|
517
|
-
# also <tt>Multimap#keys</tt> and <tt>Multimap#containers</tt>.
|
518
|
-
#
|
519
|
-
# map = Multimap["a" => 100, "b" => [200, 300]]
|
520
|
-
# map.values #=> [100, 200, 300]
|
521
|
-
def values
|
522
|
-
values = []
|
523
|
-
each_value { |value| values << value }
|
524
|
-
values
|
525
|
-
end
|
526
|
-
|
527
|
-
# Return an array containing the values associated with the given keys.
|
528
|
-
def values_at(*keys)
|
529
|
-
@hash.values_at(*keys)
|
530
|
-
end
|
531
|
-
|
532
|
-
def marshal_dump #:nodoc:
|
533
|
-
@hash
|
534
|
-
end
|
535
|
-
|
536
|
-
def marshal_load(hash) #:nodoc:
|
537
|
-
@hash = hash
|
538
|
-
end
|
539
|
-
|
540
|
-
def to_yaml(opts = {}) #:nodoc:
|
541
|
-
YAML::quick_emit(self, opts) do |out|
|
542
|
-
out.map(taguri, to_yaml_style) do |map|
|
543
|
-
@hash.each do |k, v|
|
544
|
-
map.add(k, v)
|
545
|
-
end
|
546
|
-
map.add('__default__', @hash.default)
|
547
|
-
end
|
548
|
-
end
|
549
|
-
end
|
550
|
-
|
551
|
-
def yaml_initialize(tag, val) #:nodoc:
|
552
|
-
default = val.delete('__default__')
|
553
|
-
@hash = val
|
554
|
-
@hash.default = default
|
555
|
-
self
|
556
|
-
end
|
557
|
-
|
558
|
-
protected
|
559
|
-
def _internal_hash #:nodoc:
|
560
|
-
@hash
|
561
|
-
end
|
562
|
-
|
563
|
-
def update_container(key) #:nodoc:
|
564
|
-
container = @hash[key]
|
565
|
-
container = container.dup if container.equal?(default)
|
566
|
-
container = yield(container)
|
567
|
-
@hash[key] = container
|
568
|
-
end
|
569
|
-
end
|
@@ -1,185 +0,0 @@
|
|
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
|
@@ -1,158 +0,0 @@
|
|
1
|
-
require '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
|