# frozen_string_literal: true require 'lolsoap/wsdl' module LolSoap # Used to build XML, with namespaces automatically added. # # @example General # builder = Builder.new(node, type) # builder.someTag do |t| # t.foo 'bar' # end # # => bar # # @example Explicitly specifying a namespace prefix # builder = Builder.new(node, type) # builder['ns2'].someTag # # => class Builder RESERVED_METHODS = %w(object_id respond_to_missing? inspect === to_s) alias :__class__ :class instance_methods.each do |m| undef_method m unless RESERVED_METHODS.include?(m.to_s) || m =~ /^__/ end # @private class Prefix instance_methods.each do |m| undef_method m unless RESERVED_METHODS.include?(m.to_s) || m =~ /^__/ end def initialize(owner, prefix) @owner = owner @prefix = prefix end def respond_to?(name) true end private def method_missing(*args, &block) @owner.__prefixed_tag__(@prefix, LolSoap::WSDL::NullType.new, *args, &block) end end def initialize(node, type = WSDL::NullType.new) @node = node @type = type || WSDL::NullType.new end # Add a tag manually, rather than through method_missing. This is so you can still # add tags for the very small number of tags that are also existing methods. def __tag__(name, *args, &block) __prefixed_tag__(@type.element_prefix(name.to_s), @type.sub_type(name.to_s), name, *args, &block) end def __attribute__(name, value) @node[name.to_s] = value.to_s end def __content__(value) @node.content = value end # @private def __prefixed_tag__(prefix, sub_type, name, *args) sub_node = @node.document.create_element(name.to_s, *args) sub_node.namespace = @node.namespace_scopes.find { |n| n.prefix == prefix } # Nokogiri doesn't currently allow to add a child element without a # namespace to a parent with a namespace: the child inherits the parent's # namespace. It's a known issue: # https://github.com/sparklemotion/nokogiri/issues/1469 Until it's fixed, # we'll use this workaround: store the parent's namespace, set it to nil # temporarily, add the child and re-add the original namespace to the # parent. if sub_node.namespace.nil? parent_namespace = @node.namespace @node.namespace = nil @node << sub_node @node.namespace = parent_namespace else @node << sub_node end builder = __class__.new(sub_node, sub_type) yield builder if block_given? builder end # Node accessor. Named to prevent method_missing conflict. def __node__ @node end # Type accessor. Named to prevent method_missing conflict. def __type__ @type end # Specify a namespace prefix explicitly def [](prefix) Prefix.new(self, prefix) end def respond_to?(name) true end def pretty_print(pp) pp.group(2, "#(LolSoap::Builder #{sprintf("0x%x", object_id)} {", "})") do pp.pp @node end end private # alias method_missing __tag__ def method_missing(name, *args, &block) if @type.has_attribute?(name.to_s) __attribute__(name, *args) else __tag__(name, *args, &block) end end end end