Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add documentation (or at least start to)

  • Loading branch information...
commit d052d21ced2902b2ed120f73d8095e7412f6dc3f 1 parent fa9a9dd
@sprsquish sprsquish authored
View
1  .gitignore
@@ -1,3 +1,4 @@
pkg
Manifest
doc
+coverage
View
75 README.rdoc
@@ -1,3 +1,78 @@
= Blather
An evented XMPP library
+
+== Features
+
+* evented architecture
+* uses libxml
+* simplified starting point
+
+== Project Pages
+
+GitHub:: https://github.com/sprsquish/blather
+
+RubyForge:: http://rubyforge.org/projects/squishtech/
+
+Lighthouse:: http://squishtech.lighthouseapp.com/projects/20652-blather
+
+== Author
+
+Jeff Smick <sprsquish@gmail.com>
+
+= Usage
+
+== Installation
+
+ sudo gem install blather
+
+== Example
+
+See the /examples directory for more advanced examples.
+
+This will auto-accept any subscription requests and echo back any messages or presence notifications.
+
+ require 'lib/blather/client'
+
+ class Echo < Blather::Client
+ # Auto-approve subscription requests
+ def receive_subscription(s)
+ send_data(s.approve!) and true if s.request?
+ end
+
+ # Echo presence
+ def receive_status(status)
+ set_status(status.state, status.message, status.from) if roster[status.from]
+ end
+
+ # Echo message
+ def receive_message(message)
+ send_data(message.reply) if message.type == :chat
+ end
+ end
+
+ Blather.run 'echo@jabber.local', 'echo', Echo.new
+
+
+= License
+
+Copyright (c) 2008 Jeff Smick
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
13 Rakefile
@@ -1,11 +1,16 @@
require 'echoe'
require 'lib/blather'
+require 'hanna/rdoctask'
Echoe.new('blather') do |p|
- p.version = Blather.version
- p.project = 'squishtech'
p.author = 'Jeff Smick'
+ p.email = 'sprsquish@gmail.com'
+
+ p.project = 'squishtech'
+ p.version = Blather::VERSION
p.summary = 'An evented XMPP library written on EventMachine and libxml-ruby'
- p.runtime_dependencies = %w[eventmachine libxml]
- p.retain_gemspec = true
+
+ p.runtime_dependencies = ['eventmachine', 'libxml >=1.0.11']
+ p.rdoc_options += %w[-S -T hanna --main README.rdoc --exclude autotest]
+ p.test_pattern = 'spec/**/*_spec.rb'
end
View
10 lib/blather.rb
@@ -36,19 +36,11 @@
XML::Parser.indent_tree_output = false
module Blather
+ VERSION = '0.1'
LOG = Logger.new STDOUT
def run(jid, password, client, host = nil, port = 5222)
EM.run { Stream.start client, JID.new(jid), password, host, port }
end
module_function :run
-
- MAJOR = 0
- MINOR = 1
- VERSION = [MAJOR, MINOR]*'.'
-
- def version
- VERSION
- end
- module_function :version
end
View
2  lib/blather/core/errors.rb
@@ -19,6 +19,6 @@ def to_s
end
# Stanza errors
- class StanzaError < StandardError; end
+ class StanzaError < BlatherError; end
class ArgumentError < StanzaError; end
end
View
16 lib/blather/core/jid.rb
@@ -1,13 +1,7 @@
-# =XMPP4R - XMPP Library for Ruby
-# License:: Ruby's license (see the LICENSE file) or GNU GPL, at your option.
-# Website::http://home.gna.org/xmpp4r/
-
module Blather
+
##
- # The JID class represents a Jabber Identifier as described by
- # RFC3920 section 3.1.
- #
- # Note that you can use JIDs also for Sorting, Hash keys, ...
+ # This is a simple modification of the JID class from XMPP4R
class JID
include Comparable
@@ -70,7 +64,7 @@ def to_s
s = @domain
s = "#{@node}@#{s}" if @node
s = "#{s}/#{@resource}" if @resource
- return s
+ s
end
##
@@ -97,9 +91,11 @@ def <=>(o)
to_s <=> o.to_s
end
- # Test id jid is strepped
+ ##
+ # Test if JID is stripped
def stripped?
@resource.nil?
end
end
+
end
View
22 lib/blather/core/roster.rb
@@ -1,5 +1,8 @@
module Blather
+ ##
+ # Local Roster
+ # Takes care of adding/removing JIDs through the stream
class Roster
include Enumerable
@@ -9,6 +12,9 @@ def initialize(stream, stanza = nil)
stanza.items.each { |i| push i, false } if stanza
end
+ ##
+ # Process any incoming stanzas adn either add or remove the
+ # corresponding RosterItem
def process(stanza)
stanza.items.each do |i|
case i.subscription
@@ -18,11 +24,18 @@ def process(stanza)
end
end
+ ##
+ # Pushes a JID into the roster
+ # then returns self to allow for chaining
def <<(elem)
push elem
self
end
+ ##
+ # Push a JID into the roster
+ # Will send the new item to the server
+ # unless overridden by calling #push(elem, false)
def push(elem, send = true)
jid = elem.respond_to?(:jid) ? elem.jid : JID.new(elem)
@items[key(jid)] = node = RosterItem.new(elem)
@@ -31,20 +44,29 @@ def push(elem, send = true)
end
alias_method :add, :push
+ ##
+ # Remove a JID from the roster
+ # Sends a remove query stanza to the server
def delete(jid)
@items.delete key(jid)
@stream.send_data Stanza::Iq::Roster.new(:set, Stanza::Iq::Roster::RosterItem.new(jid, nil, :remove))
end
alias_method :remove, :delete
+ ##
+ # Get a RosterItem by JID
def [](jid)
items[key(jid)]
end
+ ##
+ # Iterate over all RosterItems
def each(&block)
items.each &block
end
+ ##
+ # Returns a duplicate of all RosterItems
def items
@items.dup
end
View
36 lib/blather/core/roster_item.rb
@@ -1,6 +1,11 @@
module Blather
+ ##
+ # RosterItems hold internal representations of the user's roster
+ # including each JID's status.
class RosterItem
+ VALID_SUBSCRIPTION_TYPES = [:both, :from, :none, :remove, :to]
+
attr_reader :jid,
:ask,
:statuses
@@ -8,12 +13,17 @@ class RosterItem
attr_accessor :name,
:groups
+ ##
+ # item:: can be a JID, String (a@b) or a Stanza
def initialize(item)
@statuses = []
- if item.is_a?(JID)
+ case item
+ when JID
self.jid = item.stripped
- elsif item.is_a?(XMPPNode)
+ when String
+ self.jid = JID.new(item).stripped
+ when XMPPNode
self.jid = JID.new(item['jid']).stripped
self.name = item['name']
self.subscription = item['subscription']
@@ -22,36 +32,54 @@ def initialize(item)
end
end
+ ##
+ # Set the jid
def jid=(jid)
@jid = JID.new(jid).stripped
end
- VALID_SUBSCRIPTION_TYPES = [:both, :from, :none, :remove, :to].freeze
+ ##
+ # Set the subscription
+ # Ensures it is one of VALID_SUBSCRIPTION_TYPES
def subscription=(sub)
raise ArgumentError, "Invalid Type (#{sub}), use: #{VALID_SUBSCRIPTION_TYPES*' '}" if
sub && !VALID_SUBSCRIPTION_TYPES.include?(sub = sub.to_sym)
@subscription = sub ? sub : :none
end
+ ##
+ # Get the current subscription
+ # returns:: :both, :from, :none, :remove, :to or :none
def subscription
@subscription || :none
end
+ ##
+ # Set the ask value
+ # ask:: must only be nil or :subscribe
def ask=(ask)
- raise ArgumentError, "Invalid Type (#{ask}), use: #{VALID_SUBSCRIPTION_TYPES*' '}" if ask && (ask = ask.to_sym) != :subscribe
+ raise ArgumentError, "Invalid Type (#{ask}), can only be :subscribe" if ask && (ask = ask.to_sym) != :subscribe
@ask = ask ? ask : nil
end
+ ##
+ # Set the status then sorts them according to priority
+ # presence:: Status
def status=(presence)
@statuses.delete_if { |s| s.from == presence.from }
@statuses << presence
@statuses.sort!
end
+ ##
+ # Return the status with the highest priority
+ # if resource is set find the status of that specific resource
def status(resource = nil)
top = resource ? @statuses.detect { |s| s.from.resource == resource } : @statuses.first
end
+ ##
+ # Translate the RosterItem into a proper stanza that can be sent over the stream
def to_stanza(type = nil)
r = Stanza::Iq::Roster.new type
n = Stanza::Iq::Roster::RosterItem.new jid, name, subscription, ask
View
32 lib/blather/core/stanza.rb
@@ -1,4 +1,6 @@
module Blather
+ ##
+ # Base XMPP Stanza
class Stanza < XMPPNode
@@registered_callbacks = []
@@ -8,6 +10,13 @@ def self.registered_callbacks
class_inheritable_array :callback_heirarchy
+ ##
+ # Registers a callback onto the callback heirarchy stack
+ #
+ # Thanks to help from ActiveSupport every class
+ # that inherits Stanza can register a callback for itself
+ # which is added to a list and iterated over when looking for
+ # a callback to use
def self.register(callback_type, name = nil, xmlns = nil)
@@registered_callbacks << callback_type
@@ -18,16 +27,24 @@ def self.register(callback_type, name = nil, xmlns = nil)
super name, xmlns
end
+ ##
+ # Helper method that creates a unique ID for stanzas
def self.next_id
@@last_id ||= 0
@@last_id += 1
'blather%04x' % @@last_id
end
+ ##
+ # Creates a new stanza with the same name as the node
+ # then inherits all the node's attributes and properties
def self.import(node)
self.new(node.element_name).inherit(node)
end
+ ##
+ # Creates a new Stanza with the name given
+ # then attaches an ID and document (to enable searching)
def self.new(elem_name = nil)
elem = super
elem.id = next_id
@@ -39,12 +56,15 @@ def error?
self.type == :error
end
+ ##
+ # Copies itself then swaps from and to
+ # then returns the new stanza
def reply
- elem = self.copy(true)
- elem.to, elem.from = self.from, self.to
- elem
+ self.copy(true).reply!
end
+ ##
+ # Swaps from and to
def reply!
self.to, self.from = self.from, self.to
self
@@ -64,6 +84,8 @@ def to=(to)
self['to'] = to.to_s if to
end
+ ##
+ # returns:: JID created from the "to" value of the stanza
def to
JID.new(self['to']) if self['to']
end
@@ -73,6 +95,8 @@ def from=(from)
self['from'] = from.to_s if from
end
+ ##
+ # returns:: JID created from the "from" value of the stanza
def from
JID.new(self['from']) if self['from']
end
@@ -82,6 +106,8 @@ def type=(type)
self['type'] = type.to_s
end
+ ##
+ # returns:: a symbol of the type
def type
self['type'].to_sym if self['type']
end
View
2  lib/blather/core/stanza/iq.rb
@@ -1,6 +1,8 @@
module Blather
class Stanza
+ ##
+ # Base Iq stanza
class Iq < Stanza
register :iq
View
17 lib/blather/core/stanza/message.rb
@@ -1,12 +1,12 @@
module Blather
class Stanza
+ ##
+ # Base Message stanza
class Message < Stanza
- register :message
+ VALID_TYPES = [:chat, :error, :groupchat, :headline, :normal]
- def self.generate_thread_id
- Digest::MD5.hexdigest(Time.new.to_f.to_s)
- end
+ register :message
def self.new(to = nil, type = nil, body = nil)
elem = super()
@@ -16,7 +16,8 @@ def self.new(to = nil, type = nil, body = nil)
elem
end
- VALID_TYPES = [:chat, :error, :groupchat, :headline, :normal]
+ ##
+ # Ensures type is :chat, :error, :groupchat, :headline or :normal
def type=(type)
raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}" if type && !VALID_TYPES.include?(type.to_sym)
super
@@ -24,7 +25,7 @@ def type=(type)
def body=(body)
remove_child :body
- self << XML::Node.new('body', body) if body
+ self << XMPPNode.new('body', body) if body
end
def body
@@ -33,7 +34,7 @@ def body
def subject=(subject)
remove_child :subject
- self << XML::Node.new('subject', subject) if subject
+ self << XMPPNode.new('subject', subject) if subject
end
def subject
@@ -42,7 +43,7 @@ def subject
def thread=(thread)
remove_child :thread
- self << XML::Node.new('body', body) if body
+ self << XMPPNode.new('body', body) if body
end
def thread
View
12 lib/blather/core/stanza/presence.rb
@@ -1,9 +1,18 @@
module Blather
class Stanza
+ ##
+ # Base Presence stanza
class Presence < Stanza
+ VALID_TYPES = [:unavailable, :subscribe, :subscribed, :unsubscribe, :unsubscribed, :probe, :error]
+
register :presence
+ ##
+ # Creates a class based on the presence type
+ # either a Status or Subscription object is created based
+ # on the type attribute.
+ # If neither is found it instantiates a Presence object
def self.import(node)
klass = case node['type']
when nil, 'unavailable' then Status
@@ -13,7 +22,8 @@ def self.import(node)
klass.new.inherit(node)
end
- VALID_TYPES = [:unavailable, :subscribe, :subscribed, :unsubscribe, :unsubscribed, :probe, :error].freeze
+ ##
+ # Ensures type is one of :unavailable, :subscribe, :subscribed, :unsubscribe, :unsubscribed, :probe or :error
def type=(type)
raise ArgumentError, "Invalid Type (#{type}), use: #{VALID_TYPES*' '}" if type && !VALID_TYPES.include?(type.to_sym)
super
View
16 lib/blather/core/stanza/presence/status.rb
@@ -3,6 +3,8 @@ class Stanza
class Presence
class Status < Presence
+ VALID_STATES = [:away, :chat, :dnd, :xa]
+
include Comparable
register :status
@@ -14,12 +16,15 @@ def self.new(state = nil, message = nil)
elem
end
+ ##
+ # Ensures type is nil or :unavailable
def type=(type)
raise ArgumentError, "Invalid type (#{type}). Must be nil or unavailable" if type && type.to_sym != :unavailable
super
end
- VALID_STATES = [:away, :chat, :dnd, :xa].freeze
+ ##
+ # Ensure state is one of :away, :chat, :dnd, :xa or nil
def state=(state)
state = state.to_sym if state
state = nil if state == :available
@@ -29,10 +34,14 @@ def state=(state)
self << XMPPNode.new('show', state) if state
end
+ ##
+ # return:: :available if state is nil
def state
(type || content_from(:show) || :available).to_sym
end
+ ##
+ # Ensure priority is between -128 and 127
def priority=(priority)
raise ArgumentError, 'Priority must be between -128 and +127' if priority && !(-128..127).include?(priority.to_i)
@@ -53,6 +62,9 @@ def message
content_from :status
end
+ ##
+ # Compare status based on priority
+ # raises an error if the JIDs aren't the same
def <=>(o)
raise "Cannot compare status from different JIDs: #{[self.from, o.from].inspect}" unless self.from.stripped == o.from.stripped
self.priority <=> o.priority
@@ -62,4 +74,4 @@ def <=>(o)
end #Presence
end #Stanza
-end
+end #Blather
View
10 lib/blather/core/stanza/presence/subscription.rb
@@ -21,26 +21,36 @@ def to=(to)
super JID.new(to).stripped
end
+ ##
+ # Create an approve stanza
def approve!
self.type = :subscribed
morph_to_reply
end
+ ##
+ # Create a refuse stanza
def refuse!
self.type = :unsubscribed
morph_to_reply
end
+ ##
+ # Create an unsubscribe stanza
def unsubscribe!
self.type = :unsubscribe
morph_to_reply
end
+ ##
+ # Create a cancel stanza
def cancel!
self.type = :unsubscribed
morph_to_reply
end
+ ##
+ # Create a request stanza
def request!
self.type = :subscribe
morph_to_reply
View
14 lib/blather/core/stream.rb
@@ -10,7 +10,7 @@ def self.start(client, jid, pass, host = nil, port = 5222)
EM.connect host, port, self, client, jid, pass
end
- def initialize(client, jid, pass)
+ def initialize(client, jid, pass) # :nodoc:
super()
@client = client
@@ -27,25 +27,25 @@ def initialize(client, jid, pass)
@parser = Parser.new self
end
- def connection_completed
+ def connection_completed # :nodoc:
# @keepalive = EM::Timer.new(60) { send_data ' ' }
@state = :stopped
dispatch
end
- def receive_data(data)
+ def receive_data(data) # :nodoc:
@parser.parse data
rescue => e
@client.respond_to?(:rescue) ? @client.rescue(e) : raise(e)
end
- def unbind
+ def unbind # :nodoc:
# @keepalive.cancel
@state == :stopped
end
- def receive(node)
+ def receive(node) # :nodoc:
LOG.debug "\n"+('-'*30)+"\n"
LOG.debug "RECEIVING (#{node.element_name}) #{node}"
@node = node
@@ -71,6 +71,8 @@ def receive(node)
end
end
+ ##
+ # Send data over the wire
def send(stanza)
#TODO Queue if not ready
LOG.debug "SENDING: (#{caller[1]}) #{stanza}"
@@ -85,7 +87,7 @@ def ready?
@state == :ready
end
- def jid=(new_jid)
+ def jid=(new_jid) # :nodoc:
LOG.debug "NEW JID: #{new_jid}"
new_jid = JID.new new_jid
@client.jid = new_jid
View
106 lib/blather/core/stream/parser.rb
@@ -1,72 +1,74 @@
-module Blather
- module Stream
- class Parser
- STREAM_REGEX = %r{(/)?stream:stream}.freeze
+module Blather # :nodoc:
+module Stream # :nodoc:
- @@debug = false
- def self.debug; @@debug; end
- def self.debug=(debug); @@debug = debug; end
+ class Parser # :nodoc:
+ STREAM_REGEX = %r{(/)?stream:stream}.freeze
- include XML::SaxParser::Callbacks
+ @@debug = false
+ def self.debug; @@debug; end
+ def self.debug=(debug); @@debug = debug; end
- def initialize(receiver)
- @receiver = receiver
- @current = nil
+ include XML::SaxParser::Callbacks
- @parser = XML::SaxParser.new
- @parser.callbacks = self
- end
+ def initialize(receiver)
+ @receiver = receiver
+ @current = nil
+
+ @parser = XML::SaxParser.new
+ @parser.callbacks = self
+ end
- def parse(string)
- LOG.debug "PARSING: #{string}" if @@debug
- if string =~ STREAM_REGEX && $1
- @receiver.receive XMPPNode.new('stream:end')
- else
- string << "</stream:stream>" if string =~ STREAM_REGEX && !$1
+ def parse(string)
+ LOG.debug "PARSING: #{string}" if @@debug
+ if string =~ STREAM_REGEX && $1
+ @receiver.receive XMPPNode.new('stream:end')
+ else
+ string << "</stream:stream>" if string =~ STREAM_REGEX && !$1
- @parser.string = string
- @parser.parse
- end
+ @parser.string = string
+ @parser.parse
end
+ end
- def on_start_element(elem, attrs)
- LOG.debug "START ELEM: (#{[elem, attrs].inspect})" if @@debug
- e = XMPPNode.new elem
- attrs.each { |n,v| e[n] = v }
+ def on_start_element(elem, attrs)
+ LOG.debug "START ELEM: (#{[elem, attrs].inspect})" if @@debug
+ e = XMPPNode.new elem
+ attrs.each { |n,v| e[n] = v }
- if elem == 'stream:stream'
- @receiver.receive e
+ if elem == 'stream:stream'
+ @receiver.receive e
- elsif !@receiver.stopped?
- @current << e if @current
- @current = e
+ elsif !@receiver.stopped?
+ @current << e if @current
+ @current = e
- end
end
+ end
- def on_characters(chars = '')
- LOG.debug "CHARS: #{chars}" if @@debug
- @current << XML::Node.new_text(chars) if @current
- end
+ def on_characters(chars = '')
+ LOG.debug "CHARS: #{chars}" if @@debug
+ @current << XML::Node.new_text(chars) if @current
+ end
- def on_cdata_block(block)
- LOG.debug "CDATA: #{block}" if @@debug
- @current << XML::Node.new_cdata(block) if @current
- end
+ def on_cdata_block(block)
+ LOG.debug "CDATA: #{block}" if @@debug
+ @current << XML::Node.new_cdata(block) if @current
+ end
- def on_end_element(elem)
- return if elem =~ STREAM_REGEX
+ def on_end_element(elem)
+ return if elem =~ STREAM_REGEX
- LOG.debug "END ELEM: (#{@current}) #{elem}" if @@debug
- if @current.parent?
- @current = @current.parent
+ LOG.debug "END ELEM: (#{@current}) #{elem}" if @@debug
+ if @current.parent?
+ @current = @current.parent
- else
- c, @current = @current, nil
- @receiver.receive c
+ else
+ c, @current = @current, nil
+ @receiver.receive c
- end
end
end
- end
-end
+ end #Parser
+
+end #Stream
+end #Blather
View
6 lib/blather/core/stream/resource.rb
@@ -1,7 +1,7 @@
-module Blather
-module Stream
+module Blather # :nodoc:
+module Stream # :nodoc:
- class Resource
+ class Resource # :nodoc:
def initialize(stream, jid)
@stream = stream
@jid = jid
View
12 lib/blather/core/stream/sasl.rb
@@ -1,7 +1,7 @@
-module Blather
-module Stream
+module Blather # :nodoc:
+module Stream # :nodoc:
- class SASL
+ class SASL # :nodoc:
SASL_NS = 'urn:ietf:params:xml:ns:xmpp-sasl'
def initialize(stream, jid, pass = nil)
@@ -53,7 +53,7 @@ def auth_node(mechanism, content = nil)
node
end
- module DigestMD5
+ module DigestMD5 # :nodoc:
def self.extended(obj)
obj.instance_eval { @callbacks['challenge'] = proc { decode_challenge; respond } }
end
@@ -117,13 +117,13 @@ def d(s); Digest::MD5.digest(s); end
def h(s); Digest::MD5.hexdigest(s); end
end #DigestMD5
- module Plain
+ module Plain # :nodoc:
def authenticate
@stream.send auth_node('PLAIN', b64("#{@jid.stripped}\x00#{@jid.node}\x00#{@pass}"))
end
end #Plain
- module Anonymous
+ module Anonymous # :nodoc:
def authenticate
@stream.send auth_node('ANONYMOUS', b64(@jid.node))
end
View
78 lib/blather/core/stream/session.rb
@@ -1,43 +1,43 @@
-module Blather
- module Stream
-
- class Session
- def initialize(stream, to)
- @stream = stream
- @to = to
- @callbacks = {}
- end
-
- def success(&callback)
- @callbacks[:success] = callback
- end
-
- def failure(&callback)
- @callbacks[:failure] = callback
- end
-
- def receive(node)
- @node = node
- __send__(@node.element_name == 'iq' ? @node['type'] : @node.element_name)
- end
-
- def session
- response = Stanza::Iq.new :set
- response.to = @to
- sess = XMPPNode.new 'session'
- sess['xmlns'] = 'urn:ietf:params:xml:ns:xmpp-session'
- response << sess
- @stream.send response
- end
-
- def result
- @callbacks[:success].call(@jid) if @callbacks[:success]
- end
-
- def error
- @callbacks[:failure].call if @callbacks[:failure]
- end
+module Blather # :nodoc:
+module Stream # :nodoc:
+
+ class Session # :nodoc:
+ def initialize(stream, to)
+ @stream = stream
+ @to = to
+ @callbacks = {}
end
+ def success(&callback)
+ @callbacks[:success] = callback
+ end
+
+ def failure(&callback)
+ @callbacks[:failure] = callback
+ end
+
+ def receive(node)
+ @node = node
+ __send__(@node.element_name == 'iq' ? @node['type'] : @node.element_name)
+ end
+
+ def session
+ response = Stanza::Iq.new :set
+ response.to = @to
+ sess = XMPPNode.new 'session'
+ sess['xmlns'] = 'urn:ietf:params:xml:ns:xmpp-session'
+ response << sess
+ @stream.send response
+ end
+
+ def result
+ @callbacks[:success].call(@jid) if @callbacks[:success]
+ end
+
+ def error
+ @callbacks[:failure].call if @callbacks[:failure]
+ end
end
+
+end
end
View
48 lib/blather/core/stream/tls.rb
@@ -1,27 +1,29 @@
-module Blather
- module Stream
- class TLS
- def initialize(stream)
- @stream = stream
- @callbacks = {
- 'starttls' => proc { @stream.send "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>" },
- 'proceed' => proc { @stream.start_tls; @callbacks['success'].call },
- 'success' => proc { },
- 'failure' => proc { }
- }
- end
+module Blather # :nodoc:
+module Stream # :nodoc:
- def success(&callback)
- @callbacks['success'] = callback
- end
+ class TLS # :nodoc:
+ def initialize(stream)
+ @stream = stream
+ @callbacks = {
+ 'starttls' => proc { @stream.send "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>" },
+ 'proceed' => proc { @stream.start_tls; @callbacks['success'].call },
+ 'success' => proc { },
+ 'failure' => proc { }
+ }
+ end
- def failure(&callback)
- @callbacks['failure'] = callback
- end
+ def success(&callback)
+ @callbacks['success'] = callback
+ end
- def receive(node)
- @callbacks[node.element_name].call if @callbacks[node.element_name]
- end
+ def failure(&callback)
+ @callbacks['failure'] = callback
end
- end
-end
+
+ def receive(node)
+ @callbacks[node.element_name].call if @callbacks[node.element_name]
+ end
+ end #TLS
+
+end #Stream
+end #Blather
View
8 lib/blather/core/sugar.rb
@@ -1,7 +1,8 @@
-module LibXML
- module XML
+module LibXML # :nodoc:
+ module XML # :nodoc:
class Attributes
+ # Helper method for removing attributes
def remove(name)
name = name.to_s
self.each { |a| a.remove! or break if a.name == name }
@@ -11,6 +12,7 @@ def remove(name)
end #XML
end #LibXML
+## Thanks to ActiveSupport for everything below this line
class Class # :nodoc:
def class_inheritable_reader(*syms)
syms.each do |sym|
@@ -123,7 +125,7 @@ def inherited_with_inheritable_attributes(child)
alias inherited inherited_with_inheritable_attributes
end #Class
-class Object
+class Object # :nodoc:
def duplicable?; true; end
end
View
37 lib/blather/core/xmpp_node.rb
@@ -1,5 +1,9 @@
module Blather
+ ##
+ # Base XML Node
+ # All XML classes subclass XMPPNode
+ # it allows the addition of helpers
class XMPPNode < XML::Node
@@registrations = {}
@@ -8,6 +12,8 @@ class XMPPNode < XML::Node
class_inheritable_accessor :xmlns,
:name
+ ##
+ # Automatically sets the namespace registered by the subclass
def self.new(name = nil, content = nil)
name ||= self.name
@@ -20,17 +26,29 @@ def self.new(name = nil, content = nil)
elem
end
+ ##
+ # Lets a subclass register itself
+ #
+ # This registers a namespace that is used when looking
+ # up the class name of the object to instantiate when a new
+ # stanza is received
def self.register(name, xmlns = nil)
self.name = name.to_s
self.xmlns = xmlns
@@registrations[[name, xmlns]] = self
end
+ ##
+ # Find the class to use given the name and namespace of a stanza
def self.class_from_registration(name, xmlns)
name = name.to_s
@@registrations[[name, xmlns]] || @@registrations[[name, nil]]
end
+ ##
+ # Looks up the class to use then instantiates an object
+ # of that class and imports all the <tt>node</tt>'s attributes
+ # and children into it.
def self.import(node)
klass = class_from_registration(node.element_name, node.xmlns)
if klass && klass != self
@@ -40,6 +58,8 @@ def self.import(node)
end
end
+ ##
+ # Quickway of turning itself into a proper object
def to_stanza
self.class.import self
end
@@ -53,40 +73,57 @@ def xmlns
self['xmlns']
end
+ ##
+ # Remove a child with the name and (optionally) namespace given
def remove_child(name, ns = nil)
name = name.to_s
self.each { |n| n.remove! if n.element_name == name && (!ns || n.xmlns == ns) }
end
+ ##
+ # Remove all children with a given name
def remove_children(name)
name = name.to_s
self.find(name).each { |n| n.remove! }
end
+ ##
+ # Pull the content from a child
def content_from(name)
name = name.to_s
(child = self.detect { |n| n.element_name == name }) ? child.content : nil
end
+ ##
+ # Create a copy
def copy(deep = true)
self.class.new(self.element_name).inherit(self)
end
+ ##
+ # Inherit all of <tt>stanza</tt>'s attributes and children
def inherit(stanza)
inherit_attrs stanza.attributes
stanza.children.each { |c| self << c.copy(true) }
self
end
+ ##
+ # Inherit only <tt>stanza</tt>'s attributes
def inherit_attrs(attrs)
attrs.each { |a| self[a.name] = a.value }
self
end
+ ##
+ # Turn itself into a string and remove all whitespace between nodes
def to_s
+ # TODO: Fix this for HTML nodes (and any other that might require whitespace)
super.gsub(">\n<", '><')
end
+ ##
+ # Override #find to work when a node isn't attached to a document
def find(what, nslist = nil)
(self.doc ? super(what, nslist) : select { |i| i.element_name == what})
end
View
8 lib/blather/extensions/last_activity.rb
@@ -1,7 +1,7 @@
module Blather
- module Extensions
+ module Extensions #:nodoc:
- module LastActivity
+ module LastActivity #:nodoc:
def self.included(base)
base.class_eval do
@@last_activity = Time.now
@@ -23,7 +23,7 @@ def receive_last_activity(stanza)
end
end #LastActivity
- class LastActivityStanza < Query
+ class LastActivityStanza < Query #:nodoc:
register :last_activity, nil, 'jabber:iq:last'
def self.new(type = :get, seconds = nil)
@@ -53,5 +53,3 @@ def reply!(seconds)
end #LastActivityStanza
end
end
-
-Blather::Client.__send__ :include, Blather::Extensions::LastActivity
View
6 lib/blather/extensions/version.rb
@@ -1,7 +1,7 @@
module Blather
- module Extensions
+ module Extensions #:nodoc:
- module Version
+ module Version #:nodoc:
def self.included(base)
base.class_eval do
@@version = {}
@@ -27,7 +27,7 @@ def version
end
end #Version
- class VersionStanza < Iq
+ class VersionStanza < Iq #:nodoc:
def self.new(type = 'result', ver = {})
elem = super(type)
Please sign in to comment.
Something went wrong with that request. Please try again.