107 lines
2.5 KiB
Ruby
107 lines
2.5 KiB
Ruby
|
require File.expand_path(File.dirname(__FILE__) + '/edgecase')
|
||
|
|
||
|
class AboutSandwichCode < EdgeCase::Koan
|
||
|
|
||
|
def count_lines(file_name)
|
||
|
file = open(file_name)
|
||
|
count = 0
|
||
|
while line = file.gets
|
||
|
count += 1
|
||
|
end
|
||
|
count
|
||
|
ensure
|
||
|
file.close if file
|
||
|
end
|
||
|
|
||
|
def test_counting_lines
|
||
|
assert_equal __, count_lines("example_file.txt")
|
||
|
end
|
||
|
|
||
|
# ------------------------------------------------------------------
|
||
|
|
||
|
def find_line(file_name)
|
||
|
file = open(file_name)
|
||
|
while line = file.gets
|
||
|
return line if line.match(/e/)
|
||
|
end
|
||
|
ensure
|
||
|
file.close if file
|
||
|
end
|
||
|
|
||
|
def test_finding_lines
|
||
|
assert_equal __, find_line("example_file.txt")
|
||
|
end
|
||
|
|
||
|
# ------------------------------------------------------------------
|
||
|
# THINK ABOUT IT:
|
||
|
#
|
||
|
# The count_lines and find_line are similar, and yet different.
|
||
|
# They both follow the pattern of "sandwich code".
|
||
|
#
|
||
|
# Sandwich code is code that comes in three parts: (1) the top slice
|
||
|
# of bread, (2) the meat, and (3) the bottom slice of bread. The
|
||
|
# the bread part of the sandwich almost always goes together, but
|
||
|
# the meat part changes all the time.
|
||
|
#
|
||
|
# Because the changing part of the sandwich code is in the middle,
|
||
|
# abstracting the top and bottom bread slices to a library can be
|
||
|
# difficult in many languages.
|
||
|
#
|
||
|
# (Aside for C++ programmers: The idiom of capturing allocated
|
||
|
# pointers in a smart pointer constructor is an attempt to deal with
|
||
|
# the problem of sandwich code for resource allocation.)
|
||
|
#
|
||
|
# Consider the following code:
|
||
|
#
|
||
|
|
||
|
def file_sandwich(file_name)
|
||
|
file = open(file_name)
|
||
|
yield(file)
|
||
|
ensure
|
||
|
file.close if file
|
||
|
end
|
||
|
|
||
|
# Now we write:
|
||
|
|
||
|
def count_lines2(file_name)
|
||
|
file_sandwich(file_name) do |file|
|
||
|
count = 0
|
||
|
while line = file.gets
|
||
|
count += 1
|
||
|
end
|
||
|
count
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def test_counting_lines2
|
||
|
assert_equal __, count_lines2("example_file.txt")
|
||
|
end
|
||
|
|
||
|
# ------------------------------------------------------------------
|
||
|
|
||
|
def find_line2(file_name)
|
||
|
# Rewrite find_line using the file_sandwich library function.
|
||
|
end
|
||
|
|
||
|
def test_finding_lines2
|
||
|
assert_equal __, find_line2("example_file.txt")
|
||
|
end
|
||
|
|
||
|
# ------------------------------------------------------------------
|
||
|
|
||
|
def count_lines3(file_name)
|
||
|
open(file_name) do |file|
|
||
|
count = 0
|
||
|
while line = file.gets
|
||
|
count += 1
|
||
|
end
|
||
|
count
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def test_open_handles_the_file_sandwich_when_given_a_block
|
||
|
assert_equal __, count_lines3("example_file.txt")
|
||
|
end
|
||
|
|
||
|
end
|