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.rb +35 -12
  10. data/lib/blather/client.rb +1 -1
  11. data/lib/blather/client/client.rb +19 -13
  12. data/lib/blather/client/dsl.rb +16 -0
  13. data/lib/blather/client/dsl/pubsub.rb +133 -0
  14. data/lib/blather/core_ext/active_support.rb +1 -117
  15. data/lib/blather/core_ext/active_support/inheritable_attributes.rb +117 -0
  16. data/lib/blather/core_ext/nokogiri.rb +35 -0
  17. data/lib/blather/errors.rb +3 -20
  18. data/lib/blather/errors/sasl_error.rb +3 -1
  19. data/lib/blather/errors/stanza_error.rb +10 -17
  20. data/lib/blather/errors/stream_error.rb +11 -14
  21. data/lib/blather/jid.rb +1 -0
  22. data/lib/blather/roster.rb +9 -0
  23. data/lib/blather/roster_item.rb +6 -1
  24. data/lib/blather/stanza.rb +35 -18
  25. data/lib/blather/stanza/disco.rb +7 -1
  26. data/lib/blather/stanza/disco/disco_info.rb +45 -33
  27. data/lib/blather/stanza/disco/disco_items.rb +32 -21
  28. data/lib/blather/stanza/iq.rb +13 -8
  29. data/lib/blather/stanza/iq/query.rb +16 -8
  30. data/lib/blather/stanza/iq/roster.rb +33 -22
  31. data/lib/blather/stanza/message.rb +20 -31
  32. data/lib/blather/stanza/presence.rb +3 -5
  33. data/lib/blather/stanza/presence/status.rb +13 -21
  34. data/lib/blather/stanza/presence/subscription.rb +11 -16
  35. data/lib/blather/stanza/pubsub.rb +63 -0
  36. data/lib/blather/stanza/pubsub/affiliations.rb +50 -0
  37. data/lib/blather/stanza/pubsub/create.rb +43 -0
  38. data/lib/blather/stanza/pubsub/errors.rb +9 -0
  39. data/lib/blather/stanza/pubsub/event.rb +77 -0
  40. data/lib/blather/stanza/pubsub/items.rb +63 -0
  41. data/lib/blather/stanza/pubsub/publish.rb +58 -0
  42. data/lib/blather/stanza/pubsub/retract.rb +53 -0
  43. data/lib/blather/stanza/pubsub/subscribe.rb +42 -0
  44. data/lib/blather/stanza/pubsub/subscription.rb +66 -0
  45. data/lib/blather/stanza/pubsub/subscriptions.rb +55 -0
  46. data/lib/blather/stanza/pubsub/unsubscribe.rb +42 -0
  47. data/lib/blather/stanza/pubsub_owner.rb +41 -0
  48. data/lib/blather/stanza/pubsub_owner/delete.rb +34 -0
  49. data/lib/blather/stanza/pubsub_owner/purge.rb +34 -0
  50. data/lib/blather/stream.rb +76 -168
  51. data/lib/blather/stream/client.rb +1 -2
  52. data/lib/blather/stream/component.rb +9 -5
  53. data/lib/blather/stream/features.rb +53 -0
  54. data/lib/blather/stream/features/resource.rb +63 -0
  55. data/lib/blather/stream/{sasl.rb → features/sasl.rb} +53 -52
  56. data/lib/blather/stream/features/session.rb +44 -0
  57. data/lib/blather/stream/features/tls.rb +28 -0
  58. data/lib/blather/stream/parser.rb +70 -46
  59. data/lib/blather/xmpp_node.rb +113 -52
  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 +54 -18
  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
@@ -8,11 +8,23 @@ class Stanza
8
8
 
9
9
  register :message
10
10
 
11
- def initialize(to = nil, body = nil, type = :chat)
12
- super()
13
- self.to = to
14
- self.type = type
15
- self.body = body
11
+ def self.import(node)
12
+ klass = nil
13
+ node.children.each { |e| break if klass = class_from_registration(e.element_name, (e.namespace.href if e.namespace)) }
14
+
15
+ if klass && klass != self
16
+ klass.import(node)
17
+ else
18
+ new(node[:type]).inherit(node)
19
+ end
20
+ end
21
+
22
+ def self.new(to = nil, body = nil, type = :chat)
23
+ node = super(:message)
24
+ node.to = to
25
+ node.type = type
26
+ node.body = body
27
+ node
16
28
  end
17
29
 
