quality_extensions 1.1.4 → 1.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/lib/Xfind_bug_test.rb +28 -0
  2. data/lib/quality_extensions/array/all_same.rb +40 -0
  3. data/lib/quality_extensions/array/delete_if_bang.rb +145 -0
  4. data/lib/quality_extensions/array/expand_ranges.rb +3 -53
  5. data/lib/quality_extensions/array/include_any_of.rb +23 -0
  6. data/lib/quality_extensions/array/justify.rb +305 -0
  7. data/lib/quality_extensions/array/{average.rb → mean.rb} +1 -1
  8. data/lib/quality_extensions/array/select_if_bang.rb +0 -0
  9. data/lib/quality_extensions/array/sum.rb +30 -0
  10. data/lib/quality_extensions/enumerable/all_same.rb +43 -0
  11. data/lib/quality_extensions/enumerable/group_by_and_map.rb +110 -0
  12. data/lib/quality_extensions/enumerable/select_bang.rb +49 -0
  13. data/lib/quality_extensions/enumerable/select_while.rb +337 -52
  14. data/lib/quality_extensions/enumerable/select_with_index.rb +145 -0
  15. data/lib/quality_extensions/hash/hash_select.rb +5 -2
  16. data/lib/quality_extensions/hash/merge_if.rb +48 -0
  17. data/lib/quality_extensions/kernel/example_printer.rb +10 -3
  18. data/lib/quality_extensions/kernel/require_all.rb +24 -8
  19. data/lib/quality_extensions/kernel/sleep_loudly.rb +108 -0
  20. data/lib/quality_extensions/kernel/uninterruptable.rb +22 -0
  21. data/lib/quality_extensions/matrix/indexable.rb +68 -0
  22. data/lib/quality_extensions/matrix/linked_vectors.rb +137 -0
  23. data/lib/quality_extensions/module/class_methods.rb +2 -0
  24. data/lib/quality_extensions/object/non.rb +12 -0
  25. data/lib/quality_extensions/pathname.rb +435 -7
  26. data/lib/quality_extensions/range_list.rb +222 -0
  27. data/lib/quality_extensions/safe_nil.rb +5 -3
  28. data/lib/quality_extensions/string/each_char_with_index.rb +1 -2
  29. data/lib/quality_extensions/string/integer_eh.rb +3 -0
  30. data/lib/quality_extensions/string/numeric_eh.rb +74 -0
  31. data/lib/quality_extensions/string/safe_in_comment.rb +38 -0
  32. data/lib/quality_extensions/string/safe_numeric_conversion.rb +102 -0
  33. data/lib/quality_extensions/string/shell_escape.rb +4 -4
  34. data/lib/quality_extensions/string/with_knowledge_of_color.rb +8 -0
  35. data/lib/quality_extensions/table.rb +116 -0
  36. data/lib/quality_extensions/template.rb +4 -5
  37. data/lib/quality_extensions/template.rb_test_unit.rb +33 -0
  38. data/lib/quality_extensions/test/difference_highlighting-minitest.rb +321 -0
  39. data/lib/quality_extensions/test/difference_highlighting-test_unit.rb +325 -0
  40. data/lib/quality_extensions/test/difference_highlighting.rb +6 -314
  41. data/lib/quality_extensions/timeout/countdown_timer.rb +1 -0
  42. data/lib/quality_extensions/vector/enumerable.rb +51 -0
  43. metadata +35 -5
