I prefer using an XML generating mini-language (elementtree, XML::Writer, REXML, Stan, etc) to actually writing raw XML. It’s just too easy for me to forget or misstype an end tag, or forget to encode strings properly–and I find all those inline strings or even here-docs make a mess of an otherwise pretty program.
Recently I wanted some code to write FOXML for ingesting digital objects into my Fedora test instance. I’m working in Ruby so REXML seemed like the best place to start…but after I finished I ran across Builder. The Builder code turned out to be somewhat shorter, much more expressive and consequently a bit easier to read (for my eyes). Here’s a quick example of how Builder’s API improves on REXML when writing this little chunk of XML:
<dc xmlns='http://purl.org/dc/elements/1.1/'> <title>Communication in the Presence of Noise</title> </dc>
So here’s the REXML code:
dc = REXML::Element.new 'dc' dc.add_attributes 'xmlns' => 'http://purl.org/dc/elements/1.1/' title = REXML::Element.new 'title', dc title.text 'Communication in the Presence of Noise'
and the Builder code:
x = Builder::XmlMarkup.new x.dc 'xmlns' => 'http://purl.org/dc/elements/1.1' do x.title 'Communication in the Presence of Noise' end
So both are four lines, but look at how the Builder::XmlMarkup object infers the name of the element based on the message that is passed to it? Element attributes and content can be set when the element is created–something I wasn’t able to do w/ REXML. My favorite though is Builder’s use of blocks so that the hierarchical structure of the code directly mirrors that of the XML content!
So anyway, if you read this far you might actually like to see how a FOXML document can be built and ingested into Fedora–so hear goes building the document:
x = Builder::XmlMarkup.new :indent => 2 x.digitalObject 'xmlns' => 'info:fedora/fedora-system:def/foxml#' do x.objectProperties do x.property 'NAME' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'VALUE' => 'FedoraObject' x.property 'NAME' => 'info:fedora/fedora-system:def/model#state', 'VALUE' => 'A' end x.datastream 'ID' => 'DC', 'STATE' => 'A', 'CONTROL_GROUP' => 'X' do x.datastreamVersion 'ID' => 'DC.0', 'MIMETYPE' => 'text/xml' do x.xmlContent do x.tag! 'oai_dc:dc', 'xmlns:oai_dc' => 'http://www.openarchives.org/OAI/2.0/oai_dc/', 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/' do x.tag! 'dc:title', 'Communication in the Presence of Noise' x.tag! 'dc:creator', 'Claude E Shannon' x.tag! 'dc:subject', 'Information Science' end end end end end
And here’s some code to fire the foxml at Fedora in a SOAP call:
require 'Fedora-API-M-WSDLDriver' # configure api_m soap client for host = 'http://localhost:8080/fedora/services/management' user = 'fedoraAdmin' pass = 'fedoraAdmin' fedora = FedoraAPIM.new fedora.options['protocol.http.basic_auth'] << [host, user, pass] fedora.ingest SOAP::SOAPBase64.new(x.to_s), 'foxml1.0', 'added test object'













2 Comments
Great posts and a lot of work put into this. I was looking for some help getting a Ruby implementation of a invocation tool for the Fedora API. This is a great start.
I am having difficulty however with Builder. When I run this last bit of code, I’m getting a commandline error stating “uninitialized constant Builder (NameError)”
Builder 2.1.1 is installed running on Ruby 1.8.6.
Thanks for the work on the classes.
Peter, how did you install Builder? Did you install the gem? Try putting a:
at the top of your script. Also, a bunch of people hacking ruby and things like fedora hang out in irc://chat.freenode.net/#code4lib so please stop by and we can figure this stuff out interactively :-)
Post a Comment