Ox::Builder

Gem Version Build Status

Ox::Builder provides a DSL for quickly building XML documents using the ox gem. It is intended to be a practically plug-in replacement for builder gem for constructing XML documents.

The DSL verbs are meant to map closely to those provided by builder so that an existing builder template can be ported to use Ox::Builder with minimum effort.

In addition to providing a DSL for ox, this library provides template handlers for action_view and tilt, so that a template file can be rendered in Rails and/or via Tilt. The template handlers are activated automatically when rendering a file named filename.ox.

Usage

An XML document can be generated by passing a block to Ox::Builder.build:

Ruby Code:Generated XML:

doc = Ox::Builder.build do
  instruct!
  
  person id: 123 do
    name { cdata!('John Smith') }
    age 37
    nationality 'Canadian'
  end
end

<?xml version="1.0" encoding="UTF-8"?>
<person id="123">
  <name>
    <![CDATA[John Smith]]>
  </name>
  <age>37</age>
  <nationality>Canadian</nationality>
</person>
      

Methods

The following methods are available within the Ox::Builder.build DSL:

instruct!(name = :xml, attributes = { version: '1.0', encoding: 'UTF-8' })

Creates an XML processing instruction. The name or attributes can be overridden.

Ruby Code: Generated XML:
instruct!
<?xml version="1.0" encoding="UTF-8"?>
instruct! version: 2.0
<?xml version="2.0">
instruct! 'xml-stylesheet', type: 'text/xsl', href: 'style.xsl'
<?xml-stylesheet type="text/xsl" href="style.xsl"?>

tag!(name, content = nil, attributes = {})

Creates an arbitrary XML node with a given name and any attributes. If content is passed in, it will be inserted into the tag; otherwise the tag will be empty. A block can be given to create nested tags (and there is no limit to how deep nesting can be).

If passing content to a tag, any object can be given but note that to_s will be called.

Nodes can also be created dynamically by name (assuming the name is a valid ruby method name). You can also append ! to the name in case there is a conflict with an existing method (see examples below).

Ruby Code: Generated XML:
tag! :person
<person/>
tag! :person, 'John Smith', id: '123'
<person id="123">John Smith</person>
person 'John Smith', id: '123'
<person id="123">John Smith</person>
person! 'John Smith', id: '123'
<person id="123">John Smith</person>
person do
  first_name, 'John'
  last_name, 'Smith'

  address do
    country, 'Canada'
    state, 'Ontario'
  end
end
<person>
  <first_name>John</first_name>
  <last_name>Smith</last_name>
  <address>
    <country>Canada</country>
    <state>Ontario</state>
  </address>
</person>

comment!(text)

Creates an XML comment with the given text.

Ruby Code: Generated XML:
comment!('comment goes here')
<!-- comment goes here -->

doctype!(text)

Creates a doctype element with the given text.

Ruby Code: Generated XML:
doctype!('html')
<!DOCTYPE html >

cdata!(text)

Create a CDATA node (ie. text wrapped in <![CDATA[]]>)

Ruby Code: Generated XML:
cdata! 'John Smith'
<![CDATA[John Smith]]>
tag! :name do
  cdata!('John Smith')
end
<name>
  <![CDATA[John Smith]]>
<name>

Using in a Rails view

Any view file with the extension .ox or .xml.ox will be picked up by Ox::Builder and rendered as an XML file, with MIME type application/xml. There is no setup needed to get this to work. The view should not contain Ox::Builder.build, but rather the contents of the block that you would pass to it.

# test.xml.ox
instruct!

person id: 123 do
  name { cdata!('John Smith') }
  age 37
  nationality 'Canadian'
end

Migrating from builder

Using Ox::Builder instead of Builder is straightforward:

  1. Rename the view file from filename.builder to filename.ox.
  2. Remove any block parameters from the view code. As well, any code where the block parameter was used as a message receiver, use an implicit receiver instead.
  3. Remove any instances of the special xml receiver.
  4. declare! is not currently supported as it's not implemented in ox.

Therefore, the following template:

# filename.builder
xml.people do
  xml.person do |person|
    person.name { xml.cdata! 'John Smith' }
    person.age 37
  end
end

would be converted to:

# filename.ox
people do
  person do
    name { cdata! 'John Smith' }
    age 37
  end
end

Benchmarks

Benchmarks which compare Ox::Builder to Builder can be found in benchmarks/. There are three benchmarks, for various XML output sizes.

Small file (~170 bytes)

Calculating -------------------------------------
             builder   412.000  i/100ms
                  ox   640.000  i/100ms
-------------------------------------------------
             builder      4.230k (± 6.7%) i/s -     42.436k
                  ox     10.413k (±18.5%) i/s -    100.480k

Comparison:
                  ox:    10413.5 i/s
             builder:     4230.5 i/s - 2.46x slower

Medium file (107K)

Calculating -------------------------------------
             builder     1.000  i/100ms
                  ox     2.000  i/100ms
-------------------------------------------------
             builder     11.153  (± 9.0%) i/s -    223.000
                  ox     23.454  (± 8.5%) i/s -    468.000

Comparison:
                  ox:       23.5 i/s
             builder:       11.2 i/s - 2.10x slower

Huge file (11M)

Calculating -------------------------------------
             builder     1.000  i/100ms
                  ox     1.000  i/100ms
-------------------------------------------------
             builder      0.117  (± 0.0%) i/s -      8.000  in  68.435529s
                  ox      0.221  (± 0.0%) i/s -     14.000  in  63.819146s

Comparison:
                  ox:        0.2 i/s
             builder:        0.1 i/s - 1.88x slower

Installation

Add this line to your application's Gemfile:

gem 'ox-builder'

And then execute:

$ bundle

Or install it yourself as:

$ gem install ox-builder

Development

After checking out the repo, run bin/setup to install dependencies. Then, run rake spec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/dvandersluis/ox-builder.

License

The gem is available as open source under the terms of the MIT License.