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 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
- There are 5 different types of guards:
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.3'
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(STDOUT) unless const_defined?(:LOG)
48
+ LOG = Logger.new($stdout) unless const_defined?(:LOG)
52
49
  LOG.level = Logger::INFO
53
50
  end
@@ -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 "Run with #{$0} user@server/resource password [host] [port]"
9
- else
10
- client.setup(*ARGV).run
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
- attr_accessor :jid,
7
- :roster
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
- trap(:INT) { EM.stop }
34
- EM.run {
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, &hanlder)
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 stream_started(stream)
74
- @stream = stream
75
-
76
- #retreive roster
77
- if @stream.is_a?(Stream::Component)
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 stop
83
+ def close
93
84
  @stream.close_connection_after_writing
94
85
  end
95
86
 
96
- def stopped
97
- EM.stop
87
+ def unbind
88
+ EM.stop if EM.reactor_running?
98
89
  end
99
90
 
100
- def call(stanza)
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) if [:set, :get].include?(iq.type)
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
- else
162
- raise "Bad guard: #{guard.inspect}"
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
@@ -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.stop
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 status(state = nil, msg = nil)
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 roster
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(stanza)
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
- def method_missing(method, *args, &block)
92
- if Blather::Stanza.handler_list.include?(method)
93
- handle method, *args, &block
94
- else
95
- super
96
- end
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