From: Carlos Agarie Date: 2012-09-24T16:38:32+09:00 Subject: Re: sandwhich principle (ruby koans) --047d7b5daf30143b5004ca6dadf2 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Roelof, This principle says that when you have two pieces of code that are almost identical, except for the "middle" part, you can create a method that takes a block to fill the "middle" part. For example, suppose you have two methods, and both need to save information to a log file before and after execution: def hello save_in_logfile "Starting to execute method 'hello'" puts "first method" save_in_logfile "Finishing execution of method 'hello'" end def world save_in_logfile "Starting to execute method 'world'" puts "second method" save_in_logfile "Finishing execution of method 'world'" end The "sandwich" the koan talks about could be interpreted as the save_in_logfile methods being the slices of bread and the puts calls the meat. So we could use blocks to create this: def sandwich(string) save_in_logfile "Starting to execute method '#{string}'" yield save_in_logfile "Finishing to execute method '#{string}'" end And the two methods would become: def hello sandwich("hello") do puts "first method" end end def world sandwich("world") do puts "second method" end end This way, the repetitive part (save_in_logfile stuff) is called in only one place. This technique is used in the File::open method - when you call it with a block, the opened file is automatically closed after the block is executed. Hope that helps! 2012/9/24 Roelof Wobben > hello, > > I'm working on ruby koans. > > Now I have to do some sandwhich code. > > The exercise looks like this : > > require File.expand_path(File.dirname(__FILE__) + '/edgecase') > class AboutSandwichCode < EdgeCase::Koan > def count_lines(file_name) > file =3D open(file_name) > count =3D 0 > while line =3D file.gets > count +=3D 1 > end > count > ensure > file.close if file > end > def test_counting_lines > assert_equal 4, count_lines("example_file.txt") > end > # ------------------------------------------------------------------ > def find_line(file_name) > file =3D open(file_name) > while line =3D file.gets > return line if line.match(/e/) > end > ensure > file.close if file > end > def test_finding_lines > assert_equal "test\n", 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 > # 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 =3D 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 =3D 0 > while line =3D file.gets > count +=3D 1 > end > count > end > end > def test_counting_lines2 > assert_equal 4, 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 =3D 0 > while line =3D file.gets > count +=3D 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 > > But I don't get the principle. > Can anyone explain this to me ? > > Roelof > > > --=20 Carlos Agarie Control engineering student Polytechnic School University of S=E3o Paulo --047d7b5daf30143b5004ca6dadf2 Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Roelof,

This principle says that when you have two piece= s of code that are almost identical, except for the "middle" part= , you can create a method that takes a block to fill the "middle"= part.

For example, suppose you have two methods, and both nee= d to save information to a log file before and after execution:
<= br>
def hello
=A0 save_in_logfile "Starting to exe= cute method 'hello'"
=A0 puts "first method"
=A0 save_in_logfile "= Finishing execution of=A0method 'hello'"
end

def world
=A0 save_in_logfile "Starting to= execute=A0method 'world'"
=A0 puts "second method"
=A0 save_in_logfile "Finishing execution of=A0method 'world&#= 39;"=A0=A0
end

The "sandwich&q= uot; the koan talks about could be interpreted as the save_in_logfile metho= ds being the slices of bread and the puts calls the meat. So we could use b= locks to create this:

def sandwich(string)
=A0 save_in_logfile &quo= t;Starting to execute method '#{string}'"
=A0 yield<= /div>
=A0 save_in_logfile "Finishing to execute method '#{stri= ng}'"
end

And the two methods would become:

def hello
<= div>=A0 sandwich("hello") do
=A0 =A0 puts "first m= ethod"
=A0 end
end

def wo= rld
=A0 sandwich("world") do
=A0 =A0 puts "second= method"
=A0 end
end

This= way, the repetitive part (save_in_logfile stuff) is called in only one pla= ce. This technique is used in the File::open method - when you call it with= a block, the opened file is automatically closed after the block is execut= ed.

Hope that helps!

2012/9/24 Roelof Wobben <rwobben@hotmail.com>
hello,
=A0
I'm=A0working on ruby koans.

