blather 0.3.0 → 0.3.1
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.
- data/README.rdoc +17 -2
- data/Rakefile +1 -1
- data/examples/ping_pong.rb +37 -0
- data/lib/blather.rb +1 -4
- data/lib/blather/client.rb +75 -3
- data/lib/blather/client/client.rb +67 -53
- data/lib/blather/client/dsl.rb +13 -18
- data/lib/blather/stanza/disco/disco_info.rb +0 -12
- data/lib/blather/stanza/disco/disco_items.rb +0 -7
- data/lib/blather/stream.rb +4 -4
- data/lib/blather/stream/component.rb +1 -1
- data/spec/blather/client/client_spec.rb +385 -0
- data/spec/blather/client/dsl_spec.rb +101 -0
- data/spec/blather/stream/client_spec.rb +18 -18
- data/spec/blather/stream/component_spec.rb +3 -3
- data/spec/spec_helper.rb +4 -1
- metadata +4 -6
- data/lib/blather/stanza/pubsub/subscriber.rb +0 -64
- data/spec/blather/client_spec.rb +0 -0
- data/spec/blather/stanza/pubsub/subscriber_spec.rb +0 -70
data/README.rdoc
CHANGED
@@ -60,7 +60,7 @@ Guards act like AND statements. Each condition must be met if the handler is to
|
|
60
60
|
# Equivalent to saying (stanza.chat? && stanza.body)
|
61
61
|
message :chat?, :body
|
62
62
|
|
63
|
-
|
63
|
+
The different types of guards are:
|
64
64
|
|
65
65
|
# Symbol
|
66
66
|
# Checks for a non-false reply to calling the symbol on the stanza
|
@@ -93,9 +93,24 @@ There are 5 different types of guards:
|
|
93
93
|
# Equivalent to stanza.body == 'foo' || stanza.body == 'baz'
|
94
94
|
message [{:body => 'foo'}, {:body => 'baz'}]
|
95
95
|
|
96
|
+
== On the Command Line:
|
97
|
+
|
98
|
+
Default usage is:
|
99
|
+
|
100
|
+
[blather_script] [options] node@domain.com/resource password [host] [port]
|
101
|
+
|
102
|
+
Command line options:
|
103
|
+
|
104
|
+
-D, --debug Run in debug mode (you will see all XMPP communication)
|
105
|
+
-d, --daemonize Daemonize the process
|
106
|
+
--pid=[PID] Write the PID to this file
|
107
|
+
--log=[LOG] Write to the [LOG] file instead of stdout/stderr
|
108
|
+
-h, --help Show this message
|
109
|
+
-v, --version Show version
|
110
|
+
|
111
|
+
|
96
112
|
= TODO
|
97
113
|
|
98
|
-
* Better Documentation
|
99
114
|
* Add XPath guard that passes the result to the handler
|
100
115
|
* Add Disco the the DSL
|
101
116
|
* PubSub (XEP-0060: http://xmpp.org/extensions/xep-0060.html)
|
data/Rakefile
CHANGED
@@ -16,7 +16,7 @@ begin
|
|
16
16
|
gem.extensions = ['Rakefile']
|
17
17
|
|
18
18
|
gem.add_dependency 'eventmachine', '>= 0.12.6'
|
19
|
-
gem.add_dependency 'libxml-ruby', '>= 1.1.
|
19
|
+
gem.add_dependency 'libxml-ruby', '>= 1.1.2'
|
20
20
|
|
21
21
|
gem.files = FileList['examples/**/*', 'lib/**/*', 'ext/*.{rb,c}'].to_a
|
22
22
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'blather/client/dsl'
|
2
|
+
$stdout.sync = true
|
3
|
+
|
4
|
+
module Ping
|
5
|
+
extend Blather::DSL
|
6
|
+
def self.run; client.run; end
|
7
|
+
|
8
|
+
setup 'echo@jabber.local/ping', 'echo'
|
9
|
+
|
10
|
+
status :from => Blather::JID.new('echo@jabber.local/pong') do |s|
|
11
|
+
puts "serve!"
|
12
|
+
say s.from, 'ping'
|
13
|
+
end
|
14
|
+
|
15
|
+
message :chat?, :body => 'pong' do |m|
|
16
|
+
puts "ping!"
|
17
|
+
say m.from, 'ping'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Pong
|
22
|
+
extend Blather::DSL
|
23
|
+
def self.run; client.run; end
|
24
|
+
|
25
|
+
setup 'echo@jabber.local/pong', 'echo'
|
26
|
+
message :chat?, :body => 'ping' do |m|
|
27
|
+
puts "pong!"
|
28
|
+
say m.from, 'pong'
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
trap(:INT) { EM.stop }
|
33
|
+
trap(:TERM) { EM.stop }
|
34
|
+
EM.run do
|
35
|
+
Ping.run
|
36
|
+
Pong.run
|
37
|
+
end
|
data/lib/blather.rb
CHANGED
@@ -1,6 +1,3 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)
|
2
|
-
$:.unshift File.join(File.dirname(__FILE__), '..')
|
3
|
-
|
4
1
|
# Require the necessary files
|
5
2
|
%w[
|
6
3
|
rubygems
|
@@ -48,6 +45,6 @@ $:.unshift File.join(File.dirname(__FILE__), '..')
|
|
48
45
|
XML.indent_tree_output = false
|
49
46
|
|
50
47
|
module Blather
|
51
|
-
LOG = Logger.new(
|
48
|
+
LOG = Logger.new($stdout) unless const_defined?(:LOG)
|
52
49
|
LOG.level = Logger::INFO
|
53
50
|
end
|
data/lib/blather/client.rb
CHANGED
@@ -1,13 +1,85 @@
|
|
1
|
+
require 'optparse'
|
1
2
|
require File.join(File.dirname(__FILE__), *%w[client dsl])
|
2
3
|
|
3
4
|
include Blather::DSL
|
4
5
|
|
6
|
+
options = {}
|
7
|
+
optparse = OptionParser.new do |opts|
|
8
|
+
opts.banner = "Run with #{$0} [options] user@server/resource password [host] [port]"
|
9
|
+
|
10
|
+
opts.on('-D', '--debug', 'Run in debug mode (you will see all XMPP communication)') do |debug|
|
11
|
+
options[:debug] = debug
|
12
|
+
end
|
13
|
+
|
14
|
+
opts.on('-d', '--daemonize', 'Daemonize the process') do |daemonize|
|
15
|
+
options[:daemonize] = daemonize
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.on('--pid=[PID]', 'Write the PID to this file') do |pid|
|
19
|
+
if !File.writable?(File.dirname(pid))
|
20
|
+
$stderr.puts "Unable to write log file to #{pid}"
|
21
|
+
exit 1
|
22
|
+
end
|
23
|
+
options[:pid] = pid
|
24
|
+
end
|
25
|
+
|
26
|
+
opts.on('--log=[LOG]', 'Write to the [LOG] file instead of stdout/stderr') do |log|
|
27
|
+
if !File.writable?(File.dirname(log))
|
28
|
+
$stderr.puts "Unable to write log file to #{log}"
|
29
|
+
exit 1
|
30
|
+
end
|
31
|
+
options[:log] = log
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
35
|
+
puts opts
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on_tail('-v', '--version', 'Show version') do
|
40
|
+
require 'yaml'
|
41
|
+
version = YAML.load_file File.join(File.dirname(__FILE__), %w[.. .. VERSION.yml])
|
42
|
+
puts "Blather v#{version[:major]}.#{version[:minor]}.#{version[:patch]}"
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
end
|
46
|
+
optparse.parse!
|
47
|
+
|
5
48
|
at_exit do
|
6
49
|
unless client.setup?
|
7
50
|
if ARGV.length < 2
|
8
|
-
puts
|
9
|
-
|
10
|
-
|
51
|
+
puts optparse
|
52
|
+
exit 1
|
53
|
+
end
|
54
|
+
client.setup(*ARGV)
|
55
|
+
end
|
56
|
+
|
57
|
+
def run(options)
|
58
|
+
$stdin.reopen "/dev/null"
|
59
|
+
|
60
|
+
if options[:log]
|
61
|
+
log = File.new(options[:log], 'a')
|
62
|
+
log.sync = options[:debug]
|
63
|
+
Blather::LOG.level = Logger::DEBUG if options[:debug]
|
64
|
+
$stdout.reopen log
|
65
|
+
$stderr.reopen $stdout
|
66
|
+
end
|
67
|
+
trap(:INT) { EM.stop }
|
68
|
+
trap(:TERM) { EM.stop }
|
69
|
+
EM.run { client.run }
|
70
|
+
end
|
71
|
+
|
72
|
+
if options[:daemonize]
|
73
|
+
pid = fork do
|
74
|
+
Process.setsid
|
75
|
+
exit if fork
|
76
|
+
File.open(options[:pid], 'w') { |f| f << Process.pid } if options[:pid]
|
77
|
+
run options
|
78
|
+
FileUtils.rm(options[:pid]) if options[:pid]
|
11
79
|
end
|
80
|
+
::Process.detach pid
|
81
|
+
exit
|
82
|
+
else
|
83
|
+
run options
|
12
84
|
end
|
13
85
|
end
|
@@ -3,8 +3,8 @@ require File.join(File.dirname(__FILE__), *%w[.. .. blather])
|
|
3
3
|
module Blather #:nodoc:
|
4
4
|
|
5
5
|
class Client #:nodoc:
|
6
|
-
|
7
|
-
|
6
|
+
attr_reader :jid,
|
7
|
+
:roster
|
8
8
|
|
9
9
|
def initialize
|
10
10
|
@state = :initializing
|
@@ -17,6 +17,24 @@ module Blather #:nodoc:
|
|
17
17
|
setup_initial_handlers
|
18
18
|
end
|
19
19
|
|
20
|
+
def jid=(new_jid)
|
21
|
+
@jid = JID.new new_jid
|
22
|
+
end
|
23
|
+
|
24
|
+
def status
|
25
|
+
@status.state
|
26
|
+
end
|
27
|
+
|
28
|
+
def status=(state)
|
29
|
+
state, msg, to = state
|
30
|
+
|
31
|
+
status = Stanza::Presence::Status.new state, msg
|
32
|
+
status.to = to
|
33
|
+
@status = status unless to
|
34
|
+
|
35
|
+
write status
|
36
|
+
end
|
37
|
+
|
20
38
|
def setup?
|
21
39
|
@setup.is_a? Array
|
22
40
|
end
|
@@ -30,11 +48,8 @@ module Blather #:nodoc:
|
|
30
48
|
|
31
49
|
def run
|
32
50
|
raise 'not setup!' unless setup?
|
33
|
-
|
34
|
-
|
35
|
-
klass = @setup[0].node ? Blather::Stream::Client : Blather::Stream::Component
|
36
|
-
klass.start self, *@setup
|
37
|
-
}
|
51
|
+
klass = @setup[0].node ? Blather::Stream::Client : Blather::Stream::Component
|
52
|
+
@stream = klass.start self, *@setup
|
38
53
|
end
|
39
54
|
|
40
55
|
def register_tmp_handler(id, &handler)
|
@@ -42,62 +57,38 @@ module Blather #:nodoc:
|
|
42
57
|
end
|
43
58
|
|
44
59
|
def register_handler(type, *guards, &handler)
|
60
|
+
check_guards guards
|
45
61
|
@handlers[type] ||= []
|
46
62
|
@handlers[type] << [guards, handler]
|
47
63
|
end
|
48
64
|
|
49
|
-
def status
|
50
|
-
@status.state
|
51
|
-
end
|
52
|
-
|
53
|
-
def status=(state)
|
54
|
-
state, msg, to = state
|
55
|
-
|
56
|
-
status = Stanza::Presence::Status.new state, msg
|
57
|
-
status.to = to
|
58
|
-
@status = status unless to
|
59
|
-
|
60
|
-
write status
|
61
|
-
end
|
62
|
-
|
63
65
|
def write(stanza)
|
64
66
|
stanza.from ||= jid if stanza.respond_to?(:from)
|
65
67
|
@stream.send(stanza) if @stream
|
66
68
|
end
|
67
69
|
|
68
|
-
def write_with_handler(stanza, &
|
70
|
+
def write_with_handler(stanza, &handler)
|
69
71
|
register_tmp_handler stanza.id, &handler
|
70
72
|
write stanza
|
71
73
|
end
|
72
74
|
|
73
|
-
def
|
74
|
-
@stream
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
@state = :ready
|
79
|
-
call_handler_for :ready, nil
|
80
|
-
else
|
81
|
-
r = Stanza::Iq::Roster.new
|
82
|
-
register_tmp_handler r.id do |node|
|
83
|
-
roster.process node
|
84
|
-
@state = :ready
|
85
|
-
write @status
|
86
|
-
call_handler_for :ready, nil
|
87
|
-
end
|
88
|
-
write r
|
75
|
+
def post_init
|
76
|
+
case @stream
|
77
|
+
when Stream::Component then ready!
|
78
|
+
when Stream::Client then client_post_init
|
79
|
+
else raise "Don't know #{@stream.class} stream type. How the hell did this happen!?"
|
89
80
|
end
|
90
81
|
end
|
91
82
|
|
92
|
-
def
|
83
|
+
def close
|
93
84
|
@stream.close_connection_after_writing
|
94
85
|
end
|
95
86
|
|
96
|
-
def
|
97
|
-
EM.stop
|
87
|
+
def unbind
|
88
|
+
EM.stop if EM.reactor_running?
|
98
89
|
end
|
99
90
|
|
100
|
-
def
|
91
|
+
def receive_data(stanza)
|
101
92
|
if handler = @tmp_handlers.delete(stanza.id)
|
102
93
|
handler.call stanza
|
103
94
|
else
|
@@ -107,21 +98,14 @@ module Blather #:nodoc:
|
|
107
98
|
end
|
108
99
|
end
|
109
100
|
|
110
|
-
def call_handler_for(type, stanza)
|
111
|
-
if @handlers[type]
|
112
|
-
@handlers[type].find { |guards, handler| handler.call(stanza) unless guarded?(guards, stanza) }
|
113
|
-
true
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
101
|
protected
|
118
102
|
def setup_initial_handlers
|
119
103
|
register_handler :error do |err|
|
120
104
|
raise err
|
121
105
|
end
|
122
106
|
|
123
|
-
register_handler :iq do |iq|
|
124
|
-
write(StanzaError.new(iq, 'service-unavailable', :cancel).to_node)
|
107
|
+
register_handler :iq, :type => [:get, :set] do |iq|
|
108
|
+
write(StanzaError.new(iq, 'service-unavailable', :cancel).to_node)
|
125
109
|
end
|
126
110
|
|
127
111
|
register_handler :status do |status|
|
@@ -133,8 +117,30 @@ module Blather #:nodoc:
|
|
133
117
|
end
|
134
118
|
end
|
135
119
|
|
120
|
+
def ready!
|
121
|
+
@state = :ready
|
122
|
+
call_handler_for :ready, nil
|
123
|
+
end
|
124
|
+
|
125
|
+
def client_post_init
|
126
|
+
write_with_handler Stanza::Iq::Roster.new do |node|
|
127
|
+
roster.process node
|
128
|
+
write @status
|
129
|
+
ready!
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def call_handler_for(type, stanza)
|
134
|
+
if @handlers[type]
|
135
|
+
@handlers[type].find { |guards, handler| handler.call(stanza) unless guarded?(guards, stanza) }
|
136
|
+
true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
136
140
|
##
|
137
141
|
# If any of the guards returns FALSE this returns true
|
142
|
+
# the logic is reversed to allow short circuiting
|
143
|
+
# (why would anyone want to loop over more values than necessary?)
|
138
144
|
def guarded?(guards, stanza)
|
139
145
|
guards.find do |guard|
|
140
146
|
case guard
|
@@ -158,8 +164,16 @@ module Blather #:nodoc:
|
|
158
164
|
end
|
159
165
|
when Proc
|
160
166
|
!guard.call(stanza)
|
161
|
-
|
162
|
-
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def check_guards(guards)
|
172
|
+
guards.each do |guard|
|
173
|
+
case guard
|
174
|
+
when Array then guard.each { |g| check_guards([g]) }
|
175
|
+
when Symbol, Proc, Hash then nil
|
176
|
+
else raise "Bad guard: #{guard.inspect}"
|
163
177
|
end
|
164
178
|
end
|
165
179
|
end
|
data/lib/blather/client/dsl.rb
CHANGED
@@ -13,14 +13,13 @@ module Blather
|
|
13
13
|
# host and port are optional defaulting to the domain in the JID and 5222 respectively
|
14
14
|
def setup(jid, password, host = nil, port = nil)
|
15
15
|
client.setup(jid, password, host, port)
|
16
|
-
at_exit { client.run }
|
17
16
|
end
|
18
17
|
|
19
18
|
##
|
20
19
|
# Shutdown the connection.
|
21
20
|
# Flushes the write buffer then stops EventMachine
|
22
21
|
def shutdown
|
23
|
-
client.
|
22
|
+
client.close
|
24
23
|
end
|
25
24
|
|
26
25
|
##
|
@@ -37,13 +36,13 @@ module Blather
|
|
37
36
|
|
38
37
|
##
|
39
38
|
# Set current status
|
40
|
-
def
|
39
|
+
def set_status(state = nil, msg = nil)
|
41
40
|
client.status = state, msg
|
42
41
|
end
|
43
42
|
|
44
43
|
##
|
45
44
|
# Direct access to the roster
|
46
|
-
def
|
45
|
+
def my_roster
|
47
46
|
client.roster
|
48
47
|
end
|
49
48
|
|
@@ -51,7 +50,7 @@ module Blather
|
|
51
50
|
# Write data to the stream
|
52
51
|
# Anything that resonds to #to_s can be paseed to the stream
|
53
52
|
def write(stanza)
|
54
|
-
client.write
|
53
|
+
client.write stanza
|
55
54
|
end
|
56
55
|
|
57
56
|
##
|
@@ -68,7 +67,9 @@ module Blather
|
|
68
67
|
end
|
69
68
|
|
70
69
|
##
|
71
|
-
#
|
70
|
+
# Request items or info from an entity
|
71
|
+
# discover (items|info), [jid], [node] do |response|
|
72
|
+
# end
|
72
73
|
def discover(what, who, where, &callback)
|
73
74
|
stanza = Blather::Stanza.class_from_registration(:query, "http://jabber.org/protocol/disco##{what}").new
|
74
75
|
stanza.to = who
|
@@ -78,22 +79,16 @@ module Blather
|
|
78
79
|
write stanza
|
79
80
|
end
|
80
81
|
|
81
|
-
##
|
82
|
-
# PubSub proxy
|
83
|
-
def pubsub
|
84
|
-
client.pubsub
|
85
|
-
end
|
86
|
-
|
87
82
|
##
|
88
83
|
# Checks to see if the method is part of the handlers list.
|
89
84
|
# If so it creates a handler, otherwise it'll pass it back
|
90
85
|
# to Ruby's method_missing handler
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
86
|
+
Blather::Stanza.handler_list.each do |handler_name|
|
87
|
+
module_eval <<-METHOD, __FILE__, __LINE__
|
88
|
+
def #{handler_name}(*args, &callback)
|
89
|
+
handle :#{handler_name}, *args, &callback
|
90
|
+
end
|
91
|
+
METHOD
|
97
92
|
end
|
98
93
|
end #DSL
|
99
94
|
end #Blather
|