functional-ruby 0.7.4 → 0.7.5
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/README.md +46 -12
- data/lib/functional.rb +19 -0
- data/lib/functional/catalog.rb +487 -0
- data/lib/functional/collection.rb +403 -0
- data/lib/functional/inflect.rb +127 -0
- data/lib/functional/search.rb +132 -0
- data/lib/functional/sort.rb +41 -0
- data/lib/functional/utilities.rb +0 -3
- data/lib/functional/version.rb +1 -1
- data/md/catalog.md +32 -0
- data/md/collection.md +32 -0
- data/md/inflect.md +32 -0
- data/md/pattern_matching.md +2 -2
- data/md/platform.md +32 -0
- data/md/search.md +32 -0
- data/md/sort.md +32 -0
- data/md/utilities.md +2 -2
- data/spec/functional/catalog_spec.rb +1206 -0
- data/spec/functional/collection_spec.rb +752 -0
- data/spec/functional/inflect_spec.rb +85 -0
- data/spec/functional/pattern_matching_spec.rb +0 -2
- data/spec/functional/search_spec.rb +187 -0
- data/spec/functional/sort_spec.rb +61 -0
- data/spec/spec_helper.rb +9 -0
- metadata +23 -2
@@ -0,0 +1,132 @@
|
|
1
|
+
module Functional
|
2
|
+
|
3
|
+
module Search
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# Conduct a linear search against an unsorted collection and
|
7
|
+
# return the index where the item was found. Returns nil if
|
8
|
+
# the item is not found.
|
9
|
+
#
|
10
|
+
# The default behavior is to search the entire collections. The
|
11
|
+
# options hash can be used to provide optional low and high indexes
|
12
|
+
# (:imin and :imax). If either :imin or :imax is out of range the
|
13
|
+
# natural collection boundary will be used.
|
14
|
+
#
|
15
|
+
# When a block is given the block will be applied to both arguments.
|
16
|
+
# Using a block in this way allows computation against a specific field
|
17
|
+
# in a data set of hashes or objects.
|
18
|
+
#
|
19
|
+
# @yield iterates over each element in the data set
|
20
|
+
# @yieldparam item each element in the data set
|
21
|
+
#
|
22
|
+
# @param [Enumerable] data the data set to search
|
23
|
+
# @param [Hash] opts search options
|
24
|
+
# @param [Block] block optional block for per-item processing
|
25
|
+
#
|
26
|
+
# @option opts [Integer] :imin minimum index to search
|
27
|
+
# @option opts [Integer] :imax maximum index to search
|
28
|
+
#
|
29
|
+
# @return [Array] the index where the item is found or nil when the
|
30
|
+
# collection is empty or nil
|
31
|
+
def linear_search(data, key, opts={}, &block)
|
32
|
+
|
33
|
+
imin, imax = check_search_options(data, key, opts, &block)
|
34
|
+
return nil if imin.nil? || imax.nil?
|
35
|
+
return imin if imin == imax
|
36
|
+
|
37
|
+
index = nil
|
38
|
+
(imin..imax).each do |i|
|
39
|
+
if (block_given? && yield(data[i]) == key) || data[i] == key
|
40
|
+
index = i
|
41
|
+
break
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
return index
|
46
|
+
end
|
47
|
+
|
48
|
+
# Conduct a binary search against the sorted collection and return
|
49
|
+
# a pair of indexes indicating the result of the search. The
|
50
|
+
# indexes will be returned as a two-element array.
|
51
|
+
#
|
52
|
+
# The default behavior is to search the entire collections. The
|
53
|
+
# options hash can be used to provide optional low and high indexes
|
54
|
+
# (:imin and :imax). If either :imin or :imax is out of range the
|
55
|
+
# natural collection boundary will be used.
|
56
|
+
#
|
57
|
+
# When a block is given the block will be applied to both arguments.
|
58
|
+
# Using a block in this way allows computation against a specific field
|
59
|
+
# in a data set of hashes or objects.
|
60
|
+
#
|
61
|
+
# When the key is found both returned indexes will be the index of
|
62
|
+
# the item. When the key is not found but the value is within the
|
63
|
+
# range of value in the data set the returned indexes will be
|
64
|
+
# immediately above and below where the key would reside. When
|
65
|
+
# the key is below the lowest value in the search range the result
|
66
|
+
# will be nil and the lowest index. When the key is higher than the
|
67
|
+
# highest value in the search range the result will be the highest
|
68
|
+
# index and nil.
|
69
|
+
#
|
70
|
+
# @yield iterates over each element in the data set
|
71
|
+
# @yieldparam item each element in the data set
|
72
|
+
#
|
73
|
+
# @param [Enumerable] data the data set to search
|
74
|
+
# @param [Hash] opts search options
|
75
|
+
# @param [Block] block optional block for per-item processing
|
76
|
+
#
|
77
|
+
# @option opts [Integer] :imin minimum index to search
|
78
|
+
# @option opts [Integer] :imax maximum index to search
|
79
|
+
#
|
80
|
+
# @return [Array] pair of indexes (see above) or nil when the collection
|
81
|
+
# is empty or nil
|
82
|
+
def binary_search(data, key, opts={}, &block)
|
83
|
+
|
84
|
+
imin, imax = check_search_options(data, key, opts, &block)
|
85
|
+
return nil if imin.nil? && imax.nil?
|
86
|
+
return [imin, imax] if imin == imax || imin.nil? || imax.nil?
|
87
|
+
|
88
|
+
while (imax >= imin)
|
89
|
+
imid = (imin + imax) / 2
|
90
|
+
current = data[imid]
|
91
|
+
current = yield(current) if block_given?
|
92
|
+
if current < key
|
93
|
+
imin = imid + 1
|
94
|
+
elsif current > key
|
95
|
+
imax = imid - 1
|
96
|
+
else
|
97
|
+
imin = imax = imid
|
98
|
+
break
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
return imax, imin
|
103
|
+
end
|
104
|
+
|
105
|
+
alias_method :bsearch, :binary_search
|
106
|
+
alias_method :half_interval_search, :binary_search
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
# :nodoc:
|
111
|
+
# @private
|
112
|
+
def check_search_options(data, key, opts={})
|
113
|
+
return [nil, nil] if data.nil? || data.empty?
|
114
|
+
|
115
|
+
imin = [opts[:imin].to_i, 0].max
|
116
|
+
imax = opts[:imax].nil? ? data.size-1 : [opts[:imax], data.size-1].min
|
117
|
+
return [nil, nil] if imin > imax
|
118
|
+
|
119
|
+
if block_given?
|
120
|
+
min, max = yield(data[imin]), yield(data[imax])
|
121
|
+
else
|
122
|
+
min, max = data[imin], data[imax]
|
123
|
+
end
|
124
|
+
return [nil, imin] if key < min
|
125
|
+
return [imin, imin] if key == min
|
126
|
+
return [imax, nil] if key > max
|
127
|
+
return [imax, imax] if key == max
|
128
|
+
|
129
|
+
return [imin, imax]
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Functional
|
2
|
+
|
3
|
+
module Sort
|
4
|
+
extend self
|
5
|
+
|
6
|
+
# Sorts the collection using the insertion sort algorithm.
|
7
|
+
#
|
8
|
+
# When a block is given the block will be applied to both arguments.
|
9
|
+
# Using a block in this way allows computation against a specific field
|
10
|
+
# in a data set of hashes or objects.
|
11
|
+
#
|
12
|
+
# @yield iterates over each element in the data set
|
13
|
+
# @yieldparam item each element in the data set
|
14
|
+
#
|
15
|
+
# @param [Enumerable] data the data set to search
|
16
|
+
# @param [Hash] opts search options
|
17
|
+
#
|
18
|
+
# @return [Array] the sorted collection
|
19
|
+
def insertion_sort!(data, opts={})
|
20
|
+
return data if data.nil? || data.size <= 1
|
21
|
+
|
22
|
+
(1..(data.size-1)).each do |j|
|
23
|
+
|
24
|
+
key = block_given? ? yield(data[j]) : data[j]
|
25
|
+
value = data[j]
|
26
|
+
i = j - 1
|
27
|
+
current = block_given? ? yield(data[i]) : data[i]
|
28
|
+
|
29
|
+
while i >= 0 && current > key
|
30
|
+
data[i+1] = data[i]
|
31
|
+
i = i - 1
|
32
|
+
current = block_given? ? yield(data[i]) : data[i]
|
33
|
+
end
|
34
|
+
|
35
|
+
data[i+1] = value
|
36
|
+
end
|
37
|
+
|
38
|
+
return data
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
data/lib/functional/utilities.rb
CHANGED
data/lib/functional/version.rb
CHANGED
data/md/catalog.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Catalog
|
2
|
+
|
3
|
+
TBD...
|
4
|
+
|
5
|
+
## Copyright
|
6
|
+
|
7
|
+
*Functional Ruby* is Copyright © 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
|
8
|
+
It is free software and may be redistributed under the terms specified in the LICENSE file.
|
9
|
+
|
10
|
+
## License
|
11
|
+
|
12
|
+
Released under the MIT license.
|
13
|
+
|
14
|
+
http://www.opensource.org/licenses/mit-license.php
|
15
|
+
|
16
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
|
+
> of this software and associated documentation files (the "Software"), to deal
|
18
|
+
> in the Software without restriction, including without limitation the rights
|
19
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
20
|
+
> copies of the Software, and to permit persons to whom the Software is
|
21
|
+
> furnished to do so, subject to the following conditions:
|
22
|
+
>
|
23
|
+
> The above copyright notice and this permission notice shall be included in
|
24
|
+
> all copies or substantial portions of the Software.
|
25
|
+
>
|
26
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
27
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
28
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
29
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
30
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
31
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
32
|
+
> THE SOFTWARE.
|
data/md/collection.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Collection
|
2
|
+
|
3
|
+
TBD...
|
4
|
+
|
5
|
+
## Copyright
|
6
|
+
|
7
|
+
*Functional Ruby* is Copyright © 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
|
8
|
+
It is free software and may be redistributed under the terms specified in the LICENSE file.
|
9
|
+
|
10
|
+
## License
|
11
|
+
|
12
|
+
Released under the MIT license.
|
13
|
+
|
14
|
+
http://www.opensource.org/licenses/mit-license.php
|
15
|
+
|
16
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
|
+
> of this software and associated documentation files (the "Software"), to deal
|
18
|
+
> in the Software without restriction, including without limitation the rights
|
19
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
20
|
+
> copies of the Software, and to permit persons to whom the Software is
|
21
|
+
> furnished to do so, subject to the following conditions:
|
22
|
+
>
|
23
|
+
> The above copyright notice and this permission notice shall be included in
|
24
|
+
> all copies or substantial portions of the Software.
|
25
|
+
>
|
26
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
27
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
28
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
29
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
30
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
31
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
32
|
+
> THE SOFTWARE.
|
data/md/inflect.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Inflect
|
2
|
+
|
3
|
+
TBD...
|
4
|
+
|
5
|
+
## Copyright
|
6
|
+
|
7
|
+
*Functional Ruby* is Copyright © 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
|
8
|
+
It is free software and may be redistributed under the terms specified in the LICENSE file.
|
9
|
+
|
10
|
+
## License
|
11
|
+
|
12
|
+
Released under the MIT license.
|
13
|
+
|
14
|
+
http://www.opensource.org/licenses/mit-license.php
|
15
|
+
|
16
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
|
+
> of this software and associated documentation files (the "Software"), to deal
|
18
|
+
> in the Software without restriction, including without limitation the rights
|
19
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
20
|
+
> copies of the Software, and to permit persons to whom the Software is
|
21
|
+
> furnished to do so, subject to the following conditions:
|
22
|
+
>
|
23
|
+
> The above copyright notice and this permission notice shall be included in
|
24
|
+
> all copies or substantial portions of the Software.
|
25
|
+
>
|
26
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
27
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
28
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
29
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
30
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
31
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
32
|
+
> THE SOFTWARE.
|
data/md/pattern_matching.md
CHANGED
@@ -36,13 +36,13 @@ This gem may not make much sense if you don't understand how Erlang dispatches f
|
|
36
36
|
In the Ruby class file where you want to use pattern matching, require the *functional-ruby* gem:
|
37
37
|
|
38
38
|
```ruby
|
39
|
-
require 'functional
|
39
|
+
require 'functional'
|
40
40
|
```
|
41
41
|
|
42
42
|
Then include `PatternMatching` in your class:
|
43
43
|
|
44
44
|
```ruby
|
45
|
-
require 'functional
|
45
|
+
require 'functional'
|
46
46
|
|
47
47
|
class Foo
|
48
48
|
include PatternMatching
|
data/md/platform.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Platform
|
2
|
+
|
3
|
+
TBD...
|
4
|
+
|
5
|
+
## Copyright
|
6
|
+
|
7
|
+
*Functional Ruby* is Copyright © 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
|
8
|
+
It is free software and may be redistributed under the terms specified in the LICENSE file.
|
9
|
+
|
10
|
+
## License
|
11
|
+
|
12
|
+
Released under the MIT license.
|
13
|
+
|
14
|
+
http://www.opensource.org/licenses/mit-license.php
|
15
|
+
|
16
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
|
+
> of this software and associated documentation files (the "Software"), to deal
|
18
|
+
> in the Software without restriction, including without limitation the rights
|
19
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
20
|
+
> copies of the Software, and to permit persons to whom the Software is
|
21
|
+
> furnished to do so, subject to the following conditions:
|
22
|
+
>
|
23
|
+
> The above copyright notice and this permission notice shall be included in
|
24
|
+
> all copies or substantial portions of the Software.
|
25
|
+
>
|
26
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
27
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
28
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
29
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
30
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
31
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
32
|
+
> THE SOFTWARE.
|
data/md/search.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Search
|
2
|
+
|
3
|
+
TBD...
|
4
|
+
|
5
|
+
## Copyright
|
6
|
+
|
7
|
+
*Functional Ruby* is Copyright © 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
|
8
|
+
It is free software and may be redistributed under the terms specified in the LICENSE file.
|
9
|
+
|
10
|
+
## License
|
11
|
+
|
12
|
+
Released under the MIT license.
|
13
|
+
|
14
|
+
http://www.opensource.org/licenses/mit-license.php
|
15
|
+
|
16
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
|
+
> of this software and associated documentation files (the "Software"), to deal
|
18
|
+
> in the Software without restriction, including without limitation the rights
|
19
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
20
|
+
> copies of the Software, and to permit persons to whom the Software is
|
21
|
+
> furnished to do so, subject to the following conditions:
|
22
|
+
>
|
23
|
+
> The above copyright notice and this permission notice shall be included in
|
24
|
+
> all copies or substantial portions of the Software.
|
25
|
+
>
|
26
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
27
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
28
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
29
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
30
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
31
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
32
|
+
> THE SOFTWARE.
|
data/md/sort.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Sort
|
2
|
+
|
3
|
+
TBD...
|
4
|
+
|
5
|
+
## Copyright
|
6
|
+
|
7
|
+
*Functional Ruby* is Copyright © 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
|
8
|
+
It is free software and may be redistributed under the terms specified in the LICENSE file.
|
9
|
+
|
10
|
+
## License
|
11
|
+
|
12
|
+
Released under the MIT license.
|
13
|
+
|
14
|
+
http://www.opensource.org/licenses/mit-license.php
|
15
|
+
|
16
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
17
|
+
> of this software and associated documentation files (the "Software"), to deal
|
18
|
+
> in the Software without restriction, including without limitation the rights
|
19
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
20
|
+
> copies of the Software, and to permit persons to whom the Software is
|
21
|
+
> furnished to do so, subject to the following conditions:
|
22
|
+
>
|
23
|
+
> The above copyright notice and this permission notice shall be included in
|
24
|
+
> all copies or substantial portions of the Software.
|
25
|
+
>
|
26
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
27
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
28
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
29
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
30
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
31
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
32
|
+
> THE SOFTWARE.
|
data/md/utilities.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# Utility Functions
|
2
2
|
|
3
|
-
|
3
|
+
Start by requiring the Functional Ruby gem:
|
4
4
|
|
5
5
|
```ruby
|
6
|
-
require 'functional
|
6
|
+
require 'functional'
|
7
7
|
```
|
8
8
|
|
9
9
|
This gives you access to a few constants and functions:
|
@@ -0,0 +1,1206 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Functional
|
4
|
+
|
5
|
+
describe Catalog do
|
6
|
+
|
7
|
+
let(:hash_sample) {
|
8
|
+
{
|
9
|
+
7 => 8,
|
10
|
+
17 => 14,
|
11
|
+
27 => 6,
|
12
|
+
32 => 12,
|
13
|
+
37 => 8,
|
14
|
+
22 => 4,
|
15
|
+
42 => 3,
|
16
|
+
12 => 8,
|
17
|
+
47 => 2
|
18
|
+
}.freeze
|
19
|
+
}
|
20
|
+
|
21
|
+
let(:hash_sample_for_block) {
|
22
|
+
{
|
23
|
+
7 => {:count => 8},
|
24
|
+
17 => {:count => 14},
|
25
|
+
27 => {:count => 6},
|
26
|
+
32 => {:count => 12},
|
27
|
+
37 => {:count => 8},
|
28
|
+
22 => {:count => 4},
|
29
|
+
42 => {:count => 3},
|
30
|
+
12 => {:count => 8},
|
31
|
+
47 => {:count => 2}
|
32
|
+
}.freeze
|
33
|
+
}
|
34
|
+
|
35
|
+
let(:catalog_sample) {
|
36
|
+
[
|
37
|
+
[7, 8],
|
38
|
+
[17, 14],
|
39
|
+
[27, 6],
|
40
|
+
[32, 12],
|
41
|
+
[37, 8],
|
42
|
+
[22, 4],
|
43
|
+
[42, 3],
|
44
|
+
[12, 8],
|
45
|
+
[47, 2]
|
46
|
+
].freeze
|
47
|
+
}
|
48
|
+
|
49
|
+
let(:catalog_sample_for_block) {
|
50
|
+
[
|
51
|
+
[7, {:count => 8}],
|
52
|
+
[17, {:count => 14}],
|
53
|
+
[27, {:count => 6}],
|
54
|
+
[32, {:count => 12}],
|
55
|
+
[37, {:count => 8}],
|
56
|
+
[22, {:count => 4}],
|
57
|
+
[42, {:count => 3}],
|
58
|
+
[12, {:count => 8}],
|
59
|
+
[47, {:count => 2}]
|
60
|
+
].freeze
|
61
|
+
}
|
62
|
+
|
63
|
+
context 'creation' do
|
64
|
+
|
65
|
+
context '#initialize' do
|
66
|
+
|
67
|
+
it 'creates an empty Catalog when no arguments are given' do
|
68
|
+
catalog = Catalog.new
|
69
|
+
catalog.should be_empty
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'creates a Catalog from a hash' do
|
73
|
+
catalog = Catalog.new(hash_sample, :from => :hash)
|
74
|
+
catalog.size.should eq 9
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'creates a Catalog from an array' do
|
78
|
+
catalog = Catalog.new([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], :from => :array)
|
79
|
+
catalog.size.should eq 5
|
80
|
+
catalog.first.should eq [1, 2]
|
81
|
+
catalog.last.should eq [9, 10]
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'creates a Catalog from a catalog' do
|
85
|
+
catalog = Catalog.new(catalog_sample, :from => :catalog)
|
86
|
+
catalog.size.should eq 9
|
87
|
+
catalog.first.should eq catalog_sample.first
|
88
|
+
catalog.last.should eq catalog_sample.last
|
89
|
+
|
90
|
+
catalog = Catalog.new(catalog_sample, :from => :catalogue)
|
91
|
+
catalog.size.should eq 9
|
92
|
+
catalog.first.should eq catalog_sample.first
|
93
|
+
catalog.last.should eq catalog_sample.last
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'assumes the arguments are a catalog when no :from is given' do
|
97
|
+
catalog = Catalog.new(catalog_sample)
|
98
|
+
catalog.size.should eq 9
|
99
|
+
catalog.first.should eq catalog_sample.first
|
100
|
+
catalog.last.should eq catalog_sample.last
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'creates an empty Catalog when :from is unrecognized' do
|
104
|
+
catalog = Catalog.new(hash_sample, :from => :bogus)
|
105
|
+
catalog.should be_empty
|
106
|
+
end
|
107
|
+
|
108
|
+
it 'uses the given block to create key/value pairs' do
|
109
|
+
sample = [
|
110
|
+
{:x => 1, :y => 2},
|
111
|
+
{:x => 3, :y => 4},
|
112
|
+
{:x => 5, :y => 6}
|
113
|
+
]
|
114
|
+
|
115
|
+
expected = [ [1, 2], [3, 4], [5, 6] ]
|
116
|
+
|
117
|
+
catalog = Catalog.new(sample){|item| [ item[:x], item[:y] ] }
|
118
|
+
catalog.should eq expected
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context '#from_array' do
|
123
|
+
|
124
|
+
it 'creates a Catalog from an Array' do
|
125
|
+
catalog = Catalog.from_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
|
126
|
+
catalog.size.should eq 5
|
127
|
+
catalog.first.should eq [1, 2]
|
128
|
+
catalog.last.should eq [9, 10]
|
129
|
+
|
130
|
+
catalog = Catalog.from_array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
|
131
|
+
catalog.size.should eq 5
|
132
|
+
catalog.first.should eq [1, 2]
|
133
|
+
catalog.last.should eq [9, 10]
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'creates an empty Catalog from an empty Array' do
|
137
|
+
catalog = Catalog.from_array([])
|
138
|
+
catalog.should be_empty
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'throws out the last element when given an odd-size array' do
|
142
|
+
catalog = Catalog.from_array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11])
|
143
|
+
catalog.size.should eq 5
|
144
|
+
catalog.first.should eq [1, 2]
|
145
|
+
catalog.last.should eq [9, 10]
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'creates a Catalog when given an array and a block' do
|
149
|
+
sample = [
|
150
|
+
{:count => 13},
|
151
|
+
{:count => 18},
|
152
|
+
{:count => 13},
|
153
|
+
{:count => 14},
|
154
|
+
{:count => 13},
|
155
|
+
{:count => 16},
|
156
|
+
{:count => 14},
|
157
|
+
{:count => 13}
|
158
|
+
].freeze
|
159
|
+
|
160
|
+
catalog = Catalog.from_array(sample){|item| item[:count]}
|
161
|
+
catalog.size.should eq 4
|
162
|
+
catalog.first.should eq [13, 18]
|
163
|
+
catalog.last.should eq [14, 13]
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
context '#from_hash' do
|
168
|
+
|
169
|
+
it 'creates a Catalog from a hash' do
|
170
|
+
catalog = Catalog.from_hash(hash_sample)
|
171
|
+
catalog.size.should eq 9
|
172
|
+
|
173
|
+
catalog = Catalog.from_hash(:one => 1, :two => 2, :three => 3)
|
174
|
+
catalog.size.should eq 3
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'creates an empty Catalog from an empty hash' do
|
178
|
+
catalog = Catalog.from_hash({})
|
179
|
+
catalog.should be_empty
|
180
|
+
end
|
181
|
+
|
182
|
+
it 'creates a Catalog when given a hash and a block' do
|
183
|
+
catalog = Catalog.from_hash(hash_sample_for_block){|item| item[:count]}
|
184
|
+
catalog.size.should eq 9
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context '#from_catalog' do
|
189
|
+
|
190
|
+
context 'creates a Catalog from a catalog' do
|
191
|
+
|
192
|
+
specify do
|
193
|
+
catalog = Catalog.from_catalog(catalog_sample)
|
194
|
+
catalog.size.should eq 9
|
195
|
+
catalog.first.should eq catalog_sample.first
|
196
|
+
catalog.last.should eq catalog_sample.last
|
197
|
+
end
|
198
|
+
|
199
|
+
specify do
|
200
|
+
catalog = Catalog.from_catalog([:one, 1], [:two, 2], [:three, 3])
|
201
|
+
catalog.size.should eq 3
|
202
|
+
catalog.first.should eq [:one, 1]
|
203
|
+
catalog.last.should eq [:three, 3]
|
204
|
+
end
|
205
|
+
|
206
|
+
specify do
|
207
|
+
catalog = Catalog.from_catalog([[:one, 1], [:two, 2], [:three, 3]])
|
208
|
+
catalog.size.should eq 3
|
209
|
+
catalog.first.should eq [:one, 1]
|
210
|
+
catalog.last.should eq [:three, 3]
|
211
|
+
end
|
212
|
+
|
213
|
+
specify do
|
214
|
+
catalog = Catalog.from_catalog([:one, 1], [:two, 2])
|
215
|
+
catalog.size.should eq 2
|
216
|
+
catalog.first.should eq [:one, 1]
|
217
|
+
catalog.last.should eq [:two, 2]
|
218
|
+
end
|
219
|
+
|
220
|
+
specify do
|
221
|
+
catalog = Catalog.from_catalog([[:one, 1], [:two, 2]])
|
222
|
+
catalog.size.should eq 2
|
223
|
+
catalog.first.should eq [:one, 1]
|
224
|
+
catalog.last.should eq [:two, 2]
|
225
|
+
end
|
226
|
+
|
227
|
+
specify do
|
228
|
+
catalog = Catalog.from_catalog([:one, 1])
|
229
|
+
catalog.size.should eq 1
|
230
|
+
catalog.first.should eq [:one, 1]
|
231
|
+
catalog.last.should eq [:one, 1]
|
232
|
+
end
|
233
|
+
|
234
|
+
specify do
|
235
|
+
catalog = Catalog.from_catalog([[:one, 1]])
|
236
|
+
catalog.size.should eq 1
|
237
|
+
catalog.first.should eq [:one, 1]
|
238
|
+
catalog.last.should eq [:one, 1]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'creates an empty Catalog from an empty catalog' do
|
243
|
+
catalog = Catalog.from_catalog({})
|
244
|
+
catalog.should be_empty
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'creates a Catalog when given a catalog and a block' do
|
248
|
+
catalog = Catalog.from_catalog(catalog_sample_for_block){|item| item[:count]}
|
249
|
+
catalog.size.should eq 9
|
250
|
+
catalog.first.should eq [catalog_sample.first[0], catalog_sample.first[1]]
|
251
|
+
catalog.last.should eq [catalog_sample.last[0], catalog_sample.last[1]]
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
context '#==' do
|
257
|
+
|
258
|
+
it 'returns true for equal catalogs' do
|
259
|
+
catalog_1 = Catalog.from_hash(hash_sample)
|
260
|
+
catalog_2 = Catalog.from_hash(hash_sample)
|
261
|
+
catalog_1.should eq catalog_2
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
it 'returns false for unequal catalogs' do
|
266
|
+
catalog_1 = Catalog.new
|
267
|
+
catalog_2 = Catalog.from_hash(hash_sample)
|
268
|
+
catalog_1.should_not eq catalog_2
|
269
|
+
end
|
270
|
+
|
271
|
+
it 'compares with an equal Catalog object' do
|
272
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
273
|
+
catalog_2 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
274
|
+
catalog_1.should == catalog_2
|
275
|
+
end
|
276
|
+
|
277
|
+
it 'compares with an equal catalog array' do
|
278
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
279
|
+
catalog_2 = [[1, 1], [2, 2], [3, 3]]
|
280
|
+
catalog_1.should == catalog_2
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'compares with a non-equal Catalog objects' do
|
284
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
285
|
+
catalog_2 = Catalog.new([[1, 1], [2, 2], [3, 3], [4, 4]])
|
286
|
+
catalog_1.should_not == catalog_2
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'compares with a non-equal catalog array' do
|
290
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
291
|
+
catalog_2 = [[1, 1], [2, 2], [3, 3], [4, 4]]
|
292
|
+
catalog_1.should_not == catalog_2
|
293
|
+
end
|
294
|
+
|
295
|
+
it 'returns false when compared with any other object' do
|
296
|
+
Catalog.new.should_not == :foo
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
context '#!=' do
|
301
|
+
|
302
|
+
it 'returns false for equal catalogs' do
|
303
|
+
catalog_1 = Catalog.from_hash(hash_sample)
|
304
|
+
catalog_2 = Catalog.from_hash(hash_sample)
|
305
|
+
(catalog_1 != catalog_2).should be_false
|
306
|
+
end
|
307
|
+
|
308
|
+
|
309
|
+
it 'returns true for unequal catalogs' do
|
310
|
+
catalog_1 = Catalog.from_hash(hash_sample)
|
311
|
+
catalog_2 = Catalog.new
|
312
|
+
(catalog_1 != catalog_2).should be_true
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'compares with an equal Catalog object' do
|
316
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
317
|
+
catalog_2 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
318
|
+
(catalog_1 != catalog_2).should be_false
|
319
|
+
end
|
320
|
+
|
321
|
+
it 'compares with an equal catalog array' do
|
322
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
323
|
+
catalog_2 = [[1, 1], [2, 2], [3, 3]]
|
324
|
+
(catalog_1 != catalog_2).should be_false
|
325
|
+
end
|
326
|
+
|
327
|
+
it 'compares with a non-equal Catalog objects' do
|
328
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
329
|
+
catalog_2 = Catalog.new([[1, 1], [2, 2], [3, 3], [4, 4]])
|
330
|
+
(catalog_1 != catalog_2).should be_true
|
331
|
+
end
|
332
|
+
|
333
|
+
it 'compares with a non-equal catalog array' do
|
334
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
335
|
+
catalog_2 = [[1, 1], [2, 2], [3, 3], [4, 4]]
|
336
|
+
(catalog_1 != catalog_2).should be_true
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'returns true when compared with any other object' do
|
340
|
+
(Catalog.new != :foo).should be_true
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
context '<=>' do
|
345
|
+
|
346
|
+
let(:low) { [[1, 1], [2, 2]] }
|
347
|
+
let(:high) { [[3, 3], [4, 4]] }
|
348
|
+
|
349
|
+
it 'returns a negative number when less than another Catalog' do
|
350
|
+
small = Catalog.new(low)
|
351
|
+
big = Catalog.new(high)
|
352
|
+
(small <=> big).should < 0
|
353
|
+
end
|
354
|
+
|
355
|
+
it 'returns a negative number when less than a catalog' do
|
356
|
+
small = Catalog.new(low)
|
357
|
+
(small <=> high).should < 0
|
358
|
+
end
|
359
|
+
|
360
|
+
it 'returns zero when equal to another Catalog' do
|
361
|
+
small = Catalog.new(low)
|
362
|
+
big = Catalog.new(low)
|
363
|
+
(small <=> big).should eq 0
|
364
|
+
end
|
365
|
+
|
366
|
+
it 'returns zero when equal to a catalog' do
|
367
|
+
small = Catalog.new(low)
|
368
|
+
(small <=> low).should eq 0
|
369
|
+
end
|
370
|
+
|
371
|
+
it 'returns a positive number when greater than another Catalog' do
|
372
|
+
small = Catalog.new(low)
|
373
|
+
big = Catalog.new(high)
|
374
|
+
(big <=> small).should > 0
|
375
|
+
end
|
376
|
+
|
377
|
+
it 'returns a positive number when greater than a catalog' do
|
378
|
+
big = Catalog.new(high)
|
379
|
+
(big <=> low).should > 0
|
380
|
+
end
|
381
|
+
|
382
|
+
it 'raises an error when compated to an invalid object' do
|
383
|
+
lambda {
|
384
|
+
Catalog.new <=> :foo
|
385
|
+
}.should raise_error(TypeError)
|
386
|
+
end
|
387
|
+
end
|
388
|
+
|
389
|
+
context '#[]' do
|
390
|
+
|
391
|
+
it 'returns nil when empty' do
|
392
|
+
catalog = Catalog.new
|
393
|
+
catalog[0].should be_nil
|
394
|
+
end
|
395
|
+
|
396
|
+
it 'returns the element at a valid positive index' do
|
397
|
+
catalog = Catalog.from_catalog(catalog_sample)
|
398
|
+
catalog[0].should eq catalog_sample[0]
|
399
|
+
end
|
400
|
+
|
401
|
+
it 'returns the element at a valid negative index' do
|
402
|
+
catalog = Catalog.from_catalog(catalog_sample)
|
403
|
+
catalog[-1].should eq catalog_sample[-1]
|
404
|
+
end
|
405
|
+
|
406
|
+
it 'returns nil for an invalid positive index' do
|
407
|
+
catalog = Catalog.from_catalog(catalog_sample)
|
408
|
+
catalog[100].should be_nil
|
409
|
+
end
|
410
|
+
|
411
|
+
it 'returns nil for an invalid negative index' do
|
412
|
+
catalog = Catalog.from_catalog(catalog_sample)
|
413
|
+
catalog[-100].should be_nil
|
414
|
+
end
|
415
|
+
end
|
416
|
+
|
417
|
+
context '#[]=' do
|
418
|
+
|
419
|
+
let(:catalog) { Catalog.from_hash(:one => 1, :two => 2, :three => 3) }
|
420
|
+
|
421
|
+
it 'accepts a one-element hash as a value' do
|
422
|
+
catalog[0] = {:foo => :bar}
|
423
|
+
catalog[0].should eq [:foo, :bar]
|
424
|
+
end
|
425
|
+
|
426
|
+
it 'accepts a two-element array as a value' do
|
427
|
+
catalog[0] = [:foo, :bar]
|
428
|
+
catalog[0].should eq [:foo, :bar]
|
429
|
+
end
|
430
|
+
|
431
|
+
it 'raises an exception when given in invalid value' do
|
432
|
+
lambda {
|
433
|
+
catalog[0] = :foo
|
434
|
+
}.should raise_error(ArgumentError)
|
435
|
+
end
|
436
|
+
|
437
|
+
it 'updates the index when given a valid positive index' do
|
438
|
+
catalog[1] = [:foo, :bar]
|
439
|
+
catalog.should eq [[:one, 1], [:foo, :bar], [:three, 3]]
|
440
|
+
end
|
441
|
+
|
442
|
+
it 'updates the index when given an invalid negative index' do
|
443
|
+
catalog[-2] = [:foo, :bar]
|
444
|
+
catalog.should eq [[:one, 1], [:foo, :bar], [:three, 3]]
|
445
|
+
end
|
446
|
+
|
447
|
+
it 'raises an exception when given an invalid positive index' do
|
448
|
+
lambda {
|
449
|
+
catalog[100] = [:foo, :bar]
|
450
|
+
}.should raise_error(ArgumentError)
|
451
|
+
end
|
452
|
+
|
453
|
+
it 'raises an exception when given an invalid negative index' do
|
454
|
+
lambda {
|
455
|
+
catalog[-100] = [:foo, :bar]
|
456
|
+
}.should raise_error(ArgumentError)
|
457
|
+
end
|
458
|
+
|
459
|
+
end
|
460
|
+
|
461
|
+
context '#&' do
|
462
|
+
|
463
|
+
it 'returns a new Catalog object' do
|
464
|
+
catalog_1 = Catalog.new
|
465
|
+
catalog_2 = Catalog.new
|
466
|
+
intersection = catalog_1 & catalog_2
|
467
|
+
intersection.object_id.should_not eq catalog_1.object_id
|
468
|
+
intersection.object_id.should_not eq catalog_2.object_id
|
469
|
+
end
|
470
|
+
|
471
|
+
it 'intersects two empty Catalogs' do
|
472
|
+
catalog_1 = Catalog.new
|
473
|
+
catalog_2 = Catalog.new
|
474
|
+
intersection = catalog_1 & catalog_2
|
475
|
+
intersection.should be_empty
|
476
|
+
end
|
477
|
+
|
478
|
+
it 'intersects an empty Catalog with a non-empty Catalog' do
|
479
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
480
|
+
catalog_2 = Catalog.new
|
481
|
+
intersection = catalog_1 & catalog_2
|
482
|
+
intersection.should be_empty
|
483
|
+
end
|
484
|
+
|
485
|
+
it 'intersects two non-empty Catalogs' do
|
486
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
487
|
+
catalog_2 = Catalog.new([[2, 2], [3, 3], [4, 4], [5, 5]])
|
488
|
+
intersection = catalog_1 & catalog_2
|
489
|
+
intersection.should eq [[2, 2], [3, 3]]
|
490
|
+
end
|
491
|
+
|
492
|
+
it 'intersects a Catalog object with a catalog' do
|
493
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
494
|
+
catalog_2 = [[2, 2], [3, 3], [4, 4], [5, 5]]
|
495
|
+
intersection = catalog_1 & catalog_2
|
496
|
+
intersection.should eq [[2, 2], [3, 3]]
|
497
|
+
end
|
498
|
+
|
499
|
+
it 'removes duplicates when intersecting two non-empty Catalogs' do
|
500
|
+
catalog_1 = Catalog.new([[1, 1], [1, 1], [1, 1], [2, 2], [3, 3]])
|
501
|
+
catalog_2 = Catalog.new([[1, 1], [3, 3], [4, 4], [5, 5]])
|
502
|
+
intersection = catalog_1 & catalog_2
|
503
|
+
intersection.should eq [[1, 1], [3, 3]]
|
504
|
+
end
|
505
|
+
|
506
|
+
it 'raises an error when given a non-Catalog object' do
|
507
|
+
lambda {
|
508
|
+
catalog_1 = Catalog.new([[1, 1], [1, 1], [1, 1], [2, 2], [3, 3]])
|
509
|
+
intersection = catalog_1 & :foo
|
510
|
+
}.should raise_error(TypeError)
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
context '#+' do
|
515
|
+
|
516
|
+
it 'returns a new Catalog object' do
|
517
|
+
catalog_1 = Catalog.new
|
518
|
+
catalog_2 = Catalog.new
|
519
|
+
sum = catalog_1 + catalog_2
|
520
|
+
sum.object_id.should_not eq catalog_1.object_id
|
521
|
+
sum.object_id.should_not eq catalog_2.object_id
|
522
|
+
end
|
523
|
+
|
524
|
+
it 'adds two empty Catalogs' do
|
525
|
+
catalog_1 = Catalog.new
|
526
|
+
catalog_2 = Catalog.new
|
527
|
+
sum = catalog_1 + catalog_2
|
528
|
+
sum.should be_empty
|
529
|
+
end
|
530
|
+
|
531
|
+
it 'adds an empty Catalog with a non-empty Catalog' do
|
532
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
533
|
+
catalog_2 = Catalog.new
|
534
|
+
sum = catalog_1 + catalog_2
|
535
|
+
sum.should eq [[1, 1], [2, 2], [3, 3]]
|
536
|
+
end
|
537
|
+
|
538
|
+
it 'adds two non-empty Catalogs' do
|
539
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
540
|
+
catalog_2 = Catalog.new([[2, 2], [3, 3], [4, 4], [5, 5]])
|
541
|
+
sum = catalog_1 + catalog_2
|
542
|
+
sum.should eq [[1, 1], [2, 2], [3, 3], [2, 2], [3, 3], [4, 4], [5, 5]]
|
543
|
+
end
|
544
|
+
|
545
|
+
it 'adds a Catalog object with a catalog' do
|
546
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
547
|
+
catalog_2 = [[2, 2], [3, 3], [4, 4], [5, 5]]
|
548
|
+
sum = catalog_1 + catalog_2
|
549
|
+
sum.should eq [[1, 1], [2, 2], [3, 3],[2, 2], [3, 3], [4, 4], [5, 5] ]
|
550
|
+
end
|
551
|
+
|
552
|
+
it 'raises an error when given a non-Catalog object' do
|
553
|
+
lambda {
|
554
|
+
catalog_1 = Catalog.new([[1, 1], [1, 1], [1, 1], [2, 2], [3, 3]])
|
555
|
+
sum = catalog_1 + :foo
|
556
|
+
}.should raise_error(TypeError)
|
557
|
+
end
|
558
|
+
end
|
559
|
+
|
560
|
+
context '#|' do
|
561
|
+
|
562
|
+
it 'returns a new Catalog object' do
|
563
|
+
catalog_1 = Catalog.new
|
564
|
+
catalog_2 = Catalog.new
|
565
|
+
union = catalog_1 | catalog_2
|
566
|
+
union.object_id.should_not eq catalog_1.object_id
|
567
|
+
union.object_id.should_not eq catalog_2.object_id
|
568
|
+
end
|
569
|
+
|
570
|
+
it 'unions two empty Catalogs' do
|
571
|
+
catalog_1 = Catalog.new
|
572
|
+
catalog_2 = Catalog.new
|
573
|
+
union = catalog_1 | catalog_2
|
574
|
+
union.should be_empty
|
575
|
+
end
|
576
|
+
|
577
|
+
it 'unions an empty Catalog with a non-empty Catalog' do
|
578
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
579
|
+
catalog_2 = Catalog.new
|
580
|
+
union = catalog_1 | catalog_2
|
581
|
+
union.should eq [[1, 1], [2, 2], [3, 3]]
|
582
|
+
end
|
583
|
+
|
584
|
+
it 'unions two non-empty Catalogs' do
|
585
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
586
|
+
catalog_2 = Catalog.new([[4, 4], [5, 5]])
|
587
|
+
union = catalog_1 | catalog_2
|
588
|
+
union.should eq [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
|
589
|
+
end
|
590
|
+
|
591
|
+
it 'unions a Catalog object with a catalog' do
|
592
|
+
catalog_1 = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
593
|
+
catalog_2 = [[4, 4], [5, 5]]
|
594
|
+
union = catalog_1 | catalog_2
|
595
|
+
union.should eq [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
|
596
|
+
end
|
597
|
+
|
598
|
+
it 'removes duplicates when intersecting two non-empty Catalogs' do
|
599
|
+
catalog_1 = Catalog.new([[1, 1], [1, 1], [1, 1], [2, 2], [3, 3]])
|
600
|
+
catalog_2 = Catalog.new([[1, 1], [3, 3], [4, 4], [5, 5]])
|
601
|
+
union = catalog_1 | catalog_2
|
602
|
+
union.should eq [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
|
603
|
+
end
|
604
|
+
|
605
|
+
it 'raises an error when given a non-Catalog object' do
|
606
|
+
lambda {
|
607
|
+
catalog_1 = Catalog.new([[1, 1], [1, 1], [1, 1], [2, 2], [3, 3]])
|
608
|
+
union = catalog_1 | :foo
|
609
|
+
}.should raise_error(TypeError)
|
610
|
+
end
|
611
|
+
end
|
612
|
+
|
613
|
+
context '#push' do
|
614
|
+
|
615
|
+
it 'returns the Catalog' do
|
616
|
+
catalog_1 = Catalog.new
|
617
|
+
catalog_2 = catalog_1.push([1, 2])
|
618
|
+
catalog_1.object_id.should eq catalog_2.object_id
|
619
|
+
end
|
620
|
+
|
621
|
+
it 'appends a two-element array onto the catalog' do
|
622
|
+
catalog = Catalog.new([[1, 1], [2, 2]])
|
623
|
+
catalog = catalog.push([3, 3])
|
624
|
+
catalog.should eq [[1, 1], [2, 2], [3, 3]]
|
625
|
+
end
|
626
|
+
|
627
|
+
it 'appends a one-element hash onto the catalog' do
|
628
|
+
catalog = Catalog.new([[1, 1], [2, 2]])
|
629
|
+
catalog = catalog.push({3 => 3})
|
630
|
+
catalog.should eq [[1, 1], [2, 2], [3, 3]]
|
631
|
+
end
|
632
|
+
|
633
|
+
it 'raises an error for an invalid datatype' do
|
634
|
+
lambda {
|
635
|
+
Catalog.new.push(:foo)
|
636
|
+
}.should raise_error(TypeError)
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
context '#pop' do
|
641
|
+
|
642
|
+
it 'returns nil when empty' do
|
643
|
+
Catalog.new.pop.should be_nil
|
644
|
+
end
|
645
|
+
|
646
|
+
it 'returns the last element' do
|
647
|
+
catalog = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
648
|
+
pop = catalog.pop
|
649
|
+
pop.should eq [3, 3]
|
650
|
+
end
|
651
|
+
|
652
|
+
it 'removes the last element' do
|
653
|
+
catalog = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
654
|
+
catalog.pop
|
655
|
+
catalog.should eq [[1, 1], [2, 2]]
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
context '#peek' do
|
660
|
+
|
661
|
+
it 'returns nil when empty' do
|
662
|
+
Catalog.new.peek.should be_nil
|
663
|
+
end
|
664
|
+
|
665
|
+
it 'returns the last element' do
|
666
|
+
catalog = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
667
|
+
peek = catalog.pop
|
668
|
+
peek.should eq [3, 3]
|
669
|
+
end
|
670
|
+
|
671
|
+
it 'does not remove the last element' do
|
672
|
+
catalog = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
673
|
+
catalog.peek
|
674
|
+
catalog.should eq [[1, 1], [2, 2], [3, 3]]
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
context '#keys' do
|
679
|
+
|
680
|
+
it 'returns an empty array when empty' do
|
681
|
+
Catalog.new.keys.should be_empty
|
682
|
+
end
|
683
|
+
|
684
|
+
it 'returns an array with all first elements in the catalog' do
|
685
|
+
catalog = Catalog.new([[0, 0], [0, 1], [1, 0], [1, 1], [2, 2], [3, 3]])
|
686
|
+
keys = catalog.keys
|
687
|
+
keys.should eq [0, 0, 1, 1, 2, 3]
|
688
|
+
end
|
689
|
+
end
|
690
|
+
|
691
|
+
context '#values' do
|
692
|
+
|
693
|
+
it 'returns an empty array when empty' do
|
694
|
+
Catalog.new.values.should be_empty
|
695
|
+
end
|
696
|
+
|
697
|
+
it 'returns an array with all last elements in the catalog' do
|
698
|
+
catalog = Catalog.new([[0, 0], [0, 1], [1, 0], [1, 1], [2, 2], [3, 3]])
|
699
|
+
values = catalog.values
|
700
|
+
values.should eq [0, 1, 0, 1, 2, 3]
|
701
|
+
end
|
702
|
+
end
|
703
|
+
|
704
|
+
context '#first' do
|
705
|
+
|
706
|
+
it 'returns nil when empty' do
|
707
|
+
Catalog.new.first.should be_nil
|
708
|
+
end
|
709
|
+
|
710
|
+
it 'returns the first element when not empty' do
|
711
|
+
catalog = Catalog.from_catalog(catalog_sample)
|
712
|
+
catalog.first.should == catalog_sample.first
|
713
|
+
end
|
714
|
+
end
|
715
|
+
|
716
|
+
context '#last' do
|
717
|
+
|
718
|
+
it 'returns nil when empty' do
|
719
|
+
Catalog.new.last.should be_nil
|
720
|
+
end
|
721
|
+
|
722
|
+
it 'returns the last element when not empty' do
|
723
|
+
catalog = Catalog.from_catalog(catalog_sample)
|
724
|
+
catalog.last.should == catalog_sample.last
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
context '#iterators' do
|
729
|
+
|
730
|
+
let(:sample) {
|
731
|
+
[
|
732
|
+
[7, 8],
|
733
|
+
[17, 14],
|
734
|
+
[27, 6],
|
735
|
+
[32, 12],
|
736
|
+
[37, 8],
|
737
|
+
[22, 4],
|
738
|
+
[42, 3],
|
739
|
+
[12, 8],
|
740
|
+
[47, 2]
|
741
|
+
].freeze
|
742
|
+
}
|
743
|
+
|
744
|
+
let(:catalog) { Catalog.new(sample) }
|
745
|
+
|
746
|
+
specify '#each' do
|
747
|
+
|
748
|
+
index = 0
|
749
|
+
catalog.each do |item|
|
750
|
+
item.should eq sample[index]
|
751
|
+
index = index + 1
|
752
|
+
end
|
753
|
+
end
|
754
|
+
|
755
|
+
specify '#each_pair' do
|
756
|
+
|
757
|
+
index = 0
|
758
|
+
catalog.each_pair do |key, value|
|
759
|
+
key.should eq sample[index].first
|
760
|
+
value.should eq sample[index].last
|
761
|
+
index = index + 1
|
762
|
+
end
|
763
|
+
end
|
764
|
+
|
765
|
+
specify '#each_key' do
|
766
|
+
|
767
|
+
index = 0
|
768
|
+
catalog.each_key do |key|
|
769
|
+
key.should eq sample[index].first
|
770
|
+
index = index + 1
|
771
|
+
end
|
772
|
+
end
|
773
|
+
|
774
|
+
specify '#each_value' do
|
775
|
+
|
776
|
+
index = 0
|
777
|
+
catalog.each_value do |value|
|
778
|
+
value.should eq sample[index].last
|
779
|
+
index = index + 1
|
780
|
+
end
|
781
|
+
end
|
782
|
+
end
|
783
|
+
|
784
|
+
context '#empty?' do
|
785
|
+
|
786
|
+
it 'returns true when empty' do
|
787
|
+
catalog = Catalog.new
|
788
|
+
catalog.should be_empty
|
789
|
+
end
|
790
|
+
|
791
|
+
it 'returns false when not empty' do
|
792
|
+
catalog = Catalog.from_hash(:one => 1, :two => 2, :three => 3)
|
793
|
+
catalog.should_not be_empty
|
794
|
+
end
|
795
|
+
end
|
796
|
+
|
797
|
+
context '#include?' do
|
798
|
+
|
799
|
+
it 'returns false when empty' do
|
800
|
+
Catalog.new.include?([1, 1]).should be_false
|
801
|
+
end
|
802
|
+
|
803
|
+
it 'returns true when the key/value array is found' do
|
804
|
+
catalog = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
805
|
+
catalog.include?([2, 2]).should be_true
|
806
|
+
end
|
807
|
+
|
808
|
+
it 'returns true when the key/value pair is found' do
|
809
|
+
catalog = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
810
|
+
catalog.include?(2, 2).should be_true
|
811
|
+
end
|
812
|
+
|
813
|
+
it 'returns true for given a one-element hash that matches' do
|
814
|
+
catalog = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
815
|
+
catalog.include?({2 => 2}).should be_true
|
816
|
+
end
|
817
|
+
|
818
|
+
it 'returns true for an implicit one-element hash that matches' do
|
819
|
+
catalog = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
820
|
+
catalog.include?(2 => 2).should be_true
|
821
|
+
end
|
822
|
+
|
823
|
+
it 'returns false when not found' do
|
824
|
+
catalog = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
825
|
+
catalog.include?([4, 4]).should be_false
|
826
|
+
end
|
827
|
+
|
828
|
+
it 'returns false when given an invalid lookup item' do
|
829
|
+
catalog = Catalog.new([[1, 1], [2, 2], [3, 3]])
|
830
|
+
catalog.include?(:foo).should be_false
|
831
|
+
end
|
832
|
+
end
|
833
|
+
|
834
|
+
context '#size' do
|
835
|
+
|
836
|
+
it 'returns zero when is empty' do
|
837
|
+
catalog = Catalog.new
|
838
|
+
catalog.size.should eq 0
|
839
|
+
end
|
840
|
+
|
841
|
+
it 'returns the correct positive integer when not empty' do
|
842
|
+
catalog = Catalog.from_hash(:one => 1, :two => 2, :three => 3)
|
843
|
+
catalog.size.should eq 3
|
844
|
+
end
|
845
|
+
end
|
846
|
+
|
847
|
+
context '#slice' do
|
848
|
+
|
849
|
+
let(:catalog) {
|
850
|
+
Catalog.new([[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]])
|
851
|
+
}
|
852
|
+
|
853
|
+
it 'returns the element at index' do
|
854
|
+
slice = catalog.slice(2)
|
855
|
+
slice.should eq [[3, 3]]
|
856
|
+
slice.should be_a Catalog
|
857
|
+
catalog.should eq [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
|
858
|
+
end
|
859
|
+
|
860
|
+
it 'returns the elements from index through length' do
|
861
|
+
slice = catalog.slice(1, 2)
|
862
|
+
slice.should eq [[2, 2], [3, 3]]
|
863
|
+
slice.should be_a Catalog
|
864
|
+
catalog.should eq [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
|
865
|
+
end
|
866
|
+
|
867
|
+
it 'returns a catalog specified by range' do
|
868
|
+
slice = catalog.slice(1..2)
|
869
|
+
slice.should eq [[2, 2], [3, 3]]
|
870
|
+
slice.should be_a Catalog
|
871
|
+
catalog.should eq [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
|
872
|
+
end
|
873
|
+
|
874
|
+
it 'returns the element at the negative index' do
|
875
|
+
slice = catalog.slice(-3)
|
876
|
+
slice.should eq [[3, 3]]
|
877
|
+
slice.should be_a Catalog
|
878
|
+
catalog.should eq [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
|
879
|
+
end
|
880
|
+
|
881
|
+
it 'returns an empty Catalog if the index is out of range' do
|
882
|
+
slice = catalog.slice(10, 2)
|
883
|
+
slice.should be_empty
|
884
|
+
slice.should be_a Catalog
|
885
|
+
catalog.should eq [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
|
886
|
+
end
|
887
|
+
end
|
888
|
+
|
889
|
+
context '#slice!' do
|
890
|
+
|
891
|
+
let(:catalog) {
|
892
|
+
Catalog.new([[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]])
|
893
|
+
}
|
894
|
+
|
895
|
+
it 'returns the element at index' do
|
896
|
+
slice = catalog.slice!(2)
|
897
|
+
slice.should eq [[3, 3]]
|
898
|
+
slice.should be_a Catalog
|
899
|
+
catalog.should eq [[1, 1], [2, 2], [4, 4], [5, 5]]
|
900
|
+
end
|
901
|
+
|
902
|
+
it 'returns the elements from index through length' do
|
903
|
+
slice = catalog.slice!(1, 2)
|
904
|
+
slice.should eq [[2, 2], [3, 3]]
|
905
|
+
slice.should be_a Catalog
|
906
|
+
catalog.should eq [[1, 1], [4, 4], [5, 5]]
|
907
|
+
end
|
908
|
+
|
909
|
+
it 'returns a catalog specified by range' do
|
910
|
+
slice = catalog.slice!(1..2)
|
911
|
+
slice.should eq [[2, 2], [3, 3]]
|
912
|
+
slice.should be_a Catalog
|
913
|
+
catalog.should eq [[1, 1], [4, 4], [5, 5]]
|
914
|
+
end
|
915
|
+
|
916
|
+
it 'returns the element at the negative index' do
|
917
|
+
slice = catalog.slice!(-3)
|
918
|
+
slice.should eq [[3, 3]]
|
919
|
+
slice.should be_a Catalog
|
920
|
+
catalog.should eq [[1, 1], [2, 2], [4, 4], [5, 5]]
|
921
|
+
end
|
922
|
+
|
923
|
+
it 'returns an empty Catalog if the index is out of range' do
|
924
|
+
slice = catalog.slice!(10, 2)
|
925
|
+
slice.should be_empty
|
926
|
+
slice.should be_a Catalog
|
927
|
+
catalog.should eq [[1, 1], [2, 2], [3, 3], [4, 4], [5, 5]]
|
928
|
+
end
|
929
|
+
end
|
930
|
+
|
931
|
+
context 'sorting' do
|
932
|
+
|
933
|
+
let(:unsorted_catalog) {
|
934
|
+
[
|
935
|
+
[7, 8],
|
936
|
+
[17, 14],
|
937
|
+
[27, 6],
|
938
|
+
[32, 12],
|
939
|
+
[37, 9],
|
940
|
+
[22, 4],
|
941
|
+
[42, 3],
|
942
|
+
[12, 10],
|
943
|
+
[47, 2]
|
944
|
+
].freeze
|
945
|
+
}
|
946
|
+
|
947
|
+
let(:catalog_sorted_by_key) {
|
948
|
+
[
|
949
|
+
[7, 8],
|
950
|
+
[12, 10],
|
951
|
+
[17, 14],
|
952
|
+
[22, 4],
|
953
|
+
[27, 6],
|
954
|
+
[32, 12],
|
955
|
+
[37, 9],
|
956
|
+
[42, 3],
|
957
|
+
[47, 2]
|
958
|
+
].freeze
|
959
|
+
}
|
960
|
+
|
961
|
+
let(:catalog_sorted_by_value) {
|
962
|
+
[
|
963
|
+
[47, 2],
|
964
|
+
[42, 3],
|
965
|
+
[22, 4],
|
966
|
+
[27, 6],
|
967
|
+
[7, 8],
|
968
|
+
[37, 9],
|
969
|
+
[12, 10],
|
970
|
+
[32, 12],
|
971
|
+
[17, 14]
|
972
|
+
].freeze
|
973
|
+
}
|
974
|
+
|
975
|
+
let(:catalog_reversed) {
|
976
|
+
[
|
977
|
+
[47, 2],
|
978
|
+
[42, 3],
|
979
|
+
[37, 9],
|
980
|
+
[32, 12],
|
981
|
+
[27, 6],
|
982
|
+
[22, 4],
|
983
|
+
[17, 14],
|
984
|
+
[12, 10],
|
985
|
+
[7, 8]
|
986
|
+
].freeze
|
987
|
+
}
|
988
|
+
|
989
|
+
let(:catalog) { Catalog.new(unsorted_catalog) }
|
990
|
+
|
991
|
+
specify '#sort_by_key' do
|
992
|
+
sorted = catalog.sort_by_key
|
993
|
+
sorted.should eq catalog_sorted_by_key
|
994
|
+
catalog.should eq unsorted_catalog
|
995
|
+
sorted.should be_a Catalog
|
996
|
+
end
|
997
|
+
|
998
|
+
specify '#sort_by_key!' do
|
999
|
+
sorted = catalog.sort_by_key!
|
1000
|
+
sorted.should eq catalog_sorted_by_key
|
1001
|
+
catalog.should eq catalog_sorted_by_key
|
1002
|
+
sorted.object_id.should eq catalog.object_id
|
1003
|
+
end
|
1004
|
+
|
1005
|
+
specify '#sort_by_value' do
|
1006
|
+
sorted = catalog.sort_by_value
|
1007
|
+
sorted.should eq catalog_sorted_by_value
|
1008
|
+
catalog.should eq unsorted_catalog
|
1009
|
+
sorted.should be_a Catalog
|
1010
|
+
end
|
1011
|
+
|
1012
|
+
specify '#sort_by_value!' do
|
1013
|
+
sorted = catalog.sort_by_value!
|
1014
|
+
sorted.should eq catalog_sorted_by_value
|
1015
|
+
catalog.should eq catalog_sorted_by_value
|
1016
|
+
sorted.object_id.should eq catalog.object_id
|
1017
|
+
end
|
1018
|
+
|
1019
|
+
specify '#sort' do
|
1020
|
+
sorted = catalog.sort
|
1021
|
+
sorted.should eq catalog_sorted_by_key
|
1022
|
+
catalog.should eq unsorted_catalog
|
1023
|
+
sorted.should be_a Catalog
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
specify '#sort!' do
|
1027
|
+
sorted = catalog.sort!
|
1028
|
+
sorted.should eq catalog_sorted_by_key
|
1029
|
+
catalog.should eq catalog_sorted_by_key
|
1030
|
+
sorted.object_id.should eq catalog.object_id
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
specify '#sort with block' do
|
1034
|
+
sorted = catalog.sort{|a, b| b <=> a}
|
1035
|
+
sorted.should eq catalog_reversed
|
1036
|
+
catalog.should eq unsorted_catalog
|
1037
|
+
sorted.should be_a Catalog
|
1038
|
+
end
|
1039
|
+
|
1040
|
+
specify '#sort! with block' do
|
1041
|
+
sorted = catalog.sort!{|a, b| b <=> a}
|
1042
|
+
sorted.should eq catalog_reversed
|
1043
|
+
catalog.should eq catalog_reversed
|
1044
|
+
sorted.should be_a Catalog
|
1045
|
+
end
|
1046
|
+
end
|
1047
|
+
|
1048
|
+
context 'conversion' do
|
1049
|
+
|
1050
|
+
let(:sample) {
|
1051
|
+
[
|
1052
|
+
[7, 8],
|
1053
|
+
[17, 14],
|
1054
|
+
[47, 2]
|
1055
|
+
].freeze
|
1056
|
+
}
|
1057
|
+
|
1058
|
+
let(:sample_as_hash) {
|
1059
|
+
{
|
1060
|
+
7 => 8,
|
1061
|
+
17 => 14,
|
1062
|
+
47 => 2
|
1063
|
+
}.freeze
|
1064
|
+
}
|
1065
|
+
|
1066
|
+
let(:sample_as_array) {
|
1067
|
+
[7, 8, 17, 14, 47, 2]
|
1068
|
+
}
|
1069
|
+
|
1070
|
+
let(:catalog) { Catalog.new(sample) }
|
1071
|
+
|
1072
|
+
context '#to_a' do
|
1073
|
+
|
1074
|
+
specify { Catalog.new.to_a.should eq [] }
|
1075
|
+
|
1076
|
+
specify { catalog.to_a.should eq sample_as_array }
|
1077
|
+
end
|
1078
|
+
|
1079
|
+
context '#to_hash' do
|
1080
|
+
|
1081
|
+
specify { Catalog.new.to_hash.should == {} }
|
1082
|
+
|
1083
|
+
specify { catalog.to_hash.should eq sample_as_hash }
|
1084
|
+
end
|
1085
|
+
|
1086
|
+
context '#to_catalog' do
|
1087
|
+
specify { Catalog.new.to_catalog.should eq [] }
|
1088
|
+
|
1089
|
+
specify do
|
1090
|
+
cat = catalog.to_catalog
|
1091
|
+
cat.should eq sample
|
1092
|
+
cat.should be_a Array
|
1093
|
+
end
|
1094
|
+
end
|
1095
|
+
|
1096
|
+
context '#to_s' do
|
1097
|
+
|
1098
|
+
specify { Catalog.new.to_s.should eq '[]' }
|
1099
|
+
specify { Catalog.from_hash(:one => 1, :two => 2).to_s.should eq '[[:one, 1], [:two, 2]]' }
|
1100
|
+
end
|
1101
|
+
end
|
1102
|
+
|
1103
|
+
context 'deletion' do
|
1104
|
+
|
1105
|
+
let(:sample) {
|
1106
|
+
[
|
1107
|
+
[7, 8],
|
1108
|
+
[17, 14],
|
1109
|
+
[47, 2]
|
1110
|
+
].freeze
|
1111
|
+
}
|
1112
|
+
|
1113
|
+
let(:catalog) { Catalog.new(sample) }
|
1114
|
+
|
1115
|
+
context 'delete' do
|
1116
|
+
|
1117
|
+
it 'deletes the specified item' do
|
1118
|
+
item = catalog.delete([17, 14])
|
1119
|
+
item.should eq [17, 14]
|
1120
|
+
catalog.should eq [[7, 8], [47, 2]]
|
1121
|
+
end
|
1122
|
+
|
1123
|
+
it 'deletes the item matching the given key/value pair' do
|
1124
|
+
item = catalog.delete(17, 14)
|
1125
|
+
item.should eq [17, 14]
|
1126
|
+
catalog.should eq [[7, 8], [47, 2]]
|
1127
|
+
end
|
1128
|
+
|
1129
|
+
it 'deletes the item matching the given one-item hash' do
|
1130
|
+
item = catalog.delete({17 => 14})
|
1131
|
+
item.should eq [17, 14]
|
1132
|
+
catalog.should eq [[7, 8], [47, 2]]
|
1133
|
+
end
|
1134
|
+
|
1135
|
+
it 'deletes the item matching the implied one-item hash' do
|
1136
|
+
item = catalog.delete(17 => 14)
|
1137
|
+
item.should eq [17, 14]
|
1138
|
+
catalog.should eq [[7, 8], [47, 2]]
|
1139
|
+
end
|
1140
|
+
|
1141
|
+
it 'returns nil if the item is not found' do
|
1142
|
+
item = catalog.delete([1, 2])
|
1143
|
+
item.should be_nil
|
1144
|
+
end
|
1145
|
+
|
1146
|
+
it 'returns the result of the given block if the item is not found' do
|
1147
|
+
item = catalog.delete([1, 2]){ 'not found' }
|
1148
|
+
item.should eq 'not found'
|
1149
|
+
end
|
1150
|
+
end
|
1151
|
+
|
1152
|
+
context 'delete_at' do
|
1153
|
+
|
1154
|
+
it 'deletes the item at the specified index' do
|
1155
|
+
item = catalog.delete_at(1)
|
1156
|
+
item.should eq [17, 14]
|
1157
|
+
catalog.should eq [[7, 8], [47, 2]]
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
it 'returns nil if the index is out of range' do
|
1161
|
+
item = catalog.delete_at(100)
|
1162
|
+
item.should be_nil
|
1163
|
+
end
|
1164
|
+
|
1165
|
+
it 'returns the result of the given block if the index is out of range' do
|
1166
|
+
item = catalog.delete_at(100){ 'not found' }
|
1167
|
+
item.should eq 'not found'
|
1168
|
+
end
|
1169
|
+
end
|
1170
|
+
|
1171
|
+
context 'delete_if' do
|
1172
|
+
|
1173
|
+
it 'can yield the key/value pair on iteration' do
|
1174
|
+
catalog.delete_if {|item| item.last > 5}
|
1175
|
+
catalog.should eq [[47, 2]]
|
1176
|
+
end
|
1177
|
+
|
1178
|
+
it 'can yield the key and value separately on iteration' do
|
1179
|
+
catalog.delete_if {|key, value| value > 5}
|
1180
|
+
catalog.should eq [[47, 2]]
|
1181
|
+
end
|
1182
|
+
|
1183
|
+
it 'removes the matched items' do
|
1184
|
+
catalog.delete_if {|key, value| value > 5}
|
1185
|
+
catalog.should eq [[47, 2]]
|
1186
|
+
end
|
1187
|
+
|
1188
|
+
it 'does nothing on no matches' do
|
1189
|
+
result = catalog.delete_if {|item| false }
|
1190
|
+
result.should eq catalog
|
1191
|
+
end
|
1192
|
+
|
1193
|
+
it 'returns self' do
|
1194
|
+
result = catalog.delete_if {|key, value| value > 5}
|
1195
|
+
result.object_id.should eq catalog.object_id
|
1196
|
+
end
|
1197
|
+
|
1198
|
+
it 'raises an exception if a block is not given' do
|
1199
|
+
lambda {
|
1200
|
+
catalog.delete_if
|
1201
|
+
}.should raise_error(ArgumentError)
|
1202
|
+
end
|
1203
|
+
end
|
1204
|
+
end
|
1205
|
+
end
|
1206
|
+
end
|