Last active
June 4, 2018 22:59
-
-
Save havenwood/501b7a7e57f512ec95cfcb7f9a44f0d0 to your computer and use it in GitHub Desktop.
An Example Implementation of Enumerable#step in Pure Ruby
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'minitest/autorun' | |
module Enumerable | |
def step step = 1 | |
raise TypeError, 'no implicit conversion of Object into Integer' unless step.respond_to? :to_int | |
step_count = step.to_int | |
raise TypeError, "can't convert #{step.class} to Integer (#{step.class}#to_int gives #{step_count.class})" unless step_count.instance_of? Integer | |
raise TypeError, "step can't be 0" if step_count.zero? | |
raise TypeError, "step can't be negative" if step_count.negative? | |
if block_given? | |
each_slice step_count do |this_step, *_rest| | |
yield this_step | |
end | |
self | |
else | |
lazy_size = size.fdiv(step_count).ceil if size | |
Enumerator.new lazy_size do |yielder| | |
each_slice step_count do |this_step, *_rest| | |
yielder << this_step | |
end | |
end | |
end | |
end | |
end | |
class ImplicitConversionExample | |
def to_int | |
4 | |
end | |
end | |
describe 'Enumerable#step' do | |
describe 'when no block given' do | |
it 'returns an enumerator' do | |
enum = 1.upto(10).step(4) | |
assert_instance_of Enumerator, enum | |
end | |
it 'returns the correct enumerator' do | |
enum = 1.upto(10).step(4) | |
assert_equal [1, 5, 9], enum.to_a | |
end | |
it 'returns a sized enumerator when possible' do | |
assert_equal 10, 1.upto(10).step(1).size | |
assert_equal 5, 1.upto(10).step(2).size | |
assert_equal 4, 1.upto(10).step(3).size | |
assert_equal 3, 1.upto(10).step(4).size | |
assert_equal 2, 1.upto(10).step(5).size | |
assert_equal 2, 1.upto(10).step(6).size | |
assert_equal 1, 1.upto(10).step(10).size | |
assert_equal 1, 1.upto(10).step(11).size | |
end | |
it 'works with unsized enumerators' do | |
enum = ('a'..'g').to_enum.step(2) | |
assert_equal %w[a c e g], enum.to_a | |
assert_nil enum.size | |
end | |
it 'raises TypeError if unsteppable' do | |
assert_raises TypeError do | |
1.upto(10).step(Object.new) | |
end | |
end | |
it 'raises a TypeError if #to_int does not return an Integer' do | |
assert_raises TypeError do | |
1.upto(2).step(Object.new) | |
end | |
end | |
it 'calls #to_int to coerce step to an Integer' do | |
obj = ImplicitConversionExample.new | |
assert_equal [1, 5, 9], 1.upto(10).step(obj).to_a | |
end | |
it 'raises an ArgumentError if step is 0' do | |
assert_raises TypeError do | |
-1.upto(1).step(0) | |
end | |
end | |
it 'raises an ArgumentError if step is 0.0' do | |
assert_raises TypeError do | |
-1.upto(1).step(0.0) | |
end | |
end | |
it 'raises an ArgumentError if step is negative' do | |
assert_raises TypeError do | |
-1.upto(1).step(-2) | |
end | |
end | |
end | |
describe 'when a block is given' do | |
it 'returns self' do | |
enum = 1.upto(2) | |
assert_equal enum.to_a, enum.step { }.to_a | |
end | |
it 'enumerates correctly' do | |
a = [] | |
1.upto(2) { |n| a << n } | |
assert_equal [1, 2], a | |
end | |
it 'raises TypeError if unsteppable' do | |
assert_raises TypeError do | |
1.upto(10).step(Object.new) { } | |
end | |
end | |
it 'raises a TypeError if #to_int does not return an Integer' do | |
assert_raises TypeError do | |
1.upto(2).step(Object.new) { } | |
end | |
end | |
it 'calls #to_int to coerce step to an Integer' do | |
obj = ImplicitConversionExample.new | |
a = [] | |
1.upto(10).step(obj) { |n| a << n } | |
assert_equal [1, 5, 9], a | |
end | |
it 'raises an ArgumentError if step is 0' do | |
assert_raises TypeError do | |
-1.upto(1).step(0) { } | |
end | |
end | |
it 'raises an ArgumentError if step is 0.0' do | |
assert_raises TypeError do | |
-1.upto(1).step(0.0) { } | |
end | |
end | |
it 'raises an ArgumentError if step is negative' do | |
assert_raises TypeError do | |
-1.upto(1).step(-2) { } | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment