On Thu, 25 Jan 2001, Kevin Smith wrote: > Niklas Backlund wrote: > >parameter value > >parameter value > >long-parameter WHATEVER > > data > > data > >WHATEVER > >parameter value > >... > >Is there a neat, "Ruby-way" of pulling this off? Or do I > >just have to reconsider my choice of file format? > > Hmmm....my first cut (in pseudocode) would be: > for each line > if it's a short parameter > process that parameter > else > memorize the terminator > read lines until you hit the terminator > process that parameter > end > end Kevin, one comment on your style. I tend to outline my routines in a same way, and I guess it's a good thing to do. But if you pay attention to outlining style, then you have half-baked Ruby code already. 1) drop "each", since "for line in stream" works already 2) use '_' instead of space, and you get method names on the fly 3) plain English doesn't always give me hint what could be the variables, but Ruby like pseudo-code tries to talk to me I guess I'm approaching the point when it's as fast to write real (but rough) ruby code instead of using English. If these tests are along the way it should be ### class TestParamReader < RUNIT::TestCase def test_read2 pr = ParamReader.new pr.read("foo bar") assert_equals({"foo" => "bar"}, pr.params) pr = ParamReader.new pr.read("foo bar\nzak zok") assert_equals({"foo" => "bar", "zak" => "zok"}, pr.params) pr = ParamReader.new pr.read("foo bar\nzak zok\n\n") assert_equals({"foo" => "bar", "zak" => "zok"}, pr.params) pr = ParamReader.new pr.read("foo bar\n bar2\nzak zok\n zok2\n") assert_equals({"foo" => "bar\nbar2", "zak" => "zok\nzok2"}, pr.params) pr = ParamReader.new pr.read("foo bar\nzak zok \n zok2 \n\n zok3\nfoo bar2\n") assert_equals({"foo" => "bar\nbar2", "zak" => "zok \nzok2 \n zok3"}, pr.params) end end ### Following snippet passes these tests: ### class ParamReader attr_reader :params def initialize @params = {} end def read(file) new_param_re = /^[^\s]/ for line in file next if line.strip == "" # skip empty rows value = line name, value = line.split(/\s/, 2) if line =~ new_param_re if @params.has_key? name value = "\n" + value # if appending, add new lines too else @params[name] = "" # if not, initialize hash end # remove leading space (only one) and trailing new line if any @params[name] += value.chomp.sub(/^ /, "") end end # If you're willing to live with some constraints (not tested): # - requires newer ruby: @params = Hash.new { "" } # - doesn't handle empty rows gracefully # - doesn't handle the formatting of the beginning of the # long parameters continuation lines (strip leading space away?) def read2(file) @params = Hash.new { "" } new_param_re = /^[\s]/ for line in file value = line name, value = value.split(/\n/) if line =~ new_param_re @params[name] += value end end end ### Kevin notes: > You could extract the entire if/else into a > method to keep the main loop simpler. Aside from > Ruby's nice line-oriented input methods, I don't > see any obvious opportunities to come up with a > really cool Ruby-specific solution. Agreed. I kept everything in one loop and one routine, but it messes up the code. Also I couldn't see any really nice way of doing this. - Aleksi