butler 1.8.0 → 1.8.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (57) hide show
  1. data/CHANGELOG +40 -0
  2. data/README +9 -9
  3. data/Rakefile +15 -71
  4. data/bin/botcontrol +151 -146
  5. data/data/butler/dialogs/botcontrol.rb +8 -3
  6. data/data/butler/dialogs/create.rb +18 -18
  7. data/data/butler/dialogs/create_config.rb +8 -0
  8. data/data/butler/dialogs/en/create_config.yaml +2 -0
  9. data/data/butler/dialogs/en/list.yaml +2 -1
  10. data/data/butler/dialogs/info.rb +2 -2
  11. data/data/butler/dialogs/list.rb +13 -8
  12. data/data/butler/plugins/games/countdown.rb +41 -0
  13. data/data/butler/plugins/games/roll.rb +59 -0
  14. data/lib/access.rb +6 -107
  15. data/lib/access/admin.rb +3 -0
  16. data/lib/access/role.rb +37 -2
  17. data/lib/access/savable.rb +5 -0
  18. data/lib/access/user.rb +21 -2
  19. data/lib/access/yamlbase.rb +4 -0
  20. data/lib/butler.rb +4 -4
  21. data/lib/butler/bot.rb +13 -13
  22. data/lib/butler/irc/client.rb +10 -2
  23. data/lib/butler/irc/parser.rb +7 -2
  24. data/lib/butler/irc/parser/commands.rb +24 -7
  25. data/lib/butler/irc/parser/generic.rb +27 -315
  26. data/lib/butler/irc/parser/rfc2812.rb +328 -0
  27. data/lib/butler/irc/socket.rb +1 -1
  28. data/lib/butler/irc/user.rb +13 -0
  29. data/lib/butler/plugin.rb +1 -1
  30. data/lib/butler/plugin/configproxy.rb +4 -4
  31. data/lib/butler/plugins.rb +1 -1
  32. data/lib/butler/version.rb +1 -1
  33. data/lib/configuration.rb +22 -71
  34. data/lib/dialogline.rb +12 -0
  35. data/lib/event.rb +5 -2
  36. data/lib/installer.rb +52 -24
  37. data/lib/iterator.rb +17 -7
  38. data/lib/log.rb +32 -5
  39. data/lib/log/comfort.rb +33 -16
  40. data/lib/log/entry.rb +25 -5
  41. data/lib/log/fakeio.rb +1 -0
  42. data/lib/log/splitter.rb +6 -2
  43. data/lib/ostructfixed.rb +5 -0
  44. data/lib/ruby/exception/detailed.rb +3 -3
  45. data/lib/scheduler.rb +19 -4
  46. data/lib/scriptfile.rb +9 -2
  47. data/lib/string.rb +176 -0
  48. data/lib/string/ascii.rb +31 -0
  49. data/lib/string/mbencoded.rb +79 -0
  50. data/lib/string/sbencoded.rb +77 -0
  51. data/lib/string/utf8.rb +157 -0
  52. data/lib/templater.rb +68 -10
  53. data/lib/w3validator.rb +86 -0
  54. data/test/irc/serverlistings/test_rpl_hiddenhost.txt +60 -0
  55. data/test/test_access.rb +101 -0
  56. data/test/test_configuration.rb +63 -0
  57. metadata +19 -2
@@ -12,6 +12,18 @@ begin
12
12
  require 'readline'
13
13
  rescue LoadError; warn "DialogLine starts without readline support." end
14
14
 
15
+ # == Indexing
16
+ # Author: Stefan Rusterholz
17
+ # Contact: apeiros@gmx.net>
18
+ # Version: 1.0.0
19
+ # Date: 2007-10-12
20
+ #
21
+ # == About
22
+ # Abstract command line dialogs with multilingualization.
23
+ #
24
+ # == Synopsis
25
+ #
26
+ #
15
27
  class DialogLine
16
28
  module VERSION
17
29
  MAJOR = 0
@@ -114,7 +114,8 @@ class Event
114
114
  end
115
115
  end
116
116
 
117
- def initialize(options={}, &block)
117
+ # Don't use directly, use the creation methods.
118
+ def initialize(options={}, &block) # :nodoc:
118
119
  @block = block
119
120
  @start = options.delete(:start) || Time.now
120
121
  @stop = options.delete(:stop)
@@ -128,6 +129,7 @@ class Event
128
129
  @count = 0
129
130
  end
130
131
 
132
+ # Invoke the event
131
133
  def call(*args)
132
134
  @block.call(*args)
133
135
  end
@@ -264,7 +266,8 @@ class Event
264
266
  def to_f