@@ -0,0 +1,110 @@
1
+ #--
2
+ # Author:: Tyler Rick, Erik Veenstra
3
+ # Copyright:: -
4
+ # License:: -
5
+ # Submit to Facets?:: Maybe
6
+ # Developer notes::
7
+ # Changes::
8
+ # * Created, based on Facets group_by
9
+ #++
10
+
11
+ module Enumerable
12
+
13
+ # #group_by_and_map is used to group items in a collection by something they
14
+ # have in common. The common factor is the key in the resulting hash, the
15
+ # array of like elements is the value.
16
+ #
17
+ # This differs from the normal group_by in that it lets you map the values
18
+ # (perhaps removing the key from the value since that would be redundant)
19
+ # all in one step.
20
+ #
21
+ #
22
+ # # need better example
23
+ # (1..6).group_by_and_map { |n| next n % 3, n }
24
+ # # => { 0 => [3,6], 1 => [1, 4], 2 => [2,5] }
25
+ #
26
+ # [
27
+ # ['31a4', 'v1.3'],
28
+ # ['9f2b', 'current'],
29
+ # ['9f2b', 'v2.0']
30
+ # ].group_by_and_map { |e| e[0], e[1] }
31
+ # # => {"31a4"=>["v1.3"], "9f2b"=>["current", "v2.0"]}
32
+ #
33
+ # results.group_by_and_map { |a| a[0], a[1..-1] }
34
+ #
35
+ # CREDIT: Erik Veenstra, Tyler Rick
36
+
37
+ def group_by_and_map #:yield:
38
+ h = Hash.new
39
+ each { |e|
40
+ result = yield(e)
41
+ #p result
42
+ (h[result[0]] ||= []) << result[1]
43
+ }
44
+ h
45
+ end
46
+
47
+ end
48
+
49
+ # _____ _
50
+ # |_ _|__ ___| |_
51
+ # | |/ _ \/ __| __|
52
+ # | | __/\__ \ |_
53
+ # |_|\___||___/\__|
54
+ #
55
+ =begin test
56
+ require 'spec'
57
+
58
+ describe 'Enumerable#group_by_and_map' do
59
+ it '' do
60
+ (1..6).group_by_and_map { |n| next n % 3, n }.should ==
61
+ {0=>[3, 6], 1=>[1, 4], 2=>[2, 5]}
62
+ end
63
+
64
+ it '' do
65
+ [
66
+ ['r1', 'v1.3'],
67
+ ['r9', 'current'],
68
+ ['r9', 'v2.0']
69
+ ].group_by_and_map { |e| next e[0], e[1] }.should ==
70
+ {"r1"=>["v1.3"], "r9"=>["current", "v2.0"]}
71
+ end
72
+
73
+ it 'beats the alternatives' do
74
+ expected = {1=>["a", "b"], 2=>["c"], 3=>["d"]}
75
+ result = [
76
+ [1, 'a'],
77
+ [1, 'b'],
78
+ [2, 'c'],
79
+ [3, 'd'],
80
+ ].group_by_and_map { |e| next e[0], e[1] }
81
+ result.should == expected
82
+ result.should be_instance_of Hash
83
+ result[1].should == ['a', 'b']
84
+
85
+ # group_by and then map? Then it's no longer a hash and is icky to work with.
86
+ result = [
87
+ [1, 'a'],
88
+ [1, 'b'],
89
+ [2, 'c'],
90
+ [3, 'd'],
91
+ ].group_by { |e| e[0] }.map { |(k,v)| [k,v.map(&:last)]}
92
+ result.should == expected.to_a
93
+ result.should be_instance_of Array
94
+ # Yuck
95
+ result[0].should == [1, ['a', 'b']]
96
+ result[1].should == [2, ['c']]
97
+
98
+ # map and then group_by? As soon as you use map to discard the key so it's not in the value, that information is lost and can't be used by the group_by.
99
+ result = [
100
+ [1, 'a'],
101
+ [1, 'b'],
102
+ [2, 'c'],
103
+ [3, 'd'],
104
+ ].map {|a| a[1]}
105
+ result.should == ["a", "b", "c", "d"] # oops, now how do we do a group_by on the key we just removed?
106
+ result.should be_instance_of Array
107
+ end
108
+ end
109
+ =end
110
+
@@ -0,0 +1,49 @@
1
+ #--
2
+ # Author:: Tyler Rick
3
+ # Copyright:: Copyright (c) 2009, Tyler Rick
4
+ # License:: Ruby License
5
+ # Submit to Facets?:: Yes
6
+ # Developer notes::
7
+ # History::
8
+ #++
9
+
10
+ module Enumerable
11
+ # def reject!
12
+ # if self.is_a? Range
13
+ # to_a.reject! { yield }
14
+ # else
15
+ # raise NoMethodError
16
+ # end
17
+ # end
18
+
19
+ def select!
20
+ reject! { |x| !yield(x) }
21
+ end
22
+ end
23
+
24
+
25
+ # _____ _
26
+ # |_ _|__ ___| |_
27
+ # | |/ _ \/ __| __|
28
+ # | | __/\__ \ |_
29
+ # |_|\___||___/\__|
30
+ #
31
+ =begin test
32
+ require 'spec'
33
+
34
+ describe 'Enumerable#select!' do
35
+ it 'reject!' do
36
+ a = %w[a b c d]
37
+ a.reject! {|e| e =~ /[ab]/}.should == a
38
+ a.should == %w[c d]
39
+ end
40
+
41
+ it 'select!' do
42
+ a = %w[a b c d]
43
+ a.select! {|e| e =~ /[ab]/}.should == a
44
+ a.should == %w[a b]
45
+ end
46
+ end
47
+ =end
48
+
49
+
@@ -5,48 +5,200 @@
5
5
  # Submit to Facets?:: Yes.
