blather 0.4.7 → 0.4.8
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +162 -0
- data/examples/{print_heirarchy.rb → print_hierarchy.rb} +5 -5
- data/examples/stream_only.rb +27 -0
- data/lib/blather.rb +4 -0
- data/lib/blather/client/client.rb +91 -73
- data/lib/blather/client/dsl.rb +156 -32
- data/lib/blather/client/dsl/pubsub.rb +86 -54
- data/lib/blather/core_ext/active_support.rb +9 -9
- data/lib/blather/core_ext/active_support/inheritable_attributes.rb +2 -2
- data/lib/blather/core_ext/nokogiri.rb +12 -7
- data/lib/blather/errors.rb +25 -14
- data/lib/blather/errors/sasl_error.rb +21 -3
- data/lib/blather/errors/stanza_error.rb +37 -21
- data/lib/blather/errors/stream_error.rb +27 -17
- data/lib/blather/jid.rb +79 -24
- data/lib/blather/roster.rb +39 -21
- data/lib/blather/roster_item.rb +43 -21
- data/lib/blather/stanza.rb +88 -40
- data/lib/blather/stanza/disco.rb +12 -2
- data/lib/blather/stanza/disco/disco_info.rb +112 -20
- data/lib/blather/stanza/disco/disco_items.rb +81 -12
- data/lib/blather/stanza/iq.rb +94 -38
- data/lib/blather/stanza/iq/query.rb +16 -22
- data/lib/blather/stanza/iq/roster.rb +98 -20
- data/lib/blather/stanza/message.rb +266 -111
- data/lib/blather/stanza/presence.rb +118 -42
- data/lib/blather/stanza/presence/status.rb +140 -60
- data/lib/blather/stanza/presence/subscription.rb +44 -10
- data/lib/blather/stanza/pubsub.rb +70 -15
- data/lib/blather/stanza/pubsub/affiliations.rb +36 -7
- data/lib/blather/stanza/pubsub/create.rb +26 -4
- data/lib/blather/stanza/pubsub/errors.rb +13 -4
- data/lib/blather/stanza/pubsub/event.rb +56 -10
- data/lib/blather/stanza/pubsub/items.rb +46 -6
- data/lib/blather/stanza/pubsub/publish.rb +52 -7
- data/lib/blather/stanza/pubsub/retract.rb +45 -6
- data/lib/blather/stanza/pubsub/subscribe.rb +30 -4
- data/lib/blather/stanza/pubsub/subscription.rb +74 -6
- data/lib/blather/stanza/pubsub/subscriptions.rb +35 -9
- data/lib/blather/stanza/pubsub/unsubscribe.rb +30 -4
- data/lib/blather/stanza/pubsub_owner.rb +17 -7
- data/lib/blather/stanza/pubsub_owner/delete.rb +23 -5
- data/lib/blather/stanza/pubsub_owner/purge.rb +23 -5
- data/lib/blather/stream.rb +96 -29
- data/lib/blather/stream/parser.rb +6 -9
- data/lib/blather/xmpp_node.rb +101 -153
- data/spec/blather/client/client_spec.rb +1 -1
- data/spec/blather/errors_spec.rb +5 -5
- data/spec/blather/stanza/message_spec.rb +56 -0
- data/spec/blather/stanza/presence/status_spec.rb +1 -1
- data/spec/blather/stanza_spec.rb +3 -3
- data/spec/blather/xmpp_node_spec.rb +19 -74
- metadata +6 -10
- data/README.rdoc +0 -185
- data/examples/drb_client.rb +0 -5
- data/examples/ping.rb +0 -11
- data/examples/pong.rb +0 -6
- data/examples/pubsub/cli.rb +0 -64
- data/examples/pubsub/ping_pong.rb +0 -18
data/lib/blather/roster.rb
CHANGED
@@ -1,20 +1,27 @@
|
|
1
1
|
module Blather
|
2
2
|
|
3
|
-
|
4
|
-
# Local Roster
|
3
|
+
# Local Roster
|
5
4
|
# Takes care of adding/removing JIDs through the stream
|
6
5
|
class Roster
|
7
6
|
include Enumerable
|
8
7
|
|
8
|
+
# Create a new roster
|
9
|
+
#
|
10
|
+
# @param [Blather::Stream] stream the stream the roster should use to
|
11
|
+
# update roster entries
|
12
|
+
# @param [Blather::Stanza::Roster] stanza a roster stanza used to preload
|
13
|
+
# the roster
|
14
|
+
# @return [Blather::Roster]
|
9
15
|
def initialize(stream, stanza = nil)
|
10
16
|
@stream = stream
|
11
17
|
@items = {}
|
12
18
|
stanza.items.each { |i| push i, false } if stanza
|
13
19
|
end
|
14
20
|
|
15
|
-
|
16
|
-
# Process any incoming stanzas adn either add or remove the
|
21
|
+
# Process any incoming stanzas and either adds or removes the
|
17
22
|
# corresponding RosterItem
|
23
|
+
#
|
24
|
+
# @param [Blather::Stanza::Roster] stanza a roster stanza
|
18
25
|
def process(stanza)
|
19
26
|
stanza.items.each do |i|
|
20
27
|
case i.subscription
|
@@ -24,18 +31,21 @@ module Blather
|
|
24
31
|
end
|
25
32
|
end
|
26
33
|
|
27
|
-
##
|
28
34
|
# Pushes a JID into the roster
|
29
|
-
#
|
35
|
+
#
|
36
|
+
# @param [String, Blather::JID, #jid] elem a JID to add to the roster
|
37
|
+
# @return [self]
|
38
|
+
# @see #push
|
30
39
|
def <<(elem)
|
31
40
|
push elem
|
32
41
|
self
|
33
42
|
end
|
34
43
|
|
35
|
-
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
44
|
+
# Push a JID into the roster and update the server
|
45
|
+
#
|
46
|
+
# @param [String, Blather::JID, #jid] elem a jid to add to the roster
|
47
|
+
# @param [true, false] send send the update over the wire
|
48
|
+
# @see Blather::JID
|
39
49
|
def push(elem, send = true)
|
40
50
|
jid = elem.respond_to?(:jid) ? elem.jid : JID.new(elem)
|
41
51
|
@items[key(jid)] = node = RosterItem.new(elem)
|
@@ -44,35 +54,41 @@ module Blather
|
|
44
54
|
end
|
45
55
|
alias_method :add, :push
|
46
56
|
|
47
|
-
|
48
|
-
#
|
49
|
-
#
|
57
|
+
# Remove a JID from the roster and update the server
|
58
|
+
#
|
59
|
+
# @param [String, Blather::JID] jid the JID to remove from the roster
|
50
60
|
def delete(jid)
|
51
61
|
@items.delete key(jid)
|
52
|
-
|
62
|
+
item = Stanza::Iq::Roster::RosterItem.new(jid, nil, :remove)
|
63
|
+
@stream.write Stanza::Iq::Roster.new(:set, item)
|
53
64
|
end
|
54
65
|
alias_method :remove, :delete
|
55
66
|
|
56
|
-
##
|
57
67
|
# Get a RosterItem by JID
|
68
|
+
#
|
69
|
+
# @param [String, Blather::JID] jid the jid of the item to return
|
70
|
+
# @return [Blather::RosterItem, nil] the associated RosterItem
|
58
71
|
def [](jid)
|
59
72
|
items[key(jid)]
|
60
73
|
end
|
61
74
|
|
62
|
-
##
|
63
75
|
# Iterate over all RosterItems
|
76
|
+
#
|
77
|
+
# @yield [Blather::RosterItem] yields each RosterItem
|
64
78
|
def each(&block)
|
65
79
|
items.each &block
|
66
80
|
end
|
67
81
|
|
68
|
-
|
69
|
-
#
|
82
|
+
# Get a duplicate of all RosterItems
|
83
|
+
#
|
84
|
+
# @return [Array<Blather::RosterItem>] a duplicate of all RosterItems
|
70
85
|
def items
|
71
86
|
@items.dup
|
72
87
|
end
|
73
88
|
|
74
|
-
##
|
75
89
|
# A hash of items keyed by group
|
90
|
+
#
|
91
|
+
# @return [Hash<group => Array<RosterItem>>]
|
76
92
|
def grouped
|
77
93
|
self.inject(Hash.new{|h,k|h[k]=[]}) do |hash, item|
|
78
94
|
item[1].groups.each { |group| hash[group] << item[1] }
|
@@ -81,13 +97,15 @@ module Blather
|
|
81
97
|
end
|
82
98
|
|
83
99
|
private
|
100
|
+
# Creates a stripped jid
|
84
101
|
def self.key(jid)
|
85
102
|
JID.new(jid).stripped.to_s
|
86
103
|
end
|
87
104
|
|
105
|
+
# Instance method to wrap around the class method
|
88
106
|
def key(jid)
|
89
107
|
self.class.key(jid)
|
90
108
|
end
|
91
|
-
end
|
109
|
+
end # Roster
|
92
110
|
|
93
|
-
end
|
111
|
+
end # Blather
|
data/lib/blather/roster_item.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
module Blather
|
2
2
|
|
3
|
-
##
|
4
3
|
# RosterItems hold internal representations of the user's roster
|
5
4
|
# including each JID's status.
|
6
5
|
class RosterItem
|
7
|
-
VALID_SUBSCRIPTION_TYPES = [:both, :from, :none, :remove, :to]
|
6
|
+
VALID_SUBSCRIPTION_TYPES = [:both, :from, :none, :remove, :to].freeze
|
8
7
|
|
9
8
|
attr_reader :jid,
|
10
9
|
:ask,
|
@@ -18,8 +17,19 @@ module Blather
|
|
18
17
|
super
|
19
18
|
end
|
20
19
|
|
21
|
-
|
22
|
-
#
|
20
|
+
# Create a new RosterItem
|
21
|
+
#
|
22
|
+
# @overload initialize(jid)
|
23
|
+
# Create a new RosterItem based on a JID
|
24
|
+
# @param [Blather::JID] jid the JID object
|
25
|
+
# @overload initialize(jid)
|
26
|
+
# Create a new RosterItem based on a JID string
|
27
|
+
# @param [String] jid a JID string
|
28
|
+
# @overload initialize(node)
|
29
|
+
# Create a new RosterItem based on a stanza
|
30
|
+
# @param [Blather::Stanza::Iq::Roster::RosterItem] node a RosterItem
|
31
|
+
# stanza
|
32
|
+
# @return [Blather::RosterItem] the new RosterItem
|
23
33
|
def initialize(item)
|
24
34
|
@statuses = []
|
25
35
|
@groups = []
|
@@ -40,54 +50,66 @@ module Blather
|
|
40
50
|
@groups = [nil] if @groups.empty?
|
41
51
|
end
|
42
52
|
|
43
|
-
##
|
44
53
|
# Set the jid
|
54
|
+
#
|
55
|
+
# @param [String, Blather::JID] jid the new jid
|
56
|
+
# @see Blather::JID
|
45
57
|
def jid=(jid)
|
46
58
|
@jid = JID.new(jid).stripped
|
47
59
|
end
|
48
60
|
|
49
|
-
##
|
50
61
|
# Set the subscription
|
51
62
|
# Ensures it is one of VALID_SUBSCRIPTION_TYPES
|
63
|
+
#
|
64
|
+
# @param [#to_sym] sub the new subscription
|
52
65
|
def subscription=(sub)
|
53
|
-
|
54
|
-
|
66
|
+
if sub && !VALID_SUBSCRIPTION_TYPES.include?(sub = sub.to_sym)
|
67
|
+
raise ArgumentError, "Invalid Type (#{sub}), use: #{VALID_SUBSCRIPTION_TYPES*' '}"
|
68
|
+
end
|
55
69
|
@subscription = sub ? sub : :none
|
56
70
|
end
|
57
71
|
|
58
|
-
##
|
59
72
|
# Get the current subscription
|
60
|
-
#
|
73
|
+
#
|
74
|
+
# @return [:both, :from, :none, :remove, :to]
|
61
75
|
def subscription
|
62
76
|
@subscription || :none
|
63
77
|
end
|
64
78
|
|
65
|
-
##
|
66
79
|
# Set the ask value
|
67
|
-
#
|
80
|
+
#
|
81
|
+
# @param [nil, :subscribe] ask the new ask
|
68
82
|
def ask=(ask)
|
69
|
-
|
83
|
+
if ask && (ask = ask.to_sym) != :subscribe
|
84
|
+
raise ArgumentError, "Invalid Type (#{ask}), can only be :subscribe"
|
85
|
+
end
|
70
86
|
@ask = ask ? ask : nil
|
71
87
|
end
|
72
88
|
|
73
|
-
##
|
74
89
|
# Set the status then sorts them according to priority
|
75
|
-
#
|
90
|
+
#
|
91
|
+
# @param [Blather::Stanza::Status] the new status
|
76
92
|
def status=(presence)
|
77
93
|
@statuses.delete_if { |s| s.from == presence.from }
|
78
94
|
@statuses << presence
|
79
95
|
@statuses.sort!
|
80
96
|
end
|
81
97
|
|
82
|
-
|
83
|
-
#
|
84
|
-
#
|
98
|
+
# The status with the highest priority
|
99
|
+
#
|
100
|
+
# @param [String, nil] resource the resource to get the status of
|
85
101
|
def status(resource = nil)
|
86
|
-
top =
|
102
|
+
top = if resource
|
103
|
+
@statuses.detect { |s| s.from.resource == resource }
|
104
|
+
else
|
105
|
+
@statuses.first
|
106
|
+
end
|
87
107
|
end
|
88
108
|
|
89
|
-
|
90
|
-
#
|
109
|
+
# Translate the RosterItem into a proper stanza that can be sent over the
|
110
|
+
# stream
|
111
|
+
#
|
112
|
+
# @return [Blather::Stanza::Iq::Roster]
|
91
113
|
def to_stanza(type = nil)
|
92
114
|
r = Stanza::Iq::Roster.new type
|
93
115
|
n = Stanza::Iq::Roster::RosterItem.new jid, name, subscription, ask
|
data/lib/blather/stanza.rb
CHANGED
@@ -1,95 +1,143 @@
|
|
1
1
|
module Blather
|
2
|
-
|
3
|
-
# Base XMPP Stanza
|
2
|
+
|
3
|
+
# # Base XMPP Stanza
|
4
|
+
#
|
5
|
+
# All stanzas inherit this class. It provides a set of methods and helpers
|
6
|
+
# common to all XMPP Stanzas
|
7
|
+
#
|
8
|
+
# @handler :stanza
|
4
9
|
class Stanza < XMPPNode
|
10
|
+
# @private
|
5
11
|
@@last_id = 0
|
12
|
+
# @private
|
6
13
|
@@handler_list = []
|
7
14
|
|
8
|
-
class_inheritable_array :
|
15
|
+
class_inheritable_array :handler_hierarchy
|
9
16
|
|
10
|
-
|
11
|
-
# Registers a callback onto the callback heirarchy stack
|
17
|
+
# Registers a callback onto the callback stack
|
12
18
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
19
|
+
# @param [Symbol] handler the name of the handler
|
20
|
+
# @param [Symbol, String, nil] name the name of the first element in the
|
21
|
+
# stanza. If nil the inherited name will be used. If that's nil the
|
22
|
+
# handler name will be used.
|
23
|
+
# @param [String, nil] ns the namespace of the stanza
|
17
24
|
def self.register(handler, name = nil, ns = nil)
|
18
25
|
@@handler_list << handler
|
19
|
-
self.
|
20
|
-
self.
|
26
|
+
self.handler_hierarchy ||= [:stanza]
|
27
|
+
self.handler_hierarchy.unshift handler
|
21
28
|
|
22
29
|
name = name || self.registered_name || handler
|
23
30
|
super name, ns
|
24
31
|
end
|
25
32
|
|
33
|
+
# The handler stack for the current stanza class
|
34
|
+
#
|
35
|
+
# @return [Array<Symbol>]
|
26
36
|
def self.handler_list
|
27
37
|
@@handler_list
|
28
38
|
end
|
29
39
|
|
30
|
-
##
|
31
40
|
# Helper method that creates a unique ID for stanzas
|
41
|
+
#
|
42
|
+
# @return [String] a new unique ID
|
32
43
|
def self.next_id
|
33
44
|
@@last_id += 1
|
34
45
|
'blather%04x' % @@last_id
|
35
46
|
end
|
36
47
|
|
37
|
-
|
38
|
-
# Helper method to generate stanza guard methods
|
39
|
-
#
|
40
|
-
# attribute_helpers_for(:type, [:subscribe, :unsubscribe])
|
48
|
+
# Check if the stanza is an error stanza
|
41
49
|
#
|
42
|
-
#
|
43
|
-
|
44
|
-
|
45
|
-
[values].flatten.each do |v|
|
46
|
-
define_method("#{v}?") { __send__(attr) == v }
|
47
|
-
end
|
50
|
+
# @return [true, false]
|
51
|
+
def error?
|
52
|
+
self.type == :error
|
48
53
|
end
|
49
54
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
# Copies itself then swaps from and to
|
54
|
-
# then returns the new stanza
|
55
|
+
# Creates a copy with to and from swapped
|
56
|
+
#
|
57
|
+
# @return [Blather::Stanza]
|
55
58
|
def reply
|
56
59
|
self.dup.reply!
|
57
60
|
end
|
58
61
|
|
59
|
-
##
|
60
62
|
# Swaps from and to
|
63
|
+
#
|
64
|
+
# @return [self]
|
61
65
|
def reply!
|
62
66
|
self.to, self.from = self.from, self.to
|
63
67
|
self
|
64
68
|
end
|
65
69
|
|
66
|
-
|
70
|
+
# Get the stanza's ID
|
71
|
+
#
|
72
|
+
# @return [String, nil]
|
73
|
+
def id
|
74
|
+
read_attr :id
|
75
|
+
end
|
67
76
|
|
68
|
-
|
77
|
+
# Set the stanza's ID
|
78
|
+
#
|
79
|
+
# @param [#to_s] id the new stanza ID
|
80
|
+
def id=(id)
|
81
|
+
write_attr :id, id
|
82
|
+
end
|
69
83
|
|
70
|
-
|
71
|
-
#
|
84
|
+
# Get the stanza's to
|
85
|
+
#
|
86
|
+
# @return [Blather::JID, nil]
|
72
87
|
def to
|
73
88
|
JID.new(self[:to]) if self[:to]
|
74
89
|
end
|
75
90
|
|
76
|
-
|
77
|
-
#
|
91
|
+
# Set the stanza's to field
|
92
|
+
#
|
93
|
+
# @param [#to_s] to the new JID for the to field
|
94
|
+
def to=(to)
|
95
|
+
write_attr :to, to
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get the stanza's from
|
99
|
+
#
|
100
|
+
# @return [Blather::JID, nil]
|
78
101
|
def from
|
79
102
|
JID.new(self[:from]) if self[:from]
|
80
103
|
end
|
81
104
|
|
82
|
-
|
105
|
+
# Set the stanza's from field
|
106
|
+
#
|
107
|
+
# @param [#to_s] from the new JID for the from field
|
108
|
+
def from=(from)
|
109
|
+
write_attr :from, from
|
110
|
+
end
|
83
111
|
|
84
|
-
|
85
|
-
#
|
86
|
-
#
|
87
|
-
|
112
|
+
# Get the stanza's type
|
113
|
+
#
|
114
|
+
# @return [Symbol, nil]
|
115
|
+
def type
|
116
|
+
read_attr :type, :to_sym
|
117
|
+
end
|
118
|
+
|
119
|
+
# Set the stanza's type
|
120
|
+
#
|
121
|
+
# @param [#to_s] type the new stanza type
|
122
|
+
def type=(type)
|
123
|
+
write_attr :type, type
|
124
|
+
end
|
125
|
+
|
126
|
+
# Create an error stanza from the current stanza
|
127
|
+
#
|
128
|
+
# @param [String] name the error name
|
129
|
+
# @param [<Blather::StanzaError::VALID_TYPES>] type the error type
|
130
|
+
# @param [String, nil] text the error text
|
131
|
+
# @param [Array<XML::Node>] extras an array of extra nodes to attach to
|
132
|
+
# the error
|
133
|
+
#
|
134
|
+
# @return [Blather::StanzaError]
|
88
135
|
def as_error(name, type, text = nil, extras = [])
|
89
136
|
StanzaError.new self, name, type, text, extras
|
90
137
|
end
|
91
138
|
|
92
|
-
|
139
|
+
protected
|
140
|
+
# @private
|
93
141
|
def reply_if_needed!
|
94
142
|
unless @reversed_endpoints
|
95
143
|
reply!
|
data/lib/blather/stanza/disco.rb
CHANGED
@@ -1,15 +1,25 @@
|
|
1
1
|
module Blather
|
2
2
|
class Stanza
|
3
3
|
|
4
|
+
# # Disco Base class
|
5
|
+
#
|
6
|
+
# Use Blather::Stanza::DiscoInfo or Blather::Stanza::DiscoItems
|
4
7
|
class Disco < Iq::Query
|
8
|
+
|
9
|
+
# Get the name of the node
|
10
|
+
#
|
11
|
+
# @return [String] the node name
|
5
12
|
def node
|
6
13
|
query[:node]
|
7
14
|
end
|
8
15
|
|
16
|
+
# Set the name of the node
|
17
|
+
#
|
18
|
+
# @param [#to_s] node the new node name
|
9
19
|
def node=(node)
|
10
20
|
query[:node] = node
|
11
21
|
end
|
12
22
|
end
|
13
23
|
|
14
|
-
end #Stanza
|
15
|
-
end #Blather
|
24
|
+
end # Stanza
|
25
|
+
end # Blather
|