sprsquish-blather 0.3.4 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. data/LICENSE +1 -1
  2. data/README.rdoc +41 -12
  3. data/examples/echo.rb +1 -1
  4. data/examples/execute.rb +0 -5
  5. data/examples/pubsub/cli.rb +64 -0
  6. data/examples/pubsub/ping_pong.rb +18 -0
  7. data/examples/rosterprint.rb +14 -0
  8. data/examples/xmpp4r/echo.rb +35 -0
  9. data/lib/blather/client/client.rb +19 -13
  10. data/lib/blather/client/dsl/pubsub.rb +133 -0
  11. data/lib/blather/client/dsl.rb +16 -0
  12. data/lib/blather/client.rb +1 -1
  13. data/lib/blather/core_ext/active_support/inheritable_attributes.rb +117 -0
  14. data/lib/blather/core_ext/active_support.rb +1 -117
  15. data/lib/blather/core_ext/nokogiri.rb +35 -0
  16. data/lib/blather/errors/sasl_error.rb +3 -1
  17. data/lib/blather/errors/stanza_error.rb +10 -17
  18. data/lib/blather/errors/stream_error.rb +11 -14
  19. data/lib/blather/errors.rb +3 -20
  20. data/lib/blather/jid.rb +1 -0
  21. data/lib/blather/roster.rb +9 -0
  22. data/lib/blather/roster_item.rb +6 -1
  23. data/lib/blather/stanza/disco/disco_info.rb +45 -33
  24. data/lib/blather/stanza/disco/disco_items.rb +32 -21
  25. data/lib/blather/stanza/disco.rb +7 -1
  26. data/lib/blather/stanza/iq/query.rb +16 -8
  27. data/lib/blather/stanza/iq/roster.rb +33 -22
  28. data/lib/blather/stanza/iq.rb +13 -8
  29. data/lib/blather/stanza/message.rb +20 -31
  30. data/lib/blather/stanza/presence/status.rb +13 -21
  31. data/lib/blather/stanza/presence/subscription.rb +11 -16
  32. data/lib/blather/stanza/presence.rb +3 -5
  33. data/lib/blather/stanza/pubsub/affiliations.rb +50 -0
  34. data/lib/blather/stanza/pubsub/create.rb +43 -0
  35. data/lib/blather/stanza/pubsub/errors.rb +9 -0
  36. data/lib/blather/stanza/pubsub/event.rb +77 -0
  37. data/lib/blather/stanza/pubsub/items.rb +63 -0
  38. data/lib/blather/stanza/pubsub/publish.rb +58 -0
  39. data/lib/blather/stanza/pubsub/retract.rb +53 -0
  40. data/lib/blather/stanza/pubsub/subscribe.rb +42 -0
  41. data/lib/blather/stanza/pubsub/subscription.rb +66 -0
  42. data/lib/blather/stanza/pubsub/subscriptions.rb +55 -0
  43. data/lib/blather/stanza/pubsub/unsubscribe.rb +42 -0
  44. data/lib/blather/stanza/pubsub.rb +63 -0
  45. data/lib/blather/stanza/pubsub_owner/delete.rb +34 -0
  46. data/lib/blather/stanza/pubsub_owner/purge.rb +34 -0
  47. data/lib/blather/stanza/pubsub_owner.rb +41 -0
  48. data/lib/blather/stanza.rb +35 -18
  49. data/lib/blather/stream/client.rb +1 -2
  50. data/lib/blather/stream/component.rb +9 -5
  51. data/lib/blather/stream/features/resource.rb +63 -0
  52. data/lib/blather/stream/{sasl.rb → features/sasl.rb} +53 -52
  53. data/lib/blather/stream/features/session.rb +44 -0
  54. data/lib/blather/stream/features/tls.rb +28 -0
  55. data/lib/blather/stream/features.rb +53 -0
  56. data/lib/blather/stream/parser.rb +70 -46
  57. data/lib/blather/stream.rb +76 -168
  58. data/lib/blather/xmpp_node.rb +113 -52
  59. data/lib/blather.rb +35 -12
  60. data/spec/blather/client/client_spec.rb +44 -58
  61. data/spec/blather/client/dsl/pubsub_spec.rb +465 -0
  62. data/spec/blather/client/dsl_spec.rb +19 -6
  63. data/spec/blather/core_ext/nokogiri_spec.rb +83 -0
  64. data/spec/blather/errors/sasl_error_spec.rb +8 -8
  65. data/spec/blather/errors/stanza_error_spec.rb +25 -33
  66. data/spec/blather/errors/stream_error_spec.rb +21 -16
  67. data/spec/blather/errors_spec.rb +4 -11
  68. data/spec/blather/jid_spec.rb +31 -30
  69. data/spec/blather/roster_item_spec.rb +34 -23
  70. data/spec/blather/roster_spec.rb +27 -12
  71. data/spec/blather/stanza/discos/disco_info_spec.rb +61 -42
  72. data/spec/blather/stanza/discos/disco_items_spec.rb +47 -35
  73. data/spec/blather/stanza/iq/query_spec.rb +34 -11
  74. data/spec/blather/stanza/iq/roster_spec.rb +47 -30
  75. data/spec/blather/stanza/iq_spec.rb +19 -14
  76. data/spec/blather/stanza/message_spec.rb +30 -17
  77. data/spec/blather/stanza/presence/status_spec.rb +43 -20
  78. data/spec/blather/stanza/presence/subscription_spec.rb +41 -21
  79. data/spec/blather/stanza/presence_spec.rb +34 -21
  80. data/spec/blather/stanza/pubsub/affiliations_spec.rb +57 -0
  81. data/spec/blather/stanza/pubsub/create_spec.rb +56 -0
  82. data/spec/blather/stanza/pubsub/event_spec.rb +84 -0
  83. data/spec/blather/stanza/pubsub/items_spec.rb +79 -0
  84. data/spec/blather/stanza/pubsub/publish_spec.rb +83 -0
  85. data/spec/blather/stanza/pubsub/retract_spec.rb +75 -0
  86. data/spec/blather/stanza/pubsub/subscribe_spec.rb +61 -0
  87. data/spec/blather/stanza/pubsub/subscription_spec.rb +97 -0
  88. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +59 -0
  89. data/spec/blather/stanza/pubsub/unsubscribe_spec.rb +61 -0
  90. data/spec/blather/stanza/pubsub_owner/delete_spec.rb +50 -0
  91. data/spec/blather/stanza/pubsub_owner/purge_spec.rb +50 -0
  92. data/spec/blather/stanza/pubsub_owner_spec.rb +27 -0
  93. data/spec/blather/stanza/pubsub_spec.rb +62 -0
  94. data/spec/blather/stanza_spec.rb +53 -38
  95. data/spec/blather/stream/client_spec.rb +231 -88
  96. data/spec/blather/stream/component_spec.rb +14 -5
  97. data/spec/blather/stream/parser_spec.rb +145 -0
  98. data/spec/blather/xmpp_node_spec.rb +192 -96
  99. data/spec/fixtures/pubsub.rb +311 -0
  100. data/spec/spec_helper.rb +5 -4
  101. metadata +53 -17
  102. data/Rakefile +0 -139
  103. data/ext/extconf.rb +0 -65
  104. data/ext/push_parser.c +0 -209
  105. data/lib/blather/core_ext/libxml.rb +0 -28
  106. data/lib/blather/stream/resource.rb +0 -48
  107. data/lib/blather/stream/session.rb +0 -36
  108. data/lib/blather/stream/stream_handler.rb +0 -39
  109. data/lib/blather/stream/tls.rb +0 -33
  110. data/spec/blather/core_ext/libxml_spec.rb +0 -58
