removed duplicate word "The" in comment.
[ruby_koans.git] / src / about_sandwich_code.rb
1 require File.expand_path(File.dirname(__FILE__) + '/edgecase')
2
3 class AboutSandwichCode < EdgeCase::Koan
4
5   def count_lines(file_name)
6     file = open(file_name)
7     count = 0
8     while line = file.gets
9       count += 1
10     end
11     count
12   ensure
13     file.close if file
14   end
15
16   def test_counting_lines
17     assert_equal __(4), count_lines("example_file.txt")
18   end
19
20   # ------------------------------------------------------------------
21
22   def find_line(file_name)
23     file = open(file_name)
24     while line = file.gets
25       return line if line.match(/e/)
26     end
27   ensure
28     file.close if file
29   end
30
31   def test_finding_lines
32     assert_equal __("test\n"), find_line("example_file.txt")
33   end
34
35   # ------------------------------------------------------------------
36   # THINK ABOUT IT:
37   #
38   # The count_lines and find_line are similar, and yet different.
39   # They both follow the pattern of "sandwich code".
40   #
41   # Sandwich code is code that comes in three parts: (1) the top slice
42   # of bread, (2) the meat, and (3) the bottom slice of bread.  The
43   # bread part of the sandwich almost always goes together, but
44   # the meat part changes all the time.
45   #
46   # Because the changing part of the sandwich code is in the middle,
47   # abstracting the top and bottom bread slices to a library can be
48   # difficult in many languages.
49   #
50   # (Aside for C++ programmers: The idiom of capturing allocated
51   # pointers in a smart pointer constructor is an attempt to deal with
52   # the problem of sandwich code for resource allocation.)
53   #
54   # Consider the following code:
55   #
56
57   def file_sandwich(file_name)
58     file = open(file_name)
59     yield(file)
60   ensure
61     file.close if file
62   end
63
64   # Now we write:
65
66   def count_lines2(file_name)
67     file_sandwich(file_name) do |file|
68       count = 0
69       while line = file.gets
70         count += 1
71       end
72       count
73     end
74   end
75
76   def test_counting_lines2
77     assert_equal __(4), count_lines2("example_file.txt")
78   end
79
80   # ------------------------------------------------------------------
81
82   def find_line2(file_name)
83     # Rewrite find_line using the file_sandwich library function.
84     #--
85     file_sandwich(file_name) do |file|
86       file.each do |line|
87         return line if line =~ /e/
88       end
89     end
90     #++
91   end
92
93   def test_finding_lines2
94     assert_equal __("test\n"), find_line2("example_file.txt")
95   end
96
97   # ------------------------------------------------------------------
98
99   def count_lines3(file_name)
100     open(file_name) do |file|
101       count = 0
102       while line = file.gets
103         count += 1
104       end
105       count
106     end
107   end
108
109   def test_open_handles_the_file_sandwich_when_given_a_block
110     assert_equal __(4), count_lines3("example_file.txt")
111   end
112
113 end