sarah 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/sarah.rb +388 -0
- data/sarah.gemspec +15 -0
- data/test/00class.rb +16 -0
- data/test/01instance.rb +31 -0
- data/test/02new.rb +39 -0
- data/test/03set_get.rb +59 -0
- data/test/04stack.rb +34 -0
- data/test/run_tests.sh +8 -0
- metadata +77 -0
data/lib/sarah.rb
ADDED
@@ -0,0 +1,388 @@
|
|
1
|
+
# Sarah - Combination sequential array/random-access hash
|
2
|
+
#
|
3
|
+
# Sequential values beginning at key (index) 0 are stored in an array.
|
4
|
+
# Values with sparse or non-numeric keys are stored in a hash. Values
|
5
|
+
# may migrate between the two if holes in the sequential key sequence
|
6
|
+
# are created or removed.
|
7
|
+
#
|
8
|
+
# @author Brian Katzung <briank@kappacs.com>, Kappa Computer Solutions, LLC
|
9
|
+
# @copyright 2013 Brian Katzung and Kappa Computer Solutions, LLC
|
10
|
+
# @license MIT License
|
11
|
+
|
12
|
+
class Sarah
|
13
|
+
|
14
|
+
# @!attribute default
|
15
|
+
# The default value returned for non-existent keys.
|
16
|
+
attr_accessor :default
|
17
|
+
|
18
|
+
# @!attribute default_proc
|
19
|
+
# @return [Proc]
|
20
|
+
# The default proc to call for non-existent keys. This takes precedence
|
21
|
+
# over the default value. It is passed the Sarah and the referenced key
|
22
|
+
# (or nil) as parameters.
|
23
|
+
attr_accessor :default_proc
|
24
|
+
|
25
|
+
# Initialize a new instance.
|
26
|
+
#
|
27
|
+
# If passed a block, the block is called to provide default values
|
28
|
+
# instead of using the :default option value. The block is passed the
|
29
|
+
# hash and the requested key (or nil for a shift or pop on an empty
|
30
|
+
# sequential array).
|
31
|
+
#
|
32
|
+
# @param opts [Array] Setup options.
|
33
|
+
# @option opts :default The default value to return for a non-existent key.
|
34
|
+
# @option opts :default_proc The default proc to call for a non-existent
|
35
|
+
# key.
|
36
|
+
# @option opts :array An array (or hash!) to use for initialization (first).
|
37
|
+
# @option opts :hash A hash (or array!) to use for initialization (second).
|
38
|
+
# @option opts :from An array or hash to use for initialization (third).
|
39
|
+
def initialize (opts = {}, &block)
|
40
|
+
clear
|
41
|
+
@default = opts[:default]
|
42
|
+
@default_proc = block || opts[:default_proc]
|
43
|
+
merge! opts[:array], opts[:hash], opts[:from]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Clear all sequential array and random-access hash values.
|
47
|
+
#
|
48
|
+
# @return [Sarah]
|
49
|
+
def clear
|
50
|
+
@seq = []
|
51
|
+
@rnd = {}
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
# Test key existence.
|
56
|
+
#
|
57
|
+
# @param key The key to check for existence.
|
58
|
+
# @return [Boolean]
|
59
|
+
def has_key? (key)
|
60
|
+
@rnd.has_key?(key) or (key.is_a? Integer and
|
61
|
+
key >= -@seq.size and key < @seq.size)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Get a value by sequential or random-access key. If the key does not
|
65
|
+
# exist, the default value or initial block value is returned. If
|
66
|
+
# called with a range or an additional length parameter, a slice is
|
67
|
+
# returned instead.
|
68
|
+
#
|
69
|
+
# @param key The key for the value to be returned, or a range.
|
70
|
+
# @param len [Integer] An optional slice length.
|
71
|
+
# @return [Object, Array]
|
72
|
+
def [] (key, len = nil)
|
73
|
+
if len
|
74
|
+
slice(key, len)
|
75
|
+
elsif key.is_a? Range
|
76
|
+
slice(key)
|
77
|
+
elsif @rnd.has_key? key
|
78
|
+
@rnd[key]
|
79
|
+
elsif key.is_a? Integer and key >= -@seq.size and key < @seq.size
|
80
|
+
@seq[key]
|
81
|
+
else
|
82
|
+
@default_proc ? @default_proc.call(self, key) : @default
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Get a value by sequential or random-access key. If the key does not
|
87
|
+
# exist, the local default or block value is returned. If no local or
|
88
|
+
# block value is supplied, a KeyError exception is raised instead.
|
89
|
+
#
|
90
|
+
# If a local block is supplied, it is passed the key as a parameter.
|
91
|
+
#
|
92
|
+
# @param key The key for the value to be returned.
|
93
|
+
# @param default The value to return if the key does not exist.
|
94
|
+
def fetch (key, *default)
|
95
|
+
if @rnd.has_key? key
|
96
|
+
@rnd[key]
|
97
|
+
elsif key.is_a? Integer and key >= -@seq.size and key < @seq.size
|
98
|
+
@seq[key]
|
99
|
+
elsif default.size > 0
|
100
|
+
default[0]
|
101
|
+
elsif block_given?
|
102
|
+
yield key
|
103
|
+
else
|
104
|
+
raise KeyError.new("key not found: #{key}")
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Shift and return the first sequential array value.
|
109
|
+
#
|
110
|
+
# @return Object
|
111
|
+
def shift
|
112
|
+
if @seq.size > 0
|
113
|
+
@seq.shift
|
114
|
+
elsif @default_proc
|
115
|
+
@default_proc.call self, nil
|
116
|
+
else
|
117
|
+
@default
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Pop and return the last sequential array value
|
122
|
+
def pop
|
123
|
+
if @seq.size > 0
|
124
|
+
@seq.pop
|
125
|
+
elsif @default_proc
|
126
|
+
@default_proc.call self, nil
|
127
|
+
else
|
128
|
+
@default
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Iterate a block (required) over each key and value.
|
133
|
+
#
|
134
|
+
# @return [Sarah]
|
135
|
+
def each
|
136
|
+
@seq.each_index { |i| yield(i, @seq[i]) }
|
137
|
+
@rnd.each { |kv| yield(*kv) }
|
138
|
+
self
|
139
|
+
end
|
140
|
+
|
141
|
+
alias_method :each_pair, :each
|
142
|
+
|
143
|
+
# Iterate a block (required) over each key.
|
144
|
+
#
|
145
|
+
# @return [Sarah]
|
146
|
+
def each_index
|
147
|
+
@seq.each_index { |i| yield(i) }
|
148
|
+
@rnd.keys.each { |k| yield(k) }
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
# Set a value by sequential or random-access key.
|
153
|
+
#
|
154
|
+
# @param key The key for which the value should be set.
|
155
|
+
# @param value The value to set for the key.
|
156
|
+
# @return Returns the value.
|
157
|
+
def []= (key, value)
|
158
|
+
if key.is_a? Integer and key.abs <= @seq.size
|
159
|
+
key += @seq.size if key < 0
|
160
|
+
@seq[key] = value
|
161
|
+
@rnd.delete key
|
162
|
+
else
|
163
|
+
@rnd[key] = value
|
164
|
+
end
|
165
|
+
|
166
|
+
# Move adjacent random-access keys to the sequential array
|
167
|
+
key = @seq.size
|
168
|
+
while @rnd.has_key? key
|
169
|
+
@seq[key] = @rnd.delete key
|
170
|
+
key += 1
|
171
|
+
end
|
172
|
+
|
173
|
+
value
|
174
|
+
end
|
175
|
+
|
176
|
+
# Set values and/or key/value pairs.
|
177
|
+
#
|
178
|
+
# <tt>set([val1, ..., valN,] [key1 => kval1, ..., keyN => kvalN])</tt>
|
179
|
+
#
|
180
|
+
# @param list [Array] A list of sequential values or random-access
|
181
|
+
# key/value pairs to set.
|
182
|
+
# @return [Sarah]
|
183
|
+
def set (*list)
|
184
|
+
hash = (list.size > 0 and list[-1].is_a? Hash) ? list.pop : nil
|
185
|
+
merge! list
|
186
|
+
merge! hash if hash
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
# Set key/value pairs.
|
191
|
+
#
|
192
|
+
# <tt>set_kv(key1, val1, ..., keyN, valN)</tt>
|
193
|
+
#
|
194
|
+
# @param kvlist [Array] The list of key/value pairs to set.
|
195
|
+
# @return [Sarah]
|
196
|
+
def set_pairs (*kvlist)
|
197
|
+
kvlist.each_slice(2) { |kv| self.[]=(*kv) }
|
198
|
+
self
|
199
|
+
end
|
200
|
+
|
201
|
+
alias_method :set_kv, :set_pairs
|
202
|
+
|
203
|
+
# Append arrays/Sarahs (or merge hashes) of values.
|
204
|
+
#
|
205
|
+
# @param ahlist [Array<Array, Hash, Sarah>] The structures to append.
|
206
|
+
# @return [Sarah]
|
207
|
+
def append! (*ahlist)
|
208
|
+
ahlist.each do |ah|
|
209
|
+
if ah.respond_to? :seq_values and ah.respond_to? :rnd
|
210
|
+
push *ah.seq_values
|
211
|
+
merge! ah.rnd
|
212
|
+
elsif ah.respond_to? :each_pair
|
213
|
+
merge! ah
|
214
|
+
elsif ah.respond_to? :each_index
|
215
|
+
push *ah
|
216
|
+
end
|
217
|
+
end
|
218
|
+
self
|
219
|
+
end
|
220
|
+
|
221
|
+
# Insert arrays/Sarahs (or merge hashes) of values.
|
222
|
+
#
|
223
|
+
# @param ahlist [Array<Array, Hash, Sarah>] The structures to insert.
|
224
|
+
# @return [Sarah]
|
225
|
+
def insert! (*ahlist)
|
226
|
+
ahlist.reverse_each do |ah|
|
227
|
+
if ah.respond_to? :seq_values and ah.respond_to? :rnd
|
228
|
+
unshift @ah.seq_values
|
229
|
+
merge! ah.rnd
|
230
|
+
elsif ah.respond_to? :each_pair
|
231
|
+
merge! ah
|
232
|
+
elsif ah.respond_to? :each_index
|
233
|
+
unshift *ah
|
234
|
+
end
|
235
|
+
end
|
236
|
+
self
|
237
|
+
end
|
238
|
+
|
239
|
+
# Load/merge from a hash and/or array/Sarah (beginning at key 0).
|
240
|
+
#
|
241
|
+
# @param ahlist [Array<Array, Hash, Sarah>] The structures to load/merge.
|
242
|
+
# @return [Sarah]
|
243
|
+
def merge! (*ahlist)
|
244
|
+
ahlist.each do |ah|
|
245
|
+
if ah.respond_to? :each_pair
|
246
|
+
ah.each_pair { |kv| self.[]=(*kv) }
|
247
|
+
elsif ah.respond_to? :each_index
|
248
|
+
ah.each_index { |i| self[i] = ah[i] }
|
249
|
+
end
|
250
|
+
end
|
251
|
+
self
|
252
|
+
end
|
253
|
+
|
254
|
+
alias_method :update, :merge!
|
255
|
+
|
256
|
+
# Unshift (insert) sequential values beginning at key 0.
|
257
|
+
#
|
258
|
+
# @param vlist [Array] A list of values to unshift (insert).
|
259
|
+
# @return [Sarah]
|
260
|
+
def unshift (*vlist)
|
261
|
+
(@seq.size...@seq.size+vlist.size).each { |k| @rnd.delete k }
|
262
|
+
@seq.unshift *vlist
|
263
|
+
self
|
264
|
+
end
|
265
|
+
|
266
|
+
# Push (append) sequential values.
|
267
|
+
#
|
268
|
+
# @param vlist [Array] A list of values to push (append).
|
269
|
+
# @return [Sarah]
|
270
|
+
def push (*vlist)
|
271
|
+
(@seq.size...@seq.size+vlist.size).each { |k| @rnd.delete k }
|
272
|
+
@seq.push *vlist
|
273
|
+
self
|
274
|
+
end
|
275
|
+
|
276
|
+
# Delete by sequential or random-access key, returning any existing value
|
277
|
+
# (or the default or block value, otherwise).
|
278
|
+
#
|
279
|
+
# @param key The key to be deleted.
|
280
|
+
# @return The value of the deleted key.
|
281
|
+
def delete_key (key)
|
282
|
+
return @rnd.delete key if @rnd.has_key? key
|
283
|
+
if key.is_a? Integer
|
284
|
+
key += @seq.size if key < 0
|
285
|
+
if key >= 0 and key < @seq.size
|
286
|
+
result = @seq[key]
|
287
|
+
|
288
|
+
# Move any following keys to the random-access hash
|
289
|
+
(key+1...@seq.size).each { |i| @rnd[i] = @seq[i] }
|
290
|
+
|
291
|
+
# Truncate the sequential array
|
292
|
+
@seq = @seq[0...key]
|
293
|
+
|
294
|
+
return result
|
295
|
+
end
|
296
|
+
end
|
297
|
+
@default_proc ? @default_proc.call(self, nil) : @default
|
298
|
+
end
|
299
|
+
|
300
|
+
# Return the sequential array size.
|
301
|
+
#
|
302
|
+
# @return [Integer]
|
303
|
+
def seq_size; @seq.size; end
|
304
|
+
|
305
|
+
# Return the random-access hash size.
|
306
|
+
#
|
307
|
+
# @return [Integer]
|
308
|
+
def rnd_size; @rnd.size; end
|
309
|
+
|
310
|
+
# Return the total size.
|
311
|
+
#
|
312
|
+
# @return [Integer]
|
313
|
+
def size; @seq.size + @rnd.size; end
|
314
|
+
|
315
|
+
alias_method :seq_length, :seq_size
|
316
|
+
alias_method :rnd_length, :rnd_size
|
317
|
+
alias_method :length, :size
|
318
|
+
|
319
|
+
# Return the sequential-access array.
|
320
|
+
#
|
321
|
+
# @return [Array]
|
322
|
+
def seq; @seq; end
|
323
|
+
|
324
|
+
# Return the random-access hash.
|
325
|
+
#
|
326
|
+
# @return [Hash]
|
327
|
+
def rnd; @rnd; end
|
328
|
+
|
329
|
+
# Return the sequential array keys (indexes).
|
330
|
+
#
|
331
|
+
# @return [Array<Integer>]
|
332
|
+
def seq_keys; 0...@seq.size; end
|
333
|
+
|
334
|
+
# Return the random-access hash keys.
|
335
|
+
#
|
336
|
+
# @return [Array]
|
337
|
+
def rnd_keys; @rnd.keys; end
|
338
|
+
|
339
|
+
# Return all the keys.
|
340
|
+
#
|
341
|
+
# @return [Array]
|
342
|
+
def keys; seq_keys.to_a + rnd_keys; end
|
343
|
+
|
344
|
+
# Return the hash values.
|
345
|
+
def rnd_values; @rnd.values; end
|
346
|
+
|
347
|
+
# Return all the values.
|
348
|
+
def values; @seq + @rnd.values; end
|
349
|
+
|
350
|
+
alias_method :seq_values, :seq
|
351
|
+
|
352
|
+
# Slice all.
|
353
|
+
#
|
354
|
+
# @return [Sarah, Object]
|
355
|
+
def slice (*params)
|
356
|
+
res = @seq.slice *params
|
357
|
+
(res.is_a? Array) ? (self.class.new :array => res, :hash => @rnd,
|
358
|
+
:default_proc => @default_proc, :default => @default) : res
|
359
|
+
end
|
360
|
+
|
361
|
+
# Slice all in place.
|
362
|
+
#
|
363
|
+
# @return [Sarah, Object]
|
364
|
+
def slice! (*params)
|
365
|
+
res = @seq.slice! *params
|
366
|
+
(res.is_a? Array) ? (self.class.new :array => res, :hash => @rnd,
|
367
|
+
:default_proc => @default_proc, :default => @default) : res
|
368
|
+
end
|
369
|
+
|
370
|
+
# Slice sequential.
|
371
|
+
#
|
372
|
+
# @return [Sarah, Object]
|
373
|
+
def seq_slice (*params)
|
374
|
+
res = @seq.slice *params
|
375
|
+
(res.is_a? Array) ? (self.class.new :array => res,
|
376
|
+
:default_proc => @default_proc, :default => @default) : res
|
377
|
+
end
|
378
|
+
|
379
|
+
# Slice sequential in place.
|
380
|
+
#
|
381
|
+
# @return [Sarah, Object]
|
382
|
+
def seq_slice! (*params)
|
383
|
+
res = @seq.slice! *params
|
384
|
+
(res.is_a? Array) ? (self.class.new :array => res,
|
385
|
+
:default_proc => @default_proc, :default => @default) : res
|
386
|
+
end
|
387
|
+
|
388
|
+
end
|
data/sarah.gemspec
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = "sarah"
|
3
|
+
s.version = "0.0.1"
|
4
|
+
s.date = "2013-07-08"
|
5
|
+
s.authors = ["Brian Katzung"]
|
6
|
+
s.email = ["briank@kappacs.com"]
|
7
|
+
s.homepage = "http://rubygems.org/gems/sarah"
|
8
|
+
s.summary = "Sequential array/random-access hash"
|
9
|
+
s.description = "Implements a hybrid data structure composed of a sequential array and random-access hash"
|
10
|
+
s.license = "MIT"
|
11
|
+
|
12
|
+
s.files = Dir.glob("lib/**/*") + %w{sarah.gemspec}
|
13
|
+
s.test_files = Dir.glob("test/**/*")
|
14
|
+
s.require_path = 'lib'
|
15
|
+
end
|
data/test/00class.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'sarah'
|
3
|
+
|
4
|
+
class TestSarah < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def test_cmethod_new
|
7
|
+
assert_respond_to Sarah, :new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_new_1
|
11
|
+
assert Sarah.new, "Failed to create new Sarah"
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
# END
|
data/test/01instance.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'sarah'
|
3
|
+
|
4
|
+
class TestSarah < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@s = Sarah.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_imethods_accessors
|
11
|
+
[
|
12
|
+
:default, :default=, :default_proc, :default_proc=
|
13
|
+
].each { |method| assert_respond_to @s, method }
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_imethods_user_api
|
17
|
+
[
|
18
|
+
:clear, :has_key?, :[], :[]=, :fetch,
|
19
|
+
:shift, :pop, :each, :each_pair, :each_index,
|
20
|
+
:set, :set_pairs, :set_kv, :append!, :merge!,
|
21
|
+
:unshift, :push, :delete_key,
|
22
|
+
:size, :seq_size, :rnd_size, :length, :seq_length, :rnd_length,
|
23
|
+
:seq, :rnd, :keys, :seq_keys, :rnd_keys,
|
24
|
+
:values, :seq_values, :rnd_values,
|
25
|
+
:slice, :slice!, :seq_slice, :seq_slice!
|
26
|
+
].each { |method| assert_respond_to @s, method }
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
# END
|
data/test/02new.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'sarah'
|
3
|
+
|
4
|
+
class TestSarah < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def test_new_array
|
7
|
+
s = Sarah.new(:array => [1, 2])
|
8
|
+
assert_equal([1, 2], s.seq, "new array seq [...]")
|
9
|
+
assert_equal({}, s.rnd, "new array rnd {}")
|
10
|
+
assert_equal(2, s.seq_size, "new array seq_size")
|
11
|
+
assert_equal(0, s.rnd_size, "new array rnd_size")
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_new_hash
|
15
|
+
s = Sarah.new(:hash => { :one => 1, :two => 2 })
|
16
|
+
assert_equal([], s.seq, "new hash seq []")
|
17
|
+
assert_equal({ :one => 1, :two => 2 }, s.rnd, "new hash rnd {...}")
|
18
|
+
assert_equal(0, s.seq_size, "new hash seq_size")
|
19
|
+
assert_equal(2, s.rnd_size, "new hash rnd_size")
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_new_mixed
|
23
|
+
s = Sarah.new(:array => [1, 2], :hash => { :one => 1, :two => 2 })
|
24
|
+
assert_equal([1, 2], s.seq, "new mixed seq [...]")
|
25
|
+
assert_equal({ :one => 1, :two => 2 }, s.rnd, "new mixed rnd {...}")
|
26
|
+
assert_equal(2, s.seq_size, "new mixed seq_size")
|
27
|
+
assert_equal(2, s.rnd_size, "new mixed rnd_size")
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_new_consecutive
|
31
|
+
s = Sarah.new(:array => [0, 1], :hash => { 2 => :two, 3 => :three })
|
32
|
+
assert_equal([0, 1, :two, :three], s.seq, "new consecutive [...]")
|
33
|
+
assert_equal(4, s.seq_size, "new consecutive seq_size")
|
34
|
+
assert_equal(0, s.rnd_size, "new consecutive rnd_size")
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# END
|
data/test/03set_get.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'sarah'
|
3
|
+
|
4
|
+
class TestSarah < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@s = Sarah.new(:array => [1, 2], :hash => { :a => 3, :b => 4 })
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_clear
|
11
|
+
assert_equal(4, @s.size, "has size 4 before clear")
|
12
|
+
@s.clear
|
13
|
+
assert_equal(0, @s.size, "has size 0 after clear")
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_has_key
|
17
|
+
assert_equal(true, @s.has_key?(0), "has key 0")
|
18
|
+
assert_equal(true, @s.has_key?(-1), "has key -1")
|
19
|
+
assert_equal(true, @s.has_key?(:a), "has key :a")
|
20
|
+
assert_equal(false, @s.has_key?(2), "has no key 2")
|
21
|
+
assert_equal(false, @s.has_key?(:c), "has no key :c")
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_get_set
|
25
|
+
assert_equal(1, @s[0], "get key 0 value 1")
|
26
|
+
assert_equal(2, @s[1], "get key 1 value 2")
|
27
|
+
assert_equal(3, @s[:a], "get key :a value 3")
|
28
|
+
assert_equal(4, @s[:b], "get key :b value 4")
|
29
|
+
|
30
|
+
@s[2] = 5
|
31
|
+
@s[:c] = 6
|
32
|
+
assert_equal(5, @s[2], "set/get key 2 value 5")
|
33
|
+
assert_equal(6, @s[:c], "set/get key :c value 6")
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_default
|
37
|
+
assert_equal(1, @s[0], "confirm key 0 value 1")
|
38
|
+
assert_equal(nil, @s[2], "default default nil")
|
39
|
+
|
40
|
+
s = Sarah.new(:array => [0], :default => false)
|
41
|
+
assert_equal(0, s[0], "confirm key 0 value 0")
|
42
|
+
assert_equal(false, s[1], "explicit default false")
|
43
|
+
|
44
|
+
s = Sarah.new(:array => [0]) { |s, key| key }
|
45
|
+
assert_equal(0, s[0], "confirm key 0 value 0")
|
46
|
+
assert_equal(1, s[1], "block default 1")
|
47
|
+
assert_equal(2, s[2], "block default 2")
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_fetch
|
51
|
+
assert_equal(1, @s.fetch(0), "fetch key 0 value 0")
|
52
|
+
assert_raises(KeyError, "fetch with exception") { @s.fetch 2 }
|
53
|
+
assert_equal(false, @s.fetch(2, false), "fetch with default")
|
54
|
+
assert_equal(2, @s.fetch(2) { |key| key }, "fetch with block")
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
# END
|
data/test/04stack.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'sarah'
|
3
|
+
|
4
|
+
class TestSarah < MiniTest::Unit::TestCase
|
5
|
+
|
6
|
+
def test_stack
|
7
|
+
s = Sarah.new
|
8
|
+
s.push 1, 2, 3
|
9
|
+
s.unshift 4, 5, 6
|
10
|
+
assert_equal([4, 5, 6, 1, 2, 3], s.seq, "push + unshift")
|
11
|
+
assert_equal(4, s.shift, "shift")
|
12
|
+
assert_equal(3, s.pop, "pop")
|
13
|
+
assert_equal([5, 6, 1, 2], s.seq, "after shift, pop")
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_append
|
17
|
+
s = Sarah.new
|
18
|
+
s.append! [1], { :one => 1 }, [2], [3]
|
19
|
+
s.append! [4], { :two => 2 }, [5], [6]
|
20
|
+
assert_equal([1, 2, 3, 4, 5, 6], s.seq, "append ary")
|
21
|
+
assert_equal({ :one => 1, :two => 2 }, s.rnd, "append hsh")
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_insert
|
25
|
+
s = Sarah.new
|
26
|
+
s.insert! [1], { :one => 1 }, [2], [3]
|
27
|
+
s.insert! [4], { :two => 2 }, [5], [6]
|
28
|
+
assert_equal([4, 5, 6, 1, 2, 3], s.seq, "insert ary")
|
29
|
+
assert_equal({ :one => 1, :two => 2 }, s.rnd, "insert hsh")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
# END
|
data/test/run_tests.sh
ADDED
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sarah
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Brian Katzung
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2013-07-08 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Implements a hybrid data structure composed of a sequential array and random-access hash
|
22
|
+
email:
|
23
|
+
- briank@kappacs.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files: []
|
29
|
+
|
30
|
+
files:
|
31
|
+
- lib/sarah.rb
|
32
|
+
- sarah.gemspec
|
33
|
+
- test/02new.rb
|
34
|
+
- test/00class.rb
|
35
|
+
- test/04stack.rb
|
36
|
+
- test/01instance.rb
|
37
|
+
- test/run_tests.sh
|
38
|
+
- test/03set_get.rb
|
39
|
+
has_rdoc: true
|
40
|
+
homepage: http://rubygems.org/gems/sarah
|
41
|
+
licenses:
|
42
|
+
- MIT
|
43
|
+
post_install_message:
|
44
|
+
rdoc_options: []
|
45
|
+
|
46
|
+
require_paths:
|
47
|
+
- lib
|
48
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
segments:
|
54
|
+
- 0
|
55
|
+
version: "0"
|
56
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
requirements: []
|
65
|
+
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.3.7
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: Sequential array/random-access hash
|
71
|
+
test_files:
|
72
|
+
- test/02new.rb
|
73
|
+
- test/00class.rb
|
74
|
+
- test/04stack.rb
|
75
|
+
- test/01instance.rb
|
76
|
+
- test/run_tests.sh
|
77
|
+
- test/03set_get.rb
|