blather 0.4.1 → 0.4.2

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.
@@ -2,16 +2,27 @@ module Blather
2
2
  # Main error class
3
3
  class BlatherError < StandardError
4
4
  class_inheritable_array :handler_heirarchy
5
-
6
5
  self.handler_heirarchy ||= []
7
- self.handler_heirarchy << :error
8
6
 
7
+ @@handler_list = []
8
+
9
+ ##
10
+ # Register the class's handler
9
11
  def self.register(handler)
12
+ @@handler_list << handler
10
13
  self.handler_heirarchy.unshift handler
11
14
  end
12
15
 
16
+ ##
17
+ # The list of registered handlers
18
+ def self.handler_list
19
+ @@handler_list
20
+ end
21
+
22
+ register :error
23
+
13
24
  # HACK!! until I can refactor the entire Error object model
14
- def id
25
+ def id # :nodoc:
15
26
  nil
16
27
  end
17
28
  end
data/lib/blather/jid.rb CHANGED
@@ -7,29 +7,23 @@ module Blather
7
7
 
8
8
  PATTERN = /^(?:([^@]*)@)??([^@\/]*)(?:\/(.*?))?$/.freeze
9
9
 
10
- ##
11
- # Get the JID's node
12
- attr_reader :node
13
-
14
- ##
15
- # Get the JID's domain
16
- attr_reader :domain
10
+ attr_reader :node,
11
+ :domain,
12
+ :resource
17
13
 
18
14
  ##
19
- # Get the JID's resource
20
- attr_reader :resource
21
-
22
- ##
23
- # If a JID is passed in just return it.
24
- # No need to copy out all the values
15
+ # Create a new JID. If called as new('a@b/c'), parse the string and split (node, domain, resource).
16
+ # * +node+ - can be any of the following:
17
+ # * a string representing the JID ("node@domain.tld/resource")
18
+ # * a JID. in which case nothing will be done and the original JID will be passed back
19
+ # * a string representing the node
20
+ # * +domain+ - the domain of the JID
21
+ # * +resource+ - the resource the connection should be bound to
25
22
  def self.new(node, domain = nil, resource = nil)
26
23
  node.is_a?(JID) ? node : super
27
24
  end
28
25
 
29
- ##
30
- # Create a new JID. If called as new('a@b/c'), parse the string and
31
- # split (node, domain, resource)
32
- def initialize(node, domain = nil, resource = nil)
26
+ def initialize(node, domain = nil, resource = nil) # :nodoc:
33
27
  @resource = resource
34
28
  @domain = domain
35
29
  @node = node
@@ -62,14 +56,12 @@ module Blather
62
56
 
63
57
  ##
64
58
  # Returns a new JID with resource removed.
65
- # return:: [JID]
66
59
  def stripped
67
- self.class.new @node, @domain
60
+ dup.strip!
68
61
  end
69
62
 
70
63
  ##
71
64
  # Removes the resource (sets it to nil)
72
- # return:: [JID] self
73
65
  def strip!
74
66
  @resource = nil
75
67
  self
@@ -80,8 +72,8 @@ module Blather
80
72
  # helpful for sorting etc.
81
73
  #
82
74
  # String representations are compared, see JID#to_s
83
- def <=>(o)
84
- to_s <=> o.to_s
75
+ def <=>(other)
76
+ to_s <=> other.to_s
85
77
  end
86
78
  alias_method :eql?, :==
87
79
 
@@ -34,22 +34,6 @@ module Blather
34
34
  'blather%04x' % @@last_id
35
35
  end
36
36
 
37
- ##
38
- # Creates a new stanza with the same name as the node
39
- # then inherits all the node's attributes and properties
40
- def self.import(node)
41
- self.new(node.element_name).inherit(node)
42
- end
43
-
44
- ##
45
- # Automatically set the stanza's ID
46
- # and attach it to a document so XPath searching works
47
- def self.new(name = nil)
48
- node = super
49
- node.name = name.to_s if name
50
- node
51
- end
52
-
53
37
  ##
54
38
  # Helper method to generate stanza guard methods
55
39
  #
@@ -1,14 +1,50 @@
1
1
  module Blather
2
2
  class Stanza
3
3
 
4
- ##
5
- # Base Iq stanza
4
+ # = Iq Stanza
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).
11
+ #
12
+ # == ID Attribute
13
+ #
14
+ # Iq Stanzas require the ID attribute be set. Blather will handle this automatically when a new Iq is created.
15
+ #
16
+ # == Type Attribute
17
+ #
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).
22
+ #
23
+ # Blather provides a helper for each possible type:
24
+ #
25
+ # Iq#get?
26
+ # Iq#set?
27
+ # Iq#result?
28
+ # Iq#error?
29
+ #
30
+ # Blather treats the +type+ attribute like a normal ruby object attribute providing a getter and setter.
31
+ # The default +type+ is +get+.
32
+ #
33
+ # iq = Iq.new
34
+ # iq.type # => :get
35
+ # iq.get? # => true
36
+ # iq.type = :set
37
+ # iq.set? # => true
38
+ # iq.get? # => false
39
+ #
40
+ # iq.type = :invalid # => RuntimeError
41
+ #
6
42
  class Iq < Stanza