18
30
  VALID_TYPES.each do |valid_type|
@@ -26,32 +38,9 @@ class Stanza
26
38
  super
27
39
  end
28
40
 
29
- def body=(body)
30
- remove_child :body
31
- self << XMPPNode.new('body', body) if body
32
- end
33
-
34
- def body
35
- content_from :body
36
- end
37
-
38
- def subject=(subject)
39
- remove_child :subject
40
- self << XMPPNode.new('subject', subject) if subject
41
- end
42
-
43
- def subject
44
- content_from :subject
45
- end
46
-
47
- def thread=(thread)
48
- remove_child :thread
49
- self << XMPPNode.new('thread', thread) if thread
50
- end
51
-
52
- def thread
53
- content_from :thread
54
- end
41
+ content_attr_accessor :body
42
+ content_attr_accessor :subject
43
+ content_attr_accessor :thread
55
44
  end
56
45
 
57
46
  end #Stanza
@@ -24,13 +24,11 @@ class Stanza
24
24
 
25
25
  ##
26
26
  # Ensure element_name is "presence" for all subclasses
27
- def initialize
27
+ def self.new
28
28
  super :presence
29
29
  end
30
30
 
31
- VALID_TYPES.each do |valid_type|
32
- define_method("#{valid_type}?") { self.type == valid_type }
33
- end
31
+ attribute_helpers_for(:type, VALID_TYPES)
34
32
 
35
33
  ##
36
34
  # Ensures type is one of :unavailable, :subscribe, :subscribed, :unsubscribe, :unsubscribed, :probe or :error
@@ -42,4 +40,4 @@ class Stanza
42
40
  end
43
41
 
44
42
  end #Stanza
45
- end
43
+ end
@@ -9,12 +9,15 @@ class Presence
9
9
 
10
10
  register :status, :status
11
11
 
12
- def initialize(state = nil, message = nil)
13
- super()
14
- self.state = state
15
- self.message = message
12
+ def self.new(state = nil, message = nil)
13
+ node = super()
14
+ node.state = state
15
+ node.message = message
16
+ node
16
17
  end
17
18
 
19
+ attribute_helpers_for(:state, VALID_STATES)
20
+
18
21
  ##
19
22
  # Ensures type is nil or :unavailable
20
23
  def type=(type)
@@ -29,8 +32,7 @@ class Presence
29
32
  state = nil if state == :available
30
33
  raise ArgumentError, "Invalid Status (#{state}), use: #{VALID_STATES*' '}" if state && !VALID_STATES.include?(state)
31
34
 
32
- remove_child :show
33
- self << XMPPNode.new('show', state) if state
35
+ set_content_for :show, state
34
36
  end
35
37
 
36
38
  ##
@@ -43,23 +45,13 @@ class Presence
43
45
  # Ensure priority is between -128 and 127
44
46
  def priority=(new_priority)
45
47
  raise ArgumentError, 'Priority must be between -128 and +127' if new_priority && !(-128..127).include?(new_priority.to_i)
46
-
47
- remove_child :priority
48
- self << XMPPNode.new('priority', new_priority) if new_priority
49
- end
50
-
51
- def priority
52
- content_from(:priority).to_i
48
+ set_content_for :priority, new_priority
49
+
53
50
  end
54
51
 
55
- def message=(msg)
56
- remove_child :status
57
- self << XMPPNode.new('status', msg) if msg
58
- end
52
+ content_attr_reader :priority, :to_i
59
53
 
60
- def message
61
- content_from :status
62
- end
54
+ content_attr_accessor :message, nil, :status
63
55
 
64
56
  ##
65
57
  # Compare status based on priority
@@ -75,4 +67,4 @@ class Presence
75
67
 
76
68
  end #Presence
77
69
  end #Stanza
78
- end #Blather
70
+ end #Blather
@@ -5,10 +5,11 @@ class Presence
5
5
  class Subscription < Presence
6
6
  register :subscription, :subscription
7
7
 
8
- def initialize(to = nil, type = nil)
9
- super()
10
- self.to = to
11
- self.type = type
8
+ def self.new(to = nil, type = nil)
9
+ node = super()
10
+ node.to = to
11
+ node.type = type
12
+ node
12
13
  end
13
14
 
14
15
  def inherit(node)
@@ -24,49 +25,43 @@ class Presence
24
25
  # Create an approve stanza
25
26
  def approve!
26
27
  self.type = :subscribed
27
- morph_to_reply
28
+ reply_if_needed!
28
29
  end
