lib/blather/xmpp_node.rb in blather-0.4.7 vs lib/blather/xmpp_node.rb in blather-0.4.8
- old
+ new
@@ -1,196 +1,124 @@
module Blather
- ##
# Base XML Node
- # All XML classes subclass XMPPNode
- # it allows the addition of helpers
+ # All XML classes subclass XMPPNode it allows the addition of helpers
class XMPPNode < Nokogiri::XML::Node
+ # @private
BASE_NAMES = %w[presence message iq].freeze
+ # @private
@@registrations = {}
class_inheritable_accessor :registered_ns,
:registered_name
- ##
- # Lets a subclass register itself
+ # Register a new stanza class to a name and/or namespace
#
# This registers a namespace that is used when looking
# up the class name of the object to instantiate when a new
# stanza is received
+ #
+ # @param [#to_s] name the name of the node
+ # @param [String, nil] ns the namespace the node belongs to
def self.register(name, ns = nil)
self.registered_name = name.to_s
self.registered_ns = ns
@@registrations[[self.registered_name, self.registered_ns]] = self
end
- ##
# Find the class to use given the name and namespace of a stanza
- def self.class_from_registration(name, xmlns)
+ #
+ # @param [#to_s] name the name to lookup
+ # @param [String, nil] xmlns the namespace the node belongs to
+ # @return [Class, nil] the class appropriate for the name/ns combination
+ def self.class_from_registration(name, ns = nil)
name = name.to_s
- @@registrations[[name, xmlns]] || @@registrations[[name, nil]]
+ @@registrations[[name, ns]] || @@registrations[[name, nil]]
end
- ##
- # Looks up the class to use then instantiates an object
- # of that class and imports all the <tt>node</tt>'s attributes
- # and children into it.
+ # Import an XML::Node to the appropriate class
+ #
+ # Looks up the class the node should be then creates it based on the
+ # elements of the XML::Node
+ # @param [XML::Node] node the node to import
+ # @return the appropriate object based on the node name and namespace
def self.import(node)
- klass = class_from_registration(node.element_name, (node.namespace.href if node.namespace))
+ ns = (node.namespace.href if node.namespace)
+ klass = class_from_registration(node.element_name, ns)
if klass && klass != self
klass.import(node)
else
new(node.element_name).inherit(node)
end
end
- ##
- # Provides an attribute reader helper. Default behavior is to
- # conver the values of the attribute into a symbol. This can
- # be turned off by passing <tt>:to_sym => false</tt>
+ # Create a new Node object
#
- # class Node
- # attribute_reader :type
- # attribute_reader :name, :to_sym => false
- # end
- #
- # n = Node.new
- # n[:type] = 'foo'
- # n.type == :foo
- # n[:name] = 'bar'
- # n.name == 'bar'
- def self.attribute_reader(*syms)
- opts = syms.last.is_a?(Hash) ? syms.pop : {}
- convert_str = "val.#{opts[:call]} if val" if opts[:call]
- syms.flatten.each do |sym|
- class_eval(<<-END, __FILE__, __LINE__)
- def #{sym}
- val = self[:#{sym}]
- #{convert_str}
- end
- END
- end
- end
+ # @param [String, nil] name the element name
+ # @param [XML::Document, nil] doc the document to attach the node to. If
+ # not provided one will be created
+ # @return a new object with the registered name and namespace
+ def self.new(name = nil, doc = nil)
+ name ||= self.registered_name
- ##
- # Provides an attribute writer helper.
- #
- # class Node
- # attribute_writer :type
- # end
- #
- # n = Node.new
- # n.type = 'foo'
- # n[:type] == 'foo'
- def self.attribute_writer(*syms)
- syms.flatten.each do |sym|
- next if sym.is_a?(Hash)
- class_eval(<<-END, __FILE__, __LINE__)
- def #{sym}=(value)
- self[:#{sym}] = value
- end
- END
- end
+ node = super name.to_s, (doc || Nokogiri::XML::Document.new)
+ node.document.root = node unless doc
+ node.namespace = self.registered_ns unless BASE_NAMES.include?(name.to_s)
+ node
end
- ##
- # Provides an attribute accessor helper combining
- # <tt>attribute_reader</tt> and <tt>attribute_writer</tt>
+ # Helper method to read an attribute
#
- # class Node
- # attribute_accessor :type
- # attribute_accessor :name, :to_sym => false
- # end
- #
- # n = Node.new
- # n.type = 'foo'
- # n.type == :foo
- # n.name = 'bar'
- # n.name == 'bar'
- def self.attribute_accessor(*syms)
- attribute_reader *syms
- attribute_writer *syms
+ # @param [#to_sym] attr_name the name of the attribute
+ # @param [String, Symbol, nil] to_call the name of the method to call on
+ # the returned value
+ # @return nil or the value
+ def read_attr(attr_name, to_call = nil)
+ val = self[attr_name.to_sym]
+ val && to_call ? val.__send__(to_call) : val
end
- ##
- # Provides a content reader helper that returns the content of a node
- # +method+ is the method to create
- # +conversion+ is a method to call on the content before sending it back
- # +node+ is the name of the content node (this defaults to the method name)
+ # Helper method to write a value to an attribute
#
- # class Node
- # content_attr_reader :body
- # content_attr_reader :type, :to_sym
- # content_attr_reader :id, :to_i, :identity
- # end
- #
- # n = Node.new 'foo'
- # n.to_s == "<foo><body>foobarbaz</body><type>error</type><identity>1000</identity></foo>"
- # n.body == 'foobarbaz'
- # n.type == :error
- # n.id == 1000
- def self.content_attr_reader(method, conversion = nil, node = nil)
- node ||= method
- conversion = "val.#{conversion} if val.respond_to?(:#{conversion})" if conversion
- class_eval(<<-END, __FILE__, __LINE__)
- def #{method}
- val = content_from :#{node}
- #{conversion}
- end
- END
+ # @param [#to_sym] attr_name the name of the attribute
+ # @param [#to_s] value the value to set the attribute to
+ def write_attr(attr_name, value)
+ self[attr_name.to_sym] = value
end
- ##
- # Provides a content writer helper that creates or updates the content of a node
- # +method+ is the method to create
- # +node+ is the name of the node to create (defaults to the method name)
+ # Helper method to read the content of a node
#
- # class Node
- # content_attr_writer :body
- # content_attr_writer :id, :identity
- # end
- #
- # n = Node.new 'foo'
- # n.body = 'thebodytext'
- # n.id = 'id-text'
- # n.to_s == '<foo><body>thebodytext</body><identity>id-text</identity></foo>'
- def self.content_attr_writer(method, node = nil)
- node ||= method
- class_eval(<<-END, __FILE__, __LINE__)
- def #{method}=(val)
- set_content_for :#{node}, val
- end
- END
+ # @param [#to_sym] node the name of the node
+ # @param [String, Symbol, nil] to_call the name of the method to call on
+ # the returned value
+ # @return nil or the value
+ def read_content(node, to_call = nil)
+ val = content_from node.to_sym
+ val && to_call ? val.__send__(to_call) : val
end
- ##
- # Provides a quick way of building +content_attr_reader+ and +content_attr_writer+
- # for the same method and node
- def self.content_attr_accessor(method, conversion = nil, node = nil)
- content_attr_reader method, conversion, node
- content_attr_writer method, node
- end
-
- ##
- # Automatically sets the namespace registered by the subclass
- def self.new(name = nil, doc = nil)
- name ||= self.registered_name
-
- node = super name.to_s, (doc || Nokogiri::XML::Document.new)
- node.document.root = node unless doc
- node.namespace = self.registered_ns unless BASE_NAMES.include?(name.to_s)
- node
- end
-
- ##
- # Quickway of turning itself into a proper object
+ # Turn the object into a proper stanza
+ #
+ # @return a stanza object
def to_stanza
self.class.import self
end
+ # @private
alias_method :nokogiri_namespace=, :namespace=
+ # Attach a namespace to the node
+ #
+ # @overload namespace=(ns)
+ # Attach an already created XML::Namespace
+ # @param [XML::Namespace] ns the namespace object
+ # @overload namespace=(ns)
+ # Create a new namespace and attach it
+ # @param [String] ns the namespace uri
+ # @overload namespace=(namespaces)
+ # Createa and add new namespaces from a hash
+ # @param [Hash] namespaces a hash of prefix => uri pairs
def namespace=(namespaces)
case namespaces
when Nokogiri::XML::Namespace
self.nokogiri_namespace = namespaces
when String
@@ -204,38 +132,50 @@
self.nokogiri_namespace = ns
end
end
end
+ # Helper method to get the node's namespace
+ #
+ # @return [XML::Namespace, nil] The node's namespace object if it exists
def namespace_href
namespace.href if namespace
end
- ##
# Remove a child with the name and (optionally) namespace given
+ #
+ # @param [String] name the name or xpath of the node to remove
+ # @param [String, nil] ns the namespace the node is in
def remove_child(name, ns = nil)
child = xpath(name, ns).first
child.remove if child
end
- ##
- # Remove all children with a given name
+ # Remove all children with a given name regardless of namespace
+ #
+ # @param [String] name the name of the nodes to remove
def remove_children(name)
xpath("./*[local-name()='#{name}']").remove
end
- ##
- # Pull the content from a child
+ # The content of the named node
+ #
+ # @param [String] name the name or xpath of the node
+ # @param [String, nil] ns the namespace the node is in
+ # @return [String, nil] the content of the node
def content_from(name, ns = nil)
child = xpath(name, ns).first
child.content if child
end
- ##
# Sets the content for the specified node.
# If the node exists it is updated. If not a new node is created
- # If the node exists and the content is nil, the node will be removed entirely
+ # If the node exists and the content is nil, the node will be removed
+ # entirely
+ #
+ # @param [String] node the name of the node to update/create
+ # @param [String, nil] content the content to set within the node
def set_content_for(node, content = nil)
if content
child = xpath(node).first
self << (child = XMPPNode.new(node, self.document)) unless child
child.content = content
@@ -244,12 +184,14 @@
end
end
alias_method :copy, :dup
- ##
- # Inherit all of <tt>stanza</tt>'s attributes and children
+ # Inherit the attributes and children of an XML::Node
+ #
+ # @param [XML::Node] stanza the node to inherit
+ # @return [self]
def inherit(stanza)
set_namespace stanza.namespace if stanza.namespace
inherit_attrs stanza.attributes
stanza.children.each do |c|
self << (n = c.dup)
@@ -257,14 +199,20 @@
n.namespace = ns if ns
end
self
end
- ##
- # Inherit only <tt>stanza</tt>'s attributes
+ # Inherit a set of attributes
+ #
+ # @param [Hash] attrs a hash of attributes to set on the node
+ # @return [self]
def inherit_attrs(attrs)
attrs.each { |name, value| self[name] = value }
self
end
- end #XMPPNode
-end
+ def inspect
+ self.to_xml
+ end
+ end # XMPPNode
+
+end # Blather