Auto 4.0.0.alpha.1-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.yardopts +7 -0
  2. data/Gemfile +19 -0
  3. data/LICENSE.md +31 -0
  4. data/README.md +109 -0
  5. data/Rakefile +41 -0
  6. data/bin/auto +110 -0
  7. data/bin/auto-conf +45 -0
  8. data/conf/example.json +100 -0
  9. data/conf/example.yml +125 -0
  10. data/docs/Contributing.md +77 -0
  11. data/docs/Events.md +103 -0
  12. data/docs/Todo.md +21 -0
  13. data/docs/Upgrade.md +16 -0
  14. data/ext/dsl_base.c +49 -0
  15. data/ext/libauto/auto.h +20 -0
  16. data/ext/libauto/extconf.rb +16 -0
  17. data/ext/libauto/libauto.c +29 -0
  18. data/ext/libauto/libauto.h +28 -0
  19. data/ext/libauto/logger.c +177 -0
  20. data/ext/libauto/logger.h +44 -0
  21. data/lib/auto.rb +43 -0
  22. data/lib/auto/api.rb +7 -0
  23. data/lib/auto/api/events.rb +166 -0
  24. data/lib/auto/api/object.rb +29 -0
  25. data/lib/auto/api/plugin.rb +155 -0
  26. data/lib/auto/api/timers.rb +93 -0
  27. data/lib/auto/bot.rb +338 -0
  28. data/lib/auto/config.rb +181 -0
  29. data/lib/auto/configure.rb +410 -0
  30. data/lib/auto/configure/shell.rb +154 -0
  31. data/lib/auto/dsl/base.rb +74 -0
  32. data/lib/auto/dsl/irc.rb +13 -0
  33. data/lib/auto/irc.rb +8 -0
  34. data/lib/auto/irc/common.rb +63 -0
  35. data/lib/auto/irc/library.rb +89 -0
  36. data/lib/auto/irc/object/channel.rb +21 -0
  37. data/lib/auto/irc/object/entity.rb +90 -0
  38. data/lib/auto/irc/object/message.rb +99 -0
  39. data/lib/auto/irc/object/user.rb +139 -0
  40. data/lib/auto/irc/protocol.rb +164 -0
  41. data/lib/auto/irc/protocol/numerics.rb +60 -0
  42. data/lib/auto/irc/sasl/diffie_hellman.rb +36 -0
  43. data/lib/auto/irc/sasl/mech.rb +15 -0
  44. data/lib/auto/irc/sasl/mech/dh_blowfish.rb +83 -0
  45. data/lib/auto/irc/sasl/mech/plain.rb +39 -0
  46. data/lib/auto/irc/server.rb +301 -0
  47. data/lib/auto/irc/state/channel_manager.rb +6 -0
  48. data/lib/auto/irc/state/support.rb +142 -0
  49. data/lib/auto/irc/state/user_manager.rb +6 -0
  50. data/lib/auto/irc/std/commands.rb +99 -0
  51. data/lib/auto/irc/std/numerics.rb +216 -0
  52. data/lib/auto/rubyext/integer.rb +25 -0
  53. data/lib/auto/rubyext/string.rb +10 -0
  54. data/lib/auto/version.rb +18 -0
  55. data/lib/libauto.so +0 -0
  56. data/spec/api_events_spec.rb +68 -0
  57. data/spec/config_json_spec.rb +116 -0
  58. data/spec/config_other_spec.rb +29 -0
  59. data/spec/config_yaml_spec.rb +136 -0
  60. data/spec/helper.rb +19 -0
  61. data/spec/irc_object_entity_spec.rb +51 -0
  62. data/spec/logger_spec.rb +30 -0
  63. data/spec/plugin_base_spec.rb +35 -0
  64. data/spec/timers_spec.rb +42 -0
  65. metadata +238 -0
