blather 0.4.7 → 0.4.8

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 (59) hide show
  1. data/README.md +162 -0
  2. data/examples/{print_heirarchy.rb → print_hierarchy.rb} +5 -5
  3. data/examples/stream_only.rb +27 -0
  4. data/lib/blather.rb +4 -0
  5. data/lib/blather/client/client.rb +91 -73
  6. data/lib/blather/client/dsl.rb +156 -32
  7. data/lib/blather/client/dsl/pubsub.rb +86 -54
  8. data/lib/blather/core_ext/active_support.rb +9 -9
  9. data/lib/blather/core_ext/active_support/inheritable_attributes.rb +2 -2
  10. data/lib/blather/core_ext/nokogiri.rb +12 -7
  11. data/lib/blather/errors.rb +25 -14
  12. data/lib/blather/errors/sasl_error.rb +21 -3
  13. data/lib/blather/errors/stanza_error.rb +37 -21
  14. data/lib/blather/errors/stream_error.rb +27 -17
  15. data/lib/blather/jid.rb +79 -24
  16. data/lib/blather/roster.rb +39 -21
  17. data/lib/blather/roster_item.rb +43 -21
  18. data/lib/blather/stanza.rb +88 -40
  19. data/lib/blather/stanza/disco.rb +12 -2
  20. data/lib/blather/stanza/disco/disco_info.rb +112 -20
  21. data/lib/blather/stanza/disco/disco_items.rb +81 -12
  22. data/lib/blather/stanza/iq.rb +94 -38
  23. data/lib/blather/stanza/iq/query.rb +16 -22
  24. data/lib/blather/stanza/iq/roster.rb +98 -20
  25. data/lib/blather/stanza/message.rb +266 -111
  26. data/lib/blather/stanza/presence.rb +118 -42
  27. data/lib/blather/stanza/presence/status.rb +140 -60
  28. data/lib/blather/stanza/presence/subscription.rb +44 -10
  29. data/lib/blather/stanza/pubsub.rb +70 -15
  30. data/lib/blather/stanza/pubsub/affiliations.rb +36 -7
  31. data/lib/blather/stanza/pubsub/create.rb +26 -4
  32. data/lib/blather/stanza/pubsub/errors.rb +13 -4
  33. data/lib/blather/stanza/pubsub/event.rb +56 -10
  34. data/lib/blather/stanza/pubsub/items.rb +46 -6
  35. data/lib/blather/stanza/pubsub/publish.rb +52 -7
  36. data/lib/blather/stanza/pubsub/retract.rb +45 -6
  37. data/lib/blather/stanza/pubsub/subscribe.rb +30 -4
  38. data/lib/blather/stanza/pubsub/subscription.rb +74 -6
  39. data/lib/blather/stanza/pubsub/subscriptions.rb +35 -9
  40. data/lib/blather/stanza/pubsub/unsubscribe.rb +30 -4
  41. data/lib/blather/stanza/pubsub_owner.rb +17 -7
  42. data/lib/blather/stanza/pubsub_owner/delete.rb +23 -5
  43. data/lib/blather/stanza/pubsub_owner/purge.rb +23 -5
  44. data/lib/blather/stream.rb +96 -29
  45. data/lib/blather/stream/parser.rb +6 -9
  46. data/lib/blather/xmpp_node.rb +101 -153
  47. data/spec/blather/client/client_spec.rb +1 -1
  48. data/spec/blather/errors_spec.rb +5 -5
  49. data/spec/blather/stanza/message_spec.rb +56 -0
  50. data/spec/blather/stanza/presence/status_spec.rb +1 -1
  51. data/spec/blather/stanza_spec.rb +3 -3
  52. data/spec/blather/xmpp_node_spec.rb +19 -74
  53. metadata +6 -10
  54. data/README.rdoc +0 -185
  55. data/examples/drb_client.rb +0 -5
  56. data/examples/ping.rb +0 -11
  57. data/examples/pong.rb +0 -6
  58. data/examples/pubsub/cli.rb +0 -64
  59. data/examples/pubsub/ping_pong.rb +0 -18
@@ -1,36 +1,62 @@
1
1
  module Blather
2
2
  class Stanza
3
3
 
4
- ##
5
- # DiscoInfo object ()
6
- #
4
+ # # DiscoInfo Stanza
5
+ #
6
+ # [XEP-0030 Disco Info](http://xmpp.org/extensions/xep-0030.html#info)
7
+ #
8
+ # Disco Info node that provides or retreives information about a jabber entity
9
+ #
10
+ # @handler :disco_info
7
11
  class DiscoInfo < Disco
