>>>>> "Joshua" == Joshua Drake <jd.nospam / commandprompt.com> writes: Joshua> I am the author of the Programming in ruby on IBM Joshua> Developworks. [...] Joshua> I am getting ready to release the second article, but I Joshua> want to get some feedback from the ruby community first. I Joshua> would like the second article to be more ruby centric and Joshua> will need some help. I think Joshua just made the first entry into the Code Amelioration Contest. (See [ruby-talk:19064]) I found it interesting to peruse the responses to Joshua's request for help. There has been some talk about the "Ruby Way" of doing things, but no one has been able to describe exactly what means when a piece of Ruby code is written in the "Ruby Way". For example, we can say things like "The Ruby/FOX library isn't very Ruby-like", but we find it difficult to express exactly what it is that makes it feel wrong. BTW, I'm not picking on Lyle Johnson's fine work on FXRuby, but it is an example of a recent discussion on the topic (see [ruby-talk:19946]). Matz (and a few others) can spot the Ruby Way pretty reliably. The rest of us need a little help in learning to identify it. Below I have abstracted some candidate "rules of thumb" for defining the Ruby Way. They were all taken from responses to Joshua's request for help and based on a suggested change to his code. NOTE: These are only *candidate* guidelines based on actual code improvement suggestions. I did this to start a discussion, not to preemptively define the "Ruby Way". In fact, I'm not sure *I* agree with all the suggestions below. It is my hope that some good guidelines will be generated by consensus and discussion. Ok, without further ado, here is a list of candidate guidelines ... ---------------------------------------------------------------------- I) Factor redundant code into a method ORIGINAL print "\n\tFirst Name: " enterFirstName = STDIN.gets enterFirstName.chop! if enterFirstName == "END" print $closing break end # Repeated 2 more times ... BETTER def prompt_and_read(prompt) print "\n", prompt, ": " result = gets throw :done if !result || result == "END\n" result.chop end # ... first_name = prompt_and_read "First name" last_name = prompt_and_read "Last name" phone = prompt_and_read "Phone" ---------------------------------------------------------------------- II) Use catch/throw for control structures, use rescue/raise for communicating errors. ORIGINAL class MyLoopExit < Exception; end begin loop do # Note: prompt_and_read can raise MyLoopExit first_name = prompt_and_read "First name" last_name = prompt_and_read "Last name" phone = prompt_and_read "Phone" myfile.puts first_name + "\t" + last_name + "\t" + phone resure MyLoopExit end BETTER catch (:done) do loop do # Note: prompt_and_read can throw :done first_name = prompt_and_read "First name" last_name = prompt_and_read "Last name" phone = prompt_and_read "Phone" myfile.puts first_name + "\t" + last_name + "\t" + phone end end ---------------------------------------------------------------------- III) Take advantage of iterators. ORIGINAL first_name = prompt_and_read "First name" last_name = prompt_and_read "Last name" phone = prompt_and_read "Phone" BETTER FIELDS = [ "First name", "Last name", "Phone" ] FIELDS.each do |prompt| ... end ---------------------------------------------------------------------- IV) Take advantage of blocks to do automatic closing ORIGINAL begin myfile = File.open('phonespec.txt', 'a') # ... ensure myfile.close end file = File.open BETTER File.open('phonespec.txt', 'a') do |myfile| # ... end ---------------------------------------------------------------------- V) Avoid Unnecessary Globals ORIGINAL $closing = "\n\nTo start ... " BETTER closing = "\n\nTo start ... " ---------------------------------------------------------------------- VI) Use true/false to represent booleans, not 1/0. ORIGINAL while 1 BETTER while true ---------------------------------------------------------------------- VII) Use loop do ... end for infinite loops ORIGINAL while true # loop stuff ... end BETTER loop do # loop stuff ... end ---------------------------------------------------------------------- VIII) Use File.open rather than IO.open or just plain open. (Applies to readlines and other methods in File as well). ORIGINAL open ("phonespec.txt") # or IO.open ("phonespec.txt") BETTER File.open ("phonespec.txt") loop do # loop stuff ... end ---------------------------------------------------------------------- IX) Chop input as read ORIGINAL searchData = gets searchData.chop! BETTER searchData = gets.chop [What if gets returns a nil?] ---------------------------------------------------------------------- X) Ask objects to convert themselves ORIGINAL String(numres) BETTER numres.to_s ---------------------------------------------------------------------- XI) Take advantage of grep and other methods in Enumerable ORIGINAL IO.foreach("phonespec.txt") do |data| if myreg =~ data print "Found result: ", data numres += 1 end end BETTER found = File.readlines("phonespec.txt").grep(myreg) found.each {|f| print "Found result: #{ f }"} ---------------------------------------------------------------------- -- -- Jim Weirich jweirich / one.net http://w3.one.net/~jweirich --------------------------------------------------------------------- "Beware of bugs in the above code; I have only proved it correct, not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)