Now I have to do = some sandwhich code.
=A0
The exercise looks like this :
=A0
req= uire File.expand_path(File.dirname(__FILE__) + '/edgecase')
clas= s AboutSandwichCode < EdgeCase::Koan
=A0 def count_lines(file_name)
=A0=A0=A0 file =3D open(file_name)
=A0= =A0=A0 count =3D 0
=A0=A0=A0 while line =3D file.gets
=A0=A0=A0=A0=A0= count +=3D 1
=A0=A0=A0 end
=A0=A0=A0 count
=A0 ensure
=A0=A0= =A0 file.close if file
=A0 end
=A0 def test_counting_lines
=A0=A0=A0 assert_equal 4, count_lines("example_file.txt")
=A0 = end
=A0 # --------------------------------------------------------------= ----
=A0 def find_line(file_name)
=A0=A0=A0 file =3D open(file_name)<= br>=A0=A0=A0 while line =3D file.gets
=A0=A0=A0=A0=A0 return line if line.match(/e/)
=A0=A0=A0 end
=A0 ensu= re
=A0=A0=A0 file.close if file
=A0 end
=A0 def test_finding_lines=
=A0=A0=A0 assert_equal "test\n", find_line("example_file= .txt")
=A0 end
=A0 # -------------------------------------------= -----------------------
=A0 # THINK ABOUT IT:
=A0 #
=A0 # The count_lines and find_line are s= imilar, and yet different.
=A0 # They both follow the pattern of "s= andwich code".
=A0 #
=A0 # Sandwich code is code that comes in t= hree parts: (1) the top slice
=A0 # of bread, (2) the meat, and (3) the bottom slice of bread.=A0 The
= =A0 # bread part of the sandwich almost always goes together, but
=A0 # = the meat part changes all the time.
=A0 #
=A0 # Because the changing = part of the sandwich code is in the middle,
=A0 # abstracting the top and bottom bread slices to a library can be
= =A0 # difficult in many languages.
=A0 #
=A0 # (Aside for C++ program= mers: The idiom of capturing allocated
=A0 # pointers in a smart pointer= constructor is an attempt to deal with
=A0 # the problem of sandwich code for resource allocation.)
=A0 #
= =A0 # Consider the following code:
=A0 #
=A0 def file_sandwich(file_n= ame)
=A0=A0=A0 file =3D open(file_name)
=A0=A0=A0 yield(file)
=A0 = ensure
=A0=A0=A0 file.close if file
=A0 end
=A0 # Now we write:
=A0 def count_lines2(file_name)
=A0=A0= =A0 file_sandwich(file_name) do |file|
=A0=A0=A0=A0=A0 count =3D 0
= =A0=A0=A0=A0=A0 while line =3D file.gets
=A0=A0=A0=A0=A0=A0=A0 count += =3D 1
=A0=A0=A0=A0=A0 end
=A0=A0=A0=A0=A0 count
=A0=A0=A0 end
= =A0 end
=A0 def test_counting_lines2
=A0=A0=A0 assert_equal 4, count_lines2(&quo= t;example_file.txt")
=A0 end
=A0 # -----------------------------= -------------------------------------
=A0 def find_line2(file_name)
= =A0=A0=A0 # Rewrite find_line using the file_sandwich library function.
=A0 end
=A0 def test_finding_lines2
=A0=A0=A0 assert_equal __, find_l= ine2("example_file.txt")
=A0 end
=A0 # --------------------= ----------------------------------------------
=A0 def count_lines3(file= _name)
=A0=A0=A0 open(file_name) do |file|
=A0=A0=A0=A0=A0 count =3D 0
=A0=A0=A0=A0=A0 while line =3D file.gets
= =A0=A0=A0=A0=A0=A0=A0 count +=3D 1
=A0=A0=A0=A0=A0 end
=A0=A0=A0=A0= =A0 count
=A0=A0=A0 end
=A0 end
=A0 def test_open_handles_the_file= _sandwich_when_given_a_block
=A0=A0=A0 assert_equal __, count_lines3(&qu= ot;example_file.txt")
=A0 end
end

But I don't get the principle.
Can anyone expl= ain this to me ?
=A0
Roelof

=A0<= br>



--
Carlos Agari= e
Control engineering student
Polytechnic School
University of S= =E3o Paulo

--047d7b5daf30143b5004ca6dadf2--