8
12
  register :disco_info, nil, 'http://jabber.org/protocol/disco#info'
9
13
 
14
+ # Create a new DiscoInfo stanza
15
+ # @param [:get, :set, :result, :error, nil] type the Iq stanza type
16
+ # @param [String, nil] node the name of the node the info belongs to
17
+ # @param [Array<Array, DiscoInfo::Identity>, nil] identities a list of
18
+ # identities. these are passed directly to DiscoInfo::Identity.new
19
+ # @param [Array<Array, DiscoInfo::Identity>, nil] features a list of
20
+ # features. these are passed directly to DiscoInfo::Feature.new
21
+ # @return [DiscoInfo] a new DiscoInfo stanza
10
22
  def self.new(type = nil, node = nil, identities = [], features = [])
11
23
  new_node = super type
12
24
  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) }
25
+ [identities].flatten.each { |i| new_node.query << Identity.new(i) }
26
+ [features].flatten.each { |f| new_node.query << Feature.new(f) }
15
27
  new_node
16
28
  end
17
29
 
18
- ##
19
30
  # List of identity objects
20
31
  def identities
21
- query.find('//query_ns:identity', :query_ns => self.class.registered_ns).map { |i| Identity.new i }
32
+ query.find('//ns:identity', :ns => self.class.registered_ns).map do |i|
33
+ Identity.new i
34
+ end
22
35
  end
23
36
 
24
- ##
25
37
  # List of feature objects
26
38
  def features
27
- query.find('//query_ns:feature', :query_ns => self.class.registered_ns).map { |i| Feature.new i }
39
+ query.find('//ns:feature', :ns => self.class.registered_ns).map do |f|
40
+ Feature.new f
41
+ end
28
42
  end
29
43
 
30
44
  class Identity < XMPPNode
31
- attribute_accessor :category, :type, :call => :to_sym
32
- attribute_accessor :name
33
-
45
+ # Create a new DiscoInfo Identity
46
+ # @overload new(node)
47
+ # Imports the XML::Node to create a Identity object
48
+ # @param [XML::Node] node the node object to import
49
+ # @overload new(opts = {})
50
+ # Creates a new Identity using a hash of options
51
+ # @param [Hash] opts a hash of options
52
+ # @option opts [String] :name the name of the identity
53
+ # @option opts [String] :type the type of the identity
54
+ # @option opts [String] :category the category of the identity
55
+ # @overload new(name, type = nil, category = nil)
56
+ # Create a new Identity by name
57
+ # @param [String] name the name of the Identity
58
+ # @param [String, nil] type the type of the Identity
59
+ # @param [String, nil] category the category of the Identity
34
60
  def self.new(name, type = nil, category = nil)
35
61
  new_node = super :identity
36
62
 
@@ -49,18 +75,66 @@ class Stanza
49
75
  new_node
50
76
  end
51
77
 
78
+ # The Identity's category
79
+ # @return [Symbol, nil]
80
+ def category
81
+ read_attr :category, :to_sym
82
+ end
83
+
84
+ # Set the Identity's category
85
+ # @param [String, Symbol] category the new category
86
+ def category=(category)
87
+ write_attr :category, category
88
+ end
89
+
90
+ # The Identity's type
91
+ # @return [Symbol, nil]
92
+ def type
93
+ read_attr :type, :to_sym
94
+ end
95
+
96
+ # Set the Identity's type
97
+ # @param [String, Symbol] type the new category
98
+ def type=(type)
99
+ write_attr :type, type
100
+ end
101
+
102
+ # The Identity's name
103
+ # @return [String]
104
+ def name
105
+ read_attr :name
106
+ end
107
+
108
+ # Set the Identity's name
109
+ # @param [String] name the new name for the identity
110
+ def name=(name)
111
+ write_attr :name, name
112
+ end
113
+
114
+ # Compare two Identity objects by name, type and category
115
+ # @param [DiscoInfo::Identity] o the Identity object to compare against
116
+ # @return [true, false]
52
117
  def eql?(o)
53
- raise "Cannot compare #{self.class} with #{o.class}" unless o.is_a?(self.class)
118
+ unless o.is_a?(self.class)
119
+ raise "Cannot compare #{self.class} with #{o.class}"
120
+ end
121
+
54
122
  o.name == self.name &&
55
123
  o.type == self.type &&
