|
|
# A DSL for validated XML outputclass XMLGrammar # Create an instance of this class, specifying a stream or object to # hold the output. This can be any object that responds to <<(String). def initialize(out) @out = out # Remember where to send our output end # Invoke the block in an instance that outputs to the specified stream. def self.generate(out, &block) new(out).instance_eval(&block) end # Define an allowed element (or tag) in the grammar. # This class method is the grammar-specification DSL # and defines the methods that constitute the XML-output DSL. def self.element(tagname, attributes={}) @allowed_attributes ||= {} @allowed_attributes[tagname] = attributes class_eval %Q{ def #{tagname}(attributes={}, &block) tag(:#{tagname},attributes,&block) end } end # These are constants used when defining attribute values. OPT = :opt # for optional attributes REQ = :req # for required attributes BOOL = :bool # for attributes whose value is their own name def self.allowed_attributes @allowed_attributes end # Output the specified object as CDATA, return nil. def content(text) @out << text.to_s nil end # Output the specified object as a comment, return nil. def comment(text) @out << "<!-- #{text} -->" nil end # Output a tag with the specified name and attribute. # If there is a block, invoke it to output or return content. # Return nil. def tag(tagname, attributes={}) # Output the tag name @out << "<#{tagname}" # Get the allowed attributes for this tag. allowed = self.class.allowed_attributes[tagname] # First, make sure that each of the attributes is allowed. # Assuming they are allowed, output all of the specified ones. attributes.each_pair do |key,value| raise "unknown attribute: #{key}" unless allowed.include?(key) @out << " #{key}='#{value}'" end # Now look through the allowed attributes, checking for # required attributes that were omitted and for attributes with # default values that we can output. allowed.each_pair do |key,value| # If this attribute was already output, do nothing. next if attributes.has_key? key if (value == REQ) raise "required attribute '#{key}' missing in <#{tagname}>" elsif value.is_a? String @out << " #{key}='#{value}'" end end if block_given? # This block has content @out << '>' # End the opening tag content = yield # Invoke the block to output or return content if content # If any content returned @out << content.to_s # Output it as a string end @out << "</#{tagname}>" # Close the tag else # Otherwise, this is an empty tag, so just close it. @out << '/>' end nil # Tags output themselves, so they don't return any content. endend
class HTMLForm < XMLGrammar element :form, :action => REQ, :method => "GET", :enctype => "application/x-www-form-urlencoded", :name => OPT element :input, :type => "text", :name => OPT, :value => OPT, :maxlength => OPT, :size => OPT, :src => OPT, :checked => BOOL, :disabled => BOOL, :readonly => BOOL element :textarea, :rows => REQ, :cols => REQ, :name => OPT, :disabled => BOOL, :readonly => BOOL element :button, :name => OPT, :value => OPT, :type => "submit", :disabled => OPTend
HTMLForm.generate(STDOUT) do comment "This is a simple HTML form" form :name => "registration", :action => "http://www.example.com/register.cgi" do content "Name:" input :name => "name" content "Address:" textarea :name => "address", :rows=>6, :cols=>40 do "Please enter your mailing address here" end button { "Submit" } endend#<!-- This is a simple HTML form --><form name='registration' action='http://www.example.com/register.cgi' enctype='application/x-www-form-urlencoded' method='GET'>Name:<input name='name' type='text'/>Address:<textarea rows='6' cols='40' name='address'>Please enter your mailing address here</textarea><button type='submit'>Submit</button></form>>Exit code: 0 |
|