Skip to content

Commit

Permalink
allow a simpler syntax for using a builder on detached nodes
Browse files Browse the repository at this point in the history
rather than

node = nil
Nokogiri::XML::Builder.with(document.root) do |xml|
  xml.element do |xml|
    node = xml.parent
    xml.parent.unlink
  end
end

you can just do
node = Nokogiri::XML::Builder.detached(document) do |xml|
  xml.element
end
  • Loading branch information
ccutrer committed Feb 19, 2015
1 parent c154c90 commit 09fa87e
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 11 deletions.
54 changes: 43 additions & 11 deletions lib/nokogiri/xml/builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ class Builder
# A context object for use when the block has no arguments
attr_accessor :context

# The detached children when building detached nodes
attr_reader :children

attr_accessor :arity # :nodoc:

###
Expand All @@ -254,6 +257,25 @@ def self.with root, &block
new({}, root, &block)
end

###
# Create a builder with an existing document. This is for use when
# you have an existing document that you would like to augment with
# builder methods. The builder context created will not have a
# parent, so any elements created will need to later be attached
# to the document
#
# For example:
#
# doc = Nokogiri::XML::Document.new
# doc << Nokogiri::XML::Builder.detached(doc) do |xml|
# # ... Use normal builder methods here ...
# xml.awesome # add the "awesome" tag below "some_tag"
# end
#
def self.detached document, &block
new({}, nil, document, &block).children
end

###
# Create a new Builder object. +options+ are sent to the top level
# Document that is being built.
Expand All @@ -263,9 +285,13 @@ def self.with root, &block
# Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
# ...
# end
def initialize options = {}, root = nil, &block
def initialize options = {}, root = nil, document = nil, &block

if root
if document
@doc = document
@parent = nil
@children = Nokogiri::XML::NodeSet.new(@doc)
elsif root
@doc = root.document
@parent = root
else
Expand Down Expand Up @@ -318,15 +344,17 @@ def comment string
# Build a tag that is associated with namespace +ns+. Raises an
# ArgumentError if +ns+ has not been defined higher in the tree.
def [] ns
if @parent != @doc
@ns = @parent.namespace_definitions.find { |x| x.prefix == ns.to_s }
end
return self if @ns

@parent.ancestors.each do |a|
next if a == doc
@ns = a.namespace_definitions.find { |x| x.prefix == ns.to_s }
if @parent
if @parent != @doc
@ns = @parent.namespace_definitions.find { |x| x.prefix == ns.to_s }
end
return self if @ns

@parent.ancestors.each do |a|
next if a == doc
@ns = a.namespace_definitions.find { |x| x.prefix == ns.to_s }
return self if @ns
end
end

@ns = { :pending => ns.to_s }
Expand Down Expand Up @@ -380,7 +408,11 @@ def method_missing method, *args, &block # :nodoc:
###
# Insert +node+ as a child of the current Node
def insert(node, &block)
node = @parent.add_child(node)
if @parent
node = @parent.add_child(node)
else
@children << node
end
if block_given?
old_parent = @parent
@parent = node
Expand Down
14 changes: 14 additions & 0 deletions test/xml/test_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,20 @@ def test_builder_can_handle_namespace_override
assert_nil doc.at_xpath("//*[local-name() = 'products']").namespace
end

def test_detached
doc = Nokogiri::XML::Document.new
doc.root = doc.create_element('root')
nodes = Nokogiri::XML::Builder.detached(doc) do |xml|
xml['foo'].bar('xmlns:foo' => 'fooey')
xml['foo2'].bar('xmlns:foo2' => 'fooey')
end
assert_equal doc.root.children.length, 0
doc.root << nodes
assert_equal doc.root.children.length, 2
assert_equal doc.root.children.first.namespace.prefix, 'foo'
assert_equal doc.root.children.last.namespace.prefix, 'foo2'
end

private

def namespaces_defined_on(node)
Expand Down

0 comments on commit 09fa87e

Please sign in to comment.