29
30
 
30
31
  ##
31
32
  # Create a refuse stanza
32
33
  def refuse!
33
34
  self.type = :unsubscribed
34
- morph_to_reply
35
+ reply_if_needed!
35
36
  end
36
37
 
37
38
  ##
38
39
  # Create an unsubscribe stanza
39
40
  def unsubscribe!
40
41
  self.type = :unsubscribe
41
- morph_to_reply
42
+ reply_if_needed!
42
43
  end
43
44
 
44
45
  ##
45
46
  # Create a cancel stanza
46
47
  def cancel!
47
48
  self.type = :unsubscribed
48
- morph_to_reply
49
+ reply_if_needed!
49
50
  end
50
51
 
51
52
  ##
52
53
  # Create a request stanza
53
54
  def request!
54
55
  self.type = :subscribe
55
- morph_to_reply
56
+ reply_if_needed!
56
57
  end
57
58
 
58
59
  def request?
59
60
  self.type == :subscribe
60
61
  end
61
62
 
62
- private
63
- def morph_to_reply
64
- self.to = self.from if self.from
65
- self.from = nil
66
- self
67
- end
68
63
  end #Subscription
69
64
 
70
65
  end #Presence
71
66
  end #Stanza
72
- end
67
+ end
@@ -0,0 +1,63 @@
1
+ module Blather
2
+ class Stanza
3
+
4
+ class PubSub < Iq
5
+ register :pubsub_node, :pubsub, 'http://jabber.org/protocol/pubsub'
6
+
7
+ def self.import(node)
8
+ klass = nil
9
+ if pubsub = node.document.find_first('//ns:pubsub', :ns => self.registered_ns)
10
+ pubsub.children.each { |e| break if klass = class_from_registration(e.element_name, (e.namespace.href if e.namespace)) }
11
+ end
12
+ (klass || self).new(node[:type]).inherit(node)
13
+ end
14
+
15
+ ##
16
+ # Ensure the namespace is set to the query node
17
+ def self.new(type = nil, host = nil)
18
+ new_node = super type
19
+ new_node.to = host
20
+ new_node.pubsub
21
+ new_node
22
+ end
23
+
24
+ ##
25
+ # Kill the pubsub node before running inherit
26
+ def inherit(node)
27
+ remove_children :pubsub
28
+ super
29
+ end
30
+
31
+ def pubsub
32
+ p = find_first('ns:pubsub', :ns => self.class.registered_ns) ||
33
+ find_first('pubsub', :ns => self.class.registered_ns)
34
+
35
+ unless p
36
+ self << (p = XMPPNode.new('pubsub', self.document))
37
+ p.namespace = self.class.registered_ns
38
+ end
39
+ p
40
+ end
41
+ end
42
+
43
+ class PubSubItem < XMPPNode
44
+ def self.new(id = nil, payload = nil, document = nil)
45
+ new_node = super 'item', document
46
+ new_node.id = id
47
+ new_node.payload = payload if payload
48
+ new_node
49
+ end
50
+
51
+ attribute_accessor :id
52
+
53
+ def payload=(payload = nil)
54
+ self.content = payload
55
+ end
56
+
57
+ def payload
58
+ content.empty? ? nil : content
59
+ end
60
+ end
61
+
62
+ end #Stanza
63
+ end #Blather
@@ -0,0 +1,50 @@
1
+ module Blather
2
+ class Stanza
3
+ class PubSub
4
+
5
+ class Affiliations < PubSub
6
+ register :pubsub_affiliations, :affiliations, self.registered_ns
7
+
8
+ include Enumerable
9
+ alias_method :find, :xpath
10
+
11
+ def self.new(type = nil, host = nil)
12
+ new_node = super
13
+ new_node.affiliations
14
+ new_node
15
+ end
16
+
17
+ ##
18
+ # Kill the affiliations node before running inherit
19
+ def inherit(node)
20
+ affiliations.remove
21
+ super
22
+ end
23
+
24
+ def affiliations
25
+ aff = pubsub.find_first('pubsub_ns:affiliations', :pubsub_ns => self.class.registered_ns)
26
+ self.pubsub << (aff = XMPPNode.new('affiliations', self.document)) unless aff
27
+ aff
28
+ end
29
+
30
+ def each(&block)
31
+ list.each &block
32
+ end
33
+
34
+ def size
35
+ list.size
36
+ end
37
+
38
+ def list
39
+ items = affiliations.find('//ns:affiliation', :ns => self.class.registered_ns)
40
+ items.inject({}) do |hash, item|
41
+ hash[item[:affiliation].to_sym] ||= []
42
+ hash[item[:affiliation].to_sym] << item[:node]
43
+ hash
44
+ end
45
+ end
46
+ end #Affiliations
47
+
48
+ end #PubSub
49
+ end #Stanza
50
+ end #Blather
@@ -0,0 +1,43 @@
1
+ module Blather
2
+ class Stanza
3
+ class PubSub
4
+
5
+ class Create < PubSub
6
+ register :pubsub_create, :create, self.registered_ns
7
+
8
+ def self.new(type = :set, host = nil, node = nil)
9
+ new_node = super(type, host)
10
+ new_node.create_node
11
+ new_node.configure_node
12
+ new_node.node = node
13
+ new_node
14
+ end
15
+
16
+ def node
17
+ create_node[:node]
18
+ end
19
+
20
+ def node=(node)
21
+ create_node[:node] = node
22
+ end
23
+
24
+ def create_node
25
+ unless create_node = pubsub.find_first('ns:create', :ns => self.class.registered_ns)
26
+ self.pubsub << (create_node = XMPPNode.new('create', self.document))
27
+ create_node.namespace = self.pubsub.namespace
28
+ end
29
+ create_node
30
+ end
31
+
32
+ def configure_node
33
+ unless configure_node = pubsub.find_first('ns:configure', :ns => self.class.registered_ns)
34
+ self.pubsub << (configure_node = XMPPNode.new('configure', self.document))
35
+ configure_node.namespace = self.pubsub.namespace
36
+ end
37
+ configure_node
38
+ end
39
+ end #Retract
40
+
41
+ end #PubSub
42
+ end #Stanza
43
+ end #Blather
@@ -0,0 +1,9 @@
1
+ module Blather
2
+ class Stanza
3
+
4
+ class PubSubErrors < PubSub
5
+ attribute_accessor :node, :to_sym => false
6
+ end
7
+
8
+ end #Stanza
9
+ end #Blather
@@ -0,0 +1,77 @@
1
+ module Blather
2
+ class Stanza
3
+ class PubSub
4
+
5
+ class Event < Message
6
+ register :pubsub_event, :event, 'http://jabber.org/protocol/pubsub#event'
7
+
8
+ ##
9
+ # Ensure the event_node is created
10
+ def self.new(type = nil)
11
+ node = super
12
+ node.event_node
13
+ node
14
+ end
15
+
16
+ ##
17
+ # Kill the event_node node before running inherit
18
+ def inherit(node)
19
+ event_node.remove
20
+ super
21
+ end
22
+
23
+ def node
24
+ !purge? ? items_node[:node] : purge_node[:node]
25
+ end
26
+
27
+ def retractions
28
+ items_node.find('ns:retract', :ns => self.class.registered_ns).map { |i| i[:id] }
29
+ end
30
+
31
+ def retractions?
32
+ !retractions.empty?
33
+ end
34
+
35
+ def items
36
+ items_node.find('ns:item', :ns => self.class.registered_ns).map { |i| PubSubItem.new(nil,nil,self.document).inherit i }
37
+ end
38
+
39
+ def items?
40
+ !items.empty?
41
+ end
42
+
43
+ def purge?
44
+ purge_node
45
+ end
46
+
47
+ def event_node
48
+ node = find_first('ns:event', :ns => self.class.registered_ns)
49
+ node = find_first('event', self.class.registered_ns) unless node
50
+ unless node
51
+ (self << (node = XMPPNode.new('event', self.document)))
52
+ node.namespace = self.class.registered_ns
53
+ end
54
+ node
55
+ end
56
+
57
+ def items_node
58
+ node = find_first('event/ns:items', :ns => self.class.registered_ns)
59
+ unless node
60
+ (self.event_node << (node = XMPPNode.new('items', self.document)))
61
+ node.namespace = event_node.namespace
62
+ end
63
+ node
64
+ end
65
+
66
+ def purge_node
67
+ event_node.find_first('ns:purge', :ns => self.class.registered_ns)
68
+ end
69
+
70
+ def subscription_ids
71
+ find('//ns:header[@name="SubID"]', :ns => 'http://jabber.org/protocol/shim').map { |n| n.content }
72
+ end
73
+ end
74
+
75
+ end #PubSub
76
+ end #Stanza
77
+ end #Blather