Monday, December 04, 2006

From "C" style printf to Ruby ERB, a better way to printf

Let's start with a quick "Hello, World" ruby program using ERB and the DATA pseudo file.
require "erb"
hw_end = "World"
ERB.new(DATA.read).run
__END__
Hello, <%=hw_end%>

So, this is a bit more complex than puts "Hello, World"; However, it served its purpose. Now could it be that this is a better way to express formated text output in a script versus printf? So, lets simulate a database table viewer with column headers and rows using text.
# create some sample data
Stats = Struct.new(:table_name, :ex_dt, :maint_dt, :maint_t)
data = [ Stats.new("table1", "Fri Dec 01 03:47:50 PST 2006", "2006-12-01", "03:47"), Stats.new("table2", "Fri Dec 01 03:48:50 PST 2006", "2006-12-01", "03:48") ]

For our text output we might be tempted to do the following:
# here is the a printf version
printf("%-26s|%-28s|%-10s|%-10s\n", "table_name", "extract_dt", "maint_dt", "maint_time")
printf("%-26s|%-28s|%-10s|%-10s\n", "-"*26, "-"*28, "-"*10, "-"*10)
data.each do |st|
printf("%-26s|%28s|%10s|%10s\n", st.table_name, st.ex_dt, st.maint_dt, st.maint_t)
end

While this works well, maybe it would be better expressed using ERB and the DATA pseudo file? Take a look.
require "erb"
ERB.new(DATA.read, 0, "%").run
__END__
table_name |extract_dt |maint_dt |maint_time
--------------------------|----------------------------|----------|----------
% data.each do |st|
<%="%-26s|%28s|%10s|%10s" % [st.table_name, st.ex_dt, st.maint_dt, st.maint_t]%>
% end

Now to me this is a much nicer, cleaner expression, do you agree?

I would like a better expression for the row format, spacing, and alignment. That is replace the "%%-26s|%28s|%10s|%10s"... with something simpler, any ideas?

The one caveat I should highlight is for each ruby file there is only one DATA pseudo file. I would suggest using here-docs when more templates are needed. The ERB rdoc has good examples and save the DATA pseudo file for .

For reference ERB recognizes the following tags in a template.
<% Ruby code -- inline with output %>
<%= Ruby expression -- replace with result %>
<%# comment -- ignored -- useful in testing %>
% a line of Ruby code -- treated as <% line %> (optional -- see ERB.new)
%% replaced with % if first thing on a line and % processing is used
<%% or %%> -- replace with <% or %> respectively

UPDATE:
I recently discovered that the DATA psuedo file is only for the main program and not the file it appears within. Sometimes this is the same file as in my example; However, it does not have to be. So, if you execute ruby some.rb, then DATA is the __END__ within some.rb. And while some.rb may require another.rb which has its own __END__, that one is ignored. So, when this occurs I would use HERE docs which provide the same clarity I was in search of.

2 Comments:

Anonymous Anonymous said...

I played with roughly the same problem a while ago, and this was the (quite clumsy) result.

http://pastie.caboo.se/28002

using ERB sure has some appeal as well though...

1:59 AM  
Blogger Johnny P said...

Nice! I am not sure I would say clumsy, it is a better printf. Hard to beat ERB when it is baked in.

11:42 PM  

Post a Comment

<< Home