6
6
  # Developer notes::
7
7
  # Changes::
8
+ # To do::
9
+ # * !! Why does Hash#each_with_index yield |(k,v), i| but my select_with_index yields flat |k, v, i| ?
8
10
  #++
9
11
 
12
+ require 'facets/kernel/require_local'
13
+ require_local 'select_with_index'
10
14
 
11
15
  module Enumerable
16
+ # Original version before I changed it to use select so that Hash#select_until would return a hash instead of an array.
17
+ # def select_until(inclusive = true)
18
+ # return self unless block_given?
19
+ #
20
+ # selected = []
21
+ # if inclusive
22
+ # each do |item|
23
+ # selected << item
24
+ # break if yield(item)
25
+ # end
26
+ # else
27
+ # each do |item|
28
+ # break if yield(item)
29
+ # selected << item
30
+ # end
31
+ # end
32
+ # selected
33
+ # end
34
+
12
35
  # Returns an array containing all _consecutive_ elements of +enum+ for which +block+ is not false, starting at the first element.
13
- # So it is very much like select, only it stops searching as soon as <tt>block</tt> ceases to be true. (That means it will stop searching immediately if the first element doesn't match.)
36
+ # So it is very much like +select+, but unlike +select+, it stops searching as soon as +block+ ceases to be true. (That means it will stop searching immediately if the first element doesn't match.)
14
37
  #
15
38
  # This might be preferable to +select+, for example, if:
16
39
  # * you have a very large collection of elements
17
40
  # * the desired elements are expected to all be consecutively occuring and are all at the beginning of the collection
18
41
  # * it would be costly to continue iterating all the way to the very end
19
42
  #
20
- # This is probably only useful for collections that have some kind of predictable ordering (such as Arrays).
43
+ # If +inclusive+ is false, only those elements occuring _before_ the first element for which +block+ is true will be returned.
44
+ # If +inclusive+ is true (the default), those elements occuring up to and _including_ the first element for which +block+ is true will be returned.
45
+ #
46
+ # (0..3).select_until {|v| v == 1} # => [0, 1]
47
+ # (0..3).select_until(false) {|v| v == 1} # => [0]
48
+ #
49
+ # {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 2} ) # => {"a"=>1, "b"=>2}
50
+ # {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 2} ) # => {"a"=>1}
51
+ #
52
+ def select_until(inclusive = true)
53
+ return self unless block_given?
54
+
55
+ done = false
56
+ if inclusive
57
+ select do |*args|
58
+ returning = !done
59
+ done = true if yield(*args)
60
+ returning
61
+ end
62
+ else
63
+ select do |*args|
64
+ done = true if yield(*args)
65
+ !done
66
+ end
67
+ end
68
+ end
69
+
70
+ # Same as select_until but each time an element of the enum is yielded, an index/counter is also included as an argument.
21
71
  #
