blather 0.4.7 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
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