kxi 1.0.0
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 +7 -0
- data/lib/kxi/cli/anonymous_argument.rb +51 -0
- data/lib/kxi/cli/argument.rb +57 -0
- data/lib/kxi/cli/argument_values.rb +84 -0
- data/lib/kxi/cli/explicit_argument.rb +39 -0
- data/lib/kxi/cli/flag_argument.rb +16 -0
- data/lib/kxi/cli/named_argument.rb +60 -0
- data/lib/kxi/cli/verb.rb +281 -0
- data/lib/kxi/collections/array_collection.rb +107 -0
- data/lib/kxi/collections/enumerable.rb +528 -0
- data/lib/kxi/collections/enumerator.rb +32 -0
- data/lib/kxi/collections/hash_collection.rb +101 -0
- data/lib/kxi/collections/protected_collection.rb +20 -0
- data/lib/kxi/exceptions/abstract_exception.rb +35 -0
- data/lib/kxi/exceptions/argument_exception.rb +22 -0
- data/lib/kxi/exceptions/collection_exception.rb +14 -0
- data/lib/kxi/exceptions/invalid_type_exception.rb +33 -0
- data/lib/kxi/exceptions/no_argument_exception.rb +21 -0
- data/lib/kxi/exceptions/not_implemented_exception.rb +13 -0
- data/lib/kxi/exceptions/out_of_range_exception.rb +44 -0
- data/lib/kxi/exceptions/verb_expected_exception.rb +21 -0
- data/lib/kxi/reflection/stack_frame.rb +81 -0
- data/lib/kxi/version.rb +4 -0
- data/lib/kxi.rb +22 -0
- metadata +165 -0
@@ -0,0 +1,107 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2017-12-28.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Collections
|
5
|
+
# Makes array enumerable
|
6
|
+
class ArrayCollection < KXI::Collections::Enumerable
|
7
|
+
# Instantiates the {KXI::Collections::ArrayCollection} class
|
8
|
+
# @param array [Array] Array for enumeration
|
9
|
+
def initialize(array = [])
|
10
|
+
super()
|
11
|
+
@arr = array
|
12
|
+
end
|
13
|
+
|
14
|
+
# Creates a new {KXI::Collections::Enumerator} bound to this instance
|
15
|
+
# @return [KXI::Collections::Enumerator] Enumerator bound to this instance
|
16
|
+
def create_enumerator
|
17
|
+
ArrayEnumerator.new(@arr)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Obtains value in array at specific index
|
21
|
+
# @param index [Number] Index in array to obtain
|
22
|
+
# @return [Object] Value in array at specified index
|
23
|
+
# @raise [KXI::Exceptions::OutOfRangeException] Raised when given index is out of range of array
|
24
|
+
def [](index)
|
25
|
+
raise(KXI::Exceptions::OutOfRangeException.new(index)) if @arr.length == 0
|
26
|
+
raise(KXI::Exceptions::OutOfRangeException.new(index, 0, @arr.length - 1)) if index < 0 or index >= @arr.length
|
27
|
+
lock do
|
28
|
+
return @arr[index]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Sets value of array at specific index
|
33
|
+
# @param index [Number] Index in array to set
|
34
|
+
# @param value [Object] Value to set
|
35
|
+
# @return [Object] Set value
|
36
|
+
# @raise [KXI::Exceptions::OutOfRangeException] Raised when given index is out of range of array
|
37
|
+
# @raise [KXI::Exceptions::CollectionException] Raised when collection cannot be locked for writing
|
38
|
+
def []=(index, value)
|
39
|
+
raise(KXI::Exceptions::OutOfRangeException.new(index)) if @arr.length == 0
|
40
|
+
raise(KXI::Exceptions::OutOfRangeException.new(index, 0, @arr.length - 1)) if index < 0 or index >= @arr.length
|
41
|
+
lock(true) do
|
42
|
+
@arr[index] = value
|
43
|
+
end
|
44
|
+
return value
|
45
|
+
end
|
46
|
+
|
47
|
+
# Adds value into array
|
48
|
+
# @param value [Object] Value to set
|
49
|
+
# @return [Number] Assigned index
|
50
|
+
# @raise [KXI::Exceptions::CollectionException] Raised when collection cannot be locked for writing
|
51
|
+
def add(value)
|
52
|
+
idx = @arr.length
|
53
|
+
lock(true) do
|
54
|
+
@arr.push(value)
|
55
|
+
end
|
56
|
+
return idx
|
57
|
+
end
|
58
|
+
|
59
|
+
# Removes item at specific index
|
60
|
+
# @param index [Number] Index to remove
|
61
|
+
# @return [Object] Removed item
|
62
|
+
# @raise [KXI::Exceptions::OutOfRangeException] Raised when given index is out of range of array
|
63
|
+
# @raise [KXI::Exceptions::CollectionException] Raised when collection cannot be locked for writing
|
64
|
+
def remove_at(index)
|
65
|
+
raise(KXI::Exceptions::OutOfRangeException.new(index)) if @arr.length == 0
|
66
|
+
raise(KXI::Exceptions::OutOfRangeException.new(index, 0, @arr.length - 1)) if index < 0 or index >= @arr.length
|
67
|
+
ret = nil
|
68
|
+
lock(true) do
|
69
|
+
ret = @arr.delete_at(index)
|
70
|
+
end
|
71
|
+
return ret
|
72
|
+
end
|
73
|
+
|
74
|
+
# Enumerates array
|
75
|
+
class ArrayEnumerator < KXI::Collections::Enumerator
|
76
|
+
# Instantiates the {KXI::Collections::ArrayCollection::ArrayEnumerator} class
|
77
|
+
# @param array [Array] Array for enumeration
|
78
|
+
def initialize(array)
|
79
|
+
@arr = array
|
80
|
+
@current = 0
|
81
|
+
end
|
82
|
+
|
83
|
+
# Selects first item in collection
|
84
|
+
# @return [Bool] True if collection contains elements; otherwise false
|
85
|
+
def rewind
|
86
|
+
@current = 0
|
87
|
+
return @arr.length > 0
|
88
|
+
end
|
89
|
+
|
90
|
+
# Advances enumerator to next item
|
91
|
+
# @return [Bool] True if item is available; false otherwise
|
92
|
+
def next
|
93
|
+
@current = @current + 1
|
94
|
+
return @current < @arr.length
|
95
|
+
end
|
96
|
+
|
97
|
+
# Returns current item
|
98
|
+
# @return [Object] Current item
|
99
|
+
def current
|
100
|
+
@arr[@current]
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private :create_enumerator
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,528 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2017-12-28.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Collections
|
5
|
+
# Allows it's superclass to be enumerated
|
6
|
+
# @note Order of items is not guaranteed
|
7
|
+
# @abstract
|
8
|
+
class Enumerable
|
9
|
+
# Instantiates the {KXI::Collections::Enumerable} class
|
10
|
+
def initialize
|
11
|
+
@locks = 0
|
12
|
+
@alter = false
|
13
|
+
end
|
14
|
+
|
15
|
+
# Allows protected usage of {KXI::Collections::Enumerator} within bounds of block
|
16
|
+
# @return [nil]
|
17
|
+
# @yield [enumerator] Protected block of code
|
18
|
+
# @yieldparam enumerator [KXI::Collections::Enumerator] Enumerator bound to this instance
|
19
|
+
# @yieldreturn [void]
|
20
|
+
# @raise [KXI::Exceptions::AbstractException] When method {#create_enumerator} is not implemented in superclass
|
21
|
+
def enumerator
|
22
|
+
lock do
|
23
|
+
yield(create_enumerator)
|
24
|
+
end
|
25
|
+
return nil
|
26
|
+
end
|
27
|
+
|
28
|
+
# Creates a new {KXI::Collections::Enumerator} bound to this instance
|
29
|
+
# @return [KXI::Collections::Enumerator] Enumerator bound to this instance
|
30
|
+
# @raise [KXI::Exceptions::AbstractException] When method is not implemented in superclass
|
31
|
+
# @abstract
|
32
|
+
def create_enumerator
|
33
|
+
raise(KXI::Exceptions::AbstractException.new(Enumerable))
|
34
|
+
end
|
35
|
+
|
36
|
+
# Calls block with each item of collection
|
37
|
+
# @return [nil]
|
38
|
+
# @yield [item] Function that works with items of collection
|
39
|
+
# @yieldparam item [Object] Item of collection
|
40
|
+
# @yieldreturn [Void]
|
41
|
+
def foreach
|
42
|
+
enumerator do |e|
|
43
|
+
return nil unless e.rewind
|
44
|
+
loop do
|
45
|
+
yield(e.current)
|
46
|
+
break unless e.next
|
47
|
+
end
|
48
|
+
end
|
49
|
+
return nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Selects specific values
|
53
|
+
# @return [KXI::Collections::ArrayCollection] Collection of selected values
|
54
|
+
# @yield [item] Function that selects values from items of collection
|
55
|
+
# @yieldparam item [Object] Item of collection
|
56
|
+
# @yieldreturn [Object] Selected value from item
|
57
|
+
def select
|
58
|
+
ret = []
|
59
|
+
foreach do |i|
|
60
|
+
ret.push(yield(i))
|
61
|
+
end
|
62
|
+
return KXI::Collections::ArrayCollection.new(ret)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Concatenates selected collections of items
|
66
|
+
# @return [KXI::Collections::ArrayCollection] Collection of selected values
|
67
|
+
# @yield [item] Function that selects collections of items from given items
|
68
|
+
# @yieldparam item [Object] Item of collection
|
69
|
+
# @yieldreturn [Array, KXI::Collections::Enumerable] Selected collection of items
|
70
|
+
# @raise [KXI::Exceptions::CollectionException] Raised if selection function returns object that is not of type {KXI::Collections::Enumerable} nor Array
|
71
|
+
def select_many
|
72
|
+
ret = []
|
73
|
+
foreach do |i|
|
74
|
+
col = yield(i)
|
75
|
+
if col.is_a?(Array)
|
76
|
+
col.each { |j| ret.push(j) }
|
77
|
+
elsif col.is_a?(Enumerable)
|
78
|
+
col.foreach { |j| ret.push(j) }
|
79
|
+
else
|
80
|
+
raise(KXI::Exceptions::CollectionException.new('Selection function returned invalid type of object!'))
|
81
|
+
end
|
82
|
+
end
|
83
|
+
return KXI::Collections::ArrayCollection.new(ret)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Selects only those items that satisfy given function
|
87
|
+
# @return [KXI::Collections::ArrayCollection] Collection of selected items
|
88
|
+
# @yield [item] Function that evaluates whether item should be selected
|
89
|
+
# @yieldparam item [Object] Item of collection
|
90
|
+
# @yieldreturn [Bool] True if item should be selected; otherwise false
|
91
|
+
def where
|
92
|
+
ret = []
|
93
|
+
foreach do |i|
|
94
|
+
ret.push(i) if yield(i)
|
95
|
+
end
|
96
|
+
return KXI::Collections::ArrayCollection.new(ret)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Counts items in collection
|
100
|
+
# @return [Number] Number of items
|
101
|
+
# @overload count()
|
102
|
+
# Counts the total number of items in collection
|
103
|
+
# @return [Number] Number of items in collection
|
104
|
+
# @overload count()
|
105
|
+
# Counts the number of items that satisfy given function
|
106
|
+
# @return [Number] Number of items that satisfy given function
|
107
|
+
# @yield [item] Function that evaluates whether item should be counted
|
108
|
+
# @yieldparam item [Object] Item of collection
|
109
|
+
# @yieldreturn [Bool] True if item should be counted; otherwise false
|
110
|
+
def count
|
111
|
+
count = 0
|
112
|
+
if block_given?
|
113
|
+
foreach do |item|
|
114
|
+
count = count + 1 if yield(item)
|
115
|
+
end
|
116
|
+
else
|
117
|
+
enumerator do |e|
|
118
|
+
return 0 unless e.rewind
|
119
|
+
count += 1
|
120
|
+
while e.next
|
121
|
+
count = count + 1
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
return count
|
126
|
+
end
|
127
|
+
|
128
|
+
# Checks if collection contains any item
|
129
|
+
# @return [Bool] True if collection contains any item; otherwise false
|
130
|
+
# @overload any()
|
131
|
+
# Checks if collection is not empty
|
132
|
+
# @return [Bool] True if collection is not empty; otherwise false
|
133
|
+
# @overload any()
|
134
|
+
# Checks if there is any item in collection that satisfies given function
|
135
|
+
# @return [Bool] True if collection contains item that satisfies given function; otherwise false
|
136
|
+
# @yield [item] Function used for checking
|
137
|
+
# @yieldparam item [Object] Item of collection
|
138
|
+
# @yieldreturn [Bool] True if item satisfies given function; otherwise false
|
139
|
+
def any
|
140
|
+
if block_given?
|
141
|
+
foreach do |item|
|
142
|
+
return true if yield(item)
|
143
|
+
end
|
144
|
+
return false
|
145
|
+
else
|
146
|
+
enumerator do |e|
|
147
|
+
return false unless e.rewind
|
148
|
+
return true
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Checks if all items in collection satisfy given function
|
154
|
+
# @return [Bool] True if all items of collection satisfy given function; otherwise false
|
155
|
+
# @yield [item] Function used for checking
|
156
|
+
# @yieldparam item [Object] Item of collection
|
157
|
+
# @yieldreturn [Bool] True if item satisfies given function; otherwise false
|
158
|
+
def all
|
159
|
+
foreach do |item|
|
160
|
+
return false unless yield(item)
|
161
|
+
end
|
162
|
+
return true
|
163
|
+
end
|
164
|
+
|
165
|
+
# Gets first item, or default
|
166
|
+
# @overload first(df = nil)
|
167
|
+
# Gets first item of collection
|
168
|
+
# @param df [Object, nil] Default value
|
169
|
+
# @return [Object, nil] First item of collection; default value if collection is empty
|
170
|
+
# @overload first(df = nil)
|
171
|
+
# Gets first item of collection that satisfies given function
|
172
|
+
# @param df [Object, nil] Default value
|
173
|
+
# @return [Object, nil] First item of collection that satisfies given function; default value if no such item exists
|
174
|
+
# @yield [item] Function for evaluation
|
175
|
+
# @yieldparam item [Object] Item of collection
|
176
|
+
# @yieldreturn [Bool] True if item satisfies given function; false otherwise
|
177
|
+
def first(df = nil)
|
178
|
+
if block_given?
|
179
|
+
foreach do |item|
|
180
|
+
return item if yield(item)
|
181
|
+
end
|
182
|
+
else
|
183
|
+
enumerator do |e|
|
184
|
+
return e.current if e.rewind
|
185
|
+
end
|
186
|
+
end
|
187
|
+
return df
|
188
|
+
end
|
189
|
+
|
190
|
+
|
191
|
+
# Gets first item
|
192
|
+
# @overload first!()
|
193
|
+
# Gets first item of collection
|
194
|
+
# @return [Object, nil] First item of collection
|
195
|
+
# @raise [KXI::Exceptions::CollectionException] Raised when collection is empty
|
196
|
+
# @overload first!()
|
197
|
+
# Gets first item of collection that satisfies given function
|
198
|
+
# @return [Object, nil] First item of collection that satisfies given function
|
199
|
+
# @raise [KXI::Exceptions::CollectionException] Raised when collection contains no element that satisfies given function
|
200
|
+
# @yield [item] Function for evaluation
|
201
|
+
# @yieldparam item [Object] Item of collection
|
202
|
+
# @yieldreturn [Bool] True if item satisfies given function; false otherwise
|
203
|
+
def first!
|
204
|
+
if block_given?
|
205
|
+
foreach do |item|
|
206
|
+
return item if yield(item)
|
207
|
+
end
|
208
|
+
else
|
209
|
+
enumerator do |e|
|
210
|
+
return e.current if e.rewind
|
211
|
+
end
|
212
|
+
end
|
213
|
+
raise(KXI::Exceptions::CollectionException.new("Collection contains no #{(block_given? ? 'such ' : '')}items!"))
|
214
|
+
end
|
215
|
+
|
216
|
+
# Gets last item, or default
|
217
|
+
# @overload last(df = nil)
|
218
|
+
# Gets last item of collection
|
219
|
+
# @param df [Object, nil] Default value
|
220
|
+
# @return [Object, nil] Last item of collection; default value if collection is empty
|
221
|
+
# @overload last(df = nil)
|
222
|
+
# Gets last item of collection that satisfies given function
|
223
|
+
# @param df [Object, nil] Default value
|
224
|
+
# @return [Object, nil] Last item of collection that satisfies given function; default value if no such item exists
|
225
|
+
# @yield [item] Function for evaluation
|
226
|
+
# @yieldparam item [Object] Item of collection
|
227
|
+
# @yieldreturn [Bool] True if item satisfies given function; false otherwise
|
228
|
+
def last(df = nil)
|
229
|
+
found = false
|
230
|
+
ret = nil
|
231
|
+
if block_given?
|
232
|
+
foreach do |item|
|
233
|
+
if yield(item)
|
234
|
+
ret = item
|
235
|
+
found = true
|
236
|
+
end
|
237
|
+
end
|
238
|
+
else
|
239
|
+
foreach do |item|
|
240
|
+
ret = item
|
241
|
+
found = true
|
242
|
+
end
|
243
|
+
end
|
244
|
+
return df unless found
|
245
|
+
return ret
|
246
|
+
end
|
247
|
+
|
248
|
+
# Gets last item
|
249
|
+
# @overload last!()
|
250
|
+
# Gets last item of collection
|
251
|
+
# @return [Object, nil] Last item of collection
|
252
|
+
# @raise [KXI::Exceptions::CollectionException] Raised if collection is empty
|
253
|
+
# @overload last!()
|
254
|
+
# Gets last item of collection that satisfies given function
|
255
|
+
# @return [Object, nil] Last item of collection that satisfies given function
|
256
|
+
# @raise [KXI::Exceptions::CollectionException] Raised when collection doesn't contain element that satisfies given function
|
257
|
+
# @yield [item] Function for evaluation
|
258
|
+
# @yieldparam item [Object] Item of collection
|
259
|
+
# @yieldreturn [Bool] True if item satisfies given function; false otherwise
|
260
|
+
def last!
|
261
|
+
found = false
|
262
|
+
ret = nil
|
263
|
+
if block_given?
|
264
|
+
foreach do |item|
|
265
|
+
if yield(item)
|
266
|
+
ret = item
|
267
|
+
found = true
|
268
|
+
end
|
269
|
+
end
|
270
|
+
else
|
271
|
+
foreach do |item|
|
272
|
+
ret = item
|
273
|
+
found = true
|
274
|
+
end
|
275
|
+
end
|
276
|
+
raise(KXI::Exceptions::CollectionException.new("Collection contains no #{(block_given? ? 'such ' : '')}items!")) unless found
|
277
|
+
return ret
|
278
|
+
end
|
279
|
+
|
280
|
+
# Skips given number of items and returns the rest
|
281
|
+
# @param count [Number] Number of items to skip
|
282
|
+
# @return [KXI::Collections::ArrayCollection] Collection of items that weren't skipped
|
283
|
+
def skip(count)
|
284
|
+
ret = []
|
285
|
+
foreach do |item|
|
286
|
+
if count > 0
|
287
|
+
count -= 1
|
288
|
+
else
|
289
|
+
ret.push(item)
|
290
|
+
end
|
291
|
+
end
|
292
|
+
return KXI::Collections::ArrayCollection.new(ret)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Returns first N elements
|
296
|
+
# @param count [Number] Number of items to take
|
297
|
+
# @return [KXI::Collections::ArrayCollection] Collection of first N items
|
298
|
+
def take(count)
|
299
|
+
ret = []
|
300
|
+
foreach do |item|
|
301
|
+
break unless count > 0
|
302
|
+
count -= 1
|
303
|
+
ret.push(item)
|
304
|
+
end
|
305
|
+
return KXI::Collections::ArrayCollection.new(ret)
|
306
|
+
end
|
307
|
+
|
308
|
+
# Aggregates all items of collection
|
309
|
+
# @param start [Object, nil] Starting value
|
310
|
+
# @return [Object] Aggregated value
|
311
|
+
# @yield [current, item] Aggregation function
|
312
|
+
# @yieldparam current [Object] Current aggregate
|
313
|
+
# @yieldparam item [Object] Item of collection
|
314
|
+
# @yieldreturn [Object] Aggregated value
|
315
|
+
def aggregate(start = nil)
|
316
|
+
foreach do |item|
|
317
|
+
start = yield(start, item)
|
318
|
+
end
|
319
|
+
return start
|
320
|
+
end
|
321
|
+
|
322
|
+
# Orders collection to ascending order
|
323
|
+
# @note Ordering is done using merge sort algorithm
|
324
|
+
# @return [KXI::Collections::ArrayCollection] Ordered collection
|
325
|
+
# @overload order_by()
|
326
|
+
# Orders collection using spaceship operator (<=>)
|
327
|
+
# @return [KXI::Collections::ArrayCollection] Ordered collection
|
328
|
+
# @overload order_by()
|
329
|
+
# Orders collection using given comparison function
|
330
|
+
# @return [KXI::Collections::ArrayCollection] Ordered collection
|
331
|
+
# @yield [what, with] Comparison function
|
332
|
+
# @yieldparam what [Object] Object that is being compared
|
333
|
+
# @yieldparam with [Object] Object that is being compared to
|
334
|
+
# @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
|
335
|
+
def order_by(&block)
|
336
|
+
block = lambda { |a, b| a <=> b } if block == nil
|
337
|
+
groups = []
|
338
|
+
g = []
|
339
|
+
foreach do |item|
|
340
|
+
if g.length == 0
|
341
|
+
g.push(item)
|
342
|
+
else
|
343
|
+
if block.call(g.last, item) <= 0
|
344
|
+
g.push(item)
|
345
|
+
else
|
346
|
+
groups.push(g)
|
347
|
+
g = [item]
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
groups.push(g) if g.length > 0
|
352
|
+
return KXI::Collections::ArrayCollection.new if groups.length == 0
|
353
|
+
while groups.length > 1
|
354
|
+
merges = []
|
355
|
+
while groups.length >= 2
|
356
|
+
merges.push(merge(groups.shift, groups.shift, &block))
|
357
|
+
end
|
358
|
+
merges.push(groups[0]) if groups.length > 0
|
359
|
+
groups = merges
|
360
|
+
end
|
361
|
+
return KXI::Collections::ArrayCollection.new(groups[0])
|
362
|
+
end
|
363
|
+
|
364
|
+
# Orders collection to descending order
|
365
|
+
# @note Ordering is done using merge sort algorithm
|
366
|
+
# @return [KXI::Collections::ArrayCollection] Ordered collection
|
367
|
+
# @overload order_by()
|
368
|
+
# Orders collection using spaceship operator (<=>)
|
369
|
+
# @return [KXI::Collections::ArrayCollection] Ordered collection
|
370
|
+
# @overload order_by()
|
371
|
+
# Orders collection using given comparison function
|
372
|
+
# @return [KXI::Collections::ArrayCollection] Ordered collection
|
373
|
+
# @yield [what, with] Comparison function
|
374
|
+
# @yieldparam what [Object] Object that is being compared
|
375
|
+
# @yieldparam with [Object] Object that is being compared to
|
376
|
+
# @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
|
377
|
+
def order_by_descending(&block)
|
378
|
+
if block == nil
|
379
|
+
return order_by { |a, b| b <=> a }
|
380
|
+
else
|
381
|
+
return order_by { |a, b| -block.call(a, b) }
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
# Selects only those items that are of at least one of specified types
|
386
|
+
# @param klass [Class] Types to use for selection
|
387
|
+
# @return [KXI::Collections::ArrayCollection] Selected items
|
388
|
+
# @see Object#is_a?
|
389
|
+
def of_type(*klass)
|
390
|
+
ret = []
|
391
|
+
foreach do |i|
|
392
|
+
klass.each do |k|
|
393
|
+
if i.is_a?(k)
|
394
|
+
ret.push(i)
|
395
|
+
break
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
return KXI::Collections::ArrayCollection.new(ret)
|
400
|
+
end
|
401
|
+
|
402
|
+
# Selects only those items that are exactly of at least one of specified types
|
403
|
+
# @param klass [Class] Types to use for selection
|
404
|
+
# @return [KXI::Collections::ArrayCollection] Selected items
|
405
|
+
# @see Object#class
|
406
|
+
def of_type!(*klass)
|
407
|
+
ret = []
|
408
|
+
foreach do |i|
|
409
|
+
klass.each do |k|
|
410
|
+
if i.class == k
|
411
|
+
ret.push(i)
|
412
|
+
break
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
return KXI::Collections::ArrayCollection.new(ret)
|
417
|
+
end
|
418
|
+
|
419
|
+
# Obtains item with the first maximal value
|
420
|
+
# @overload max()
|
421
|
+
# Compares items using spaceship operator (<=>)
|
422
|
+
# @return [Object] Item with first maximal value; nil if collection is empty
|
423
|
+
# @overload max()
|
424
|
+
# Compares items using given comparison function
|
425
|
+
# @return [Object] Item with first maximal value; nil if collection is empty
|
426
|
+
# @yield [what, with] Comparison function
|
427
|
+
# @yieldparam what [Object] Object that is being compared
|
428
|
+
# @yieldparam with [Object] Object that is being compared to
|
429
|
+
# @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
|
430
|
+
def max(&block)
|
431
|
+
block = lambda { |a, b| a <=> b } if block == nil
|
432
|
+
ret = nil
|
433
|
+
first = true
|
434
|
+
foreach do |i|
|
435
|
+
if first
|
436
|
+
ret = i
|
437
|
+
first = false
|
438
|
+
elsif block.call(i, ret) > 0
|
439
|
+
ret = i
|
440
|
+
end
|
441
|
+
end
|
442
|
+
return ret
|
443
|
+
end
|
444
|
+
|
445
|
+
# Obtains item with the first minimal value
|
446
|
+
# @overload min()
|
447
|
+
# Compares items using spaceship operator (<=>)
|
448
|
+
# @return [Object] Item with first minimal value; nil if collection is empty
|
449
|
+
# @overload min()
|
450
|
+
# Compares items using given comparison function
|
451
|
+
# @return [Object] Item with first minimal value; nil if collection is empty
|
452
|
+
# @yield [what, with] Comparison function
|
453
|
+
# @yieldparam what [Object] Object that is being compared
|
454
|
+
# @yieldparam with [Object] Object that is being compared to
|
455
|
+
# @yieldreturn [Number] Less then 0 if what < with, equal to 0 if what = with and bigger than 0 if what > with
|
456
|
+
def min(&block)
|
457
|
+
block = lambda { |a, b| a <=> b } if block == nil
|
458
|
+
ret = nil
|
459
|
+
first = true
|
460
|
+
foreach do |i|
|
461
|
+
if first
|
462
|
+
ret = i
|
463
|
+
first = false
|
464
|
+
elsif block.call(i, ret) < 0
|
465
|
+
ret = i
|
466
|
+
end
|
467
|
+
end
|
468
|
+
return ret
|
469
|
+
end
|
470
|
+
|
471
|
+
# Coverts collection to array
|
472
|
+
# @return [Array] Array equivalent to collection
|
473
|
+
def to_array
|
474
|
+
ret = []
|
475
|
+
foreach { |item| ret.push(item) }
|
476
|
+
return ret
|
477
|
+
end
|
478
|
+
|
479
|
+
# Merges two arrays in order using comparison function
|
480
|
+
# @param a [Array] First array
|
481
|
+
# @param b [Array] Second array
|
482
|
+
# @return [Array] Merged ordered array
|
483
|
+
def merge(a, b, &comp)
|
484
|
+
ret = []
|
485
|
+
while a.length > 0 or b.length > 0
|
486
|
+
if a.length > 0 and b.length > 0
|
487
|
+
c = comp.call(a[0], b[0])
|
488
|
+
if c <= 0
|
489
|
+
ret.push(a.shift)
|
490
|
+
else
|
491
|
+
ret.push(b.shift)
|
492
|
+
end
|
493
|
+
else
|
494
|
+
ret.push(a.length > 0 ? a.shift : b.shift)
|
495
|
+
end
|
496
|
+
end
|
497
|
+
return ret
|
498
|
+
end
|
499
|
+
|
500
|
+
# Indicates whether collection can be altered
|
501
|
+
# @return [Bool] True if collection can be altered; otherwise false
|
502
|
+
def can_alter?
|
503
|
+
@locks == 0 and not @alter
|
504
|
+
end
|
505
|
+
|
506
|
+
def lock(write = false)
|
507
|
+
if write
|
508
|
+
raise(KXI::Exceptions::CollectionException.new('Collection cannot be locked for writing!')) unless can_alter?
|
509
|
+
@alter = true
|
510
|
+
yield
|
511
|
+
else
|
512
|
+
raise(KXI::Exceptions::CollectionException.new('Collection cannot be locked for reading!')) if @alter
|
513
|
+
@locks += 1
|
514
|
+
yield
|
515
|
+
end
|
516
|
+
ensure
|
517
|
+
if write
|
518
|
+
@alter = false
|
519
|
+
else
|
520
|
+
@locks -= 1
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
protected :lock, :create_enumerator
|
525
|
+
private :merge
|
526
|
+
end
|
527
|
+
end
|
528
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# Created by Matyáš Pokorný on 2017-12-28.
|
2
|
+
|
3
|
+
module KXI
|
4
|
+
module Collections
|
5
|
+
# Allows iteration over a collection
|
6
|
+
# @abstract
|
7
|
+
class Enumerator
|
8
|
+
# Selects first item in collection
|
9
|
+
# @return [Bool] True if collection contains elements; otherwise false
|
10
|
+
# @abstract
|
11
|
+
def rewind
|
12
|
+
raise(KXI::Exceptions::AbstractException.new(Enumerator))
|
13
|
+
end
|
14
|
+
|
15
|
+
# Advances enumerator to next item
|
16
|
+
# @return [Bool] True if item is available; false otherwise
|
17
|
+
# @raise [KXI::Exceptions::AbstractException] When method is not implemented in superclass
|
18
|
+
# @abstract
|
19
|
+
def next
|
20
|
+
raise(KXI::Exceptions::AbstractException.new(Enumerator))
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns current item
|
24
|
+
# @return [Object] Current item
|
25
|
+
# @raise [KXI::Exceptions::AbstractException] When method is not implemented in superclass
|
26
|
+
# @abstract
|
27
|
+
def current
|
28
|
+
raise(KXI::Exceptions::AbstractException.new(Enumerator))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|