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