From: Greg McIntyre Date: 2003-09-17T21:54:52+09:00 Subject: Re: HTML generation Philip Mak wrote: > Is there a list of all the different ways of outputting HTML in Ruby > somewhere? > > I can think of using the "puts" statement, eRuby, and CGI. Any others? > e.g.: > > "puts" statement: > > puts "

Hello, world! Your name is #{name}.

" > > eRuby: > >

Hello, world! Your name is <%=name%>.

> > CGI: > > cgi.puts { cgi.p { "Hello, world! Your name is #{cgi.b name}." } } > I use something very close to CGI but a bit easier. The problem with CGI, I find, is that you need to add the blocks together for sequential elements. e.g. cgi.p { "blah blah blah" } + cgi.p { "blah blah blah" } Which can actually get quite ugly and cause havoc for your code indentation, especially when you start introducing conditionals and loops. If possible I prefer printing on the fly, resulting in something like (simplified implementation alert): def paragraph print "

"; yield; print "

" end paragraph { print "blah blah blah" } paragraph { print "blah blah blah" } If you don't want to print to $stdout, you can set $stdout = something else beforehand or if you need thread safety (I assume setting $stdout isn't a very thread-safe thing to do), uglify your code a little mode by passing an IO object around. def paragraph(io) io << "

"; yield io; io << "

" end paragraph(io) { io << "blah blah blah" } paragraph(io) { io << "blah blah blah" } (You don't really need to use the block parameter but I pass it anyway just in case.) I find this method works really well. You end up with very neat code and its indentation level mimics that in the HTML output so its structure is always clear (and can be automatically re-indented with standard code indenters). You don't have to escape strings or rearrange your code to add iteration or conditionals and that turns out to be an enormous plus for neatness IMO. In fact, I think this method is even easier and less error-prone than hand coding HTML because it guarrantees you close off your open tags! e.g. table do tr("class"=>"headings") do th { print "Name" } th { print "Age" } end for person in people.sort tr("class"=>"record") do td { print person.name } td { print person.age } end end end And you also come up with context sensitive names instead of "tr" and "td". You can use Ruby's alias command or define methods which make things easier for particular tasks, and factor out common logic. Putting in another layer also lets you go back and change your definitions later if you need to, which is nice. e.g. def ages(headings) table({'class'=>'ages'}) do tr({'class'=>'headings'}) do headings.each do |i| th { print i } end end yield end end def record(*values) tr({'class'=>'record'}) do values.each do |i| td { print i } end end end ages 'Name Age'.split do for person in people.sort record person.name, person.age end end Anyway, just my 2c. ;) -- Greg McIntyre ======[ greg@puyo.cjb.net ]===[ http://puyo.cjb.net ]===