56
124
  o.category == self.category
57
125
  end
58
126
  alias_method :==, :eql?
59
- end
127
+ end # Identity
60
128
 
61
129
  class Feature < XMPPNode
62
- attribute_accessor :var
63
-
130
+ # Create a new DiscoInfo::Feature object
131
+ # @overload new(node)
132
+ # Create a new Feature by importing an XML::Node
133
+ # @param [XML::Node] node an XML::Node to import
134
+ # @overload new(var)
135
+ # Create a new feature by var
136
+ # @param [String] var a the Feautre's var
137
+ # @return [DiscoInfo::Feature]
64
138
  def self.new(var)
65
139
  new_node = super :feature
66
140
  case var
@@ -72,13 +146,31 @@ class Stanza
72
146
  new_node
73
147
  end
74
148
 
149
+ # The Feature's var
150
+ # @return [String]
151
+ def var
152
+ read_attr :var
153
+ end
154
+
155
+ # Set the Feature's var
156
+ # @param [String] var the new var
157
+ def var=(var)
158
+ write_attr :var, var
159
+ end
160
+
161
+ # Compare two Feature objects by var
162
+ # @param [DiscoInfo::Feature] o the Feature object to compare against
163
+ # @return [true, false]
75
164
  def eql?(o)
76
- raise "Cannot compare #{self.class} with #{o.class}" unless o.is_a?(self.class)
165
+ unless o.is_a?(self.class)
166
+ raise "Cannot compare #{self.class} with #{o.class}"
167
+ end
168
+
77
169
  o.var == self.var
78
170
  end
79
171
  alias_method :==, :eql?
80
172
  end
81
- end
173
+ end # Feature
82
174
 
83
- end #Stanza
84
- end #Blather
175
+ end # Stanza
176
+ end # Blather
@@ -1,9 +1,23 @@
1
1
  module Blather
2
2
  class Stanza
3
3
 
4
+ # # DiscoItems Stanza
5
+ #
6
+ # [XEP-0030 Disco Info](http://xmpp.org/extensions/xep-0030.html#items)
7
+ #
8
+ # Disco Items node that provides or retreives items associated with a
9
+ # jabbery entity
10
+ #
11
+ # @handler :disco_items
4
12
  class DiscoItems < Disco
5
13
  register :disco_items, nil, 'http://jabber.org/protocol/disco#items'
6
14
 
15
+ # Create a new DiscoItems node
16
+ #
17
+ # @param [#to_s] type the IQ type
18
+ # @param [#to_s] node the node the items are associated with
19
+ # @param [Array<Blather::XMPPNode>] items an array of Disco::Items
20
+ # @return [Blather::Stanza::DiscoItems]
7
21
  def self.new(type = nil, node = nil, items = [])
8
22
  new_node = super type
9
23
  new_node.node = node
@@ -11,19 +25,33 @@ class Stanza
11
25
  new_node
12
26
  end
13
27
 
28
+ # Set of items associated with the node
29
+ #
30
+ # @return [Array<Blather::Stanza::DiscoItems::Item>]
14
31
  def items
15
- query.find('//query_ns:item', :query_ns => self.class.registered_ns).map { |i| Item.new i }
16
- end
17
-
18
- def node=(node)
19
- query[:node] = node
20
- end
21
-
22
- def node
23
- query[:node]
32
+ query.find('//ns:item', :ns => self.class.registered_ns).map do |i|
33
+ Item.new i
34
+ end
24
35
  end
25
36
 
37
+ # An individual Disco Item
26
38
  class Item < XMPPNode
39
+ # Create a new Blather::Stanza::DiscoItems::Item
40
+ #
41
+ # @overload new(node)
42
+ # Create a new Item by inheriting an existing node
43
+ # @param [XML::Node] node an XML::Node to inherit from
44
+ # @overload new(opts)
45
+ # Create a new Item through a hash of options
46
+ # @param [Hash] opts a hash options
47
+ # @option opts [Blather::JID, String] :jid the JID to attach to the item
48
+ # @option opts [#to_s] :node the node the item is attached to
49
+ # @option opts [#to_S] :name the name of the Item
50
+ # @overload new(jid, node = nil, name = nil)
51
+ # Create a new Item
52
+ # @param [Blather::JID, String] jid the JID to attach to the item
53
+ # @param [#to_s] node the node the item is attached to
54
+ # @param [#to_s] name the name of the Item
27
55
  def self.new(jid, node = nil, name = nil)
28
56
  new_node = super :item
