Philip Mak said:
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 "<p>Hello, world! Your name is <b>#{name}</b>.</p>"
eRuby:
<p>Hello, world! Your name is <b><%=name%></b>.</p>
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 "<p>"; yield; print "</p>"
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 << "<p>"; yield io; io << "</p>"
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.
