Html Tag Generation

Hello Ruby People

I'm working on a project which translates programming examples from the
perl cookbook to several other languages
(http://pleac.sourceforge.net/)
There is a problem for which I currently have only a ugly solution:
(Section "19.8 Formatting Lists and Tables with HTML Shortcuts")

To create the following html:
# <OL><LI>red</LI> <LI>blue</LI> <LI>green</LI></OL>

in perl you can write:
use CGI qw(:standard :html3)
print ol( li([ qw(red blue green)]) );

With the ruby cgi module, I need to write:
require 'cgi'
cgi = CGI.new('html4')
print cgi.ol{ %w(red blue green).collect{|color|
     cgi.li{color}}
}

There ruby solution has two problems:
1) because the methods like CGI::li() take a block as parameter, we can not
use a list as content parameter:
cgi.li{%w(red blue green)} returns "<LI>redbluegreen</LI>" instead of
"<LI>red</LI> <LI>blue</LI> <LI>green</LI>"

2) we need to write cgi.li{..} instead of just li(..)

The result is that the scripts looks more confusing than the solution with perls cgi module.
(In most other cases the ruby solutions looks better)

So my questions are:
Are there better ways to use the cgi module which I'm not aware of?
Or is there a better module to use for html generation, preferable in the standard lib?

Regards

Karsten Meier

Karsten Meier ha scritto:

There ruby solution has two problems:
1) because the methods like CGI::li() take a block as parameter, we can not
use a list as content parameter:
cgi.li{%w(red blue green)} returns "<LI>redbluegreen</LI>" instead of
"<LI>red</LI> <LI>blue</LI> <LI>green</LI>"

2) we need to write cgi.li{..} instead of just li(..)

> The result is that the scripts looks more confusing than the solution
> with perls cgi module.
> (In most other cases the ruby solutions looks better)
>
> So my questions are:
> Are there better ways to use the cgi module which I'm not aware of?
> Or is there a better module to use for html generation, preferable in
> the standard lib?
>
> Regards
>
> Karsten Meier
>

alternatively you could use:
>> require 'cgi'
=> true
>> include CGI::Html3 # Html4 or whatever
=> Object
>> element_init()
=> nil
>> ol{'foo'}
=> "<OL>foo</OL>"

which is even more confusing maybe..
B
ut makes me think:
maybe the element_init should be called on the various HTML mixins #included and #extended methods ?

Karsten Meier wrote:

I'm working on a project which translates programming examples from the
perl cookbook to several other languages
(http://pleac.sourceforge.net/\)

Then why you are not using Perl?

<a beat>

:wink:

To create the following html:
# <OL><LI>red</LI> <LI>blue</LI> <LI>green</LI></OL>

in perl you can write:
use CGI qw(:standard :html3)
print ol( li([ qw(red blue green)]) );

Ruby/CGI might have a bigger problem. I could not find a way to make it
produce XHTML. A project should test its complex features, and XHTML tests
super-easy when XPath queries out target details. But Ruby/CGI writes tags
like <input>, not <input/>.

For MiniRubyWiki, I just rolled my own:

class XhtmlGenerator

def initialize(stream)
  @stream = stream
end

def balance_(tag, attributes, pretty = "\n")
  write(pretty)
  write("<")
  write(tag)
  write(" " + attributes) if ! attributes.nil? and attributes != ''
  write(">")
  write(pretty)

        begin
      yield()
        ensure
      write(pretty)
      write("</")
      write(tag)
      write(">")
      write(pretty)
        end
end

def small(attributes = '')
  balance_('small', attributes, '') { yield() }
end

def em(attributes = '')
  balance_('em', attributes, '') { yield() }
end

def table(attributes = '')
  balance_('table', ' summary="cosmetic table"' + attributes) { yield() }
end

def form(attributes = '')
  balance_('form', attributes) { yield() }
end

def th(attributes = nil)
  balance_('th', attributes) { yield() }
end

def td(attributes = nil)
  balance_('td', attributes, '') { yield() }
end

def tr(attributes = nil)
  balance_('tr', attributes) { yield() }
end

def h1(attributes = nil)
  balance_('h1', attributes, '') { yield() }
end

def body(attributes = nil)
  balance_('body', attributes) { yield() }
end

def head(attributes = nil)
  balance_('head', attributes) { yield() }
end

def title(attributes = nil)
  balance_('title', attributes, '') { yield() }
end

def xhtml()
  write('<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd&quot;&gt;
')
  attributes = 'xmlns="http://www.w3.org/1999/xhtml&quot;&#39;
  balance_('html', attributes) { yield() }
end

def a_href(path, stuff = nil)
  attributes = "href=\"#{path}\""
  attributes += " " + stuff if stuff
  balance_('a', attributes, '') { yield() }
end

def textarea(attributes)
  balance_('textarea', attributes, '') { yield() }
end

def nbsp()
  write('&nbsp;')
end

def space()
  write(' ')
end

def endl()
  write("\n")
end

    def write(q)
  @stream.write(q)
    end

end

That provides this:

    x = XhtmlGenerator.new()
....
  x.xhtml() {
   titleStuff()

            style = "style=\"margin-left: 0.5cm; margin-right: 0.5cm;
margin-top: 0.2cm\""

   x.body("bgcolor=\"#eeeeee\" text=\"black\" " + style) {
                formatFileBody(x, searchTags) {
                    wikiFrame(x, contents)
                    }
    }
   }

There ruby solution has two problems:
1) because the methods like CGI::li() take a block as parameter, we can

not

use a list as content parameter:
cgi.li{%w(red blue green)} returns "<LI>redbluegreen</LI>" instead of
"<LI>red</LI> <LI>blue</LI> <LI>green</LI>"

2) we need to write cgi.li{..} instead of just li(..)

