quality_extensions 1.1.4 → 1.1.6

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.
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