functional-ruby 1.0.0 → 1.1.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 +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +37 -17
- data/doc/record.txt +63 -0
- data/doc/thread_safety.txt +9 -0
- data/lib/functional.rb +4 -0
- data/lib/functional/abstract_struct.rb +4 -2
- data/lib/functional/delay.rb +2 -0
- data/lib/functional/either.rb +2 -0
- data/lib/functional/final_struct.rb +231 -0
- data/lib/functional/final_var.rb +163 -0
- data/lib/functional/memo.rb +4 -0
- data/lib/functional/method_signature.rb +2 -0
- data/lib/functional/option.rb +2 -0
- data/lib/functional/pattern_matching.rb +1 -0
- data/lib/functional/protocol.rb +2 -0
- data/lib/functional/protocol_info.rb +3 -0
- data/lib/functional/record.rb +4 -2
- data/lib/functional/tuple.rb +247 -0
- data/lib/functional/type_check.rb +2 -0
- data/lib/functional/union.rb +2 -0
- data/lib/functional/value_struct.rb +144 -0
- data/lib/functional/version.rb +1 -1
- data/spec/functional/final_struct_spec.rb +266 -0
- data/spec/functional/final_var_spec.rb +169 -0
- data/spec/functional/record_spec.rb +30 -1
- data/spec/functional/tuple_spec.rb +679 -0
- data/spec/functional/value_struct_spec.rb +199 -0
- metadata +19 -4
@@ -24,7 +24,7 @@ module Functional
|
|
24
24
|
it 'default all fields values to nil' do
|
25
25
|
fields = [:foo, :bar, :baz]
|
26
26
|
clazz = Record.new(*fields)
|
27
|
-
|
27
|
+
|
28
28
|
record = clazz.new
|
29
29
|
|
30
30
|
fields.each do |field|
|
@@ -171,5 +171,34 @@ module Functional
|
|
171
171
|
}.to_not raise_error
|
172
172
|
end
|
173
173
|
end
|
174
|
+
|
175
|
+
context 'subclassing' do
|
176
|
+
|
177
|
+
specify 'supports all capabilities on subclasses' do
|
178
|
+
record_clazz = Functional::Record.new(:first, :middle, :last, :suffix) do
|
179
|
+
mandatory :first, :last
|
180
|
+
end
|
181
|
+
|
182
|
+
clazz = Class.new(record_clazz) do
|
183
|
+
def full_name
|
184
|
+
"#{first} #{last}"
|
185
|
+
end
|
186
|
+
|
187
|
+
def formal_name
|
188
|
+
name = [first, middle, last].select{|s| ! s.to_s.empty?}.join(' ')
|
189
|
+
suffix.to_s.empty? ? name : name + ", #{suffix}"
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
jerry = clazz.new(first: 'Jerry', last: "D'Antonio")
|
194
|
+
ted = clazz.new(first: 'Ted', middle: 'Theodore', last: 'Logan', suffix: 'Esq.')
|
195
|
+
|
196
|
+
expect(jerry.full_name).to eq "Jerry D'Antonio"
|
197
|
+
expect(jerry.formal_name).to eq "Jerry D'Antonio"
|
198
|
+
|
199
|
+
expect(ted.full_name).to eq "Ted Logan"
|
200
|
+
expect(ted.formal_name).to eq "Ted Theodore Logan, Esq."
|
201
|
+
end
|
202
|
+
end
|
174
203
|
end
|
175
204
|
end
|
@@ -0,0 +1,679 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rspec/expectations'
|
3
|
+
|
4
|
+
RSpec::Matchers.define :be_a_different_tuple_than do |expected|
|
5
|
+
match do |actual|
|
6
|
+
actual.is_a?(Functional::Tuple) && actual.object_id != expected.object_id
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module Functional
|
11
|
+
|
12
|
+
describe Tuple do
|
13
|
+
|
14
|
+
context '#initialize' do
|
15
|
+
|
16
|
+
it 'creates an empty tuple when given no arguments' do
|
17
|
+
expect(Tuple.new).to be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'creates an empty tuple when given an empty array' do
|
21
|
+
expect(Tuple.new([])).to be_empty
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'creates a tuple when given a single array argument' do
|
25
|
+
subject = Tuple.new([:foo, :bar, :baz])
|
26
|
+
|
27
|
+
expect(subject).to_not be_empty
|
28
|
+
expect(subject[0]).to eq :foo
|
29
|
+
expect(subject[1]).to eq :bar
|
30
|
+
expect(subject[2]).to eq :baz
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'creates a tuple when given a single argument that responds to #to_a' do
|
34
|
+
clazz = Class.new {
|
35
|
+
def to_a() [:foo, :bar, :baz]; end
|
36
|
+
}.new
|
37
|
+
subject = Tuple.new(clazz)
|
38
|
+
|
39
|
+
expect(subject).to_not be_empty
|
40
|
+
expect(subject[0]).to eq :foo
|
41
|
+
expect(subject[1]).to eq :bar
|
42
|
+
expect(subject[2]).to eq :baz
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'raises an exception when given a non-array argument' do
|
46
|
+
expect {
|
47
|
+
Tuple.new(:foo)
|
48
|
+
}.to raise_error(ArgumentError)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'freezes the tuple' do
|
52
|
+
expect(Tuple.new).to be_frozen
|
53
|
+
expect(Tuple.new([])).to be_frozen
|
54
|
+
expect(Tuple.new([:foo, :bar, :baz])).to be_frozen
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context '#at' do
|
59
|
+
|
60
|
+
subject { Tuple.new([:foo, :bar, :baz]) }
|
61
|
+
|
62
|
+
it 'returns the nth element when given a valid non-negative index' do
|
63
|
+
expect(subject.at(0)).to eq :foo
|
64
|
+
expect(subject.at(1)).to eq :bar
|
65
|
+
expect(subject.at(2)).to eq :baz
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'returns the nth element from the end when given a valid negative index' do
|
69
|
+
expect(subject.at(-1)).to eq :baz
|
70
|
+
expect(subject.at(-2)).to eq :bar
|
71
|
+
expect(subject.at(-3)).to eq :foo
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'returns nil when given a non-negative out-of-bounds index' do
|
75
|
+
expect(subject.at(3)).to be_nil
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'returns nil when given a negative out-of-bounds index' do
|
79
|
+
expect(subject.at(-4)).to be_nil
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'is aliased as #nth' do
|
83
|
+
expect(subject.nth(0)).to eq :foo
|
84
|
+
expect(subject.nth(1)).to eq :bar
|
85
|
+
expect(subject.nth(-2)).to eq :bar
|
86
|
+
expect(subject.nth(-3)).to eq :foo
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'is aliased as #[]' do
|
90
|
+
expect(subject[0]).to eq :foo
|
91
|
+
expect(subject[1]).to eq :bar
|
92
|
+
expect(subject[-2]).to eq :bar
|
93
|
+
expect(subject[-3]).to eq :foo
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
context '#fetch' do
|
98
|
+
|
99
|
+
subject { Tuple.new([:foo, :bar, :baz]) }
|
100
|
+
|
101
|
+
it 'returns the nth element when given a valid non-negative index' do
|
102
|
+
expect(subject.fetch(0, 42)).to eq :foo
|
103
|
+
expect(subject.fetch(1, 42)).to eq :bar
|
104
|
+
expect(subject.fetch(2, 42)).to eq :baz
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'returns the nth element from the end when given a valid negative index' do
|
108
|
+
expect(subject.fetch(-1, 42)).to eq :baz
|
109
|
+
expect(subject.fetch(-2, 42)).to eq :bar
|
110
|
+
expect(subject.fetch(-3, 42)).to eq :foo
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'returns the given default when given a non-negative out-of-bounds index' do
|
114
|
+
expect(subject.fetch(3, 42)).to eq 42
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'returns the given default when given a negative out-of-bounds index' do
|
118
|
+
expect(subject.fetch(-4, 42)).to eq 42
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context '#length' do
|
123
|
+
|
124
|
+
it 'returns 0 for an empty tuple' do
|
125
|
+
expect(Tuple.new.length).to eq 0
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'returns the length of a non-empty tuple' do
|
129
|
+
expect(Tuple.new([1, 2, 3]).length).to eq 3
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'is aliased a #size' do
|
133
|
+
expect(Tuple.new.size).to eq 0
|
134
|
+
expect(Tuple.new([1, 2, 3]).size).to eq 3
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
context '#intersect' do
|
139
|
+
|
140
|
+
it 'returns an empty tuple when self is empty' do
|
141
|
+
subject = Tuple.new
|
142
|
+
other = Tuple.new([1, 2, 3])
|
143
|
+
result = subject.intersect(other)
|
144
|
+
expect(result).to be_a_different_tuple_than(subject)
|
145
|
+
expect(result).to be_empty
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'returns an empty tuple when other is empty' do
|
149
|
+
subject = Tuple.new([1, 2, 3])
|
150
|
+
other = Tuple.new
|
151
|
+
result = subject.intersect(other)
|
152
|
+
expect(result).to be_a_different_tuple_than(subject)
|
153
|
+
expect(result).to be_empty
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'returns a tuple with all elements common to both tuples' do
|
157
|
+
subject = Tuple.new([1, 2, 3])
|
158
|
+
other = Tuple.new([2, 3, 4])
|
159
|
+
result = subject.intersect(other)
|
160
|
+
expect(result).to be_a_different_tuple_than(subject)
|
161
|
+
expect(result).to eq [2, 3]
|
162
|
+
end
|
163
|
+
|
164
|
+
it 'removes duplicates from self' do
|
165
|
+
subject = Tuple.new([1, 2, 2, 3, 3, 3])
|
166
|
+
other = Tuple.new([2, 3, 4])
|
167
|
+
result = subject.intersect(other)
|
168
|
+
expect(result).to be_a_different_tuple_than(subject)
|
169
|
+
expect(result).to eq [2, 3]
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'removes duplicates from other' do
|
173
|
+
subject = Tuple.new([1, 2, 3])
|
174
|
+
other = Tuple.new([2, 2, 3, 3, 3, 4])
|
175
|
+
result = subject.intersect(other)
|
176
|
+
expect(result).to be_a_different_tuple_than(subject)
|
177
|
+
expect(result).to eq [2, 3]
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'operates on any other that responds to #to_a' do
|
181
|
+
subject = Tuple.new([1, 2, 3])
|
182
|
+
other = Class.new {
|
183
|
+
def to_a() [2, 3, 4]; end
|
184
|
+
}.new
|
185
|
+
|
186
|
+
result = subject.intersect(other)
|
187
|
+
expect(result).to be_a_different_tuple_than(subject)
|
188
|
+
expect(result).to eq [2, 3]
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'is aliased as #&' do
|
192
|
+
subject = Tuple.new([1, 2, 3])
|
193
|
+
other = Tuple.new([2, 3, 4])
|
194
|
+
result = subject & other
|
195
|
+
expect(result).to be_a_different_tuple_than(subject)
|
196
|
+
expect(result).to eq [2, 3]
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
context '#union' do
|
201
|
+
|
202
|
+
it 'returns a copy of self when other is empty' do
|
203
|
+
subject = Tuple.new
|
204
|
+
other = Tuple.new([1, 2, 3])
|
205
|
+
result = subject.union(other)
|
206
|
+
expect(result).to be_a_different_tuple_than(subject)
|
207
|
+
expect(result).to eq [1, 2, 3]
|
208
|
+
end
|
209
|
+
|
210
|
+
it 'returns a copy of other when self is empty' do
|
211
|
+
subject = Tuple.new([1, 2, 3])
|
212
|
+
other = Tuple.new
|
213
|
+
result = subject.union(other)
|
214
|
+
expect(result).to be_a_different_tuple_than(subject)
|
215
|
+
expect(result).to eq [1, 2, 3]
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'returns a tuple with all elements from both tuples' do
|
219
|
+
subject = Tuple.new([1, 2, 3])
|
220
|
+
other = Tuple.new([1, 2, 3, 4])
|
221
|
+
result = subject.union(other)
|
222
|
+
expect(result).to be_a_different_tuple_than(subject)
|
223
|
+
expect(result).to eq [1, 2, 3, 4]
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'removes duplicates from self' do
|
227
|
+
subject = Tuple.new([1, 2, 2, 3, 3, 3])
|
228
|
+
other = Tuple.new([1, 2, 3, 4])
|
229
|
+
result = subject.union(other)
|
230
|
+
expect(result).to be_a_different_tuple_than(subject)
|
231
|
+
expect(result).to eq [1, 2, 3, 4]
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'removes duplicates from other' do
|
235
|
+
subject = Tuple.new([1, 2, 3, 4])
|
236
|
+
other = Tuple.new([1, 2, 2, 3, 3, 3])
|
237
|
+
result = subject.union(other)
|
238
|
+
expect(result).to be_a_different_tuple_than(subject)
|
239
|
+
expect(result).to eq [1, 2, 3, 4]
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'operates on any other that responds to #to_a' do
|
243
|
+
subject = Tuple.new([1, 2, 3])
|
244
|
+
other = Class.new {
|
245
|
+
def to_a() [2, 3, 4]; end
|
246
|
+
}.new
|
247
|
+
|
248
|
+
result = subject.union(other)
|
249
|
+
expect(result).to be_a_different_tuple_than(subject)
|
250
|
+
expect(result).to eq [1, 2, 3, 4]
|
251
|
+
end
|
252
|
+
|
253
|
+
it 'is aliased as #|' do
|
254
|
+
subject = Tuple.new([1, 2, 3])
|
255
|
+
other = Tuple.new([2, 3, 4])
|
256
|
+
result = subject | other
|
257
|
+
expect(result).to be_a_different_tuple_than(subject)
|
258
|
+
expect(result).to eq [1, 2, 3, 4]
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
context '#concat' do
|
263
|
+
|
264
|
+
it 'returns a copy of self when other is empty' do
|
265
|
+
subject = Tuple.new
|
266
|
+
other = Tuple.new([1, 2, 3])
|
267
|
+
result = subject.concat(other)
|
268
|
+
expect(result).to be_a_different_tuple_than(subject)
|
269
|
+
expect(result).to eq [1, 2, 3]
|
270
|
+
end
|
271
|
+
|
272
|
+
it 'returns a copy of other when self is empty' do
|
273
|
+
subject = Tuple.new([1, 2, 3])
|
274
|
+
other = Tuple.new
|
275
|
+
result = subject.concat(other)
|
276
|
+
expect(result).to be_a_different_tuple_than(subject)
|
277
|
+
expect(result).to eq [1, 2, 3]
|
278
|
+
end
|
279
|
+
|
280
|
+
it 'returns a new tuple containing all of self and other in order' do
|
281
|
+
subject = Tuple.new([1, 2, 3])
|
282
|
+
other = Tuple.new([4, 5, 6])
|
283
|
+
result = subject.concat(other)
|
284
|
+
expect(result).to be_a_different_tuple_than(subject)
|
285
|
+
expect(result).to eq [1, 2, 3, 4, 5, 6]
|
286
|
+
end
|
287
|
+
|
288
|
+
it 'does not remove duplicates from self or other' do
|
289
|
+
subject = Tuple.new([1, 2, 2, 3, 3, 3])
|
290
|
+
other = Tuple.new([4, 4, 4, 5, 5, 6])
|
291
|
+
result = subject.concat(other)
|
292
|
+
expect(result).to be_a_different_tuple_than(subject)
|
293
|
+
expect(result).to eq [1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6]
|
294
|
+
end
|
295
|
+
|
296
|
+
it 'operates on any other that responds to #to_a' do
|
297
|
+
subject = Tuple.new([1, 2, 3])
|
298
|
+
other = Class.new {
|
299
|
+
def to_a() [4, 5, 6]; end
|
300
|
+
}.new
|
301
|
+
|
302
|
+
result = subject.concat(other)
|
303
|
+
expect(result).to be_a_different_tuple_than(subject)
|
304
|
+
expect(result).to eq [1, 2, 3, 4, 5, 6]
|
305
|
+
end
|
306
|
+
|
307
|
+
it 'is aliased as #+' do
|
308
|
+
subject = Tuple.new([1, 2, 2, 3, 3, 3])
|
309
|
+
other = Tuple.new([4, 4, 4, 5, 5, 6])
|
310
|
+
result = subject + other
|
311
|
+
expect(result).to be_a_different_tuple_than(subject)
|
312
|
+
expect(result).to eq [1, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6]
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
context '#diff' do
|
317
|
+
|
318
|
+
it 'returns a copy of self when other is empty' do
|
319
|
+
subject = Tuple.new([1, 2, 3])
|
320
|
+
other = Tuple.new
|
321
|
+
result = subject.diff(other)
|
322
|
+
expect(result).to be_a_different_tuple_than(subject)
|
323
|
+
expect(result).to eq [1, 2, 3]
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'returns an empty tuple when self is empty' do
|
327
|
+
subject = Tuple.new
|
328
|
+
other = Tuple.new([1, 2, 3])
|
329
|
+
result = subject.diff(other)
|
330
|
+
expect(result).to be_a_different_tuple_than(subject)
|
331
|
+
expect(result).to be_empty
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'returns an empty tuple when self and other have identical elements' do
|
335
|
+
subject = Tuple.new([1, 2, 3])
|
336
|
+
other = Tuple.new([1, 2, 3])
|
337
|
+
result = subject.diff(other)
|
338
|
+
expect(result).to be_a_different_tuple_than(subject)
|
339
|
+
expect(result).to be_empty
|
340
|
+
end
|
341
|
+
|
342
|
+
it 'returns a tuple with all elements in self not also in other' do
|
343
|
+
subject = Tuple.new([1, 2, 3])
|
344
|
+
other = Tuple.new([3, 4, 5])
|
345
|
+
result = subject.diff(other)
|
346
|
+
expect(result).to be_a_different_tuple_than(subject)
|
347
|
+
expect(result).to eq [1, 2]
|
348
|
+
end
|
349
|
+
|
350
|
+
it 'removes duplicates from self when in other' do
|
351
|
+
subject = Tuple.new([1, 2, 3, 3, 3])
|
352
|
+
other = Tuple.new([3, 4, 5])
|
353
|
+
result = subject.diff(other)
|
354
|
+
expect(result).to be_a_different_tuple_than(subject)
|
355
|
+
expect(result).to eq [1, 2]
|
356
|
+
end
|
357
|
+
|
358
|
+
it 'removes duplicates from other when in self' do
|
359
|
+
subject = Tuple.new([1, 2, 3])
|
360
|
+
other = Tuple.new([3, 3, 3, 4, 5])
|
361
|
+
result = subject.diff(other)
|
362
|
+
expect(result).to be_a_different_tuple_than(subject)
|
363
|
+
expect(result).to eq [1, 2]
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'operates on any other that responds to #to_a' do
|
367
|
+
subject = Tuple.new([1, 2, 3])
|
368
|
+
other = Class.new {
|
369
|
+
def to_a() [3, 4, 5]; end
|
370
|
+
}.new
|
371
|
+
|
372
|
+
result = subject.diff(other)
|
373
|
+
expect(result).to be_a_different_tuple_than(subject)
|
374
|
+
expect(result).to eq [1, 2]
|
375
|
+
end
|
376
|
+
|
377
|
+
it 'is aliased as #-' do
|
378
|
+
subject = Tuple.new([1, 2, 3])
|
379
|
+
other = Tuple.new([3, 4, 5])
|
380
|
+
result = subject - other
|
381
|
+
expect(result).to be_a_different_tuple_than(subject)
|
382
|
+
expect(result).to eq [1, 2]
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
context '#repeat' do
|
387
|
+
|
388
|
+
it 'returns an empty tuple when multipled by zero' do
|
389
|
+
subject = Tuple.new([1, 2, 3])
|
390
|
+
result = subject.repeat(0)
|
391
|
+
expect(result).to be_a_different_tuple_than(subject)
|
392
|
+
expect(result).to be_empty
|
393
|
+
end
|
394
|
+
|
395
|
+
it 'returns a copy of self when multipled by one' do
|
396
|
+
subject = Tuple.new([1, 2, 3])
|
397
|
+
result = subject.repeat(1)
|
398
|
+
expect(result).to be_a_different_tuple_than(subject)
|
399
|
+
expect(result).to eq [1, 2, 3]
|
400
|
+
end
|
401
|
+
|
402
|
+
it 'returns a tuple containing elements from self repeated n times' do
|
403
|
+
subject = Tuple.new([1, 2, 3])
|
404
|
+
result = subject.repeat(3)
|
405
|
+
expect(result).to be_a_different_tuple_than(subject)
|
406
|
+
expect(result).to eq [1, 2, 3, 1, 2, 3, 1, 2, 3]
|
407
|
+
end
|
408
|
+
|
409
|
+
it 'raises an exception when given a negative argument' do
|
410
|
+
subject = Tuple.new([1, 2, 3])
|
411
|
+
expect {
|
412
|
+
subject.repeat(-2)
|
413
|
+
}.to raise_error(ArgumentError)
|
414
|
+
end
|
415
|
+
|
416
|
+
it 'is aliased as #*' do
|
417
|
+
subject = Tuple.new([1, 2, 3])
|
418
|
+
result = subject * 3
|
419
|
+
expect(result).to be_a_different_tuple_than(subject)
|
420
|
+
expect(result).to eq [1, 2, 3, 1, 2, 3, 1, 2, 3]
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
context '#uniq' do
|
425
|
+
|
426
|
+
it 'returns a empty tuple when empty' do
|
427
|
+
subject = Tuple.new
|
428
|
+
result = subject.uniq
|
429
|
+
expect(result).to be_a_different_tuple_than(subject)
|
430
|
+
expect(result).to be_empty
|
431
|
+
end
|
432
|
+
|
433
|
+
it 'returns a copy of self when there are no duplicate elements' do
|
434
|
+
subject = Tuple.new([1, 2, 3])
|
435
|
+
result = subject.uniq
|
436
|
+
expect(result).to be_a_different_tuple_than(subject)
|
437
|
+
expect(result).to eq [1, 2, 3]
|
438
|
+
end
|
439
|
+
|
440
|
+
it 'returns a new tuple with duplicates removed' do
|
441
|
+
subject = Tuple.new([1, 2, 2, 3, 3, 3])
|
442
|
+
result = subject.uniq
|
443
|
+
expect(result).to be_a_different_tuple_than(subject)
|
444
|
+
expect(result).to eq [1, 2, 3]
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
context '#each' do
|
449
|
+
|
450
|
+
it 'returns an Enumerable when no block given' do
|
451
|
+
subject = Tuple.new([1, 2, 3])
|
452
|
+
expect(subject.each).to be_a Enumerable
|
453
|
+
end
|
454
|
+
|
455
|
+
it 'enumerates over each element' do
|
456
|
+
result = []
|
457
|
+
subject = Tuple.new([1, 2, 2, 3, 3, 3])
|
458
|
+
subject.each{|item| result << item }
|
459
|
+
expect(result).to eq [1, 2, 2, 3, 3, 3]
|
460
|
+
end
|
461
|
+
|
462
|
+
it 'does not call the block when empty' do
|
463
|
+
result = false
|
464
|
+
Tuple.new.each{|item| expected = true}
|
465
|
+
expect(result).to be false
|
466
|
+
end
|
467
|
+
end
|
468
|
+
|
469
|
+
context '#each_with_index' do
|
470
|
+
|
471
|
+
it 'returns an Enumerable when no block given' do
|
472
|
+
subject = Tuple.new([1, 2, 3])
|
473
|
+
expect(subject.each_with_index).to be_a Enumerable
|
474
|
+
end
|
475
|
+
|
476
|
+
it 'enumerates over each element and index pair' do
|
477
|
+
result = {}
|
478
|
+
subject = Tuple.new([1, 2, 2, 3, 3, 3])
|
479
|
+
subject.each_with_index{|item, index| result[index] = item }
|
480
|
+
|
481
|
+
expected = {
|
482
|
+
0 => 1,
|
483
|
+
1 => 2,
|
484
|
+
2 => 2,
|
485
|
+
3 => 3,
|
486
|
+
4 => 3,
|
487
|
+
5 => 3,
|
488
|
+
}
|
489
|
+
expect(result).to eq expected
|
490
|
+
end
|
491
|
+
|
492
|
+
it 'does not call the block when empty' do
|
493
|
+
result = false
|
494
|
+
Tuple.new.each_with_index{|item, index| expected = true}
|
495
|
+
expect(result).to be false
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
context '#sequence' do
|
500
|
+
|
501
|
+
it 'returns an Enumerable when no block given' do
|
502
|
+
subject = Tuple.new([1, 2, 3])
|
503
|
+
expect(subject.sequence).to be_a Enumerable
|
504
|
+
end
|
505
|
+
|
506
|
+
it 'enumerates over each element' do
|
507
|
+
result = []
|
508
|
+
subject = Tuple.new([1, 2, 2, 3, 3, 3])
|
509
|
+
subject.sequence{|item, rest| result << item }
|
510
|
+
expect(result).to eq [1, 2, 2, 3, 3, 3]
|
511
|
+
end
|
512
|
+
|
513
|
+
it 'yields rest of tuple for each element' do
|
514
|
+
result = []
|
515
|
+
subject = Tuple.new([1, 2, 3, 4])
|
516
|
+
subject.sequence{|item, rest| result = rest; break }
|
517
|
+
expect(result).to eq [2, 3, 4]
|
518
|
+
end
|
519
|
+
|
520
|
+
it 'yields rest of tuple as a tuple' do
|
521
|
+
result = []
|
522
|
+
subject = Tuple.new([1, 2, 3, 4])
|
523
|
+
subject.sequence{|item, rest| result = rest; break }
|
524
|
+
expect(result).to be_a_different_tuple_than(subject)
|
525
|
+
end
|
526
|
+
|
527
|
+
it 'yields an empty tuple for rest when on last element' do
|
528
|
+
result = nil
|
529
|
+
subject = Tuple.new([1])
|
530
|
+
subject.sequence{|item, rest| result = rest }
|
531
|
+
expect(result).to be_a_different_tuple_than(subject)
|
532
|
+
expect(result).to eq []
|
533
|
+
end
|
534
|
+
|
535
|
+
it 'does not call the block when empty' do
|
536
|
+
result = false
|
537
|
+
Tuple.new.sequence{|item, rest| expected = true}
|
538
|
+
expect(result).to be false
|
539
|
+
end
|
540
|
+
end
|
541
|
+
|
542
|
+
context '#eql?' do
|
543
|
+
|
544
|
+
it 'returns true when compared to a tuple with identical elements' do
|
545
|
+
subject = Tuple.new([1, 2, 3])
|
546
|
+
other = Tuple.new([1, 2, 3])
|
547
|
+
expect(subject.eql?(other)).to be true
|
548
|
+
end
|
549
|
+
|
550
|
+
it 'returns false when given a tuple with different elements' do
|
551
|
+
subject = Tuple.new([1, 2, 3])
|
552
|
+
other = Tuple.new([2, 3, 4])
|
553
|
+
expect(subject.eql?(other)).to be false
|
554
|
+
end
|
555
|
+
|
556
|
+
it 'operates on any other that responds to #to_a' do
|
557
|
+
subject = Tuple.new([1, 2, 3])
|
558
|
+
other = Class.new {
|
559
|
+
def to_a() [1, 2, 3]; end
|
560
|
+
}.new
|
561
|
+
|
562
|
+
expect(subject.eql?(other)).to be true
|
563
|
+
end
|
564
|
+
|
565
|
+
it 'is aliased as #==' do
|
566
|
+
subject = Tuple.new([1, 2, 3])
|
567
|
+
identical = Tuple.new([1, 2, 3])
|
568
|
+
different = Tuple.new([2, 3, 4])
|
569
|
+
|
570
|
+
expect(subject == identical).to be true
|
571
|
+
expect(subject == different).to be false
|
572
|
+
end
|
573
|
+
end
|
574
|
+
|
575
|
+
context '#empty?' do
|
576
|
+
|
577
|
+
it 'returns true when there are no elements' do
|
578
|
+
subject = Tuple.new
|
579
|
+
expect(subject.empty?).to be true
|
580
|
+
end
|
581
|
+
|
582
|
+
it 'returns false when there are one or more elements' do
|
583
|
+
subject = Tuple.new([1, 2, 3])
|
584
|
+
expect(subject.empty?).to be false
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
context '#first' do
|
589
|
+
|
590
|
+
it 'returns nil when empty' do
|
591
|
+
subject = Tuple.new
|
592
|
+
expect(subject.first).to be nil
|
593
|
+
end
|
594
|
+
|
595
|
+
it 'returns the first element when not empty' do
|
596
|
+
subject = Tuple.new([1, 2, 3])
|
597
|
+
expect(subject.first).to eq 1
|
598
|
+
end
|
599
|
+
|
600
|
+
it 'is aliased as #head' do
|
601
|
+
expect(Tuple.new.head).to be nil
|
602
|
+
expect(Tuple.new([1, 2, 3]).head).to eq 1
|
603
|
+
end
|
604
|
+
end
|
605
|
+
|
606
|
+
context '#rest' do
|
607
|
+
|
608
|
+
it 'returns an empty tuple when empty' do
|
609
|
+
subject = Tuple.new
|
610
|
+
expect(subject.rest).to be_a_different_tuple_than(subject)
|
611
|
+
expect(subject.rest).to be_empty
|
612
|
+
end
|
613
|
+
|
614
|
+
it 'returns an empty tuple when there is only one item' do
|
615
|
+
subject = Tuple.new([1])
|
616
|
+
expect(subject.rest).to be_a_different_tuple_than(subject)
|
617
|
+
expect(subject.rest).to be_empty
|
618
|
+
end
|
619
|
+
|
620
|
+
it 'returns a tuple with all but the first element when not empty' do
|
621
|
+
subject = Tuple.new([1, 2, 3])
|
622
|
+
expect(subject.rest).to be_a_different_tuple_than(subject)
|
623
|
+
expect(subject.rest).to eq [2, 3]
|
624
|
+
end
|
625
|
+
|
626
|
+
it 'is aliased as #tail' do
|
627
|
+
expect(Tuple.new.rest).to be_a_different_tuple_than(subject)
|
628
|
+
expect(Tuple.new.rest).to be_empty
|
629
|
+
expect(Tuple.new([1, 2, 3]).rest).to be_a_different_tuple_than(subject)
|
630
|
+
expect(Tuple.new([1, 2, 3]).rest).to eq [2, 3]
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
context '#to_a' do
|
635
|
+
|
636
|
+
it 'returns an empty array when empty' do
|
637
|
+
subject = Tuple.new.to_a
|
638
|
+
expect(subject).to be_a Array
|
639
|
+
expect(subject).to be_empty
|
640
|
+
end
|
641
|
+
|
642
|
+
it 'returns an array with the same elements as self' do
|
643
|
+
subject = Tuple.new([1, 2, 3]).to_a
|
644
|
+
expect(subject).to be_a Array
|
645
|
+
expect(subject).to eq [1, 2, 3]
|
646
|
+
end
|
647
|
+
|
648
|
+
it 'returns a non-frozen array' do
|
649
|
+
expect(Tuple.new.to_a).to_not be_frozen
|
650
|
+
expect(Tuple.new([1, 2, 3]).to_a).to_not be_frozen
|
651
|
+
end
|
652
|
+
|
653
|
+
it 'is aliased as #to_ary' do
|
654
|
+
subject = Tuple.new([1, 2, 3]).to_ary
|
655
|
+
expect(subject).to be_a Array
|
656
|
+
expect(subject).to eq [1, 2, 3]
|
657
|
+
end
|
658
|
+
end
|
659
|
+
|
660
|
+
context 'reflection' do
|
661
|
+
|
662
|
+
specify '#inspect begins with the class name' do
|
663
|
+
subject = Tuple.new([1, 2, 3])
|
664
|
+
expect(subject.inspect).to match(/^#<#{described_class}:\s+/)
|
665
|
+
end
|
666
|
+
|
667
|
+
specify '#inspect includes a list of all elements' do
|
668
|
+
subject = Tuple.new([1, 2, 3])
|
669
|
+
expect(subject.inspect).to match(/\s+\[1, 2, 3\]>$/)
|
670
|
+
expect(Tuple.new.inspect).to match(/\s+\[\]>$/)
|
671
|
+
end
|
672
|
+
|
673
|
+
specify '#to_s returns the same string an an array with the same elements' do
|
674
|
+
expect(Tuple.new.to_s).to eq [].to_s
|
675
|
+
expect(Tuple.new([1, 2, 3]).to_s).to eq [1, 2, 3].to_s
|
676
|
+
end
|
677
|
+
end
|
678
|
+
end
|
679
|
+
end
|