22
- # AKA: select_top_elements_that_match
72
+ # This is probably only useful for collections that have some kind of predictable ordering (such as Arrays). Fortunately, Hashes have a predictable order in Ruby 1.9.
23
73
  #
24
- def select_until(inclusive = false, &block)
25
- selected = []
26
- inclusive ? (
27
- each do |item|
28
- selected << item
29
- break if block.call(item)
74
+ def select_until_with_index(inclusive = true)
75
+ return self unless block_given?
76
+
77
+ done = false
78
+ if inclusive
79
+ select_with_index do |*args|
80
+ returning = !done
81
+ done = true if yield(*args)
82
+ returning
30
83
  end
31
- ) : (
32
- each do |item|
33
- break if block.call(item)
34
- selected << item
84
+ else
85
+ select_with_index do |*args|
86
+ done = true if yield(*args)
87
+ !done
35
88
  end
36
- )
37
- selected
89
+ end
38
90
  end
39
91
 
40
- def select_while(&block)
41
- selected = []
42
- each do |item|
43
- break if !block.call(item)
44
- selected << item
92
+ # Better name? select_consec?
93
+ def select_while(include_first_false_element = false)
94
+ return self unless block_given?
95
+
96
+ done = false
97
+ if include_first_false_element
98
+ select do |*args|
99
+ returning = !done
100
+ done = true if !yield(*args)
101
+ returning
102
+ end
103
+ else
104
+ select do |*args|
105
+ #puts "!done=#{!done}; !yield(#{args}) => #{!yield(*args)}"
106
+ done = true if !yield(*args)
107
+ !done
108
+ end
45
109
  end
46
- selected
47
110
  end
48
- end
49
111
 
112
+ def select_while_with_index(include_first_false_element = false)
113
+ return self unless block_given?
114
+
115
+ done = false
116
+ if include_first_false_element
117
+ select_with_index do |*args|
118
+ returning = !done
119
+ done = true if !yield(*args)
120
+ returning
121
+ end
122
+ else
123
+ select_with_index do |*args|
124
+ #puts "!done=#{!done}; !yield(#{args}) => #{!yield(*args)}"
125
+ done = true if !yield(*args)
126
+ !done
127
+ end
128
+ end
129
+ end
130
+
131
+ # Whereas select_until goes until it reaches the _first_ element for which +block+ is true, select_until_last goes until it reaches the _last_ element for which +block+ is true.
132
+ #
133
+ def select_until_last(inclusive = true)
134
+ return self unless block_given?
135
+
136
+ # Find the index of the last-matching element
137
+ last = nil
138
+ #each_with_index do |item, i|
139
+ # last = i if yield(item)
140
+ #end
141
+ each_with_index do |*args|
142
+ i = args.pop
143
+ last = i if yield(*args.first)
144
+ end
145
+
146
+ # If they want to exclude the last-matching element, adjust the index by -1 (if possible)
147
+ #if last && !inclusive
148
+ # if last == 0
149
+ # # If the last-matching element was also the first element, then we want to select *none* of the elements
150
+ # last = nil
151
+ # else
152
+ # last -= 1
153
+ # end
154
+ #end
155
+ # Select all elements up to last-matching element
156
+ #self.to_a[0 .. last] # (didn't work for hashes)
157
+ #select_with_index do |item, i|
158
+ # true if last && i <= last
159
+ #end
160
+ #select_while_with_index do |item, i|
161
+ # #puts "#{i} <= #{last} => #{last && i <= last}"
162
+ # last && i <= last
163
+ #end
164
+
165
+ # Select all elements up to last-matching element
166
+ if last.nil?
167
+ select do |*args|
168
+ false
169
+ end
170
+ else
171
+ select_until_with_index(inclusive) do |*args|
172
+ i = args.last
173
+ i == last
174
+ end
175
+ end
176
+ end
177
+
178
+ def select_until_last_with_index(inclusive = true)
179
+ return self unless block_given?
180
+
181
+ # Find the index of the last-matching element
182
+ last = nil
183
+ each_with_index do |*args|
184
+ #p args
185
+ i = args.last
186
+ last = i if yield(*args)
187
+ end
188
+
189
+ # Select all elements up to last-matching element
190
+ if last.nil?
191
+ select do |*args|
192
+ false
193
+ end
194
+ else
195
+ select_until_with_index(inclusive) do |*args|
196
+ i = args.last
197
+ i == last
198
+ end
199
+ end
200
+ end
201
+ end
50
202
 
