Class: Blather::XMPPNode

Inherits:
Nokogiri::XML::Node show all
Defined in:
lib/blather/xmpp_node.rb

Overview

Base XML Node All XML classes subclass XMPPNode it allows the addition of helpers

Constant Summary collapse

BASE_NAMES =
%w[presence message iq].freeze
@@registrations =
{}

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Nokogiri::XML::Node

#[]=, #attr_set, #find_first, #nokogiri_xpath, #xpath

Class Method Details

.attribute_accessor(*syms) ⇒ Object

Provides an attribute accessor helper combining attribute_reader and attribute_writer

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'


110
111
112
113
# File 'lib/blather/xmpp_node.rb', line 110

def self.attribute_accessor(*syms)
  attribute_reader *syms
  attribute_writer *syms
end

.attribute_reader(*syms) ⇒ Object

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 :to_sym => false

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'


62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/blather/xmpp_node.rb', line 62

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

.attribute_writer(*syms) ⇒ Object

Provides an attribute writer helper.

class Node
  attribute_writer :type
end

n = Node.new
n.type = 'foo'
n[:type] == 'foo'


85
86
87
88
89
90
91
92
93
94
# File 'lib/blather/xmpp_node.rb', line 85

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
end

.class_from_registration(name, xmlns) ⇒ Object

Find the class to use given the name and namespace of a stanza



29
30
31
32
# File 'lib/blather/xmpp_node.rb', line 29

def self.class_from_registration(name, xmlns)
  name = name.to_s
  @@registrations[[name, xmlns]] || @@registrations[[name, nil]]
end

.content_attr_accessor(method, conversion = nil, node = nil) ⇒ Object

Provides a quick way of building content_attr_reader and content_attr_writer for the same method and node



169
170
171
172
# File 'lib/blather/xmpp_node.rb', line 169

def self.content_attr_accessor(method, conversion = nil, node = nil)
  content_attr_reader method, conversion, node
  content_attr_writer method, node
end

.content_attr_reader(method, conversion = nil, node = nil) ⇒ Object

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)

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


132
133
134
135
136
137
138
139
140
141
# File 'lib/blather/xmpp_node.rb', line 132

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
end

.content_attr_writer(method, node = nil) ⇒ Object

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)

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>'


157
158
159
160
161
162
163
164
# File 'lib/blather/xmpp_node.rb', line 157

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
end

.import(node) ⇒ Object

Looks up the class to use then instantiates an object of that class and imports all the node‘s attributes and children into it.



38
39
40
41
42
43
44
45
# File 'lib/blather/xmpp_node.rb', line 38

def self.import(node)
  klass = class_from_registration(node.element_name, (node.namespace.href if node.namespace))
  if klass && klass != self
    klass.import(node)
  else
    new(node.element_name).inherit(node)
  end
end

.new(name = nil, doc = nil) ⇒ Object

Automatically sets the namespace registered by the subclass



176
177
178
179
180
181
182
183
# File 'lib/blather/xmpp_node.rb', line 176

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

.register(name, ns = nil) ⇒ Object

Lets a subclass register itself

This registers a namespace that is used when looking up the class name of the object to instantiate when a new stanza is received



21
22
23
24
25
# File 'lib/blather/xmpp_node.rb', line 21

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

Instance Method Details

#content_from(name, ns = nil) ⇒ Object

Pull the content from a child



228
229
230
231
# File 'lib/blather/xmpp_node.rb', line 228

def content_from(name, ns = nil)
  child = xpath(name, ns).first
  child.content if child
end

#inherit(stanza) ⇒ Object

Inherit all of stanza‘s attributes and children



251
252
253
254
255
256
257
258
259
260
# File 'lib/blather/xmpp_node.rb', line 251

def inherit(stanza)
  set_namespace stanza.namespace if stanza.namespace
  inherit_attrs stanza.attributes
  stanza.children.each do |c|
    self << (n = c.dup)
    ns = n.namespace_definitions.find { |ns| ns.prefix == c.namespace.prefix }
    n.namespace = ns if ns
  end
  self
end

#inherit_attrs(attrs) ⇒ Object

Inherit only stanza‘s attributes



264
265
266
267
# File 'lib/blather/xmpp_node.rb', line 264

def inherit_attrs(attrs)
  attrs.each  { |name, value| self[name] = value }
  self
end

#namespace=(namespaces) ⇒ Object



192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
# File 'lib/blather/xmpp_node.rb', line 192

def namespace=(namespaces)
  case namespaces
  when Nokogiri::XML::Namespace
    self.nokogiri_namespace = namespaces
  when String
    self.add_namespace nil, namespaces
  when Hash
    if ns = namespaces.delete(nil)
      self.add_namespace nil, ns
    end
    namespaces.each do |p, n|
      ns = self.add_namespace p, n
      self.nokogiri_namespace = ns
    end
  end
end

#namespace_hrefObject



209
210
211
# File 'lib/blather/xmpp_node.rb', line 209

def namespace_href
  namespace.href if namespace
end

#nokogiri_namespace=Object



191
# File 'lib/blather/xmpp_node.rb', line 191

alias_method :nokogiri_namespace=, :namespace=

#remove_child(name, ns = nil) ⇒ Object

Remove a child with the name and (optionally) namespace given



215
216
217
218
# File 'lib/blather/xmpp_node.rb', line 215

def remove_child(name, ns = nil)
  child = xpath(name, ns).first
  child.remove if child
end

#remove_children(name) ⇒ Object

Remove all children with a given name



222
223
224
# File 'lib/blather/xmpp_node.rb', line 222

def remove_children(name)
  xpath("./*[local-name()='#{name}']").remove
end

#set_content_for(node, content = nil) ⇒ Object

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



237
238
239
240
241
242
243
244
245
# File 'lib/blather/xmpp_node.rb', line 237

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
  else
    remove_child node
  end
end

#to_stanzaObject

Quickway of turning itself into a proper object



187
188
189
# File 'lib/blather/xmpp_node.rb', line 187

def to_stanza
  self.class.import self
end