@@ -1,120 +1,4 @@
1
- # Thanks to Rails ActiveSupport for everything in this file
2
-
3
- # Allows attributes to be shared within an inheritance hierarchy, but where each descendant gets a copy of
4
- # their parents' attributes, instead of just a pointer to the same. This means that the child can add elements
5
- # to, for example, an array without those additions being shared with either their parent, siblings, or
6
- # children, which is unlike the regular class-level attributes that are shared across the entire hierarchy.
7
- class Class # :nodoc:
8
- def class_inheritable_reader(*syms)
9
- syms.each do |sym|
10
- next if sym.is_a?(Hash)
11
- class_eval <<-EOS
12
- def self.#{sym}
13
- read_inheritable_attribute(:#{sym})
14
- end
15
-
16
- def #{sym}
17
- self.class.#{sym}
18
- end
19
- EOS
20
- end
21
- end
22
-
23
- def class_inheritable_writer(*syms)
24
- syms.each do |sym|
25
- class_eval <<-EOS
26
- def self.#{sym}=(obj)
27
- write_inheritable_attribute(:#{sym}, obj)
28
- end
29
- EOS
30
- end
31
- end
32
-
33
- def class_inheritable_array_writer(*syms)
34
- syms.each do |sym|
35
- class_eval <<-EOS
36
- def self.#{sym}=(obj)
37
- write_inheritable_array(:#{sym}, obj)
38
- end
39
- EOS
40
- end
41
- end
42
-
43
- def class_inheritable_hash_writer(*syms)
44
- syms.each do |sym|
45
- class_eval <<-EOS
46
- def self.#{sym}=(obj)
47
- write_inheritable_hash(:#{sym}, obj)
48
- end
49
- EOS
50
- end
51
- end
52
-
53
- def class_inheritable_accessor(*syms)
54
- class_inheritable_reader(*syms)
55
- class_inheritable_writer(*syms)
56
- end
57
-
58
- def class_inheritable_array(*syms)
59
- class_inheritable_reader(*syms)
60
- class_inheritable_array_writer(*syms)
61
- end
62
-
63
- def class_inheritable_hash(*syms)
64
- class_inheritable_reader(*syms)
65
- class_inheritable_hash_writer(*syms)
66
- end
67
-
68
- def inheritable_attributes
69
- @inheritable_attributes ||= EMPTY_INHERITABLE_ATTRIBUTES
70
- end
71
-
72
- def write_inheritable_attribute(key, value)
73
- if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
74
- @inheritable_attributes = {}
75
- end
76
- inheritable_attributes[key] = value
77
- end
78
-
79
- def write_inheritable_array(key, elements)
80
- write_inheritable_attribute(key, []) if read_inheritable_attribute(key).nil?
81
- write_inheritable_attribute(key, read_inheritable_attribute(key) + elements)
82
- end
83
-
84
- def write_inheritable_hash(key, hash)
85
- write_inheritable_attribute(key, {}) if read_inheritable_attribute(key).nil?
86
- write_inheritable_attribute(key, read_inheritable_attribute(key).merge(hash))
87
- end
88
-
89
- def read_inheritable_attribute(key)
90
- inheritable_attributes[key]
91
- end
92
-
93
- def reset_inheritable_attributes
94
- @inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
95
- end
96
-
97
- private
98
- # Prevent this constant from being created multiple times
99
- EMPTY_INHERITABLE_ATTRIBUTES = {}.freeze unless const_defined?(:EMPTY_INHERITABLE_ATTRIBUTES)
100
-
101
- def inherited_with_inheritable_attributes(child)
102
- inherited_without_inheritable_attributes(child) if respond_to?(:inherited_without_inheritable_attributes)
103
-
104
- if inheritable_attributes.equal?(EMPTY_INHERITABLE_ATTRIBUTES)
105
- new_inheritable_attributes = EMPTY_INHERITABLE_ATTRIBUTES
106
- else
107
- new_inheritable_attributes = inheritable_attributes.inject({}) do |memo, (key, value)|
108
- memo.update(key => value.duplicable? ? value.dup : value)
109
- end
110
- end
111
-
112
- child.instance_variable_set('@inheritable_attributes', new_inheritable_attributes)
113
- end
114
-
115
- alias inherited_without_inheritable_attributes inherited
116
- alias inherited inherited_with_inheritable_attributes
117
- end #Class
1
+ require File.join(File.dirname(__FILE__), 'active_support', 'inheritable_attributes')
118
2
 
119
3
  class Object # :nodoc:
120
4
  def duplicable?; true; end
@@ -0,0 +1,35 @@
1
+ module Nokogiri # :nodoc:
2
+ module XML
3
+
4
+ class Node
5
+ alias_method :element_name, :name
6
+ alias_method :element_name=, :name=
7
+
8
+ alias_method :attr_set, :[]=
9
+ def []=(name, value)
10
+ name = name.to_s
11
+ value.nil? ? remove_attribute(name) : attr_set(name, value.to_s)
12
+ end
13
+
14
+ # alias_method :attr_get, :[]
15
+ # def [](name)
16
+ # attr_get name.to_s
17
+ # end
18
+
19
+ alias_method :nokogiri_xpath, :xpath
20
+ def xpath(*paths)
21
+ paths[0] = paths[0].to_s
22
+ if paths.size > 1 && (namespaces = paths.pop).is_a?(Hash)
23
+ paths << namespaces.inject({}) { |h,v| h[v[0].to_s] = v[1]; h }
24
+ end
25
+ nokogiri_xpath *paths
26
+ end
27
+ alias_method :find, :xpath
28
+
29
+ def find_first(*paths)
30
+ xpath(*paths).first
31
+ end
32
+ end
33
+
34
+ end #XML
35
+ end #Blather
@@ -1,6 +1,8 @@
1
1
  module Blather
2
2
 
3
3
  class SASLError < BlatherError
4
+ SASL_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-sasl'
5
+
4
6
  class_inheritable_accessor :err_name
5
7
  @@registrations = {}
6
8
 
@@ -16,7 +18,7 @@ class SASLError < BlatherError
16
18
  end
17
19
 
18
20
  def name
19
- @node.children.first.element_name.gsub('-', '_').to_sym if @node
21
+ @node.find_first('err_ns:*', :err_ns => SASL_ERR_NS).element_name.gsub('-', '_').to_sym if @node
20
22
  end
21
23
  end #SASLError
22
24
 
@@ -4,6 +4,7 @@ module Blather
4
4
  # Stanza errors
5
5
  # RFC3920 Section 9.3 (http://xmpp.org/rfcs/rfc3920.html#stanzas-error)
6
6
  class StanzaError < BlatherError
7
+ STANZA_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-stanzas'
7
8
  VALID_TYPES = [:cancel, :continue, :modify, :auth, :wait]
8
9
 
9
10
  register :stanza_error
@@ -19,9 +20,9 @@ class StanzaError < BlatherError
19
20
 
20
21
  error_node = node.find_first '//*[local-name()="error"]'
21
22
 
22
- name = error_node.find_first('child::*[name()!="text"]', 'urn:ietf:params:xml:ns:xmpp-stanzas').element_name
23
+ name = error_node.find_first('child::*[name()!="text"]', STANZA_ERR_NS).element_name
23
24
  type = error_node['type']
24
- text = node.find_first '//err_ns:text', :err_ns => 'urn:ietf:params:xml:ns:xmpp-stanzas'
25
+ text = node.find_first 'descendant::*[name()="text"]', STANZA_ERR_NS
25
26
  text = text.content if text
26
27
 
27
28
  extras = error_node.find("descendant::*[name()!='text' and name()!='#{name}']").map { |n| n }
@@ -59,27 +60,19 @@ class StanzaError < BlatherError
59
60
  # Creates an XML node from the error
60
61
  def to_node
61
62
  node = self.original.reply
63
+ node.type = 'error'
64
+ node << (error_node = XMPPNode.new('error'))
62
65
 
63
- error_node = XMPPNode.new 'error'
64
- err = XMPPNode.new(@name)
66
+ error_node << (err = XMPPNode.new(@name, error_node.document))
65
67
  err.namespace = 'urn:ietf:params:xml:ns:xmpp-stanzas'
66
- error_node << err
67
68
 
68
69
  if self.text
69
- text = XMPPNode.new('text')
70
+ error_node << (text = XMPPNode.new('text', error_node.document))
70
71
  text.namespace = 'urn:ietf:params:xml:ns:xmpp-stanzas'
71
- text << self.text
72
- error_node << text
73
- end
74
-
75
- self.extras.each do |extra|
76
- extra_copy = extra.copy
77
- extra_copy.namespace = extra.namespace
78
- error_node << extra_copy
72
+ text.content = self.text
79
73
  end
80
74
 
81
- node << error_node
82
- node.type = 'error'
75
+ self.extras.each { |extra| error_node << extra.dup }
83
76
  node
84
77
  end
85
78
 
@@ -90,7 +83,7 @@ class StanzaError < BlatherError
90
83
  end
91
84
 
92
85
  def inspect # :nodoc:
93
- "Stanza Error (#{@name}): #{self.text}"
86
+ "Stanza Error (#{@name}): #{self.text} [#{self.extras}]"
94
87
  end
95
88
  alias_method :to_s, :inspect # :nodoc:
96
89
  end #StanzaError
@@ -4,6 +4,8 @@ module Blather
4
4
  # Stream Errors
5
5
  # RFC3920 Section 9.3 (http://xmpp.org/rfcs/rfc3920.html#streams-error-rules)
6
6
  class StreamError < BlatherError
7
+ STREAM_ERR_NS = 'urn:ietf:params:xml:ns:xmpp-streams'
8
+
7
9
  register :stream_error
8
10
 
9
11
  attr_reader :text, :extras
@@ -12,11 +14,12 @@ class StreamError < BlatherError
12
14
  # Factory method for instantiating the proper class
13
15
  # for the error
14
16
  def self.import(node)
15
- name = node.find_first('descendant::*[name()!="text"]', 'urn:ietf:params:xml:ns:xmpp-streams').element_name
16
- text = node.find_first '//err_ns:text', :err_ns => 'urn:ietf:params:xml:ns:xmpp-streams'
17
+ name = node.find_first('descendant::*[name()!="text"]', STREAM_ERR_NS).element_name
18
+
19
+ text = node.find_first 'descendant::*[name()="text"]', STREAM_ERR_NS
17
20
  text = text.content if text
18
21
 
19
- extras = node.find("descendant::*[name()!='text' and name()!='#{name}']").map { |n| n }
22
+ extras = node.find("descendant::*[namespace-uri()!='#{STREAM_ERR_NS}']").map { |n| n }
20
23
 
21
24
  self.new name, text, extras
22
25
  end
@@ -41,22 +44,16 @@ class StreamError < BlatherError
41
44
  def to_node
42
45
  node = XMPPNode.new('stream:error')
43
46
 
44
- err = XMPPNode.new(@name)
47
+ node << (err = XMPPNode.new(@name, node.document))
45
48
  err.namespace = 'urn:ietf:params:xml:ns:xmpp-streams'
46
- node << err
47
49
 
48
50
  if self.text
49
- text = XMPPNode.new('text')
51
+ node << (text = XMPPNode.new('text', node.document))
50
52
  text.namespace = 'urn:ietf:params:xml:ns:xmpp-streams'
51
- text << self.text
52
- node << text
53
+ text.content = self.text
53
54
  end
54
55
 
55
- self.extras.each do |extra|
56
- extra_copy = extra.copy
57
- extra_copy.namespace = extra.namespace
58
- node << extra_copy
59
- end
56
+ self.extras.each { |extra| node << extra.dup }
60
57
  node
61
58
  end
62
59
 
@@ -67,7 +64,7 @@ class StreamError < BlatherError
67
64
  end
68
65
 
69
66
  def inspect # :nodoc:
70
- "Stream Error (#{@name}): #{self.text}"
67
+ "Stream Error (#{@name}): #{self.text}" + (self.extras.empty? ? '' : " [#{self.extras}]")
71
68
  end
72
69
  alias_method :to_s, :inspect # :nodoc:
73
70
  end #StreamError
@@ -34,31 +34,14 @@ module Blather
34
34
  end
35
35
  end
36
36
 
37
- ##
38
- # TLS negotiations broke down
39
- class TLSFailure < BlatherError
40
- register :tls_failure
41
- end
42
-
43
- class ParseWarning < BlatherError
44
- register :parse_warning
45
- attr_reader :libxml_error, :message
46
-
47
- def initialize(err)
48
- @libxml_error = err
49
- @message = err.to_s
50
- end
51
- end
52
-
53
37
  ##
54
38
  # Something bad happened while parsing the incoming stream
55
39
  class ParseError < BlatherError
56
40
  register :parse_error
57
- attr_reader :libxml_error, :message
41
+ attr_reader :message
58
42
 
59
- def initialize(err)
60
- @libxml_error = err
61
- @message = err.to_s
43
+ def initialize(msg)
44
+ @message = msg.to_s
62
45
  end
63
46
  end
64
47
  end
data/lib/blather/jid.rb CHANGED
@@ -83,6 +83,7 @@ module Blather
83
83
  def <=>(o)
84
84
  to_s <=> o.to_s
85
85
  end
86
+ alias_method :eql?, :==
86
87
 
87
88
  ##
88
89
  # Test if JID is stripped
@@ -71,6 +71,15 @@ module Blather
71
71
  @items.dup
72
72
  end
73
73
 
74
+ ##
75
+ # A hash of items keyed by group
76
+ def grouped
77
+ self.inject(Hash.new{|h,k|h[k]=[]}) do |hash, item|
78
+ item[1].groups.each { |group| hash[group] << item[1] }
79
+ hash
80
+ end
81
+ end
82
+
74
83
  private
75
84
  def self.key(jid)
76
85
  JID.new(jid).stripped.to_s
@@ -13,6 +13,11 @@ module Blather
13
13
  attr_accessor :name,
14
14
  :groups
15
15
 
16
+ def self.new(item)
17
+ return item if item.is_a?(self)
18
+ super
19
+ end
20
+
16
21
  ##
17
22
  # item:: can be a JID, String (a@b) or a Stanza
18
23
  def initialize(item)
@@ -29,7 +34,7 @@ module Blather
29
34
  self.name = item[:name]
30
35
  self.subscription = item[:subscription]
31
36
  self.ask = item[:ask]
32
- item.groups.each { |g| self.groups << g }
37
+ item.groups.each { |g| @groups << g }
33
38
  end
34
39
 
35
40
  @groups = [nil] if @groups.empty?
@@ -7,64 +7,76 @@ class Stanza
7
7
  class DiscoInfo < Disco
8
8
  register :disco_info, nil, 'http://jabber.org/protocol/disco#info'
9
9
 
10
- def initialize(type = nil, node = nil, identities = [], features = [])
11
- super type
12
-
13
- self.node = node
14
-
15
- [identities].flatten.each do |id|
16
- query << (id.is_a?(Identity) ? id : Identity.new(id[:name], id[:type], id[:category]))
17
- end
18
-
19
- [features].flatten.each do |feature|
20
- query << (feature.is_a?(Feature) ? feature : Feature.new(feature))
21
- end
10
+ def self.new(type = nil, node = nil, identities = [], features = [])
11
+ new_node = super type
12
+ new_node.node = node
13
+ [identities].flatten.each { |id| new_node.query << Identity.new(id) }
14
+ [features].flatten.each { |feature| new_node.query << Feature.new(feature) }
15
+ new_node
22
16
  end
23
17
 
24
18
  ##
25
19
  # List of identity objects
26
20
  def identities
27
- identities = query.find('identity')
28
- identities = query.find('query_ns:identity', :query_ns => self.class.ns) if identities.empty?
29
- identities.map { |i| Identity.new i }
21
+ query.find('//query_ns:identity', :query_ns => self.class.registered_ns).map { |i| Identity.new i }
30
22
  end
31
23
 
32
24
  ##
33
25
  # List of feature objects
34
26
  def features
35
- features = query.find('feature')
36
- features = query.find('query_ns:feature', :query_ns => self.class.ns) if features.empty?
37
- features.map { |i| Feature.new i }
27
+ query.find('//query_ns:feature', :query_ns => self.class.registered_ns).map { |i| Feature.new i }
38
28
  end
39
29
 
40
30
  class Identity < XMPPNode
41
- attribute_accessor :category, :type
42
- attribute_accessor :name, :to_sym => false
31
+ attribute_accessor :category, :type, :call => :to_sym
32
+ attribute_accessor :name
43
33
 
44
- def initialize(name, type = nil, category = nil)
45
- super :identity
34
+ def self.new(name, type = nil, category = nil)
35
+ new_node = super :identity
46
36
 
47
- if name.is_a?(XML::Node)
48
- self.inherit name
37
+ case name
38
+ when Nokogiri::XML::Node
39
+ new_node.inherit name
40
+ when Hash
41
+ new_node.name = name[:name]
42
+ new_node.type = name[:type]
43
+ new_node.category = name[:category]
49
44
  else
50
- self.name = name
51
- self.type = type
52
- self.category = category
45
+ new_node.name = name
46
+ new_node.type = type
47
+ new_node.category = category
53
48
  end
49
+ new_node
54
50
  end
51
+
52
+ def eql?(o)
53
+ raise "Cannot compare #{self.class} with #{o.class}" unless o.is_a?(self.class)
54
+ o.name == self.name &&
55
+ o.type == self.type &&
56
+ o.category == self.category
57
+ end
58
+ alias_method :==, :eql?
55
59
  end
56
60
 
57
61
  class Feature < XMPPNode
58
- attribute_accessor :var, :to_sym => false
62
+ attribute_accessor :var
59
63
 
60
- def initialize(var)
61
- super :feature
62
- if var.is_a?(XML::Node)
63
- self.inherit var
64
+ def self.new(var)
65
+ new_node = super :feature
66
+ case var
67
+ when Nokogiri::XML::Node
68
+ new_node.inherit var
64
69
  else
65
- self.var = var
70
+ new_node.var = var
66
71
  end
72
+ new_node
73
+ end
74
+
75
+ def eql?(o)
76
+ raise "Cannot compare #{self.class} with #{o.class}" unless o.is_a?(self.class)
77
+ o.var == self.var
67
78
  end
79
+ alias_method :==, :eql?
68
80
  end
69
81
  end
70
82
 
@@ -4,47 +4,58 @@ class Stanza
4
4
  class DiscoItems < Disco
5
5
  register :disco_items, nil, 'http://jabber.org/protocol/disco#items'
6
6
 
7
- def initialize(type = nil, node = nil, items = [])
8
- super type
9
- self.node = node
10
- [items].flatten.each do |item|
11
- query << (item.is_a?(Item) ? item : Item.new(item[:jid], item[:node], item[:name]))
12
- end
7
+ def self.new(type = nil, node = nil, items = [])
8
+ new_node = super type
9
+ new_node.node = node
10
+ [items].flatten.each { |item| new_node.query << Item.new(item) }
11
+ new_node
13
12
  end
14
13
 
15
14
  def items
16
- items = query.find('item')
17
- items = query.find('query_ns:item', :query_ns => self.class.ns) if items.empty?
18
- items.map { |i| Item.new i }
15
+ query.find('//query_ns:item', :query_ns => self.class.registered_ns).map { |i| Item.new i }
19
16
  end
20
17
 
21
18
  def node=(node)
22
- query.attributes[:node] = node
19
+ query[:node] = node
23
20
  end
24
21
 
25
22
  def node
26
- query.attributes[:node]
23
+ query[:node]
27
24
  end
28
25
 
29
26
  class Item < XMPPNode
30
- def initialize(jid, node = nil, name = nil)
31
- super :item
32
-
33
- if jid.is_a?(XML::Node)
34
- self.inherit jid
27
+ def self.new(jid, node = nil, name = nil)
28
+ new_node = super :item
29
+
30
+ case jid
31
+ when Nokogiri::XML::Node
32
+ new_node.inherit jid
33
+ when Hash
34
+ new_node.jid = jid[:jid]
35
+ new_node.node = jid[:node]
36
+ new_node.name = jid[:name]
35
37
  else
36
- self.jid = jid
37
- self.node = node
38
- self.name = name
38
+ new_node.jid = jid
39
+ new_node.node = node
40
+ new_node.name = name
39
41
  end
42
+ new_node
40
43
  end
41
44
 
42
45
  def jid
43
- (j = attributes[:jid]) ? JID.new(j) : nil
46
+ (j = self[:jid]) ? JID.new(j) : nil
44
47
  end
45
48
  attribute_writer :jid
46
49
 
47
- attribute_accessor :node, :name, :to_sym => false
50
+ attribute_accessor :node, :name
51
+
52
+ def eql?(o)
53
+ raise "Cannot compare #{self.class} with #{o.class}" unless o.is_a?(self.class)
54
+ o.jid == self.jid &&
55
+ o.node == self.node &&
56
+ o.name == self.name
57
+ end
58
+ alias_method :==, :eql?
48
59
  end
49
60
  end
50
61
 
@@ -2,7 +2,13 @@ module Blather
2
2
  class Stanza
3
3
 
4
4
  class Disco < Iq::Query
5
- attribute_accessor :node, :to_sym => false
5
+ def node
6
+ query[:node]
7
+ end
8
+
9
+ def node=(node)
10
+ query[:node] = node
11
+ end
6
12
  end
7
13
 
8
14
  end #Stanza
@@ -6,16 +6,17 @@ class Iq
6
6
  register :query, :query
7
7
 
8
8
  ##
9
- # Ensure the namespace is set to the query node
10
- def initialize(type = nil)
11
- super
12
- query.namespace = self.class.ns
9
+ # Ensure the query node is created
10
+ def self.new(type = nil)
11
+ node = super
12
+ node.query
13
+ node
13
14
  end
14
15
 
15
16
  ##
16
17
  # Kill the query node before running inherit
17
18
  def inherit(node)
18
- query.remove!
19
+ query.remove
19
20
  super
20
21
  end
21
22
 
@@ -23,9 +24,16 @@ class Iq
23
24
  # Query node accessor
24
25
  # This will ensure there actually is a query node
25
26
  def query
26
- q = find_first('query')
27
- q = find_first('//query_ns:query', :query_ns => self.class.ns) if !q && self.class.ns
28
- (self << (q = XMPPNode.new('query'))) unless q
27
+ q = if self.class.registered_ns
28
+ find_first('query_ns:query', :query_ns => self.class.registered_ns)
29
+ else
30
+ find_first('query')
31
+ end
32
+
33
+ unless q
34
+ (self << (q = XMPPNode.new('query', self.document)))
35
+ q.namespace = self.class.registered_ns
36
+ end
29
37
  q
30
38
  end
31
39