51
203
 
52
204
 
@@ -59,50 +211,183 @@ end
59
211
  #
60
212
  =begin test
61
213
  require 'test/unit'
214
+ require 'facets/string/indent'
215
+
216
+ class SelectWhileAndUntilTest < Test::Unit::TestCase
217
+ def test_basic_usage
218
+ assert_equal [0, 1], (0..3).select {|v| v <= 1}
219
+ assert_equal [0, 1], (0..3).select_while {|v| v <= 1}
220
+ assert_equal [0, 1], (0..3).select_until {|v| v == 1}
221
+ assert_equal [0], (0..3).select_until(false) {|v| v == 1}
222
+ end
223
+
224
+ def test_basic_usage_with_index
225
+ assert_equal %w[a b], %w[a b c].select_with_index {|v, i| i <= 1}
226
+ assert_equal %w[a b], %w[a b c].select_while_with_index {|v, i| i <= 1}
227
+ assert_equal %w[a b], %w[a b c].select_until_with_index {|v, i| i == 1}
228
+ assert_equal %w[a], %w[a b c].select_until_with_index(false) {|v, i| i == 1}
229
+ end
230
+
231
+ def test_when_no_matches_found
232
+ assert_equal [], (0..3).select {|v| false}
233
+ assert_equal [], (0..3).select_while {|v| false}
234
+ assert_equal [0], (0..3).select_until {|v| true}
235
+ assert_equal [], (0..3).select_until(false) {|v| true}
236
+ end
237
+
238
+ def test_with_hashes_basic_usage
239
+ if RUBY_VERSION < '1.9'
240
+ # Yuck! Hash#select returns an array instead of returning a hash like reject.
241
+ assert_equal [["a", 1], ["b", 2], ["d", 1]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select {|k, v| v <= 2}
242
+ assert_equal [["a", 1], ["b", 2] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while {|k, v| v <= 2}
243
+ assert_equal [["a", 1], ["b", 2] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 2}
244
+ assert_equal [["a", 1] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 2}
245
+ else
246
+ assert_equal( {"a"=>1, "b"=>2, "d"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select {|k, v| v <= 2})
247
+ assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while {|k, v| v <= 2} )
248
+ assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 2} )
249
+ assert_equal( {"a"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 2} )
250
+ end
251
+ end
62
252
 
