From: Robert Klemme Date: 2008-05-30T05:29:52+09:00 Subject: Re: How can a code block access an object's variables? On 29.05.2008 19:04, Rick DeNatale wrote: > On Thu, May 29, 2008 at 11:30 AM, Jacob Rubynovice wrote: >> Dear Rubyists, >> >> I am a novice Ruby developer who is interested in figuring out a way to >> design something like what is demonstrated below. The reason why >> each_line would not work in this example is because when you pass a >> block of code to the FileReader object, the memory (members of >> FileReader) is not shared with the block of code that is sent to >> FileReader. >> >> Maybe I have the wrong idea for how this would be designed. I just >> think it would be very convenient to do this sort of thing because I >> could re-use the code to open a file and read lines and only pass it >> code blocks pertainant to what I want to do with those lines of code. >> >> Please note that I do not only want to do this for an open file/read >> line script. It would also be useful if you were expanding an IP range >> that exceeds what you could store into an array object because you could >> write an each_host method that takes in a block of code that relates to >> what you want to do to each host. >> >> class FileReader >> >> def initialize(fname, &block) >> @filename = fname >> end >> def each_line(&block) >> begin >> source = File.new(@filename, "r") >> rescue => err >> exit >> end >> begin >> while(line = source.readline) >> >> line.strip! >> if((line == "") || (line =~ /[\t+\s+]/)) >> next >> end >> block.call >> end >> rescue EOFError >> source.close >> rescue => err >> puts "[!] #{err}" >> exit >> end >> end > > > This can be made much shorter using Ruby conventions: > > class FileReader > > def initialize(fname, &block) > @filename = fname > end > > def each_line > source = File.open(@filename, "r") do |f| # this will > automatically close the file > f.each do | line | # This avoids having to check for > end-of-file explicitly. > line.strip! > yield line unless ((line == "") || (line =~ /[\t+\s+]/)) > end > end > rescue => err # Note that a method definition itself acts like begin/end > puts "[!] #{err}" > exit > end > end > > The two inner lines: > line.strip! > yield line unless ((line == "") || (line =~ /[\t+\s+]/)) > > could be combined as: > > yield line unless ((line.strip! == "") || (line =~ /[\t+\s+]/)) > > But I think that the two line form might be a little clearer. > > I'm not sure just what you expect the regex to be doing, as you've > coded it it's going to skip any line which > has at least one tab followed immediately by at least one whitespace > character regardless of anything else in the line. No, the complete expression will skip empty lines as well we lines that contain either a tab, a space (which includes tab IIRC) or a plus sign. irb(main):004:0> /[\t+\s+]/ =~ " " => 0 irb(main):005:0> /[\t+\s+]/ =~ "+" => 0 irb(main):007:0> /\s/ =~ "\t" => 0 Even shorter File.foreach file do |line| next if /^\s*$/ =~ line line.chomp! # whatever end You can even stick this into a method, e.g. def File.each_non_empty file foreach file do |line| next if /^\s*$/ =~ line line.strip! yield line end end Note that the regular expression that was posted originally is most likely wrong. Kind regards robert