7
- VALID_TYPES = [:get, :set, :result, :error]
43
+ VALID_TYPES = [:get, :set, :result, :error] # :nodoc:
8
44
 
9
45
  register :iq
10
46
 
11
- def self.import(node)
47
+ def self.import(node) # :nodoc:
12
48
  klass = nil
13
49
  node.children.each { |e| break if klass = class_from_registration(e.element_name, (e.namespace.href if e.namespace)) }
14
50
 
@@ -19,6 +55,11 @@ class Stanza
19
55
  end
20
56
  end
21
57
 
58
+ ##
59
+ # 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
22
63
  def self.new(type = nil, to = nil, id = nil)
23
64
  node = super :iq
24
65
  node.type = type || :get
@@ -31,7 +72,7 @@ class Stanza
31
72
 
32
73
  ##
33
74
  # Ensures type is :get, :set, :result or :error
34
- def type=(type)
75
+ def type=(type) # :nodoc:
35
76
  raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}" if type && !VALID_TYPES.include?(type.to_sym)
36
77
  super
37
78
  end
@@ -1,14 +1,136 @@
1
1
  module Blather
2
2
  class Stanza
3
3
 
4
- ##
5
- # Base Message stanza
4
+ # Exchanging messages is a basic use of XMPP and occurs when a user generates a message stanza
5
+ # that is addressed to another entity. The sender's server is responsible for delivering the
6
+ # message to the intended recipient (if the recipient is on the same local server) or for routing
7
+ # the message to the recipient's server (if the recipient is on a remote server). Thus a message
8
+ # stanza is used to "push" information to another entity.
9
+ #
10
+ # == To Attribute
11
+ #
12
+ # An instant messaging client specifies an intended recipient for a message by providing the JID
13
+ # of an entity other than the sender in the +to+ attribute of the Message stanza. If the message
14
+ # is being sent outside the context of any existing chat session or received message, the value
15
+ # of the +to+ address SHOULD be of the form "user@domain" rather than of the form "user@domain/resource".
16
+ #
17
+ # msg = Message.new 'user@domain.tld/resource'
18
+ # msg.to == 'user@domain.tld/resource'
19
+ #
20
+ # msg.to = 'another-user@some-domain.tld/resource'
21
+ # msg.to == 'another-user@some-domain.tld/resource'
22
+ #
23
+ # The +to+ attribute on a Message stanza works like any regular ruby object attribute
24
+ #
25
+ # == Type Attribute
26
+ #
27
+ # Common uses of the message stanza in instant messaging applications include: single messages;
28
+ # messages sent in the context of a one-to-one chat session; messages sent in the context of a
29
+ # multi-user chat room; alerts, notifications, or other information to which no reply is expected;
30
+ # and errors. These uses are differentiated via the +type+ attribute. If included, the +type+
31
+ # attribute MUST have one of the following values:
32
+ #
33
+ # * +:chat+ -- The message is sent in the context of a one-to-one chat session. Typically a receiving
34
+ # client will present message of type +chat+ in an interface that enables one-to-one chat between
35
+ # the two parties, including an appropriate conversation history.
36
+ # * +:error+ -- The message is generated by an entity that experiences an error in processing a message
37
+ # received from another entity. A client that receives a message of type +error+ SHOULD present an
38
+ # appropriate interface informing the sender of the nature of the error.
39
+ # * +:groupchat+ -- The message is sent in the context of a multi-user chat environment (similar to that
40
+ # of [IRC]). Typically a receiving client will present a message of type +groupchat+ in an interface
41
+ # that enables many-to-many chat between the parties, including a roster of parties in the chatroom
42
+ # and an appropriate conversation history.
43
+ # * +:headline+ -- The message provides an alert, a notification, or other information to which no reply
44
+ # is expected (e.g., news headlines, sports updates, near-real-time market data, and syndicated content).
45
+ # Because no reply to the message is expected, typically a receiving client will present a message of
46
+ # type "headline" in an interface that appropriately differentiates the message from standalone messages,
47
+ # chat messages, or groupchat messages (e.g., by not providing the recipient with the ability to reply).
48
+ # * +:normal+ -- The message is a standalone message that is sent outside the context of a one-to-one
49
+ # conversation or groupchat, and to which it is expected that the recipient will reply. Typically a receiving
50
+ # client will present a message of type +normal+ in an interface that enables the recipient to reply, but
51
+ # without a conversation history. The default value of the +type+ attribute is +normal+.
52
+ #
53
+ # Blather provides a helper for each possible type:
54
+ #
55
+ # Message#chat?
56
+ # Message#error?
57
+ # Message#groupchat?
58
+ # Message#headline?
59
+ # Message#normal?
60
+ #
61
+ # Blather treats the +type+ attribute like a normal ruby object attribute providing a getter and setter.
62
+ # The default +type+ is +chat+.
63
+ #
64
+ # msg = Message.new
65
+ # msg.type # => :chat
66
+ # msg.chat? # => true
67
+ # msg.type = :normal
68
+ # msg.normal? # => true
69
+ # msg.chat? # => false
70
+ #
71
+ # msg.type = :invalid # => RuntimeError
72
+ #
73
+ # == Body Element
74
+ #
75
+ # The +body+ element contains human-readable XML character data that specifies the textual contents of the message;
76
+ # this child element is normally included but is optional.
77
+ #
78
+ # Blather provides an attribute-like syntax for Message +body+ elements.
79
+ #
80
+ # msg = Message.new 'user@domain.tld', 'message body'
81
+ # msg.body # => 'message body'
82
+ #
83
+ # msg.body = 'other message'
84
+ # msg.body # => 'other message'
85
+ #
86
+ # == Subject Element
87
+ #
88
+ # The +subject+ element contains human-readable XML character data that specifies the topic of the message.
89
+ #
90
+ # Blather provides an attribute-like syntax for Message +subject+ elements.
91
+ #
92
+ # msg = Message.new 'user@domain.tld', 'message subject'
93
+ # msg.subject # => 'message subject'
94
+ #
95
+ # msg.subject = 'other subject'
96
+ # msg.subject # => 'other subject'
97
+ #
98
+ # == Thread Element
99
+ #
100
+ # The primary use of the XMPP +thread+ element is to uniquely identify a conversation thread or "chat session"
101
+ # between two entities instantiated by Message stanzas of type +chat+. However, the XMPP thread element can
102
+ # also be used to uniquely identify an analogous thread between two entities instantiated by Message stanzas
103
+ # of type +headline+ or +normal+, or among multiple entities in the context of a multi-user chat room instantiated
104
+ # by Message stanzas of type +groupchat+. It MAY also be used for Message stanzas not related to a human
105
+ # conversation, such as a game session or an interaction between plugins. The +thread+ element is not used to
106
+ # identify individual messages, only conversations or messagingg sessions. The inclusion of the +thread+ element
107
+ # is optional.
108
+ #
109
+ # The value of the +thread+ element is not human-readable and MUST be treated as opaque by entities; no semantic
110
+ # meaning can be derived from it, and only exact comparisons can be made against it. The value of the +thread+
111
+ # element MUST be a universally unique identifier (UUID) as described in [UUID].
112
+ #
113
+ # The +thread+ element MAY possess a 'parent' attribute that identifies another thread of which the current
114
+ # thread is an offshoot or child; the value of the 'parent' must conform to the syntax of the +thread+ element itself.
115
+ #
116
+ # Blather provides an attribute-like syntax for Message +thread+ elements.
117
+ #
118
+ # msg = Message.new
119
+ # msg.thread = '12345'
120
+ # msg.thread # => '12345'
121
+ #
122
+ # Parent threads can be set using a hash:
123
+ #
124
+ # msg.thread = {'parent-id' => 'thread-id'}
125
+ # msg.thread # => 'thread-id'
126
+ # msg.parent_thread # => 'parent-id'
127
+ #
6
128
  class Message < Stanza