265
267
  seconds_left.to_f
266
268
  end
267
-
269
+
270
+ # Stop current thread until this event is finished.
268
271
  def join
269
272
  @wakeup << Thread.current
270
273
  sleep unless finished?
@@ -1,5 +1,29 @@
1
+ #--
2
+ # Copyright 2007 by Stefan Rusterholz.
3
+ # All rights reserved.
4
+ # See LICENSE.txt for permissions.
5
+ #++
6
+
7
+
8
+
1
9
  require 'rbconfig'
2
10
 
11
+
12
+
13
+ # == Indexing
14
+ # Author: Stefan Rusterholz
15
+ # Contact: apeiros@gmx.net>
16
+ # Version: 1.0.0
17
+ # Date: 2007-10-12
18
+ #
19
+ # == About
20
+ # Tools to install stuff. At the moment that means mostly suggest
21
+ # where stuff might go.
22
+ #
23
+ # == Synopsis
24
+ # x = Iterator.new { |iter| [1,2,3].each { |e| iter.yield(e) } }
25
+ # p x.map { |e| e*2 }
26
+ #
3
27
  class Installer
4
28
  def initialize(app_name)
5
29
  @app_name = app_name
@@ -13,43 +37,47 @@ class Installer
13
37
  end
14
38
  end
15
39
 
16
- def user_data_suggestions
40
+ def home(user=nil)
41
+ File.expand_path("~#{user}")
42
+ end
43
+
44
+ def user_data_suggestions(user=nil)
17
45
  {
18
- :osx => ["#{ENV['HOME']}/Library/Application Support/#{@app_name}", "#{ENV['HOME']}/.#{@app_name}"],
19
- :windows => ["#{ENV['HOME']}/.#{@app_name}"],
20
- :nix => ["#{ENV['HOME']}/.#{@app_name}"],
46
+ :osx => ["#{home(user)}/Library/Application Support/#{@app_name}", "#{home(user)}/.#{@app_name}"],
47
+ :windows => ["#{home(user)}/.#{@app_name}"],
48
+ :nix => ["#{home(user)}/.#{@app_name}"],
21
49
  }[os_class]
22
50
  end
23
51
 
24
- def user_config_suggestions
52
+ def user_config_suggestions(user=nil)
25
53
  {
26
- :osx => ["#{ENV['HOME']}/Library/Preferences/#{@app_name}", "#{ENV['HOME']}/.#{@app_name}/etc"],
27
- :windows => ["#{ENV['HOME']}/.#{@app_name}/etc"],
28
- :nix => ["#{ENV['HOME']}/.#{@app_name}/etc"],
54
+ :osx => ["#{home(user)}/Library/Preferences/#{@app_name}", "#{home(user)}/.#{@app_name}/etc"],
55
+ :windows => ["#{home(user)}/.#{@app_name}/etc"],
56
+ :nix => ["#{home(user)}/.#{@app_name}/etc"],
29
57
  }[os_class]
30
58
  end
31
59
 
32
- def user_documents_suggestions
60
+ def user_documents_suggestions(user=nil)
33
61
  {
34
- :osx => ["#{ENV['HOME']}/Documents"],
62
+ :osx => ["#{home(user)}/Documents"],
35
63
  :windows => [
36
- "#{ENV['HOME']}/My Documents",
37
- "#{ENV['HOME']}/#{ENV['USER']}'s Documents",
38
- "#{ENV['HOME']}/#{ENV['USER'].downcase}'s documents"
64
+ "#{home(user)}/My Documents",
65
+ "#{home(user)}/#{ENV['USER']}'s Documents",
66
+ "#{home(user)}/#{ENV['USER'].downcase}'s documents"
39
67
  ].select { |path| File.directory?(path) },
40
- :nix => ["#{ENV['HOME']}/"],
68
+ :nix => ["#{home(user)}/"],
41
69
  }[os_class]
42
70
  end
43
71
 
44
- def user_pid_suggestions
72
+ def user_pid_suggestions(user=nil)
45
73
  {
46
- :osx => ["#{ENV['HOME']}/Library/Application Support/#{@app_name}/run", "#{ENV['HOME']}/.#{@app_name}/run"],
47
- :windows => ["#{ENV['HOME']}/.#{@app_name}/run"],
48
- :nix => ["#{ENV['HOME']}/.#{@app_name}/run"],
74
+ :osx => ["#{home(user)}/Library/Application Support/#{@app_name}/run", "#{home(user)}/.#{@app_name}/run"],
75
+ :windows => ["#{home(user)}/.#{@app_name}/run"],
76
+ :nix => ["#{home(user)}/.#{@app_name}/run"],
49
77
  }[os_class]