29
57
 
@@ -42,15 +70,56 @@ class Stanza
42
70
  new_node
43
71
  end
44
72
 
73
+ # Get the JID attached to the node
74
+ #
75
+ # @return [Blather::JID, nil]
45
76
  def jid
46
77
  (j = self[:jid]) ? JID.new(j) : nil
47
78
  end
48
- attribute_writer :jid
49
79
 
50
- attribute_accessor :node, :name
80
+ # Set the JID of the node
81
+ #
82
+ # @param [Blather::JID, String, nil] jid the new JID
83
+ def jid=(jid)
84
+ write_attr :jid, jid
85
+ end
86
+
87
+ # Get the name of the node
88
+ #
89
+ # @return [String, nil]
90
+ def node
91
+ read_attr :node
92
+ end
93
+
94
+ # Set the name of the node
95
+ #
96
+ # @param [String, nil] node the new node name
97
+ def node=(node)
98
+ write_attr :node, node
99
+ end
100
+
101
+ # Get the Item name
102
+ #
103
+ # @return [String, nil]
104
+ def name
105
+ read_attr :name
106
+ end
107
+
108
+ # Set the Item name
109
+ #
110
+ # @param [#to_s] name the Item name
111
+ def name=(name)
112
+ write_attr :name, name
113
+ end
51
114
 
115
+ # Check for equality based on jid, node, and name
116
+ #
117
+ # @param [Blather::Stanza::DiscoItems::Item] o the other Item
52
118
  def eql?(o)
53
- raise "Cannot compare #{self.class} with #{o.class}" unless o.is_a?(self.class)
119
+ unless o.is_a?(self.class)
120
+ raise "Cannot compare #{self.class} with #{o.class}"
121
+ end
122
+
54
123
  o.jid == self.jid &&
55
124
  o.node == self.node &&
56
125
  o.name == self.name
@@ -1,52 +1,69 @@
1
1
  module Blather
2
2
  class Stanza
3
3
 
4
- # = Iq Stanza
4
+ # # Iq Stanza
5
5
  #
6
- # Info/Query, or IQ, is a request-response mechanism, similar in some ways to HTTP. The semantics of IQ enable an entity
7
- # to make a request of, and receive a response from, another entity. The data content of the request and response is
8
- # defined by the namespace declaration of a direct child element of the IQ element, and the interaction is tracked by the
9
- # requesting entity through use of the 'id' attribute. Thus, IQ interactions follow a common pattern of structured data
10
- # exchange such as get/result or set/result (although an error may be returned in reply to a request if appropriate).
6
+ # [RFC 3920 Section 9.2.3 - IQ Semantics](http://xmpp.org/rfcs/rfc3920.html#rfc.section.9.2.3)
11
7
  #
12
- # == ID Attribute
8
+ # Info/Query, or IQ, is a request-response mechanism, similar in some ways
9
+ # to HTTP. The semantics of IQ enable an entity to make a request of, and
10
+ # receive a response from, another entity. The data content of the request
11
+ # and response is defined by the namespace declaration of a direct child
12
+ # element of the IQ element, and the interaction is tracked by the
13
+ # requesting entity through use of the 'id' attribute. Thus, IQ interactions
14
+ # follow a common pattern of structured data exchange such as get/result or
15
+ # set/result (although an error may be returned in reply to a request if
16
+ # appropriate).
13
17
  #
14
- # Iq Stanzas require the ID attribute be set. Blather will handle this automatically when a new Iq is created.
18
+ # ## "ID" Attribute
15
19
  #
16
- # == Type Attribute
20
+ # Iq Stanzas require the ID attribute be set. Blather will handle this
21
+ # automatically when a new Iq is created.
17
22
  #
18
- # * +:get+ -- The stanza is a request for information or requirements.
19
- # * +:set+ -- The stanza provides required data, sets new values, or replaces existing values.
20
- # * +:result+ -- The stanza is a response to a successful get or set request.
21
- # * +:error+ -- An error has occurred regarding processing or delivery of a previously-sent get or set (see Stanza Errors).
23
+ # ## "Type" Attribute
24
+ #
25
+ # * `:get` -- The stanza is a request for information or requirements.
26
+ #
27
+ # * `:set` -- The stanza provides required data, sets new values, or
28
+ # replaces existing values.
29
+ #
30
+ # * `:result` -- The stanza is a response to a successful get or set request.
31
+ #
32
+ # * `:error` -- An error has occurred regarding processing or delivery of a
33
+ # previously-sent get or set (see Stanza Errors).
22
34
  #
