ponder 0.0.2 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +16 -5
- data/Rakefile +10 -0
- data/examples/echo.rb +2 -2
- data/examples/github_blog.rb +11 -11
- data/examples/redis_last_seen.rb +28 -27
- data/lib/ponder.rb +8 -19
- data/lib/ponder/connection.rb +14 -10
- data/lib/ponder/irc.rb +1 -1
- data/lib/ponder/logger/blind_io.rb +21 -7
- data/lib/ponder/logger/twoflogger.rb +7 -87
- data/lib/ponder/thaum.rb +86 -83
- data/lib/ponder/version.rb +2 -1
- data/ponder.gemspec +32 -28
- data/test/test_async_irc.rb +5 -10
- data/test/test_callback.rb +12 -11
- data/test/test_helper.rb +3 -1
- data/test/test_irc.rb +70 -71
- metadata +16 -14
- data/lib/ponder/delegate.rb +0 -11
- data/lib/ponder/logger/twoflogger18.rb +0 -93
- data/lib/ruby/1.8/string.rb +0 -5
data/README.md
CHANGED
@@ -66,13 +66,13 @@ Besides the configuration for nick, server and port as shown in the **Getting St
|
|
66
66
|
|
67
67
|
* `logging`
|
68
68
|
|
69
|
-
If `logging` is set to `true`, all incoming and outgoing traffic
|
69
|
+
If `logging` is set to `true`, all incoming and outgoing traffic and exceptions will be logged to logs/log.log.
|
70
70
|
|
71
|
-
You can also define
|
71
|
+
You can also define your own logger, use `c.logger = @my_cool_logger` or `c.logger = Logger.new(...)`. Ponder itself just uses the #info and #error methods on the logger.
|
72
72
|
|
73
|
-
You can access the logger
|
73
|
+
You can access the logger instance via `@ponder.logger`, so you could do: `@ponder.logger.info('I did this and that right now')`.
|
74
74
|
|
75
|
-
It defaults to `false`.
|
75
|
+
It defaults to `false`.
|
76
76
|
|
77
77
|
* `reconnect`
|
78
78
|
|
@@ -266,6 +266,17 @@ You can have Before Filters! They are called before each event handling process
|
|
266
266
|
|
267
267
|
This Before Filter will be called, if a channel message with the word "foo" gets in. You can use all other event types (like :query, :kick, ...) as well. Also possible is an array notation like `before_filter([:query, :channel], /foo/) ...`. If you want the filter to work an all event types, you can simply use `:all`. Filters will be called in defining order; first defined, first called. Event specific filters are called before `:all` filters.
|
268
268
|
|
269
|
+
You could use it for authentication/authorization. Example:
|
270
|
+
|
271
|
+
@ponder.before_filter :channel, // do |event_data|
|
272
|
+
if is_an_admin?(event_data[:nick])
|
273
|
+
event_data[:authorized_request] = true
|
274
|
+
else
|
275
|
+
event_data[:authorized_request] = false
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
Now, one can check for `event_data[:authorized_request]` in a callback.
|
269
280
|
### After Filters
|
270
281
|
After Filters work the same way as Before Filters do, just after the actual event handling process. An After Filter does not hinder later After Filters to fire up if it returns `false`. Example:
|
271
282
|
|
@@ -322,7 +333,7 @@ The source can be found at GitHub: [tbuehlmann/ponder](http://github.com/tbuehlm
|
|
322
333
|
You can contact me through [GitHub](http://github.com/tbuehlmann/ "GitHub") and IRC (named tbuehlmann in the Freenode network).
|
323
334
|
|
324
335
|
## Discworld Context
|
325
|
-
So, why all that silly names? Ponder Stibbons? Thaum? Twoflogger (referring to Twoflower),
|
336
|
+
So, why all that silly names? Ponder Stibbons? Thaum? Twoflogger (referring to Twoflower), BlindIo? What's the Mended Drum? Who's the Librarian? Simply put, I freaking enshrine Terry Pratchett's Discworld Novels and there were no better name for this project than Ponder. Ponder Stibbons is the Head of Inadvisably Applied Magic at the Unseen University of Ankh Morpork. He researched the Thaum, like the atom, just for magic. And I just love that character, so there we are. If you're a fan too or want to talk about the Discworld, the framework, whatever, don't hesitate to contact me.
|
326
337
|
|
327
338
|
## License
|
328
339
|
Copyright (c) 2010, 2011 Tobias Bühlmann
|
data/Rakefile
ADDED
data/examples/echo.rb
CHANGED
data/examples/github_blog.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
|
2
|
-
$LOAD_PATH.unshift Pathname.new(__FILE__).dirname.expand_path.join('..', 'lib')
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
2
|
|
4
|
-
require 'ponder'
|
5
3
|
require 'rubygems'
|
4
|
+
require 'ponder'
|
6
5
|
require 'nokogiri'
|
7
6
|
require 'open-uri'
|
8
7
|
|
@@ -10,11 +9,11 @@ require 'open-uri'
|
|
10
9
|
@ponder = Ponder::Thaum.new
|
11
10
|
|
12
11
|
@ponder.configure do |c|
|
13
|
-
c.server
|
14
|
-
c.port
|
15
|
-
c.nick
|
16
|
-
c.verbose
|
17
|
-
c.logging
|
12
|
+
c.server = 'chat.freenode.org'
|
13
|
+
c.port = 6667
|
14
|
+
c.nick = 'Ponder'
|
15
|
+
c.verbose = true
|
16
|
+
c.logging = false
|
18
17
|
end
|
19
18
|
|
20
19
|
@ponder.on :connect do
|
@@ -22,10 +21,11 @@ end
|
|
22
21
|
end
|
23
22
|
|
24
23
|
@ponder.on :channel, /^blog\?$/ do |event_data|
|
25
|
-
doc = Nokogiri::HTML(open('
|
26
|
-
title = doc.xpath('
|
27
|
-
|
24
|
+
doc = Nokogiri::HTML(open('https://github.com/blog'))
|
25
|
+
title = doc.xpath('/html/body/div/div[2]/div/div/ul/li/h2/a')[0].text
|
26
|
+
|
28
27
|
@ponder.message event_data[:channel], "Newest Github Blog Post: #{title}"
|
29
28
|
end
|
30
29
|
|
31
30
|
@ponder.connect
|
31
|
+
|
data/examples/redis_last_seen.rb
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
#
|
4
4
|
# The redis server version needs to be >= 1.3.10 for hash support.
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
require 'ponder'
|
6
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
|
+
|
9
8
|
require 'rubygems'
|
9
|
+
require 'ponder'
|
10
10
|
require 'redis'
|
11
11
|
|
12
12
|
FORMAT = '%Y-%m-%d %H:%M:%S'
|
@@ -17,28 +17,29 @@ class String
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
@
|
20
|
+
@redis = Redis.new(:thread_safe => true)
|
21
21
|
|
22
22
|
def remember(lowercase_nick, nick, user, host, channel, action,
|
23
23
|
action_content)
|
24
|
-
@
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
24
|
+
@redis.hmset(
|
25
|
+
lowercase_nick,
|
26
|
+
'nick', nick,
|
27
|
+
'user', user,
|
28
|
+
'host', host,
|
29
|
+
'channel', channel,
|
30
|
+
'action', action,
|
31
|
+
'action_content', action_content,
|
32
|
+
'updated_at', Time.now.to_i)
|
32
33
|
end
|
33
34
|
|
34
35
|
@ponder = Ponder::Thaum.new
|
35
36
|
|
36
37
|
@ponder.configure do |c|
|
37
|
-
c.server
|
38
|
-
c.port
|
39
|
-
c.nick
|
40
|
-
c.verbose
|
41
|
-
c.logging
|
38
|
+
c.server = 'chat.freenode.org'
|
39
|
+
c.port = 6667
|
40
|
+
c.nick = 'Ponder'
|
41
|
+
c.verbose = true
|
42
|
+
c.logging = false
|
42
43
|
end
|
43
44
|
|
44
45
|
@ponder.on :connect do
|
@@ -76,9 +77,9 @@ end
|
|
76
77
|
"#{event_data[:nick]} #{event_data[:reason]}")
|
77
78
|
end
|
78
79
|
|
79
|
-
def last_seen(nick
|
80
|
-
data = @
|
81
|
-
|
80
|
+
def last_seen(nick)
|
81
|
+
data = @redis.hgetall(nick)
|
82
|
+
|
82
83
|
case data['action']
|
83
84
|
when 'channel'
|
84
85
|
"#{data['nick']} wrote something in #{data['channel']} at #{Time.at(data['updated_at'].to_i).strftime(FORMAT)}."
|
@@ -101,21 +102,21 @@ end
|
|
101
102
|
|
102
103
|
@ponder.on :channel, /^!seen \S+$/ do |event_data|
|
103
104
|
nick = event_data[:message].split(' ')[1].downcase
|
104
|
-
|
105
|
+
|
105
106
|
# wildcards
|
106
107
|
if nick =~ /\*/
|
107
|
-
users = @
|
108
|
+
users = @redis.keys nick.escape_redis
|
108
109
|
results = users.length
|
109
|
-
|
110
|
+
|
110
111
|
case results
|
111
112
|
when 0
|
112
113
|
@ponder.message event_data[:channel], 'No such nick found.'
|
113
114
|
when 1
|
114
|
-
@ponder.message last_seen(users[0])
|
115
|
+
@ponder.message event_data[:channel], last_seen(users[0])
|
115
116
|
when 2..5
|
116
117
|
nicks = []
|
117
118
|
users.each do |user|
|
118
|
-
nicks << @
|
119
|
+
nicks << @redis.hgetall(user)['nick']
|
119
120
|
end
|
120
121
|
nicks = nicks.join(', ')
|
121
122
|
@ponder.message event_data[:channel], "#{results} nicks found (#{nicks})."
|
@@ -123,8 +124,8 @@ end
|
|
123
124
|
@ponder.message event_data[:channel], "Too many results (#{results})."
|
124
125
|
end
|
125
126
|
# single search
|
126
|
-
elsif @
|
127
|
-
msg = last_seen(nick
|
127
|
+
elsif @redis.exists nick
|
128
|
+
msg = last_seen(nick)
|
128
129
|
if online_nick = @ponder.whois(nick)
|
129
130
|
msg = "#{online_nick[:nick]} is online. (#{msg})"
|
130
131
|
end
|
data/lib/ponder.rb
CHANGED
@@ -1,22 +1,11 @@
|
|
1
|
-
|
2
|
-
require 'rubygems'
|
3
|
-
|
4
|
-
$LOAD_PATH.unshift Pathname.new(__FILE__).dirname.expand_path
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
2
|
|
6
3
|
module Ponder
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
require 'ponder/formatting'
|
14
|
-
require 'ponder/logger/blind_io'
|
15
|
-
|
16
|
-
if RUBY_VERSION < '1.9'
|
17
|
-
require 'ruby/1.8/string'
|
18
|
-
require 'ponder/logger/twoflogger18'
|
19
|
-
else
|
20
|
-
require 'ponder/logger/twoflogger'
|
21
|
-
end
|
4
|
+
ROOT = File.dirname($0)
|
5
|
+
|
6
|
+
autoload :Filter, 'ponder/filter'
|
7
|
+
autoload :Formatting, 'ponder/formatting'
|
8
|
+
autoload :Thaum, 'ponder/thaum'
|
9
|
+
autoload :VERSION, 'ponder/version'
|
22
10
|
end
|
11
|
+
|
data/lib/ponder/connection.rb
CHANGED
@@ -1,37 +1,41 @@
|
|
1
|
+
require 'rubygems'
|
1
2
|
require 'eventmachine'
|
2
3
|
|
3
4
|
module Ponder
|
4
5
|
class Connection < EventMachine::Connection
|
5
6
|
include EventMachine::Protocols::LineText2
|
6
|
-
|
7
|
+
|
7
8
|
def initialize(thaum)
|
8
9
|
@thaum = thaum
|
9
10
|
end
|
10
|
-
|
11
|
+
|
11
12
|
def connection_completed
|
12
13
|
@thaum.register
|
13
14
|
end
|
14
|
-
|
15
|
+
|
15
16
|
def unbind
|
16
17
|
@thaum.connected = false
|
17
|
-
@thaum.process_callbacks :disconnect
|
18
|
-
@thaum.
|
18
|
+
@thaum.process_callbacks :disconnect, {}
|
19
|
+
@thaum.logger.info '-- Ponder disconnected'
|
19
20
|
@thaum.console_logger.info '-- Ponder disconnected'
|
20
|
-
|
21
|
+
|
21
22
|
if @thaum.config.reconnect
|
22
|
-
@thaum.
|
23
|
+
@thaum.logger.info "-- Reconnecting in #{@thaum.config.reconnect_interval} seconds"
|
23
24
|
@thaum.console_logger.info "-- Reconnecting in #{@thaum.config.reconnect_interval} seconds"
|
24
|
-
|
25
|
+
|
25
26
|
EventMachine::add_timer(@thaum.config.reconnect_interval) do
|
26
27
|
reconnect @thaum.config.server, @thaum.config.port
|
27
28
|
end
|
28
29
|
else
|
29
30
|
EventMachine::stop_event_loop
|
31
|
+
@thaum.logger.close
|
32
|
+
@thaum.console_logger.close
|
30
33
|
end
|
31
34
|
end
|
32
|
-
|
35
|
+
|
33
36
|
def receive_line(line)
|
34
|
-
@thaum.parse line
|
37
|
+
@thaum.parse line
|
35
38
|
end
|
36
39
|
end
|
37
40
|
end
|
41
|
+
|
data/lib/ponder/irc.rb
CHANGED
@@ -1,11 +1,25 @@
|
|
1
1
|
module Ponder
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
2
|
+
class BlindIo
|
3
|
+
def debug(*args, &block)
|
4
|
+
end
|
5
|
+
|
6
|
+
def info(*args, &block)
|
7
|
+
end
|
8
|
+
|
9
|
+
def warn(*args, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def error(*args, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
def fatal(*args, &block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def unknown(*args, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing(*args, &block)
|
9
22
|
end
|
10
23
|
end
|
11
24
|
end
|
25
|
+
|
@@ -1,93 +1,13 @@
|
|
1
|
-
require '
|
2
|
-
require 'thread'
|
3
|
-
autoload :FileUtils, 'fileutils'
|
1
|
+
require 'logger'
|
4
2
|
|
5
3
|
module Ponder
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
@level = level
|
12
|
-
@time_format = time_format
|
13
|
-
@levels = levels
|
14
|
-
@queue = Queue.new
|
15
|
-
@mutex = Mutex.new
|
16
|
-
@running = false
|
17
|
-
|
18
|
-
define_level_shorthand_methods
|
19
|
-
self.log_dev = destination
|
20
|
-
end
|
21
|
-
|
22
|
-
def start_logging
|
23
|
-
@running = true
|
24
|
-
@thread = Thread.new do
|
25
|
-
begin
|
26
|
-
while @running do
|
27
|
-
write(@queue.pop)
|
28
|
-
end
|
29
|
-
ensure
|
30
|
-
@log_dev.close if @log_dev.is_a?(File)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def stop_logging
|
36
|
-
@running = false
|
37
|
-
end
|
38
|
-
|
39
|
-
def log_dev=(destination)
|
40
|
-
stop_logging
|
41
|
-
|
42
|
-
if destination.is_a?(Pathname)
|
43
|
-
unless destination.exist?
|
44
|
-
unless destination.dirname.directory?
|
45
|
-
FileUtils.mkdir_p destination.dirname
|
46
|
-
end
|
47
|
-
|
48
|
-
File.new(destination, 'w+')
|
49
|
-
end
|
50
|
-
@log_dev = File.open(destination, 'a+')
|
51
|
-
@log_dev.sync = true
|
52
|
-
elsif destination.is_a?(IO)
|
53
|
-
@log_dev = destination
|
54
|
-
else
|
55
|
-
raise TypeError, 'need a Pathname or IO'
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
private
|
60
|
-
|
61
|
-
def define_level_shorthand_methods
|
62
|
-
@levels.each_pair do |level_name, severity|
|
63
|
-
self.class.send(:define_method, level_name, Proc.new { |*messages| queue(severity, *messages) })
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def queue(severity, *messages)
|
68
|
-
raise(ArgumentError, 'Need a message') if messages.empty?
|
69
|
-
raise(ArgumentError, 'Need messages that respond to #to_s') if messages.any? { |message| !message.respond_to?(:to_s) }
|
70
|
-
|
71
|
-
if severity >= @levels[@level]
|
72
|
-
message_hashes = messages.map { |message| {:severity => severity, :message => message} }
|
73
|
-
|
74
|
-
@mutex.synchronize do
|
75
|
-
message_hashes.each do |hash|
|
76
|
-
@queue << hash
|
77
|
-
end
|
78
|
-
end
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
def write(*message_hashes)
|
83
|
-
begin
|
84
|
-
message_hashes.each do |hash|
|
85
|
-
@log_dev.puts "#{@levels.key(hash[:severity])} #{Time.now.strftime(@time_format)} #{hash[:message]}"
|
86
|
-
end
|
87
|
-
rescue => e
|
88
|
-
puts e.message, *e.backtrace
|
89
|
-
end
|
4
|
+
class Twoflogger < Logger
|
5
|
+
def initialize(*args)
|
6
|
+
super(*args)
|
7
|
+
self.formatter = proc do |severity, datetime, progname, msg|
|
8
|
+
"#{severity} #{datetime.strftime('%Y-%m-%d %H:%M:%S')} #{msg}\n"
|
90
9
|
end
|
91
10
|
end
|
92
11
|
end
|
93
12
|
end
|
13
|
+
|