myList.each{|n| x.li(n)}

The result is that the scripts looks more confusing than the solution
with perls cgi module.

Perl uses the design technique Yet Another Parser Hack to distinguish arrays
and scalars as parameters. You could enhance my def li() to detect array
arguments (using clean elegant reflection, not YAPH), and then iterate thru
the array.

···

--
  Phlip
  http://industrialxp.org/community/bin/view/Main/TestFirstUserInterfaces

I use a pretty low-tech trick myself, but it works pretty well. Just
a few methods and a couple of variables:

  @page = []
  @depth = 0

  # Outputs string to page being generated.
  def puts string
    @page << ' '*@depth+string
  end
  
  # Plain old paragraph.
  def para (attributes = {}, &block)
    method_missing(:p, attributes, &block)
  end
  
  # Centered paragraph.
  def parac (attributes = {}, &block)
    attributes[:style] = 'text-align: center'
    para(attributes, &block)
  end
  
  # Makes a generic tag; I never defined the "html" method,
  # or "head" or "body" or "h1" or "br"... so they all use
  # this code to build the appropriate tag. (I couldn't do
  # this with the "p" tag, since "p" *is* defined as a method
  # so I used "para" and "parac" above.
  def method_missing (methodSymbol, attributes = {})
    methodName = methodSymbol.to_s
    
    attribString = ''
    attributes.each do |key, val|
      raise methodName+' '+attributes.inspect if (key.nil? || val.nil?)
      attribString += ' '+key.to_s+'="'+val+'"'
    end
    if (!block_given?)
      puts '<'+methodName+attribString+' />'
    else
      puts '<'+methodName+attribString+'>'
      @depth += 1
      blockReturn = yield
      puts blockReturn if (blockReturn.kind_of?(String))
      @depth -= 1
      puts '</'+methodName+'>'
    end
    nil
  end

This does a pretty good job of making readable html. To use it:

    html do
      head do
        link(:rel=>'stylesheet', :type=>'text/css', :href=>'local.css')
        title do
          'pine.fm &mdash; ' + theTitle
        end
      end
      body do
        div(:id=>'headerBar') do
...

and so on. You can, of course, use {,} instead of do,end. It also
allows for <br /> style tags by calling the tag with a string instead
of a block.

Nothing too fancy, but it serves me well,

Chris

Phlip wrote:

Ruby/CGI might have a bigger problem. I could not find a way to make it
produce XHTML. A project should test its complex features, and XHTML tests
super-easy when XPath queries out target details. But Ruby/CGI writes tags
like <input>, not <input/>.

That's HTML for you.

For MiniRubyWiki, I just rolled my own:

Did you consider using Builder::XmlMarkup?

http://onestepback.org/index.cgi/Tech/Ruby/BuilderObjects.rdoc

James

···

--

http://catapult.rubyforge.com
http://orbjson.rubyforge.com
http://ooo4r.rubyforge.com
http://www.jamesbritt.com