7
- VALID_TYPES = [:chat, :error, :groupchat, :headline, :normal]
129
+ VALID_TYPES = [:chat, :error, :groupchat, :headline, :normal] # :nodoc:
8
130
 
9
131
  register :message
10
132
 
11
- def self.import(node)
133
+ def self.import(node) # :nodoc:
12
134
  klass = nil
13
135
  node.children.each { |e| break if klass = class_from_registration(e.element_name, (e.namespace.href if e.namespace)) }
14
136
 
@@ -20,7 +142,7 @@ class Stanza
20
142
  end
21
143
 
22
144
  def self.new(to = nil, body = nil, type = :chat)
23
- node = super(:message)
145
+ node = super :message
24
146
  node.to = to
25
147
  node.type = type
26
148
  node.body = body
@@ -29,16 +151,26 @@ class Stanza
29
151
 
30
152
  attribute_helpers_for :type, VALID_TYPES
31
153
 
32
- ##
33
- # Ensures type is :chat, :error, :groupchat, :headline or :normal
34
- def type=(type)
154
+ def type=(type) # :nodoc:
35
155
  raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}" if type && !VALID_TYPES.include?(type.to_sym)
36
156
  super
37
157
  end
38
158
 
39
159
  content_attr_accessor :body
40
160
  content_attr_accessor :subject
