libis-workflow-mongoid 2.0.2 → 2.0.3
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.
- 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
|