23
35
  # Blather provides a helper for each possible type:
24
36
  #
25
- # Iq#get?
26
- # Iq#set?
27
- # Iq#result?
28
- # Iq#error?
37
+ # Iq#get?
38
+ # Iq#set?
39
+ # Iq#result?
40
+ # Iq#error?
29
41
  #
30
- # Blather treats the +type+ attribute like a normal ruby object attribute providing a getter and setter.
31
- # The default +type+ is +get+.
42
+ # Blather treats the `type` attribute like a normal ruby object attribute
43
+ # providing a getter and setter. The default `type` is `get`.
32
44
  #
33
- # iq = Iq.new
34
- # iq.type # => :get
35
- # iq.get? # => true
36
- # iq.type = :set
37
- # iq.set? # => true
38
- # iq.get? # => false
45
+ # iq = Iq.new
46
+ # iq.type # => :get
47
+ # iq.get? # => true
48
+ # iq.type = :set
49
+ # iq.set? # => true
50
+ # iq.get? # => false
39
51
  #
40
- # iq.type = :invalid # => RuntimeError
52
+ # iq.type = :invalid # => RuntimeError
41
53
  #
54
+ # @handler :iq
42
55
  class Iq < Stanza
43
- VALID_TYPES = [:get, :set, :result, :error] # :nodoc:
56
+ VALID_TYPES = [:get, :set, :result, :error].freeze
44
57
 
45
58
  register :iq
46
59
 
47
- def self.import(node) # :nodoc:
60
+ # @private
61
+ def self.import(node)
48
62
  klass = nil
49
- node.children.each { |e| break if klass = class_from_registration(e.element_name, (e.namespace.href if e.namespace)) }
63
+ node.children.detect do |e|
64
+ ns = e.namespace ? e.namespace.href : nil
65
+ klass = class_from_registration(e.element_name, ns)
66
+ end
50
67
 
51
68
  if klass && klass != self
52
69
  klass.import(node)
@@ -55,11 +72,12 @@ class Stanza
55
72
  end
56
73
  end
57
74
 
58
- ##
59
75
  # Create a new Iq
60
- # * +type+ - the type of stanza (:get, :set, :result, :error)
61
- # * +to+ - the JID of the inteded recipient
62
- # * +id+ - the stanza's ID. Leaving this nil will set the ID to the next unique number
76
+ #
77
+ # @param [Symbol, nil] type the type of stanza (:get, :set, :result, :error)
78
+ # @param [Blather::JID, String, nil] jid the JID of the inteded recipient
79
+ # @param [#to_s] id the stanza's ID. Leaving this nil will set the ID to
80
+ # the next unique number
63
81
  def self.new(type = nil, to = nil, id = nil)
64
82
  node = super :iq
65
83
  node.type = type || :get
@@ -68,15 +86,53 @@ class Stanza
68
86
  node
69
87
  end
70
88
 
71
- attribute_helpers_for :type, VALID_TYPES
89
+ # Check if the IQ is of type :get
90
+ #
91
+ # @return [true, false]
92
+ def get?
93
+ self.type == :get
94
+ end
95
+
96
+ # Check if the IQ is of type :set
97
+ #
98
+ # @return [true, false]
99
+ def set?
100
+ self.type == :set
101
+ end
102
+
103
+ # Check if the IQ is of type :result
104
+ #
105
+ # @return [true, false]
106
+ def result?
107
+ self.type == :result
108
+ end
109
+
110
+ # Check if the IQ is of type :error
111
+ #
112
+ # @return [true, false]
113
+ def error?
114
+ self.type == :error
115
+ end
72
116
 
73
- ##
74
117
  # Ensures type is :get, :set, :result or :error
75
- def type=(type) # :nodoc:
76
- raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}" if type && !VALID_TYPES.include?(type.to_sym)
118
+ #
119
+ # @param [#to_sym] type the Iq type. Must be one of VALID_TYPES
120
+ def type=(type)
121
+ if type && !VALID_TYPES.include?(type.to_sym)
122
+ raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}"
123
+ end
77
124
  super
78
125
  end
126
+
127
+ # Overrides the parent method to ensure the reply is of type :result
128
+ #
129
+ # @return [self]
130
+ def reply!
131
+ super
132
+ self.type = :result
133
+ self
134
+ end
79
135
  end
80
136
 
81
- end #Stanza
137
+ end
82
138
  end