41
- content_attr_accessor :thread
161
+
162
+ content_attr_reader :thread
163
+
164
+ def parent_thread # :nodoc:
165
+ n = find_first('thread')
166
+ n[:parent] if n
167
+ end
168
+
169
+ def thread=(thread) # :nodoc:
170
+ parent, thread = thread.to_a.flatten if thread.is_a?(Hash)
171
+ set_content_for :thread, thread
172
+ find_first('thread')[:parent] = parent
173
+ end
42
174
  end
43
175
 
44
176
  end #Stanza
@@ -1,10 +1,54 @@
1
1
  module Blather
2
2
  class Stanza
3
3
 
4
- ##
5
- # Base Presence stanza
4
+ # = Presence Stanza
5
+ #
6
+ # Within Blather most of the interaction with Presence stanzas will be through one of its child classes: Status or Subscription.
7
+ #
8
+ # Presence stanzas are used to express an entity's current network availability (offline or online, along with
9
+ # various sub-states of the latter and optional user-defined descriptive text), and to notify other entities of
10
+ # that availability. Presence stanzas are also used to negotiate and manage subscriptions to the presence of other entities.
11
+ #
12
+ # == Type Attribute
13
+ #
14
+ # The +type+ attribute of a presence stanza is optional. A presence stanza that does not possess a +type+ attribute
15
+ # is used to signal to the server that the sender is online and available for communication. If included, the +type+
16
+ # attribute specifies a lack of availability, a request to manage a subscription to another entity's presence, a
17
+ # request for another entity's current presence, or an error related to a previously-sent presence stanza. If included,
18
+ # the +type+ attribute must have one of the following values:
19
+ #
20
+ # * +:unavailable+ -- Signals that the entity is no longer available for communication
21
+ # * +:subscribe+ -- The sender wishes to subscribe to the recipient's presence.
22
+ # * +:subscribed+ -- The sender has allowed the recipient to receive their presence.
23
+ # * +:unsubscribe+ -- The sender is unsubscribing from another entity's presence.
24
+ # * +:unsubscribed+ -- The subscription request has been denied or a previously-granted subscription has been cancelled.
25
+ # * +:probe+ -- A request for an entity's current presence; should be generated only by a server on behalf of a user.
26
+ # * +:error+ -- An error has occurred regarding processing or delivery of a previously-sent presence stanza.
27
+ #
28
+ # Blather provides a helper for each possible type:
29
+ #
30
+ # Presence#unavailabe?
31
+ # Presence#unavailable?
32
+ # Presence#subscribe?
33
+ # Presence#subscribed?
34
+ # Presence#unsubscribe?
35
+ # Presence#unsubscribed?
36
+ # Presence#probe?
37
+ # Presence#error?
38
+ #
39
+ # Blather treats the +type+ attribute like a normal ruby object attribute providing a getter and setter.
40
+ # The default +type+ is nil.
41
+ #
42
+ # presence = Presence.new
43
+ # presence.type # => nil
44
+ # presence.type = :unavailable
45
+ # presence.unavailable? # => true
46
+ # presence.error? # => false
47
+ #
48
+ # presence.type = :invalid # => RuntimeError
49
+ #
6
50
  class Presence < Stanza
7
- VALID_TYPES = [:unavailable, :subscribe, :subscribed, :unsubscribe, :unsubscribed, :probe, :error]
51
+ VALID_TYPES = [:unavailable, :subscribe, :subscribed, :unsubscribe, :unsubscribed, :probe, :error] # :nodoc:
8
52
 
9
53
  register :presence
10
54
 
@@ -13,7 +57,7 @@ class Stanza
13
57
  # either a Status or Subscription object is created based
14
58
  # on the type attribute.
15
59
  # If neither is found it instantiates a Presence object
16
- def self.import(node)
60
+ def self.import(node) # :nodoc:
17
61
  klass = case node['type']
18
62
  when nil, 'unavailable' then Status
19
63
  when /subscribe/ then Subscription
@@ -32,7 +76,7 @@ class Stanza
32
76
 
33
77
  ##
34
78
  # Ensures type is one of :unavailable, :subscribe, :subscribed, :unsubscribe, :unsubscribed, :probe or :error
35
- def type=(type)
79
+ def type=(type) # :nodoc:
36
80
  raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}" if type && !VALID_TYPES.include?(type.to_sym)
37
81
  super
38
82
  end