63
- class TheTest < Test::Unit::TestCase
64
- def test_1
65
- assert_equal [1, 2], (1..4).select_while {|i| i <= 2}
66
- assert_equal [1, 2], (1..4).select_until {|i| i == 3}
253
+ def test_with_hashes_basic_usage_with_index
254
+ if RUBY_VERSION < '1.9'
255
+ assert_equal [["a", 1], ["b", 2]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_with_index {|k, v, i| i <= 1}
256
+ assert_equal [["a", 1], ["b", 2]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while_with_index {|k, v, i| i <= 1}
257
+ assert_equal [["a", 1], ["b", 2]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_with_index {|k, v, i| i == 1}
258
+ assert_equal [["a", 1] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_with_index(false) {|k, v, i| i == 1}
259
+ else
260
+ assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_with_index {|k, v, i| i <= 1} )
261
+ assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while_with_index {|k, v, i| i <= 1} )
262
+ assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_with_index {|k, v, i| i == 1} )
263
+ assert_equal( {"a"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_with_index(false) {|k, v, i| i == 1} )
264
+ end
67
265
  end
68
266
 
69
- def test_not_same_as_select
267
+ def test_with_hashes_when_no_matches_found
268
+ if RUBY_VERSION < '1.9'
269
+ assert_equal [], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select {|k, v| false}
270
+ assert_equal [], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while {|k, v| false}
271
+ assert_equal [["a", 1]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| true}
272
+ assert_equal [], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| true}
273
+ else
274
+ assert_equal( {}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select {|k, v| false})
275
+ assert_equal( {}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_while {|k, v| false} )
276
+ assert_equal( {"a"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| true} )
277
+ assert_equal( {}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| true} )
278
+ end
279
+ end
280
+
281
+
282
+ def test_show_how_it_differs_from_plain_old_select
70
283
  # Ah, yes, it behaves the same as select in *this* simple case:
71
- assert_equal [1, 2], (1..4).select {|i| i <= 2}
284
+ assert_equal [1, 2], (1..4).select {|v| v <= 2}
72
285
 
73
286
  # But what about _this_ one... hmm?
74
- assert_equal [1, 2], [1, 2, 3, 2, 1].select_while {|i| i <= 2}
75
- assert_equal [1, 2, 2, 1], [1, 2, 3, 2, 1].select {|i| i <= 2} # Not the same! Keyword: _consecutive_.
287
+ assert_equal [1, 2], [1, 2, 3, 2, 1].select_while {|v| v <= 2}
288
+ assert_equal [1, 2, 2, 1], [1, 2, 3, 2, 1].select {|v| v <= 2} # Not the same! Keyword: _consecutive_.
76
289
 
77
290
  # Or _this_ one...
78
- assert_equal [1, 2, 1], [1, 2, 1, 99, 2].select_while {|i| i <= 2}
79
- assert_equal [1, 2], [1, 2, 1, 99, 2].select {|i| i <= 2}.uniq # Even this isn't the same.
291
+ assert_equal [1, 2, 1], [1, 2, 1, 99, 2].select_while {|v| v <= 2}
292
+ assert_equal [1, 2], [1, 2, 1, 99, 2].select {|v| v <= 2}.uniq # Even this isn't the same.
80
293
  end
81
294
 
82
- def test_inclusive_option
295
+ def test_example_use_of_inclusive_option
296
+ # Let's say we have an array with these lines of code:
297
+ lines_of_code = <<-End.indent(-6).lines.map(&:chomp)
298
+ def a
299
+ :a
300
+ end
301
+ def b
302
+ :b
303
+ end
304
+ End
305
+ #puts lines_of_code.to_a
306
+ # ... and we want to match up to and including the first line that matches /end/.
307
+
308
+ assert_equal [
309
+ 'def a',
310
+ ' :a',
311
+ ],
312
+ lines_of_code.select_until(false) {|line| line =~ /end/}
313
+ # It didn't go far enough. We actually want to *include* that last element. This is when we'd want to use inclusive=true.
314
+
83
315
  assert_equal [
84
- 'def cabbage',
85
- ' :cabbage',
86
- ], [
87
- 'def cabbage',
88
- ' :cabbage',
89
- 'end',
90
- ].select_until {|line| line =~ /end/}
91
- # Not far enough. We actually want to *include* that last element.
316
+ 'def a',
317
+ ' :a',
318
+ 'end',
319
+ ],
320
+ lines_of_code.select_until(true) {|line| line =~ /end/}
92
321
 
