blather 0.3.4 → 0.4.0

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