rack-mount 0.6.14 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
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 < Frequency
6
- NULL = "\0".freeze
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
- @boundaries = {}
31
- super
35
+ @raw_keys = []
36
+ @key_frequency = Analysis::Histogram.new
37
+ self
32
38
  end
33
39
 
34
40
  def <<(key)
35
- super
36
- key.each_pair do |k, v|
37
- analyze_capture_boundaries(v, @boundaries[k] ||= Histogram.new)
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 separators(key)
42
- @separators ||= {}
43
- @separators[key] ||= lookup_separators(key)
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 lookup_separators(key)
48
- @boundaries[key].keys_in_upper_quartile
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
- super
59
- end
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
- if index < parts.length
83
- following = parts[index+1]
84
- if following.is_a?(Regin::Character) && following.literal?
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
- boundaries
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:
@@ -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 < NestedMultimap #:nodoc:
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
- super(*args)
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
- super(*args)
41
+ _store(*args)
38
42
  end
39
43
  end
40
44
  alias_method :[]=, :store
41
45
 
42
- undef :index, :invert
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
@@ -25,7 +25,9 @@ module Rack::Mount
25
25
  @named_routes = {}
26
26
 
27
27
  @recognition_key_analyzer = Analysis::Splitting.new
28
- @generation_key_analyzer = Analysis::Frequency.new
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
- @generation_key_analyzer << route.generation_keys
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
- @generation_key_analyzer = nil
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
- @generation_keys = @generation_graph = nil
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 = @generation_key_analyzer.possible_keys[i][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: 27
4
+ hash: 1
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
- - 6
9
- - 14
10
- version: 0.6.14
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