50
78
  end
51
79
 
52
- def shared_data_suggestions
80
+ def shared_data_suggestions(user=nil)
53
81
  {
54
82
  :osx => ["/Library/Application Support/#{@app_name}", "#{Config::CONFIG['datadir']}/#{@app_name}"],
55
83
  :windows => ["#{Config::CONFIG['datadir']}/#{@app_name}"],
@@ -57,19 +85,19 @@ class Installer
57
85
  }[os_class]
58
86
  end
59
87
 
60
- def cli_binary_suggestions
88
+ def cli_binary_suggestions(user=nil)
61
89
  {
62
90
  :osx => [Config::CONFIG['bindir']],
63
91
  :windows => [Config::CONFIG['bindir']],
64
- :nix => [Config::CONFIG['bindir'], "#{ENV['HOME']}/bin"],
92
+ :nix => [Config::CONFIG['bindir'], "#{home(user)}/bin"],
65
93
  }[os_class]
66
94
  end
67
95
 
68
- def gui_binary_suggestions
96
+ def gui_binary_suggestions(user=nil)
69
97
  {
70
- :osx => ["/Applications", ENV['HOME']+"/Applications"],
98
+ :osx => ["/Applications", home(user)+"/Applications"],
71
99
  :windows => [Config::CONFIG['bindir']],
72
- :nix => [Config::CONFIG['bindir'], "#{ENV['HOME']}/bin"],
100
+ :nix => [Config::CONFIG['bindir'], "#{home(user)}/bin"],
73
101
  }[os_class]
74
102
  end
75
103
  end
@@ -6,10 +6,23 @@
6
6
 
7
7
 
8
8
 
9
+ # == Indexing
10
+ # Author: Stefan Rusterholz
11
+ # Contact: apeiros@gmx.net>
12
+ # Version: 1.0.0
13
+ # Date: 2007-10-12
14
+ #
15
+ # == About
16
+ # Similar like Enumarator, enables to create an Enumerable.
17
+ #
18
+ # == Synopsis
19
+ # x = Iterator.new { |iter| [1,2,3].each { |e| iter.yield(e) } }
20
+ # p x.map { |e| e*2 }
21
+ #
9
22
  class Iterator
10
23
  include Enumerable
11
24
 
12
- class Iteration
25
+ class Iteration # :nodoc:
13
26
  def initialize(&block)
14
27
  @block = block
15
28
  end
@@ -18,17 +31,14 @@ class Iterator
18
31
  end
19
32
  end
20
33
 
34
+ # Determine how to iterate, the block most contain the code that
35
+ # is executed to iterate.
21
36
  def initialize(&iterator)
22
37
  @iterator = iterator
23
38
  end
24
39
 
40
+ # Will iterate as defined in Iterator::new
25
41
  def each(&block)
26
42
  @iterator.call(Iteration.new(&block))
27
43
  end
28
44
  end
29
-
30
- if __FILE__ == $0 then
31
- #Iterator.new { |iter| File.open(path) { |fh| while buf = fh.read(8192); iter.yield(buf); end } }
32
- x = Iterator.new { |iter| [1,2,3].each { |e| iter.yield(e) } }
33
- p x.map { |e| e*2 }
34
- end
data/lib/log.rb CHANGED
@@ -18,6 +18,24 @@
18
18
  # $stdout.puts(exception) # same as Log::File#log(exception)
19
19
  # end
20
20
  #
21
+ # == Use-cases
22
+ # === Daemon
23
+ # Since a daemon should not output anything at all, the advice is to create a
24
+ # logger (Log::File e.g.) and assign a Log::Forward to each, $stdout and $stderr.
25
+ # It's suggested to use :info as the default level for $stdout and :warn for
26
+ # $stderr.
27
+ #
28
+ # === Application
29
+ # If you use Log with your application, you most likely want to log to a file.
30
+ # The advice for that is to simply assign a Log::File to $stderr, anything that
31
+ # prints to $stderr is now logged as :warn.
32
+ #
33
+ # === Library
34
+ # With a library you most likely just want Log::Comfort. It adds logging methods
35
+ # and convenience methods to your class. It uses @logger if set, else $stderr to
36
+ # puts a Log::Entry. That way your library has decent logging even if the
37
+ # employing app doesn't use a logger.
38
+ #
21
39
  # == Notes
22
40
  # require 'log/kernel' to get convenience methods in Kernel
23
41
  # it isn't required via 'log' alone to avoid accidental method name clashes.
@@ -50,6 +68,8 @@ module Log
50
68
  end
51
69
  end
52
70
 