@@ -0,0 +1,181 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (LICENSE.md).
3
+ autoload :JSON, 'json'
4
+ autoload :Psych, 'psych'
5
+ require 'libauto'
6
+
7
+ # Namespace: Auto
8
+ module Auto
9
+
10
+ # A class which provides a functional, simple configuration interface. It is
11
+ # very useful, as it supports both YAML and JSON for configuration. It
12
+ # determines which is appropriate by looking at file extensions.
13
+ #
14
+ # @api Auto
15
+ # @since 4.0.0
16
+ # @author noxgirl
17
+ # @author swarley
18
+ #
19
+ # @!attribute conf
20
+ # @return [Hash{}] This is the hash which contains the data parsed from
21
+ # the configuration file.
22
+ # @see #[]
23
+ #
24
+ # @!attribute type
25
+ # @return [Symbol] Type of configuration: +:yaml+ or +:json+.
26
+ class Config
27
+
28
+ attr_reader :conf, :type
29
+
30
+ # Produce a new instance, and attempt to parse.
31
+ #
32
+ # @param [String] filepath Path to configuration file.
33
+ #
34
+ # @raise [ConfigError] If the file extension is not recognized (should be +.yml+ or +.json+).
35
+ def initialize filepath
36
+
37
+ $m.debug("Trying to initialize configuration from '#{filepath}'...")
38
+
39
+ # Set path to file.
40
+ @path = filepath
41
+
42
+ # Determine the type: YAML or JSON.
43
+ case File.extname(filepath)
44
+ when ".yml"
45
+ @type = :yaml
46
+ when ".json"
47
+ @type = :json
48
+ else
49
+ raise ConfigError, "Unknown file type on #{filepath}."
50
+ end
51
+
52
+ # Process the configuration.
53
+ parse!
54
+
55
+ end
56
+
57
+ # Rehash the configuration.
58
+ #
59
+ # If an error occurs, it will revert the configuration to its prior state
60
+ # so that everything can continue to function.
61
+ def rehash!
62
+
63
+ $m.debug("Configuration file is rehashing.")
64
+
65
+ # Keep the old configuration in case of issues.
66
+ oldconf = @conf
67
+ @conf = {}
68
+
69
+ # Rehash
70
+ parse!
71
+
72
+ # Ensure it really succeeded.
73
+ if @conf.empty? or @conf.class != Hash
74
+ # Nope. Restore old configuration.
75
+ @conf = oldconf
76
+ $m.error("Failed to rehash the configuration file (parser produced empty config)! Reverting to old configuration.")
77
+ return 0
78
+ end
79
+
80
+ # bot:onRehash
81
+ $m.events.call('bot:onRehash')
82
+
83
+ # This rescue is applicable to anything that happens in here. Since if it is reached, there really was an error in general.
84
+ rescue => e
85
+ $m.error("Failed to rehash configuration file! Reverting to old configuration.", false, e.backtrace)
86
+ @conf = oldconf
87
+ return 0
88
+ end
89
+
90
+ # Return value of @conf[key].
91
+ #
92
+ # @return [Object] Value of @conf[key].
93
+ # @see @conf
94
+ def [] key
95
+ @conf[key]
96
+ end
97
+
98
+ #######
99
+ private
100
+ #######
101
+
102
+ # A constant for detecting comments and strings in JSON files
103
+ COMMENT_REGEXP = %r{(?:/\'(?:[^\'\\]|\\.)*\')|(?:\"(?:[^\"\\]|\\.)*\")|(?://.*\n)|(?:/\*(?m-ix:.)+?\*/)}
104
+
105
+ # Strip comments from JSON configuration text. This will not preserve
106
+ # the string passed to it.
107
+ #
108
+ # @argument [String] text The configuration data to strip.
109
+ #
110
+ # @return [String] The stripped configuration text.
111
+ def strip_js_comments! text
112
+ # Output string
113
+ out = ""
114
+ until (match_data = text.match(COMMENT_REGEXP)).nil?
115
+ off = match_data.offset(0)
116
+ # Check to see if it starts with /, if so then return up to the lower limit, otherwise to the upper.
117
+ out << text[0...off[(match_data.to_s[0] != '/') ? 1 : 0]]
118
+ # Delete the text that we were dealing with.
119
+ text[0...off[1]] = ''
120
+ end
121
+ # Append anything left
122
+ text = (out << text)
123
+ end
124
+
125
+ # The same as strip_js_comments! with original string preservation.
126
+ # @see strip_js_comments!
127
+ def strip_js_comments text
128
+ # Preserve
129
+ strip_js_comments!(text.clone)
130
+ end
131
+
132
+ # Parse the configuration file, and output the data to {#x}.
133
+ #
134
+ # @raise [ConfigError] If the file does not exist.
135
+ # @raise [ConfigError] If the file cannot be processed.
136
+ def parse!
137
+
138
+ # Ensure foremost that the configuration file exists.
139
+ unless File.exists? @path
140
+ raise ConfigError, "Configuration file '#@path' does not exist!"
141
+ end
142
+
143
+ # Get the data from the file.
144
+ f = File.open(@path)
145
+ data = f.read
146
+ f.close
147
+
148
+ conf = {}
149
+ # JSON
150
+ if @type == :json
151
+
152
+ # Strip comments out of the data.
153
+ data = strip_js_comments!(data)
154
+
155
+ # Process the JSON.
156
+ begin
157
+ conf = JSON.parse(data)
158
+ rescue => e
159
+ raise ConfigError, "Failed to process the JSON in '#@path'", e.backtrace
160
+ end
161
+
162
+ else # Must be YAML
163
+
164
+ # Process the YAML.
165
+ begin
166
+ conf = Psych.load data
167
+ rescue => e
168
+ raise ConfigError, "Failed to process the YAML in '#@path'", e.backtrace
169
+ end
170
+
171
+ end
172
+
173
+ @conf = conf
174
+
175
+ end # def parse
176
+
177
+ end # class Config
178
+
179
+ end # module Auto
180
+
181
+ # vim: set ts=4 sts=2 sw=2 et:
@@ -0,0 +1,410 @@
1
+ # Copyright (c) 2013, Autumn Perrault, et al. All rights reserved.
2
+ # This free software is distributed under the FreeBSD license (LICENSE.md).
3
+
4
+ require 'colored'
5
+ require 'highline'
6
+ require 'yaml'
7
+
8
+ $S = '>>>'.blue
9
+
10
+
11
+ # Check To make sure that we have a decent ruby version. There is no reason to support a
12
+ # version that has reached EOL.
13
+ if RUBY_VERSION < "1.9"
14
+ puts <<-EOM
15
+ The version of ruby that you are using is out dated. Please upgrade to a version of at least
16
+ 1.9.1 to run auto. The ruby source code is located here http://ruby-lang.org/en/downloads
17
+ EOM
18
+ end
19
+
20
+ # namespace Auto
21
+ module Auto
22
+
23
+ # A library for configuration generation. It depends upon the highline gem.
24
+ #
25
+ # @version 1.02
26
+ # @author swarley
27
+ # @author noxgirl
28
+ #
29
+ # @!attribute hl
30
+ # @return [HighLine] HighLine instance.
31
+ #
32
+ # @!attribute conf
33
+ # @return [Hash{}] The configuration hash.
34
+ class Configure
35
+
36
+ autoload :Shell, 'auto/configure/shell'
37
+ # Load the shell.
38
+ def shell
39
+ Auto::Configure::Shell.new(@hl)
40
+ end
41
+
42
+ VERSION = '1.02'.freeze
43
+ AUTODIR = File.join(Dir.home, '.config', 'autobot')
44
+
45
+ attr_accessor :hl, :conf
46
+
47
+ # Produce a new instance of Auto::Configure.
48
+ def initialize
49
+
50
+ # Produce a new instance of HighLine.
51
+ @hl = HighLine.new
52
+ # Prepare for configuration.
53
+ @conf = Hash.new
54
+
55
+ end
56
+
57
+ # Initiate configuration.
58
+ def generate
59
+
60
+ greeting = <<-EOM
61
+ Greetings! ^.^
62
+
63
+ I am going to assist you in configuring your installation of Auto. :) I suggest
64
+ that, if you're not already reading it, you consult the installation guide:
65
+ https://github.com/Auto/Auto/wiki/Install-Guide
66
+
67
+ When specifying lists, separate elements by commas.
68
+
69
+ Remember, if you need additional help, you're free to use the mailing list at
70
+ https://groups.google.com/group/autobot-talk, or to join the official IRC
71
+ channel at #auto on irc.freenode.net. :)
72
+
73
+ Let us begin!
74
+ EOM
75
+ puts greeting.yellow.bold
76
+
77
+ conf_libraries
78
+ conf_database
79
+
80
+ dump
81
+
82
+ end
83
+
84
+ # Configure libraries.
85
+ def conf_libraries
86
+ puts ">>> Currently, the only available library is the IRC library. I will load this automatically.".cyan.bold
87
+ @conf['libraries'] = ['irc']
88
+
89
+ conf_irclib
90
+ end
91
+
92
+ # Configure the IRC library.
93
+ def conf_irclib
94
+
95
+ # Create the configuration hash.
96
+ @conf['irc'] = {}
97
+
98
+ # Add the first server.
99
+ conf_irc_add_server
100
+
101
+ # Add subsequent servers.
102
+ another = @hl.agree("#$S Would you like to add another IRC server? ") { |q| q.default = 'n' }
103
+ while another
104
+ conf_irc_add_server
105
+ another = @hl.agree("#$S Would you like to add another IRC server? ") { |q| q.default = 'n' }
106
+ end
107
+
108
+ end
109
+
110
+ # Add an IRC server.
111
+ def conf_irc_add_server
112
+
113
+ # We need a name.
114
+ name = @hl.ask("#$S What is the name of this IRC server? ")
115
+ while @conf['irc'].include? name
116
+ puts "You've already specified that server. Use a different name.".red.bold
117
+ name = @hl.ask("#$S What is the name of this IRC server?")
118
+ end
119
+
120
+ # We need an address.
121
+ address = @hl.ask("#$S What is the address of <%= color('#{name}', :blue, :bold) %>? ")
122
+
123
+ # And a port.
124
+ port = @hl.ask("#$S What is the port of <%= color('#{name}', :blue, :bold) %>? ", Integer) { |q| q.default = 6667 }
125
+
126
+ # Does it use SSL?
127
+ ssl = @hl.agree("#$S Does <%= color('#{address}:#{port}', :blue, :bold) %> use SSL? ") { |q| q.default = 'n' }
128
+
129
+ # What nickname(s) should we use?
130
+ nicks = @hl.ask("#$S What nicknames should I use on <%= color('#{name}', :blue, :bold) %> (list in descending priority)? ",
131
+ ->(str) { str.split(/,\s*/) }) { |q| q.default = 'auto' }
132
+ nicksvalid = true
133
+ nicks.each { |n| nicksvalid = false unless n =~ /^[\w\d\[\]\{\}\^\-\_\`]+$/ }
134
+ until nicksvalid
135
+ puts "You entered an invalid nickname. Try again.".red.bold
136
+ nicks = @hl.ask("#$S What nicknames should I use on <%= color('#{name}', :blue, :bold) %> (list in descending priority)? ",
137
+ ->(str) { str.split(/,\s*/) }) { |q| q.default = 'auto' }
138
+ nicksvalid = true
139
+ nicks.each { |n| nicksvalid = false unless n =~ /^[\w\d\[\]\{\}\^\-\_\`]+$/ }
140
+ end
141
+
142
+ # What username?
143
+ user = @hl.ask("#$S What username should I use on <%= color('#{name}', :blue, :bold) %>? ") { |q| q.default = 'auto' }
144
+
145
+ # What GECOS?
146
+ gecos = @hl.ask("#$S What real name or GECOS should I use on <%= color('#{name}', :blue, :bold) %>? ") { |q| q.default = 'Auto (http://auto.autoproj.org)' }
147
+
148
+ # Save the data.
149
+ @conf['irc'][name] = {
150
+ 'address' => address,
151
+ 'port' => port,
152
+ 'useSSL' => ssl,
153
+ 'nickname' => nicks,
154
+ 'username' => user,
155
+ 'realName' => gecos
156
+ }
157
+
158
+ # Should we use SASL?
159
+ sasl = @hl.agree("#$S Should I use SASL to authenticate with services on <%= color('#{name}', :blue, :bold) %>? ") { |q| q.default = 'n' }
160
+
161
+ if sasl
162
+
163
+ sasl_user = @hl.ask("#$S What username (i.e. accountname) should I use in SASL authentication? ")
164
+ sasl_pass = @hl.ask("#$S What is the password for <%= color('#{sasl_user}', :blue, :bold) %>? ") { |q| q.echo = false }
165
+ sasl_to = @hl.ask("#$S After how many seconds should SASL authentication time out? ", Integer) { |q| q.default = 15 }
166
+
167
+ @conf['irc'][name]['SASL'] = {
168
+ 'username' => sasl_user,
169
+ 'password' => sasl_pass,
170
+ 'timeout' => sasl_to,
171
+ }
172
+
173
+ else
174
+ # Perhaps NickServ or some other service?
175
+ auth = @hl.agree("#$S OK. Should I identify with a service (e.g. NickServ)? ") { |q| q.default = 'n' }
176
+
177
+ if auth
178
+
179
+ service = @hl.ask("#$S What service should I message? ") { |q| q.default = 'NickServ' }
180
+ command = @hl.ask("#$S What command should I use to identify? ") { |q| q.default = 'IDENTIFY' }
181
+ password = @hl.ask("#$S What password should I use? ") { |q| q.echo = false }
182
+
183
+ @conf['irc'][name]['nickIdentify'] = {
184
+ 'service' => service,
185
+ 'command' => command,
186
+ 'password' => password
187
+ }
188
+
189
+ end
190
+
191
+ end
192
+
193
+ # Setup autojoin.
194
+ conf_irc_autojoin name
195
+
196
+ end
197
+
198
+ # Configure autojoin.
199
+ #
200
+ # @param [String] name Name of IRC server.
201
+ def conf_irc_autojoin(name)
202
+ another = @hl.agree("#$S Should I automatically join a channel on <%= color('#{name}', :blue, :bold) %>? ") { |q| q.default = 'y' }
203
+ @conf['irc'][name]['autojoin'] = []
204
+
205
+ while another
206
+ @conf['irc'][name]['autojoin'] << conf_irc_add_channel
207
+ another = @hl.agree("#$S Should I automatically join another channel? ") { |q| q.default = 'n' }
208
+ end
209
+ end
210
+
211
+ # Add an IRC channel.
212
+ #
213
+ # @param [String] server The name of the IRC server.
214
+ def conf_irc_add_channel
215
+
216
+ # What's it called?
217
+ name = @hl.ask("#$S What is the name of the channel? ") { |q| q.default = '#auto' }
218
+
219
+ # Does it use a key?
220
+ usekey = @hl.agree("#$S Does <%= color('#{name}', :blue, :bold) %> use a key (+k)? ") { |q| q.default = 'n' }
221
+ key = nil
222
+
223
+ if usekey
224
+ key = @hl.ask("#$S What is the key? ")
225
+ end
226
+
227
+ {'name' => name, 'key' => key}
228
+ end
229
+
230
+ # Configure the database.
231
+ def conf_database
232
+ @conf['database'] = Hash.new
233
+
234
+ msg = <<-eom
235
+ >> Auto supports three database management systems: SQLite 3, MySQL, and Postgres.
236
+ >> If you use SQLite 3, you need the 'sqlite3' gem.
237
+ >> If you use MySQL, you need the 'mysqlplus' gem.
238
+ >> If you use Postgres, you need the 'pg' gem.
239
+ >> See https://github.com/Auto/Auto/wiki/Install-Guide for more information.
240
+ eom
241
+ puts msg.cyan
242
+
243
+ type = @hl.ask("#$S What database management system should I use? (sqlite, mysql, or postgres) ") do |q|
244
+ q.default = 'sqlite'
245
+ q.validate = /^(sqlite|mysql|postgres)$/
246
+ end
247
+ @conf['database']['type'] = type
248
+
249
+ if type == 'sqlite'
250
+
251
+ file = @hl.ask("#$S What should be the filename of the database? (relative to <%= color('#{AUTODIR}', :blue, :bold) %>) ") do |q|
252
+ q.default = File.join AUTODIR, 'auto.db'
253
+ end
254
+
255
+ unless Dir.exists File.dirname(file)
256
+ puts "Warning: Directory #{File.dirname(file)} does not exist.".red.bold
257
+ end
258
+
259
+ @conf['database']['name'] = file
260
+
261
+ else # mysql and pg
262
+
263
+ address = @hl.ask("#$S What is the host address of the <%= color('#{type}', :blue, :bold) %> server? ") { |q| q.default = 'localhost' }
264
+ name = @hl.ask("#$S What is the database name on the <%= color('#{type}', :blue, :bold) %> server? ") { |q| q.default = 'auto' }
265
+ username = @hl.ask("#$S What username should I use to connect to <%= color('#{type}', :blue, :bold) %> server? ") { |q| q.default = 'auto' }
266
+ password = @hl.ask("#$S What is the password for <%= color('#{username}', :blue, :bold) %>? ") { |q| q.echo = false }
267
+
268
+ @conf['database'].merge!({
269
+ 'address' => address,
270
+ 'name' => name,
271
+ 'username' => username,
272
+ 'password' => password
273
+ })
274
+
275
+ end
276
+
277
+ end
278
+
279
+ # Dump configuration.
280
+ def dump
281
+
282
+ # A sanity check.
283
+ if @conf.empty?
284
+ puts "Configuration is inexplicably empty: aborting.".red.bold
285
+ exit 1
286
+ end
287
+
288
+ # Produce file.
289
+ data = <<-EOD
290
+ # Configuration file generated by auto-conf
291
+ # version #{Auto::Configure::VERSION}
292
+ # at #{Time.now}
293
+
294
+ #{YAML.dump(@conf)}
295
+ EOD
296
+
297
+ # Produce message.
298
+ final = <<-EOM
299
+ OK! Your configuration file is ready! :D
300
+
301
+ I just need to know one last thing: where to write this fabulous configuration.
302
+
303
+ By default, I will write this to <YOUR_HOME_DIRECTORY>/.config/autobot/auto.yml,
304
+ which is perfect for you if you're using the 'autobot' gem, because this is the
305
+ standard file for which the gem will look when executed.
306
+
307
+ However, if you are running a standalone installation, you probably want to
308
+ write this to your conf/ directory as auto.yml (if you're running #$0 from the
309
+ main directory, "conf/auto.yml"), as the standalone Auto will look for that
310
+ when it is executed.
311
+
312
+ In any event, you are free to write this anywhere you please. Just remember
313
+ that if it is not a default path, you must specify it when running Auto:
314
+
315
+ $ auto --config=path/to/config/file.yml
316
+
317
+ Caution: The specified file will be overwritten if it already exists.
318
+ EOM
319
+ puts final.green.bold
320
+
321
+ # Save our directories of interest into easily accessible variables.
322
+ configdir = File.join(Dir.home, '.config')
323
+
324
+ # Ensure that said directories exist regardless of any other conditions.
325
+ unless Dir.exists? configdir
326
+ puts "~ Creating missing directory #{configdir}".magenta
327
+ Dir.mkdir configdir
328
+ end
329
+ unless Dir.exists? AUTODIR
330
+ puts "~ Creating missing directory #{AUTODIR}".magenta
331
+ Dir.mkdir AUTODIR
332
+ end
333
+
334
+ # Ask for a path.
335
+ path = @hl.ask("#$S To where should the configuration be written? ", String) do |q|
336
+
337
+ # Default is ~/.config/autobot/auto.yml
338
+ q.default = File.join(autodir, 'auto.yml')
339
+
340
+ # A proc to validate the path
341
+ q.validate = proc do |path|
342
+ return false unless path =~ /\.yml$/ # it should end in .yml
343
+ return false unless Dir.exists? File.dirname(path) # the directory should exist
344
+ # We should be able to write to the file:
345
+ begin
346
+ File.open(path, 'w') { |io| io.puts "# Configuration file generated by auto-conf" }
347
+ rescue => e
348
+ return false
349
+ end
350
+ true
351
+ end
352
+
353
+ # A proc to be called in the event of an invalid path
354
+ q.responses[:not_valid] = proc do
355
+
356
+ # Choose an emergency file in ~/.config/autobot/ to which to save.
357
+ emerg_file = File.join(autodir, "auto.yml.#{Time.now.strftime('%s')}")
358
+ puts "Invalid path! Attempting to write to #{emerg_file}.....".red
359
+ File.open(path, 'w') do |io|
360
+ io.write data
361
+ end
362
+
363
+ end
364
+
365
+ end # ask()
366
+
367
+ File.open(path, 'w') { |io| io.write data } # Dump the data into the file.
368
+
369
+ # We're done.
370
+ puts "I have successfully written your configuration file to #{path}. Thank you for using Auto.".green.bold
371
+
372
+ end
373
+
374
+ end # class Configure
375
+
376
+ end # module Auto
377
+
378
+ # This will fix a certain undesirable output.
379
+ #
380
+ # HighLine::Question#append_default appends an ugly manifestation of the
381
+ # default answer.
382
+ #
383
+ # This destroys that provided by HighLine::Question and in lieu uses a prettier
384
+ # one.
385
+ class HighLine
386
+ class Question
387
+ def append_default
388
+ str = ''
389
+ if @default == 'y'
390
+ str = "<%= color('Y', :green) %>/<%= color('n', :red) %>"
391
+ elsif @default == 'n'
392
+ str = "<%= color('y', :green) %>/<%= color('N', :red) %>"
393
+ else
394
+ str = "<%= color('#@default', :bold) %>"
395
+ end
396
+
397
+ if @question =~ /([\t ]+)\Z/
398
+ @question << "[#{str}]#{$1}"
399
+ elsif @question == ""
400
+ @question << "[#{str}] "
401
+ elsif @question[-1, 1] == "\n"
402
+ @question[-2, 0] = " [#{str}]"
403
+ else
404
+ @question << " [#{str}]"
405
+ end
406
+ end
407
+ end
408
+ end
409
+
410
+ # vim: set ts=4 sts=2 sw=2 et: