libis-workflow-mongoid 2.0.2 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +0 -1
- data/lib/libis/workflow/mongoid/base.rb +42 -3
- data/lib/libis/workflow/mongoid/job.rb +25 -3
- data/lib/libis/workflow/mongoid/run.rb +9 -4
- data/lib/libis/workflow/mongoid/version.rb +1 -1
- data/lib/libis/workflow/mongoid/work_item_base.rb +16 -4
- data/lib/libis/workflow/mongoid/workflow.rb +25 -3
- data/lib/map_with_indifferent_access.rb +20 -0
- data/lib/map_with_indifferent_access/list.rb +867 -0
- data/lib/map_with_indifferent_access/map.rb +833 -0
- data/lib/map_with_indifferent_access/normalization.rb +92 -0
- data/lib/map_with_indifferent_access/normalization/deep_normalizer.rb +104 -0
- data/lib/map_with_indifferent_access/values.rb +39 -0
- data/lib/map_with_indifferent_access/version.rb +3 -0
- data/lib/map_with_indifferent_access/wraps_collection.rb +152 -0
- data/libis-workflow-mongoid.gemspec +0 -1
- data/spec/items/test_dir_item.rb +1 -1
- data/spec/items/test_file_item.rb +3 -3
- data/spec/tasks/camelize_name.rb +1 -1
- data/spec/workflow_spec.rb +2 -2
- metadata +10 -2
@@ -0,0 +1,833 @@
|
|
1
|
+
module MapWithIndifferentAccess
|
2
|
+
|
3
|
+
class Map
|
4
|
+
extend Forwardable
|
5
|
+
include MapWithIndifferentAccess::WrapsCollection
|
6
|
+
|
7
|
+
def inject(*args, &block)
|
8
|
+
inner_map.inject(*args, &block)
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_hash
|
12
|
+
inner_map.dup
|
13
|
+
end
|
14
|
+
|
15
|
+
# Try to convert `from_obj` into a {Map}.
|
16
|
+
#
|
17
|
+
# @return [Map]
|
18
|
+
# converted object if `from_obj` is convertible.
|
19
|
+
#
|
20
|
+
# @return [nil]
|
21
|
+
# if `from_obj` cannot be converted for any reason.
|
22
|
+
def self.try_convert(from_obj)
|
23
|
+
if self === from_obj
|
24
|
+
from_obj
|
25
|
+
else
|
26
|
+
hash = Hash.try_convert( from_obj )
|
27
|
+
new( hash ) if hash
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Try to convert `obj`, which might be a {Map} into a `Hash`.
|
32
|
+
#
|
33
|
+
# @return [Hash]
|
34
|
+
# converted object if `obj` is convertible.
|
35
|
+
#
|
36
|
+
# @return [nil]
|
37
|
+
# if `obj` cannot be converted for any reason.
|
38
|
+
def self.try_deconstruct(obj)
|
39
|
+
if self === obj
|
40
|
+
obj.inner_map
|
41
|
+
elsif obj.respond_to?(:to_hash )
|
42
|
+
h = obj.to_hash
|
43
|
+
Hash === h ? h : nil
|
44
|
+
else
|
45
|
+
nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# @!attribute inner_array
|
50
|
+
# @return [Array]
|
51
|
+
#
|
52
|
+
# Alias for {#inner_collection}. The encapsulated `Hash`
|
53
|
+
# instance.
|
54
|
+
alias inner_map inner_collection
|
55
|
+
|
56
|
+
# @!method to_h
|
57
|
+
#
|
58
|
+
# Alias for {#inner_collection}. Returns the
|
59
|
+
# encapsulated `Hash` instance.
|
60
|
+
|
61
|
+
# Use class_eval to hide the aliasing from Yard doc.
|
62
|
+
class_eval 'alias to_h inner_collection', __FILE__, __LINE__
|
63
|
+
|
64
|
+
# @!method keys
|
65
|
+
# Returns a new `Array` populated with the keys from this
|
66
|
+
# {Map}.
|
67
|
+
#
|
68
|
+
# @return [Array]
|
69
|
+
# @see #values
|
70
|
+
|
71
|
+
# @!method rehash
|
72
|
+
# Rebuilds the target's {#inner_map} `Hash` based on the
|
73
|
+
# current `#hash` values for each key. If values of key
|
74
|
+
# objects have changed since they were inserted, this will
|
75
|
+
# re-index that `Hash`.
|
76
|
+
#
|
77
|
+
# If {#rehash} is called while an iterator is traversing
|
78
|
+
# the {Map} or its {#inner_map} `Hash`, a `RuntimeError` will
|
79
|
+
# be raised in the iterator.
|
80
|
+
#
|
81
|
+
# @return [Map]
|
82
|
+
|
83
|
+
def_delegators(
|
84
|
+
:inner_map,
|
85
|
+
:keys,
|
86
|
+
:rehash,
|
87
|
+
)
|
88
|
+
|
89
|
+
# Initializes a new {Map} that encapsulates a new
|
90
|
+
# empty `Array` or the `Array` coerced from the given
|
91
|
+
# `basis`.
|
92
|
+
#
|
93
|
+
# When a {Map} is given as a basis, this results on the given
|
94
|
+
# and new instances sharing the same {#inner_map}. There is
|
95
|
+
# no obvious reason to do that on purpose, but there is also
|
96
|
+
# no likely harm in allowing it to happen.
|
97
|
+
#
|
98
|
+
# @param [::Hash, MapWithIndifferentAccess::Map, Object] basis
|
99
|
+
# A `Hash` or an object that can be implicitly coerced to a
|
100
|
+
# `::Hash`
|
101
|
+
def initialize(basis={})
|
102
|
+
use_basis = basis
|
103
|
+
use_basis = basis.inner_map if self.class === basis
|
104
|
+
use_basis = Hash.try_convert( use_basis )
|
105
|
+
raise ArgumentError, "Could not convert #{basis.inspect} into a Hash" unless use_basis
|
106
|
+
@inner_collection = use_basis
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the `given_key` object if it is a key in the target's
|
110
|
+
# {#inner_map} `Hash` or if neither `given_key` nor its
|
111
|
+
# `String`/`Symbol` alternative is a key in the {#inner_map}.
|
112
|
+
#
|
113
|
+
# When `given_key` is a `String` that is not a key in the
|
114
|
+
# target's {#inner_map}, returns the symbolization of
|
115
|
+
# `given_key` if that symbolization _is_ a key in the
|
116
|
+
# {#inner_map}.
|
117
|
+
#
|
118
|
+
# When `given_key` is a `Symbol` that is not a key in the
|
119
|
+
# target's {#inner_map}, returns the stringification of
|
120
|
+
# `given_key` if that stringification _is_ a key in the
|
121
|
+
# {#inner_map}.
|
122
|
+
def conform_key(given_key)
|
123
|
+
case given_key
|
124
|
+
when String
|
125
|
+
alt_key = inner_map.key?( given_key ) ? given_key : given_key.to_sym
|
126
|
+
inner_map.key?( alt_key ) ? alt_key : given_key
|
127
|
+
when Symbol
|
128
|
+
alt_key = inner_map.key?( given_key ) ? given_key : "#{given_key}"
|
129
|
+
inner_map.key?( alt_key ) ? alt_key : given_key
|
130
|
+
else
|
131
|
+
given_key
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# @!method any?
|
136
|
+
# Iterates over entries in the target {Map}, passing a
|
137
|
+
# _`[ key, externalized_value ]`_ `Array` to the block
|
138
|
+
# for each of those, and returns `true` when the block
|
139
|
+
# returns `true` for any entry.
|
140
|
+
#
|
141
|
+
# @return [Boolean]
|
142
|
+
#
|
143
|
+
# @overload any?
|
144
|
+
# @yieldparam [Array<(Object, Object)>] pair
|
145
|
+
# `[ key, externalized_value ]`
|
146
|
+
#
|
147
|
+
# @overload any?
|
148
|
+
# Due to the behavior of `Enumerable::any?` when no block
|
149
|
+
# is given, this is effectively synonymous with
|
150
|
+
# `!target_map.empty?`.
|
151
|
+
#
|
152
|
+
# @see #each
|
153
|
+
# @see Enumerable::any?
|
154
|
+
|
155
|
+
|
156
|
+
# Creates an entry or replaces the value of an existing entry
|
157
|
+
# in the target's {#inner_map} `Hash`.
|
158
|
+
#
|
159
|
+
# When the `key` conforms to a key in the target map, then the
|
160
|
+
# value of the matching entry in the target's {#inner_map} is
|
161
|
+
# replaced with the internalization of `value`.
|
162
|
+
#
|
163
|
+
# When `key` does not conform to a key in the target map, then
|
164
|
+
# a new entry is added using the given `key` and the
|
165
|
+
# internalization of `value`.
|
166
|
+
#
|
167
|
+
# Returns the given `value`.
|
168
|
+
#
|
169
|
+
# @see #conform_key
|
170
|
+
# @see Values.internalize
|
171
|
+
def[]=(key, value)
|
172
|
+
key = conform_key( key )
|
173
|
+
intern_value = Values << value
|
174
|
+
inner_map[ key ] = intern_value
|
175
|
+
value
|
176
|
+
end
|
177
|
+
|
178
|
+
alias store []=
|
179
|
+
|
180
|
+
# Returns the externalization of the value from the target's
|
181
|
+
# {#inner_map} entry having a key that conforms to the given
|
182
|
+
# `key` if applicable.
|
183
|
+
#
|
184
|
+
# When there is no entry with a conforming key, returns the
|
185
|
+
# externalization of the {#inner_map} `Hash`'s default value
|
186
|
+
# for the given `key` (normally `nil`).
|
187
|
+
#
|
188
|
+
# @see #conform_key
|
189
|
+
# @see Values.externalize
|
190
|
+
def[](key)
|
191
|
+
key = conform_key( key )
|
192
|
+
value = inner_map[ key ]
|
193
|
+
Values >> value
|
194
|
+
end
|
195
|
+
|
196
|
+
def fetch(key, *more_args)
|
197
|
+
expect_arity 1..2, key, *more_args
|
198
|
+
if block_given? && !more_args.empty?
|
199
|
+
warn "#{caller[ 0 ]}: warning: block supersedes default value argument"
|
200
|
+
end
|
201
|
+
|
202
|
+
conformed_key = conform_key( key )
|
203
|
+
|
204
|
+
value = if inner_map.key?( conformed_key )
|
205
|
+
inner_map.fetch( conformed_key )
|
206
|
+
elsif block_given?
|
207
|
+
inner_map.fetch( key ) {|key| yield key }
|
208
|
+
else
|
209
|
+
inner_map.fetch( key, *more_args )
|
210
|
+
end
|
211
|
+
|
212
|
+
Values >> value
|
213
|
+
end
|
214
|
+
|
215
|
+
# Return a {List} containing the values of entries matching
|
216
|
+
# the given keys.
|
217
|
+
#
|
218
|
+
# @return [List]
|
219
|
+
def values_at(*keys)
|
220
|
+
keys = keys.map{ |k| conform_key( k ) }
|
221
|
+
inner_result = inner_map.values_at( *keys )
|
222
|
+
List.new( inner_result )
|
223
|
+
end
|
224
|
+
|
225
|
+
# Returns `true` if the conformation of `key` is present in
|
226
|
+
# the target {Map}.
|
227
|
+
#
|
228
|
+
# @return [Boolean]
|
229
|
+
def key?(key)
|
230
|
+
case key
|
231
|
+
when String
|
232
|
+
inner_map.key?( key ) || inner_map.key?( key.to_sym )
|
233
|
+
when Symbol
|
234
|
+
inner_map.key?( key ) || inner_map.key?("#{key}")
|
235
|
+
else
|
236
|
+
inner_map.key?( key )
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
alias has_key? key?
|
241
|
+
alias include? key?
|
242
|
+
alias member? key?
|
243
|
+
|
244
|
+
# Returns the key for an entry, the externalization of which
|
245
|
+
# is equal to the externalization of `value`. Returns `nil`
|
246
|
+
# if no match is found.
|
247
|
+
#
|
248
|
+
# @return [Object, nil]
|
249
|
+
#
|
250
|
+
# @see Values.externalize
|
251
|
+
def key(value)
|
252
|
+
entry = rassoc( value )
|
253
|
+
entry ? entry.first : nil
|
254
|
+
end
|
255
|
+
|
256
|
+
# Sets the default value returned from the target's
|
257
|
+
# {#inner_map} `Hash` for a key that does not exist to
|
258
|
+
# be the internlization of `obj`.
|
259
|
+
def default=(obj)
|
260
|
+
inner_map.default = Values << obj
|
261
|
+
end
|
262
|
+
|
263
|
+
# Returns the default value, the value that would be returned
|
264
|
+
# by `<target>[key]` if the conformation of `key` did not exist
|
265
|
+
# in the target.
|
266
|
+
#
|
267
|
+
# @see #conform_key
|
268
|
+
def default(key = nil)
|
269
|
+
inner_default = inner_map.default( key )
|
270
|
+
Values >> inner_default
|
271
|
+
end
|
272
|
+
|
273
|
+
# Sets the {#inner_map} `Hash`'s default proc to a wrapper
|
274
|
+
# around `proc_obj` that passes the target {Map} and the key
|
275
|
+
# as parameters and returns the internalization of the
|
276
|
+
# wrapped proc's block.
|
277
|
+
#
|
278
|
+
# When running in Ruby 2.x or newer, the default proc can
|
279
|
+
# also be cleared by passing `nil` for `proc_obj`.
|
280
|
+
#
|
281
|
+
# @param proc_obj [Proc, nil]
|
282
|
+
# @see Hash#default_proc=
|
283
|
+
def default_proc=(proc_obj)
|
284
|
+
inner_proc = ->(_, key){
|
285
|
+
Values << proc_obj.call( self, key )
|
286
|
+
}
|
287
|
+
inner_map.default_proc = inner_proc
|
288
|
+
self._default_proc = proc_obj
|
289
|
+
self._inner_proc_for_default_proc = inner_proc
|
290
|
+
proc_obj
|
291
|
+
end
|
292
|
+
|
293
|
+
# If the target {Map}'s {#inner_map} `Hash` does not have a
|
294
|
+
# default proc assigned, then this returns `nil`.
|
295
|
+
#
|
296
|
+
# If `Proc` has been previously assigned to the target {Map}
|
297
|
+
# using {#default_proc=} and is is still applicable, then
|
298
|
+
# that `Proc` is returned.
|
299
|
+
#
|
300
|
+
# If no `Proc` was assigned to the target {Map} or that
|
301
|
+
# assignment is no longer applicable, but the {#inner_map}
|
302
|
+
# `Hash` has a default proc, then a wrapper around that
|
303
|
+
# `Proc` is returned that accepts a `Map` or `Hash`-like
|
304
|
+
# object and a key, passing the `Map`-deconstruction of the
|
305
|
+
# `Map`/`Hash` and the unmodified key value to the underlying
|
306
|
+
# `Proc`, finally returning the externalization of the value
|
307
|
+
# returned from the call to the underlying `Proc`.
|
308
|
+
#
|
309
|
+
# @return (Proc, nil)
|
310
|
+
def default_proc
|
311
|
+
return nil unless inner_map.default_proc
|
312
|
+
unless inner_map.default_proc.equal?( _inner_proc_for_default_proc )
|
313
|
+
self._default_proc = nil
|
314
|
+
end
|
315
|
+
return _default_proc if _default_proc
|
316
|
+
_default_proc = ->(map,key){
|
317
|
+
hash = self.class.try_deconstruct( map )
|
318
|
+
value = inner_map.default_proc.call( hash, key )
|
319
|
+
Values >> value
|
320
|
+
}
|
321
|
+
end
|
322
|
+
|
323
|
+
# Returns `true` if the entries in `other` (a {Map}, `Hash`,
|
324
|
+
# or other `Hash`-like object) are equal in numer and
|
325
|
+
# equivalent to the entries in the target {Map}.
|
326
|
+
#
|
327
|
+
# Entries are equivalent if their keys are equivalent with
|
328
|
+
# `String`/`Symbolic` indifference and their externalized
|
329
|
+
# values are equal using `==`.
|
330
|
+
#
|
331
|
+
# @return [Boolean]
|
332
|
+
def ==(other)
|
333
|
+
return true if equal?( other )
|
334
|
+
other = self.class.try_convert( other )
|
335
|
+
return false unless other
|
336
|
+
|
337
|
+
return true if inner_map == other.inner_map
|
338
|
+
return false if length != other.length
|
339
|
+
each do |(key, value)|
|
340
|
+
other_val = other.fetch(key) { return false }
|
341
|
+
return false unless value == other_val
|
342
|
+
end
|
343
|
+
|
344
|
+
true
|
345
|
+
end
|
346
|
+
|
347
|
+
# When a block argument is given, calls the block once for
|
348
|
+
# each of the target's entries, passing the entry's key and
|
349
|
+
# externalized value as parameters, and then returns the
|
350
|
+
# target object.
|
351
|
+
#
|
352
|
+
# When no block argument is given, returns an `Enumerator`.
|
353
|
+
#
|
354
|
+
# @overload each
|
355
|
+
# @yieldparam key
|
356
|
+
# @yieldparam value
|
357
|
+
# @return [MapWithIndifferentAccess::Map]
|
358
|
+
#
|
359
|
+
# @overload each
|
360
|
+
# @return [Enumerator]
|
361
|
+
def each
|
362
|
+
return to_enum(:each ) unless block_given?
|
363
|
+
|
364
|
+
each_key do |key|
|
365
|
+
value = fetch( key )
|
366
|
+
value = Values >> value
|
367
|
+
yield [key, value]
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
alias each_pair each
|
372
|
+
|
373
|
+
# When a block argument is given, calls the block once for each of the
|
374
|
+
# target's keys, passing the key as a parameter, and then returns the
|
375
|
+
# target object.
|
376
|
+
#
|
377
|
+
# When no block argument is given, returns an `Enumerator`.
|
378
|
+
#
|
379
|
+
# @return [MapWithIndifferentAccess::Map]
|
380
|
+
def each_key
|
381
|
+
return to_enum(:each_key ) unless block_given?
|
382
|
+
inner_map.each_key do |key|
|
383
|
+
yield key
|
384
|
+
end
|
385
|
+
self
|
386
|
+
end
|
387
|
+
|
388
|
+
# When a block argument is given, calls the block once for each of the
|
389
|
+
# target's entries, passing externalization the entry value as a parameter,
|
390
|
+
# and then returns the target.
|
391
|
+
#
|
392
|
+
# When no block argument is given, returns an `Enumerator`.
|
393
|
+
#
|
394
|
+
# @return [MapWithIndifferentAccess::Map]
|
395
|
+
def each_value
|
396
|
+
return to_enum(:each_value) unless block_given?
|
397
|
+
|
398
|
+
inner_map.each_value do |value|
|
399
|
+
value = Values >> value
|
400
|
+
yield value
|
401
|
+
end
|
402
|
+
self
|
403
|
+
end
|
404
|
+
|
405
|
+
# Returns a {List} containing the entry values from the target {Map}.
|
406
|
+
#
|
407
|
+
# @return [List]
|
408
|
+
def values
|
409
|
+
List.new( inner_map.values )
|
410
|
+
end
|
411
|
+
|
412
|
+
# Delete the externalization of the value associated with the
|
413
|
+
# conformation of the given `key` or default value.
|
414
|
+
#
|
415
|
+
# @overload delete(key)
|
416
|
+
# Returns the externalization of the value returned from
|
417
|
+
# the {#inner_map} `Hash` on deletion.
|
418
|
+
#
|
419
|
+
# @overload delete(key)
|
420
|
+
# @yieldparam key
|
421
|
+
#
|
422
|
+
# Returns the externalization of the value returned by the
|
423
|
+
# given block for the given `key`.
|
424
|
+
#
|
425
|
+
# @see #conform_key
|
426
|
+
# @see Values.externalize
|
427
|
+
def delete(key)
|
428
|
+
key = conform_key( key )
|
429
|
+
value = if block_given?
|
430
|
+
inner_map.delete( key ) { |key| yield key }
|
431
|
+
else
|
432
|
+
inner_map.delete( key )
|
433
|
+
end
|
434
|
+
Values >> value
|
435
|
+
end
|
436
|
+
|
437
|
+
# Returns a new {Map} consisting of entries for which the
|
438
|
+
# block returns `false`, given the entry's key and
|
439
|
+
# externalized value.
|
440
|
+
#
|
441
|
+
# If no block is given, then an `Enumerator` is returned
|
442
|
+
# instead.
|
443
|
+
#
|
444
|
+
# @overload reject
|
445
|
+
# @yieldparam key
|
446
|
+
# @return [Map]
|
447
|
+
#
|
448
|
+
# @overload reject
|
449
|
+
# @return [Enumerator]
|
450
|
+
#
|
451
|
+
# @see Values.externalize
|
452
|
+
def reject
|
453
|
+
return to_enum(:reject ) unless block_given?
|
454
|
+
|
455
|
+
dup.delete_if{ |key, value|
|
456
|
+
yield( key, value )
|
457
|
+
}
|
458
|
+
end
|
459
|
+
|
460
|
+
# Equivalent to {#delete_if}, but returns `nil` if no changes
|
461
|
+
# were made.
|
462
|
+
#
|
463
|
+
# @overload reject!
|
464
|
+
# @yieldparam key
|
465
|
+
# @yieldparam value
|
466
|
+
# @return [Map, nil]
|
467
|
+
#
|
468
|
+
# @overload reject!
|
469
|
+
# @return [Enumerator]
|
470
|
+
#
|
471
|
+
# @see #delete_if
|
472
|
+
def reject!
|
473
|
+
return to_enum(:reject!) unless block_given?
|
474
|
+
|
475
|
+
has_rejections = false
|
476
|
+
delete_if{ |key, value|
|
477
|
+
is_rejected = yield( key, value )
|
478
|
+
has_rejections ||= is_rejected
|
479
|
+
is_rejected
|
480
|
+
}
|
481
|
+
|
482
|
+
has_rejections ? self : nil
|
483
|
+
end
|
484
|
+
|
485
|
+
# Deletes every entry for which the block evaluates to
|
486
|
+
# `true` given the entry's key and externalized value.
|
487
|
+
#
|
488
|
+
# If no block is given, then an `Enumerator` is returned
|
489
|
+
# instead.
|
490
|
+
#
|
491
|
+
# @overload delete_if
|
492
|
+
# @yieldparam key
|
493
|
+
# @yieldparam value
|
494
|
+
# @return [Map]
|
495
|
+
#
|
496
|
+
# @overload delete_if
|
497
|
+
# @return [Enumerator]
|
498
|
+
#
|
499
|
+
# @see Values.externalize
|
500
|
+
def delete_if
|
501
|
+
return to_enum(:delete_if ) unless block_given?
|
502
|
+
|
503
|
+
inner_map.delete_if do |key, value|
|
504
|
+
value = Values >> value
|
505
|
+
yield key, value
|
506
|
+
end
|
507
|
+
|
508
|
+
self
|
509
|
+
end
|
510
|
+
|
511
|
+
# Returns a new {Map} consisting of entries for which the
|
512
|
+
# block returns `true`, given the entry's key and
|
513
|
+
# externalized value.
|
514
|
+
#
|
515
|
+
# If no block is given, then an `Enumerator` is returned
|
516
|
+
# instead.
|
517
|
+
#
|
518
|
+
# @overload select
|
519
|
+
# @yieldparam key
|
520
|
+
# @return [Map]
|
521
|
+
#
|
522
|
+
# @overload select
|
523
|
+
# @return [Enumerator]
|
524
|
+
#
|
525
|
+
# @see Values.externalize
|
526
|
+
def select
|
527
|
+
return to_enum(:select ) unless block_given?
|
528
|
+
|
529
|
+
dup.keep_if{ |key, value|
|
530
|
+
yield( key, value )
|
531
|
+
}
|
532
|
+
end
|
533
|
+
|
534
|
+
# Equivalent to {#keep_if}, but returns `nil` if no changes
|
535
|
+
# were made.
|
536
|
+
#
|
537
|
+
# @overload select!
|
538
|
+
# @yieldparam key
|
539
|
+
# @yieldparam value
|
540
|
+
# @return [Map, nil]
|
541
|
+
#
|
542
|
+
# @overload select!
|
543
|
+
# @return [Enumerator]
|
544
|
+
#
|
545
|
+
# @see #keep_if
|
546
|
+
def select!
|
547
|
+
return to_enum(:select!) unless block_given?
|
548
|
+
|
549
|
+
has_rejections = false
|
550
|
+
keep_if{ |key, value|
|
551
|
+
is_selected = yield( key, value )
|
552
|
+
has_rejections ||= ! is_selected
|
553
|
+
is_selected
|
554
|
+
}
|
555
|
+
|
556
|
+
has_rejections ? self : nil
|
557
|
+
end
|
558
|
+
|
559
|
+
# Deletes every entry for which the block evaluates to
|
560
|
+
# `false`, given the entry's key and externalized value.
|
561
|
+
#
|
562
|
+
# If no block is given, then an `Enumerator` is returned
|
563
|
+
# instead.
|
564
|
+
#
|
565
|
+
# @overload keep_if
|
566
|
+
# @yieldparam key
|
567
|
+
# @yieldparam value
|
568
|
+
# @return [Map]
|
569
|
+
#
|
570
|
+
# @overload keep_if
|
571
|
+
# @return [Enumerator]
|
572
|
+
#
|
573
|
+
# @see Values.externalize
|
574
|
+
def keep_if
|
575
|
+
return to_enum(:keep_if ) unless block_given?
|
576
|
+
|
577
|
+
inner_map.keep_if do |key, value|
|
578
|
+
value = Values >> value
|
579
|
+
yield key, value
|
580
|
+
end
|
581
|
+
|
582
|
+
self
|
583
|
+
end
|
584
|
+
|
585
|
+
# Replace the contents of the target's {#inner_map} `Hash`
|
586
|
+
# with the deconstruction of the given `Hash`-like object.
|
587
|
+
#
|
588
|
+
# @return [Map]
|
589
|
+
#
|
590
|
+
# @see .try_deconstruct
|
591
|
+
def replace(other)
|
592
|
+
other_d = self.class.try_deconstruct( other ) || other
|
593
|
+
inner_map.replace other_d
|
594
|
+
return self
|
595
|
+
end
|
596
|
+
|
597
|
+
# Searches through the map, comparing the conformation of
|
598
|
+
# `obj` with each entry's key using `==`. Returns a 2-element
|
599
|
+
# array containing the key and externalized value from the
|
600
|
+
# matching element or returns or `nil` if no match is found.
|
601
|
+
#
|
602
|
+
# @return [Array, nil]
|
603
|
+
#
|
604
|
+
# @see #rassoc
|
605
|
+
# @see #conform_key
|
606
|
+
def assoc(obj)
|
607
|
+
obj = conform_key( obj )
|
608
|
+
entry = inner_map.assoc( obj )
|
609
|
+
unless entry.nil?
|
610
|
+
value = Values >> entry[ 1 ]
|
611
|
+
entry[ 1 ] = value
|
612
|
+
end
|
613
|
+
entry
|
614
|
+
end
|
615
|
+
|
616
|
+
# Returns `true` if the internalization of `value` is present
|
617
|
+
# for some key in the target.
|
618
|
+
#
|
619
|
+
# @return [Boolean]
|
620
|
+
#
|
621
|
+
# @see Values.internalize
|
622
|
+
def has_value?(value)
|
623
|
+
value = Values >> value
|
624
|
+
each_value.any? { |v| v == value }
|
625
|
+
end
|
626
|
+
|
627
|
+
alias value? has_value?
|
628
|
+
|
629
|
+
# Searches through the map, comparing the externalization of
|
630
|
+
# `value` with the externalized value of each entry using
|
631
|
+
# `==.` Returns a 2-element array containing the key and
|
632
|
+
# externalized value from the matching element or returns or
|
633
|
+
# `nil` if no match is found.
|
634
|
+
#
|
635
|
+
# @return [Array, nil]
|
636
|
+
#
|
637
|
+
# @see #assoc
|
638
|
+
# @see Values.externalize
|
639
|
+
def rassoc(value)
|
640
|
+
value = Values >> value
|
641
|
+
entry = inner_map.detect { |(k, v)|
|
642
|
+
v = Values >> v
|
643
|
+
value == v
|
644
|
+
}
|
645
|
+
if entry
|
646
|
+
entry[ 1 ] = Values >> entry[ 1 ]
|
647
|
+
entry
|
648
|
+
else
|
649
|
+
nil
|
650
|
+
end
|
651
|
+
end
|
652
|
+
|
653
|
+
# Returns a new {Map} containing the contents of `other` (a
|
654
|
+
# {Map}, `Hash`, or other `Hash`-like object) and of the
|
655
|
+
# target {Map}.
|
656
|
+
#
|
657
|
+
# Each entry in `other` with key that is equivalent to a key
|
658
|
+
# in the target (with `String`/`Symbol` indifference) is
|
659
|
+
# treated as a collision.
|
660
|
+
#
|
661
|
+
# @overload merge(other)
|
662
|
+
# Each collision produces an entry with its key from the
|
663
|
+
# entry in target and its value from the entry in
|
664
|
+
# `other`.
|
665
|
+
#
|
666
|
+
# @overload merge(other)
|
667
|
+
# @yieldparam key
|
668
|
+
# The key from the target-side colliding entry.
|
669
|
+
#
|
670
|
+
# @yieldparam oldval
|
671
|
+
# The externalization of value from the target-side
|
672
|
+
# colliding entry.
|
673
|
+
#
|
674
|
+
# @yieldparam newval
|
675
|
+
# The externalization of value from the `other`-side
|
676
|
+
# colliding entry.
|
677
|
+
#
|
678
|
+
# Each collision produces an entry with its key from the
|
679
|
+
# target-side entry and its value from the internalization
|
680
|
+
# of the block call result.
|
681
|
+
#
|
682
|
+
# @return [Map]
|
683
|
+
#
|
684
|
+
# @see #merge!
|
685
|
+
# @see Values.externalize
|
686
|
+
# @see Values.internalize
|
687
|
+
def merge(other)
|
688
|
+
if block_given?
|
689
|
+
dup.merge!( other ){ |*args| yield *args }
|
690
|
+
else
|
691
|
+
dup.merge!( other )
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
# Adds the contents of `other` (a {Map}, `Hash`, or other
|
696
|
+
# `Hash`-like object) to the target {Map}.
|
697
|
+
#
|
698
|
+
# Each entry in `other` with key that is equivalent to a key
|
699
|
+
# in the target (with `String`/`Symbol` indifference) is
|
700
|
+
# treated as a collision.
|
701
|
+
#
|
702
|
+
# @overload merge!(other)
|
703
|
+
# Each collision produces an entry with its key from the
|
704
|
+
# entry in target and its value from the entry in
|
705
|
+
# `other`.
|
706
|
+
#
|
707
|
+
# @overload merge!(other)
|
708
|
+
# @yieldparam key
|
709
|
+
# The key from the target-side colliding entry.
|
710
|
+
#
|
711
|
+
# @yieldparam oldval
|
712
|
+
# The externalization of value from the target-side
|
713
|
+
# colliding entry.
|
714
|
+
#
|
715
|
+
# @yieldparam newval
|
716
|
+
# The externalization of value from the `other`-side
|
717
|
+
# colliding entry.
|
718
|
+
#
|
719
|
+
# Each collision produces an entry with its key from the
|
720
|
+
# target-side entry and its value from the internalization
|
721
|
+
# of the block call result.
|
722
|
+
#
|
723
|
+
# @return [Map]
|
724
|
+
#
|
725
|
+
# @see #merge
|
726
|
+
# @see Values.externalize
|
727
|
+
# @see Values.internalize
|
728
|
+
def merge!(other)
|
729
|
+
other.each_pair do |(key, value)|
|
730
|
+
key = conform_key( key )
|
731
|
+
if block_given? && inner_map.key?(key)
|
732
|
+
self[key] = yield( key, self[key], value )
|
733
|
+
else
|
734
|
+
self[key] = value
|
735
|
+
end
|
736
|
+
end
|
737
|
+
self
|
738
|
+
end
|
739
|
+
|
740
|
+
alias update merge!
|
741
|
+
|
742
|
+
# Removes an entry from the target {Map} and returns a 2-item
|
743
|
+
# `Array` _`[ <key>, <externalized value> ]`_ or the
|
744
|
+
# externalization of the {Map}'s default value if it is
|
745
|
+
# empty.
|
746
|
+
#
|
747
|
+
# Yes, the behavior when the behavior for an empty {Map} with
|
748
|
+
# a non-`nil` default is weird and troublesome, but it is
|
749
|
+
# parallel to the similarly weird behavior of `Hash#shift`.
|
750
|
+
#
|
751
|
+
# @return [Array,Object]
|
752
|
+
#
|
753
|
+
# @see Values.externalize
|
754
|
+
def shift
|
755
|
+
if inner_map.empty?
|
756
|
+
Values >> inner_map.shift
|
757
|
+
else
|
758
|
+
inner_result = inner_map.shift
|
759
|
+
[
|
760
|
+
inner_result[ 0 ],
|
761
|
+
Values >> inner_result[ 1 ]
|
762
|
+
]
|
763
|
+
end
|
764
|
+
end
|
765
|
+
|
766
|
+
# Returns a new {Map} with an {#inner_map} `Hash` created by
|
767
|
+
# using the the target's {#inner_map}'s values as keys and
|
768
|
+
# keys as values.
|
769
|
+
#
|
770
|
+
# @return [Map]
|
771
|
+
def invert
|
772
|
+
self.class.new( inner_map.invert )
|
773
|
+
end
|
774
|
+
|
775
|
+
# Makes the target's {#inner_map} `Hash` compare its keys by
|
776
|
+
# their identities, i.e. it will consider exact same objects
|
777
|
+
# as same keys.
|
778
|
+
#
|
779
|
+
# Not particularly useful for a {Map}, but included for
|
780
|
+
# conformance with the `Hash` API.
|
781
|
+
#
|
782
|
+
# @return [Map]
|
783
|
+
def compare_by_identity
|
784
|
+
inner_map.compare_by_identity
|
785
|
+
self
|
786
|
+
end
|
787
|
+
|
788
|
+
# @!method compare_by_identity?
|
789
|
+
# Returns true if the target's {#inner_map} `Hash` will
|
790
|
+
# compare its keys by their identities.
|
791
|
+
#
|
792
|
+
# Not particularly useful for a {Map}, but included for
|
793
|
+
# conformance with the `Hash` API.
|
794
|
+
#
|
795
|
+
# @return [Boolean]
|
796
|
+
#
|
797
|
+
# @see #compare_by_identity
|
798
|
+
def_delegator :inner_map, :compare_by_identity?
|
799
|
+
|
800
|
+
# Returns a new {List} containing one-dimensional flattening
|
801
|
+
# of the target {Map}. That is, for every key or value that
|
802
|
+
# is an `Array` in the {#inner_map} `Hash`, extract its
|
803
|
+
# elements into the new {List}.
|
804
|
+
#
|
805
|
+
# Unlike `Array#flatten` or {List#flatten}, this method does
|
806
|
+
# not flatten recursively by default. The optional level
|
807
|
+
# argument determines the level of recursion to flatten.
|
808
|
+
#
|
809
|
+
# @return [List]
|
810
|
+
#
|
811
|
+
# @overload flatten
|
812
|
+
#
|
813
|
+
# @overload flatten(level)
|
814
|
+
# @param level [Fixnum]
|
815
|
+
#
|
816
|
+
def flatten(*args)
|
817
|
+
List.new( inner_map.flatten(*args) )
|
818
|
+
end
|
819
|
+
|
820
|
+
private
|
821
|
+
|
822
|
+
attr_accessor \
|
823
|
+
:_default_proc,
|
824
|
+
:_inner_proc_for_default_proc
|
825
|
+
|
826
|
+
def expect_arity(arity, *args)
|
827
|
+
unless arity === args.length
|
828
|
+
raise ArgumentError, "wrong number of arguments (#{args.length} for #{arity})"
|
829
|
+
end
|
830
|
+
end
|
831
|
+
end
|
832
|
+
|
833
|
+
end
|