71
+
72
+
53
73
  require 'log/comfort'
54
74
  require 'log/converter'
55
75
  require 'log/entry'
@@ -59,10 +79,17 @@ require 'log/file'
59
79
  require 'log/nolog'
60
80
 
61
81
 
82
+
62
83
  if __FILE__ == $0 then
63
- lf = StringIO.new
64
- log = Log::File.new(lf)
65
- $stderr = Log::Forward.new(log, :warn)
66
- warn "foo"
67
- Log::Entry.deserialize(lf.string.split("\n").first)
84
+ begin
85
+ require 'stringio'
86
+ lf = StringIO.new
87
+ log = Log::File.new(lf)
88
+ $stderr = Log::Forward.new(log, :warn)
89
+ warn "foo"
90
+ p Log::Entry.deserialize(lf.string.split("\n").first)
91
+ puts "end"
92
+ rescue => e
93
+ puts e, *e.backtrace
94
+ end
68
95
  end
@@ -22,52 +22,69 @@ require 'log/entry'
22
22
  module Log
23
23
  module Comfort
24
24
  # where data is logged to
25
- attr_accessor :log_device
25
+ attr_accessor :logger
26
+
27
+ # Used for the origin-field in the log
28
+ alias log_origin class
26
29
 
27
30
  # See Log::Message.new
28
31
  # Module::log(*args) is simply: $stderr.puts(Log::Message.new(*args))
29
- def log(text, severity=:info, *args)
30
- (@log_device || $stderr).puts(Log::Entry.new(text.to_str, severity, *args))
32
+ def log(text, severity=:info, origin=nil, data=nil, *flags)
33
+ (@logger || $stderr).puts(
34
+ Log::Entry.new(text.to_str, severity, origin||log_origin, data=nil, *flags)
35
+ )
31
36
  end
32
37
 
33
38
  # See Log::Entry.new to see what arguments are valid.
34
39
  # Module::debug(text, *args) is the same as:
35
40
  # $stderr.puts(Log::Message.new(text, :debug, *args))
36
- def debug(text, *args)
37
- (@log_device || $stderr).puts(Log::Entry.new(text.to_str, :debug, *args))
41
+ def debug(text, origin=nil, data=nil, *flags)
42
+ (@logger || $stderr).puts(
43
+ Log::Entry.new(text.to_str, :debug, origin||log_origin, data=nil, *flags)
44
+ )
38
45
  end
39
-
46
+
40
47
  # See Log::Entry.new to see what arguments are valid.
41
48
  # Module::info(text, *args) is the same as:
42
49
  # $stderr.puts(Log::Message.new(text, :info, *args))
43
- def info(text, *args)
44
- (@log_device || $stderr).puts(Log::Entry.new(text.to_str, :info, *args))
50
+ def info(text, origin=nil, data=nil, *flags)
51
+ (@logger || $stderr).puts(
52
+ Log::Entry.new(text.to_str, :info, origin||log_origin, data=nil, *flags)
53
+ )
45
54
  end
46
55
 
47
56
  # See Log::Entry.new to see what arguments are valid.
48
57
  # Module::warn(text, *args) is the same as:
49
58
  # $stderr.puts(Log::Message.new(text, :warn, *args))
50
- def warn(text, *args)
51
- (@log_device || $stderr).puts(Log::Entry.new(text.to_str, :warn, *args))
59
+ def warn(text, origin=nil, data=nil, *flags)
60
+ (@logger || $stderr).puts(
61
+ Log::Entry.new(text.to_str, :warn, origin||log_origin, data=nil, *flags)
62
+ )
52
63
  end
53
64
 
54
65
  # See Log::Entry.new to see what arguments are valid.
55
66
  # Module::error(text, *args) is the same as:
56
67
  # $stderr.puts(Log::Message.new(text, :error, *args))
57
- def error(text, *args)
58
- (@log_device || $stderr).puts(Log::Entry.new(text.to_str, :error, *args))
68
+ def error(text, origin=nil, data=nil, *flags)
69
+ (@logger || $stderr).puts(
70
+ Log::Entry.new(text.to_str, :error, origin||log_origin, data=nil, *flags)
71
+ )
59
72
  end
60
73
 
61
74
  # See Log::Entry.new to see what arguments are valid.
62
75
  # Module::fail(text, *args) is the same as:
63
76
  # $stderr.puts(Log::Message.new(text, :fail, *args))
64
- def fail(text, *args)
65
- (@log_device || $stderr).puts(Log::Entry.new(text.to_str, :fail, *args))
77
+ def fail(text, origin=nil, data=nil, *flags)
78
+ (@logger || $stderr).puts(
79
+ Log::Entry.new(text.to_str, :fail, origin||log_origin, data=nil, *flags)
80
+ )
66
81
  end
