butler 1.8.0 → 1.8.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.
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