93
322
  assert_equal [
94
- 'def cabbage',
95
- ' :cabbage',
96
- 'end',
97
- ], [
98
- 'def cabbage',
99
- ' :cabbage',
100
- 'end',
101
- 'def carrot',
102
- ' :carrot',
103
- 'end',
104
- ].select_until(true) {|line| line =~ /end/}
323
+ 'def a',
324
+ ' :a',
325
+ ],
326
+ lines_of_code.select_while(false) {|line| line !~ /end/}
327
+
328
+ # Although a bit contrived, this is an example of when you might want to use include_first_false_element = true
329
+ # In practice, you'd probably seldom have a use for it, but I thought I should include it just to be consistent with select_until. Disagree?
330
+ assert_equal [
331
+ 'def a',
332
+ ' :a',
333
+ 'end',
334
+ ],
335
+ lines_of_code.select_while(true) {|line| line !~ /end/}
336
+ end
337
+ end
338
+
339
+ class SelectUntilLastTest < Test::Unit::TestCase
340
+ def test_simplest_case
341
+ assert_equal [1], [1].select_until_last {|v| v == 1}
342
+ assert_equal [], [1].select_until_last(false) {|v| v == 1} # if exclusive, we won't even return the one and only element
343
+ end
344
+
345
+ def test_basic_usage
346
+ assert_equal [1, 2, 1], [1, 2, 1, 2].select_until_last {|v| v == 1}
347
+ assert_equal [1, 2], [1, 2, 1, 2].select_until_last(false) {|v| v == 1}
348
+ end
349
+
350
+ def test_basic_usage_with_index
351
+ assert_equal %w[a b], %w[a b c].select_until_last_with_index {|v, i| i == 1}
352
+ assert_equal %w[a], %w[a b c].select_until_last_with_index(false) {|v, i| i == 1}
353
+ end
354
+
355
+ def test_when_no_matches_found
356
+ assert_equal [], [1, 2, 1, 2].select_until_last {|v| v == 3}
357
+ assert_equal [], [1, 2, 1, 2].select_until_last(false) {|v| v == 3}
358
+ assert_equal [], [1, 2, 1, 2].select_until_last_with_index(false) {|v, i| v == 3}
359
+ # It should be the same as doing this:
360
+ assert_equal [], [1, 2, 1, 2].select {|v| false}
361
+ end
362
+
363
+ def test_with_hashes_basic_usage
364
+ if RUBY_VERSION < '1.9'
365
+ assert_equal [["a", 1]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 1}
366
+ assert_equal [ ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 1}
367
+
368
+ # See the difference:
369
+ assert_equal [["a", 1], ["b", 2], ["c", 3], ["d", 1]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last {|k, v| v == 1}
370
+ assert_equal [["a", 1], ["b", 2], ["c", 3] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last(false) {|k, v| v == 1}
371
+ else
372
+ assert_equal( {"a"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until {|k, v| v == 1} )
373
+ assert_equal( {}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until(false) {|k, v| v == 1} )
374
+
375
+ # See the difference:
376
+ assert_equal( {"a"=>1, "b"=>2, "c"=>3, "d"=>1}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last {|k, v| v == 1} )
377
+ assert_equal( {"a"=>1, "b"=>2, "c"=>3 }, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last(false) {|k, v| v == 1} )
378
+ end
105
379
  end
380
+
381
+ def test_with_hashes_basic_usage_with_index
382
+ if RUBY_VERSION < '1.9'
383
+ assert_equal [["a", 1], ["b", 2]], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last_with_index {|(k, v), i| p=[k,v,i]; i <= 1}
384
+ assert_equal [["a", 1] ], {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last_with_index(false) {|(k, v), i| i == 1}
385
+ else
386
+ assert_equal( {"a"=>1, "b"=>2}, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last_with_index {|(k, v), i| p=[k,v,i]; i <= 1} )
387
+ assert_equal( {"a"=>1 }, {'a'=>1, 'b'=>2, 'c'=>3, 'd'=>1}.select_until_last_with_index(false) {|(k, v), i| i == 1} )
388
+ end
389
+ end
390
+
106
391
  end
107
392
  =end
108
393