67
82
 
68
- #
83
+ # Exception is special cased, if @logger || $stderr responds to 'exception',
84
+ # the exception is just forwarded, else it uses puts and prints
85
+ # the exception and the backtrace
69
86
  def exception(e)
70
- log = @log_device || $stderr
87
+ log = @logger || $stderr
71
88
  if log.respond_to?(:exception) then
72
89
  log.exception(e)
73
90
  else
@@ -20,20 +20,31 @@ module Log
20
20
  "%s#{RecordSeparator}" \
21
21
  "%s#{RecordSeparator}" \
22
22
  "%s#{RecordSeparator}" \
23
+ "%s#{RecordSeparator}" \
23
24
  "%s"
24
25
 
25
26
  class Entry
27
+ # the value used by #to_s if no format is given
28
+ DefaultFormat = "%{time:%FT%T} [%{severity}]: %{text} in %{origin}"
29
+
26
30
  class <<self
27
31
  attr_accessor :time_format
28
32
 
29
33
  def deserialize(line)
30
- time, severity, text, flagstr, data = line.chomp(RecordTerminator).split(RecordSeparator)
34
+ time, severity, origin, text, flagstr, data = line.chomp(RecordTerminator).split(RecordSeparator)
31
35
  flags = {}
32
36
  flagstr.split(UnitSeparator).each_cons(2) { |key, value|
33
37
  flagstr[key] = value
34
38
  }
35
39
  severity = Integer(severity) rescue severity
36
- new(text, InvSeverity[severity], Marshal.load(Log.unescape(data)), Time.at(time.to_i), flags)
40
+ new(
41
+ text,
42
+ InvSeverity[severity],
43
+ Log.unescape(origin),
44
+ Marshal.load(Log.unescape(data)),
45
+ Time.at(time.to_i),
46
+ flags
47
+ )
37
48
  end
38
49
 
39
50
  def formatter_for(entity, &formatter)
@@ -48,6 +59,10 @@ module Log
48
59
  entry.time.strftime(format || @time_format)
49
60
  end
50
61
 
62
+ def format_origin(entry)
63
+ entry.origin.to_s
64
+ end
65
+
51
66
  def format_severity(entry)
52
67
  entry.severity.to_s
53
68
  end
@@ -72,6 +87,7 @@ module Log
72
87
  @formatter = {
73
88
  "time" => method(:format_time),
74
89
  "severity" => method(:format_severity),
90
+ "origin" => method(:format_origin),
75
91
  "flags" => method(:format_flags),
76
92
  "text" => method(:format_text), #Log.method(:escape),
77
93
  }
@@ -79,12 +95,14 @@ module Log
79
95
 
80
96
  attr_reader :time
81
97
  attr_reader :severity
98
+ attr_reader :origin
82
99
  attr_reader :text
83
100
  attr_reader :flags
84
101
  attr_reader :data
85
- def initialize(text, severity=:info, data=nil, *flags)
102
+ def initialize(text, severity=:info, origin=nil, data=nil, *flags)
86
103
  @time = flags.first.kind_of?(Time) ? flags.shift : Time.now
87
104
  @severity = severity
105
+ @origin = origin.to_s
88
106
  @text = text
89
107
  @data = data
90
108
  @flags = flags.last.kind_of?(Hash) ? flags.pop : {}
@@ -120,6 +138,7 @@ module Log
120
138
  Serialized % [
121
139
  @time,
122
140
  Severity[@severity],
141
+ Log.escape(@origin),
123
142
  Log.escape(@text),
124
143
  @flags.map.join(UnitSeparator),
125
144
  Log.escape(Marshal.dump(@data))
@@ -127,7 +146,7 @@ module Log
127
146
  end
128
147
 
129
148
  def to_s(format=nil)
130
- format ||= "%{time:%FT%T} [%{severity}]: %{text}"
149
+ format ||= DefaultFormat
131
150
  format.gsub(/%(%|\{[^}]+\})/) { |match|
132
151
  if match == "%%" then
133
152
  "%"
@@ -139,10 +158,11 @@ module Log
139
158
  end
140
159
 
141
160
  def inspect
142
- "#<%s %s %s %s flags=%s data=%s>" % [
161
+ "#<%s %s %s %s %s flags=%s data=%s>" % [
143
162
  self.class,
144
163
  @time.strftime("%FT%T"),
145
164
  @severity,
165
+ @origin,
146
166
  @text.inspect,
147
167
  @flags.inspect,
148
168
  @data.inspect