cowtech-lib 1.9.8.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,3 +1,9 @@
1
- source 'http://rubygems.org'
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the cowtech-lib gem. Copyright (C) 2011 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
2
6
 
3
- gem 'jeweler'
7
+ source "http://rubygems.org"
8
+
9
+ gem "jeweler"
data/README.rdoc CHANGED
@@ -15,5 +15,5 @@ http://github.com/ShogunPanda/cowtech-lib
15
15
 
16
16
  == Copyright
17
17
 
18
- Copyright (c) 2011 Shogun. See LICENSE.txt for further details.
19
-
18
+ Copyright (C) 2011 and above Shogun <shogun_panda@me.com>.
19
+ Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
data/Rakefile CHANGED
@@ -1,17 +1,23 @@
1
- require 'rubygems'
2
- require 'jeweler'
1
+ # encoding: utf-8
2
+ #
3
+ # This file is part of the cowtech-lib gem. Copyright (C) 2011 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
+ #
6
+
7
+ require "rubygems"
8
+ require "jeweler"
3
9
  require "./lib/cowtech-lib/version.rb"
4
10
 
5
11
  Jeweler::Tasks.new do |gem|
6
- gem.name = "cowtech-lib"
7
- gem.homepage = "http://github.com/ShogunPanda/cowtech-lib"
8
- gem.license = "MIT"
9
- gem.summary = %Q{A general purpose utility library.}
10
- gem.description = %Q{A general purpose utility library.}
11
- gem.email = "shogun_panda@me.com"
12
- gem.authors = ["Shogun"]
13
- gem.version = Cowtech::Lib::Version::STRING
14
- gem.add_dependency 'open4'
12
+ gem.name = "cowtech-lib"
13
+ gem.homepage = "http://github.com/ShogunPanda/cowtech-lib"
14
+ gem.license = "MIT"
15
+ gem.summary = %Q{A general purpose utility library.}
16
+ gem.description = %Q{A general purpose utility library.}
17
+ gem.email = "shogun_panda@me.com"
18
+ gem.authors = ["Shogun"]
19
+ gem.version = Cowtech::Lib::Version::STRING
20
+ gem.add_dependency "open4"
15
21
  end
16
22
 
17
23
  Jeweler::RubygemsDotOrgTasks.new
data/cowtech-lib.gemspec CHANGED
@@ -5,21 +5,19 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "cowtech-lib"
8
- s.version = "1.9.8.1"
8
+ s.version = "2.0.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Shogun"]
12
- s.date = "2012-01-11"
12
+ s.date = "2012-04-16"
13
13
  s.description = "A general purpose utility library."
14
14
  s.email = "shogun_panda@me.com"
15
15
  s.extra_rdoc_files = [
16
- "LICENSE.txt",
17
16
  "README.rdoc"
18
17
  ]
19
18
  s.files = [
20
19
  ".document",
21
20
  "Gemfile",
22
- "LICENSE.txt",
23
21
  "README.rdoc",
24
22
  "Rakefile",
25
23
  "cowtech-lib.gemspec",
@@ -33,7 +31,7 @@ Gem::Specification.new do |s|
33
31
  s.homepage = "http://github.com/ShogunPanda/cowtech-lib"
34
32
  s.licenses = ["MIT"]
35
33
  s.require_paths = ["lib"]
36
- s.rubygems_version = "1.8.11"
34
+ s.rubygems_version = "1.8.15"
37
35
  s.summary = "A general purpose utility library."
38
36
 
39
37
  if s.respond_to? :specification_version then
data/lib/cowtech-lib.rb CHANGED
@@ -1,33 +1,15 @@
1
1
  # encoding: utf-8
2
2
  #
3
- # cowtech-lib
4
- # Author: Shogun <shogun_panda@me.com>
5
- # Copyright © 2011 and above Shogun
6
- # Released under the MIT License, which follows.
7
- #
8
- # This program is free software; you can redistribute it and/or
9
- # modify it under the terms of the GNU General Public License
10
- # as published by the Free Software Foundation; either version 2
11
- # of the License, or (at your option) any later version.
12
- #
13
- # This program is distributed in the hope that it will be useful,
14
- # but WITHOUT ANY WARRANTY; without even the implied warranty of
15
- # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
- # GNU General Public License for more details.
17
- #
18
- # You should have received a copy of the GNU General Public License
19
- # along with this program; if not, write to the Free Software
20
- # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21
- #
22
- # A copy of the GNU General Public License is available at: http://www.gnu.org/licenses/gpl.txt
3
+ # This file is part of the cowtech-lib gem. Copyright (C) 2011 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
23
5
  #
24
6
 
25
7
  module Cowtech
26
- module Lib
27
- autoload :Version, 'cowtech-lib/version'
28
- autoload :Console, 'cowtech-lib/console'
29
- autoload :OptionParser, 'cowtech-lib/option_parser'
30
- autoload :Shell, 'cowtech-lib/shell'
31
- autoload :Script, 'cowtech-lib/script'
32
- end
8
+ module Lib
9
+ autoload :Version, "cowtech-lib/version"
10
+ autoload :Console, "cowtech-lib/console"
11
+ autoload :OptionParser, "cowtech-lib/option_parser"
12
+ autoload :Shell, "cowtech-lib/shell"
13
+ autoload :Script, "cowtech-lib/script"
14
+ end
33
15
  end
@@ -1,360 +1,339 @@
1
1
  # encoding: utf-8
2
2
  #
3
- # cowtech-lib
4
- # Author: Shogun <shogun_panda@me.com>
5
- # Copyright © 2011 and above Shogun
6
- # Released under the MIT License, which follows.
7
- #
8
- # The MIT License
9
- # Permission is hereby granted, free of charge, to any person obtaining a copy
10
- # of this software and associated documentation files (the "Software"), to deal
11
- # in the Software without restriction, including without limitation the rights
12
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
- # copies of the Software, and to permit persons to whom the Software is
14
- # furnished to do so, subject to the following conditions:
15
- #
16
- # The above copyright notice and this permission notice shall be included in
17
- # all copies or substantial portions of the Software.
18
- #
19
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25
- # THE SOFTWARE.
3
+ # This file is part of the cowtech-lib gem. Copyright (C) 2011 and above Shogun <shogun_panda@me.com>.
4
+ # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
26
5
  #
27
6
 
28
7
  require "rexml/document"
29
8
 
30
9
  class Object
31
- def force_array
32
- self.is_a?(Array) ? self : [self]
33
- end
10
+ def force_array
11
+ self.is_a?(Array) ? self : [self]
12
+ end
34
13
  end
35
14
 
36
15
  class Hash
37
- def method_missing(method, *arg)
38
- # PER ORA SUPPORTIAMO SOLO I GETTER
39
- self[method.to_sym]
40
- end
41
-
42
- def respond_to?(method)
43
- self.has_key?(method.to_sym)
44
- end
16
+ def method_missing(method, *arg)
17
+ # PER ORA SUPPORTIAMO SOLO I GETTER
18
+ self[method.to_sym]
19
+ end
20
+
21
+ def respond_to?(method)
22
+ self.has_key?(method.to_sym)
23
+ end
45
24
  end
46
25
 
47
26
  module Cowtech
48
- module Lib
49
- # A class which provides some method to operate with files and to format pretty messages.
50
- # @author Shogun
51
- class Console
52
- # Indentation level
53
- attr_accessor :indent_level
54
-
55
- # Whether show executed commands
56
- attr_accessor :show_commands
57
-
58
- # Whether show output of executed commands
59
- attr_accessor :show_outputs
60
-
61
- # Whether simply print commands rather than executing them
62
- attr_accessor :skip_commands
63
-
64
- # Exit status for commands
65
- attr_reader :statuses
66
-
67
- # Indentation string(s)
68
- attr_accessor :indentator
69
-
70
- # Sets indentation level.
71
- # Arguments:
72
- # * <em>indent</em>: The new indentation level
73
- # * <em></em>: If the level is absolute or relative to the current level
74
- def indent_set(level, absolute = false)
75
- @indent_level = [(!absolute ? @indent_level : 0) + level, 0].max
76
- end
77
-
78
- # Resets indentation level to 0.
79
- def indent_reset
80
- @indent_level = 0
81
- end
82
-
83
- # Execute codes in an indented block
84
- def indent_region(level, absolute = false)
85
- old_level = @indent_level
86
- self.indent_set(level, absolute)
87
- yield
88
- @indent_level = old_level
89
- end
90
-
91
- # Indents a message.
92
- #
93
- # Arguments:
94
- # * <em>msg</em>: The message to indent
95
- # * <em>add_additional</em>: Whether add extra space to align to initial message "*"
96
- # Returns: The indentated message
97
- def indent(msg, level = nil)
98
- (@@indentator * (level || @indent_level)) + msg
99
- end
100
-
101
- # Substitute tag with color.
102
- #
103
- # Arguments:
104
- # * <em>node</em>: The node which operate on
105
- # * <em>stack</em>: The stack of old styles. <b>Do not set this by yourself!</b>
106
- #
107
- # Returns: The new text
108
- def parse_message(node, stack = [])
109
- rv = ""
110
- styles = (node.name == "text" && node.attributes["style"]) ? node.attributes["style"].split(" ") : nil
111
-
112
- # Add style of current tag
113
- if styles then
114
- styles.each do |style|
115
- rv += @@styles[style] || ""
116
- end
117
-
118
- stack.push(styles)
119
- end
120
-
121
- # Now parse subnodes
122
- node.children.each do |child|
123
- if child.node_type == :text then
124
- rv += child.to_s
125
- elsif child.name == "text" then
126
- rv += self.parse_message(child, stack)
127
- end
128
- end
129
-
130
- # Remove style of current tag
131
- if styles then
132
- stack.pop()
133
-
134
- # Restore previous style
135
- (stack.pop || ["default"]).each do |style|
136
- rv += @@styles[style]
137
- end
138
- end
139
-
140
- rv
141
- end
142
-
143
- # Prints a message.
144
- # Arguments:
145
- # * <em>msg</em>: The message to print
146
- # * <em>dots</em>: Whether add "..." to the message
147
- # * <em>newline</em>: Whether add a newline to the message
148
- # * <em>plain</em>: Whether ignore tags
149
- # * <em>must_indent</em>: Whether indent the message
150
- # * <em>internal</em>: If the method is called by another method. <b>Do not set this by yourself!</b>
151
- def write(args)
152
- msg = args[:msg]
153
-
154
- # Check some alternative syntax
155
- [:begin, :warn, :error, :debug, :info, :right, :end].each do |t|
156
- if args[t] then
157
- msg = args[t]
158
- args[:type] = t
159
- args[t] = nil
160
- end
161
- end
162
- args[:fatal] = true if args[:status] == :fail
163
-
164
- # Check for specific msg type
165
- if [:begin, :warn, :error, :debug, :info].include?(args[:type]) then
166
- mc = {:begin => "bold green", :warn => "bold yellow", :error => "bold red", :debug => "magenta", :info => "bold cyan"}
167
- color = args[:color] || mc[args[:type]]
168
-
169
- if args[:full_color] then
170
- msg = self.indent("<text style=\"#{color}\">#{msg}</text>")
171
- else
172
- msg = " <text style=\"#{color}\">*</text> #{self.indent(msg)}"
173
- end
174
- else # Add dots and indentation if needed
175
- msg = self.indent(msg + (args.fetch(:dots, true) ? "..." : ""), args[:indent] ? args[:indent] : @indent_level)
176
- end
177
-
178
- # Parse the message
179
- if !args[:plain] then
180
- begin
181
- xml = "<text>#{msg}</text>"
182
- msg = self.parse_message(REXML::Document.new(xml).root)
183
- rescue Exception => e
184
- print "[ERROR] Invalid message tagging, check XML syntax (or color requested) of the following message:\n\n\t#{xml}\n\n"
185
- print "\tThe errors was: #{e.message} (#{e.class.to_s})\n\n"
186
- exit(1)
187
- end
188
- end
189
-
190
- # Add newline if needed
191
- msg += "\n" if args.fetch(:newline, true)
192
-
193
- if args[:internal] then
194
- msg
195
- else
196
- if [:end, :right].include?(args[:type]) then
197
- # Get screen width
198
- @tty_width = `tput cols`.to_i if @tty_width < 0
199
-
200
- # Get padding
201
- pad = @tty_width - msg.inspect.gsub(/(\\e\[[0-9]+m)|(\")|(\\n)/, "").length
202
-
203
- print "\033[A" if args[:up]
204
- print "\033[0G\033[#{pad}C"
205
- end
206
-
207
- print(msg)
208
- end
209
-
210
- exit(args[:code] || 0) if args[:exit_after] || args[:fatal]
211
- end
212
-
213
- # Syntatic sugar
214
- # Prints a warning message.
215
- def warn(msg, args = nil)
216
- args ||= {}
217
-
218
- if msg.is_a?(Hash) then
219
- args.merge!(msg)
220
- else
221
- args[:msg] = msg
222
- end
223
-
224
- args[:warn] = args[:msg]
225
-
226
- self.write(args)
227
- end
228
-
229
- # Prints an error message.
230
- def error(msg, args = nil)
231
- args ||= {}
232
-
233
- if msg.is_a?(Hash) then
234
- args.merge!(msg)
235
- else
236
- args[:msg] = msg
237
- end
238
-
239
- args[:error] = args[:msg]
240
-
241
- self.write(args)
242
- end
243
-
244
- # Prints and error message then abort.
245
- def fatal(msg, args = nil)
246
- args ||= {}
247
-
248
- if msg.is_a?(Hash) then
249
- args.merge!(msg)
250
- else
251
- args[:msg] = msg
252
- end
253
-
254
- args[:error] = args[:msg]
255
- args[:exit_after] = true
256
- args[:code] ||= 1
257
-
258
- self.write(args)
259
- end
260
-
261
- # Prints an error status
262
- def status(status, args = nil)
263
- args ||= {}
264
-
265
- if status.is_a?(Hash) then
266
- args.merge!(status)
267
- else
268
- args[:status] = status
269
- end
270
-
271
- status = status.is_a?(Hash) ? status[:status] : status
272
- args[:end] = @@statuses[status] || @@statuses[:ok]
273
- args[:dots] = false
274
- args[:up] = true if args[:up] == nil
275
- self.write(args)
276
- end
277
-
278
- # Read input from the user.
279
- #
280
- # Arguments:
281
- # * <em>msg</em>: The prompt to show
282
- # * <em>valids</em>: A list of regexp to validate the input
283
- # * <em>case_sensitive</em>: Wheter the validation is case_sensitive
284
- #
285
- # Returns: The read input
286
- def read(args)
287
- # Adjust prompt
288
- msg = args[:msg] + ((msg !~ /([:?](\s*))$/) ? ":" : "")
289
- msg += " " if msg !~ / ^/
290
-
291
- # Turn choices into regular expressions
292
- regexps = (args[:valids] || []).force_array.collect do |valid|
293
- if !valid.is_a?(Regexp) then
294
- valid = Regexp.new((valid !~ /^\^/ ? "^" : "") + valid + (valid !~ /\$$/ ? "$" : ""), Regexp::EXTENDED + (args[:case_sensitive] ? Regexp::IGNORECASE : 0), "U")
295
- else
296
- valid
297
- end
298
- end
299
-
300
- rv = nil
301
-
302
- # Read input
303
- while true do
304
- # Show message
305
- print(msg)
306
-
307
- # Get reply
308
- bufs = gets.chop()
309
-
310
- # If we don't have any regexp
311
- if regexps.length == 0 then
312
- rv = bufs
313
- break
314
- end
315
-
316
- # Validate inputs
317
- regexps.each do |re|
318
- if bufs =~ re then
319
- rv = bufs
320
- break
321
- end
322
- end
323
-
324
- break if rv
325
- self.write(:warn => "Sorry, your reply was not understood. Please try again")
326
- end
327
-
328
- rv
329
- end
330
-
331
- # Create a new Console.
332
- def initialize
333
- @indent_level = 0
334
- @show_commands = false
335
- @show_outputs = false
336
- @skip_commands = false
337
- @tty_width = -1
338
- @@indentator= " "
339
-
340
- @@styles = {
341
- # Default color
342
- "default" => "\33[0m",
343
- # Text style
344
- "bold" => "\33[1m", "underline" => "\33[4m", "blink" => "\33[5m", "reverse" => "\33[7m", "concealed" => "\33[8m",
345
- # Foreground colors
346
- "black" => "\33[30m", "red" => "\33[31m", "green" => "\33[32m", "yellow" => "\33[33m", "blue" => "\33[34m", "magenta" => "\33[35m", "cyan" => "\33[36m", "white" => "\33[37m",
347
- # Background colors
348
- "bg_black" => "\33[40m", "bg_red" => "\33[41m", "bg_green" => "\33[42m", "bg_yellow" => "\33[43m", "bg_blue" => "\33[44m", "bg_magenta" => "\33[45m", "bg_cyan" => "\33[46m", "bg_white" => "\33[47m"
349
- }
350
-
351
- @@statuses = {
352
- :ok => '<text style="bold blue">[ <text style="bold green">OK</text> ]</text> ',
353
- :pass => '<text style="bold blue">[<text style="bold cyan">PASS</text>]</text> ',
354
- :fail => '<text style="bold blue">[<text style="bold red">FAIL</text>]</text> ',
355
- :warn => '<text style="bold blue">[<text style="bold yellow">WARN</text>]</text> ',
356
- }
357
- end
358
- end
359
- end
27
+ module Lib
28
+ # A class which provides some method to operate with files and to format pretty messages.
29
+ # @author Shogun
30
+ class Console
31
+ # Indentation level
32
+ attr_accessor :indent_level
33
+
34
+ # Whether show executed commands
35
+ attr_accessor :show_commands
36
+
37
+ # Whether show output of executed commands
38
+ attr_accessor :show_outputs
39
+
40
+ # Whether simply print commands rather than executing them
41
+ attr_accessor :skip_commands
42
+
43
+ # Exit status for commands
44
+ attr_reader :statuses
45
+
46
+ # Indentation string(s)
47
+ attr_accessor :indentator
48
+
49
+ # Sets indentation level.
50
+ # Arguments:
51
+ # * <em>indent</em>: The new indentation level
52
+ # * <em></em>: If the level is absolute or relative to the current level
53
+ def indent_set(level, absolute = false)
54
+ @indent_level = [(!absolute ? @indent_level : 0) + level, 0].max
55
+ end
56
+
57
+ # Resets indentation level to 0.
58
+ def indent_reset
59
+ @indent_level = 0
60
+ end
61
+
62
+ # Execute codes in an indented block
63
+ def indent_region(level, absolute = false)
64
+ old_level = @indent_level
65
+ self.indent_set(level, absolute)
66
+ yield
67
+ @indent_level = old_level
68
+ end
69
+
70
+ # Indents a message.
71
+ #
72
+ # Arguments:
73
+ # * <em>msg</em>: The message to indent
74
+ # * <em>add_additional</em>: Whether add extra space to align to initial message "*"
75
+ # Returns: The indentated message
76
+ def indent(msg, level = nil)
77
+ (@@indentator * (level || @indent_level)) + msg
78
+ end
79
+
80
+ # Substitute tag with color.
81
+ #
82
+ # Arguments:
83
+ # * <em>node</em>: The node which operate on
84
+ # * <em>stack</em>: The stack of old styles. <b>Do not set this by yourself!</b>
85
+ #
86
+ # Returns: The new text
87
+ def parse_message(node, stack = [])
88
+ rv = ""
89
+ styles = (node.name == "text" && node.attributes["style"]) ? node.attributes["style"].split(" ") : nil
90
+
91
+ # Add style of current tag
92
+ if styles then
93
+ styles.each do |style|
94
+ rv += @@styles[style] || ""
95
+ end
96
+
97
+ stack.push(styles)
98
+ end
99
+
100
+ # Now parse subnodes
101
+ node.children.each do |child|
102
+ if child.node_type == :text then
103
+ rv += child.to_s
104
+ elsif child.name == "text" then
105
+ rv += self.parse_message(child, stack)
106
+ end
107
+ end
108
+
109
+ # Remove style of current tag
110
+ if styles then
111
+ stack.pop()
112
+
113
+ # Restore previous style
114
+ (stack.pop || ["default"]).each do |style|
115
+ rv += @@styles[style]
116
+ end
117
+ end
118
+
119
+ rv
120
+ end
121
+
122
+ # Prints a message.
123
+ # Arguments:
124
+ # * <em>msg</em>: The message to print
125
+ # * <em>dots</em>: Whether add "..." to the message
126
+ # * <em>newline</em>: Whether add a newline to the message
127
+ # * <em>plain</em>: Whether ignore tags
128
+ # * <em>must_indent</em>: Whether indent the message
129
+ # * <em>internal</em>: If the method is called by another method. <b>Do not set this by yourself!</b>
130
+ def write(args)
131
+ msg = args[:msg]
132
+
133
+ # Check some alternative syntax
134
+ [:begin, :warn, :error, :debug, :info, :right, :end].each do |t|
135
+ if args[t] then
136
+ msg = args[t]
137
+ args[:type] = t
138
+ args[t] = nil
139
+ end
140
+ end
141
+ args[:fatal] = true if args[:status] == :fail
142
+
143
+ # Check for specific msg type
144
+ if [:begin, :warn, :error, :debug, :info].include?(args[:type]) then
145
+ mc = {begin: "bold green", warn: "bold yellow", error: "bold red", debug: "magenta", info: "bold cyan"}
146
+ color = args[:color] || mc[args[:type]]
147
+
148
+ if args[:full_color] then
149
+ msg = self.indent("<text style=\"#{color}\">#{msg}</text>")
150
+ else
151
+ msg = " <text style=\"#{color}\">*</text> #{self.indent(msg)}"
152
+ end
153
+ else # Add dots and indentation if needed
154
+ msg = self.indent(msg + (args.fetch(:dots, true) ? "..." : ""), args[:indent] ? args[:indent] : @indent_level)
155
+ end
156
+
157
+ # Parse the message
158
+ if !args[:plain] then
159
+ begin
160
+ xml = "<text>#{msg}</text>"
161
+ msg = self.parse_message(REXML::Document.new(xml).root)
162
+ rescue Exception => e
163
+ print "[ERROR] Invalid message tagging, check XML syntax (or color requested) of the following message:\n\n\t#{xml}\n\n"
164
+ print "\tThe errors was: #{e.message} (#{e.class.to_s})\n\n"
165
+ exit(1)
166
+ end
167
+ end
168
+
169
+ # Add newline if needed
170
+ msg += "\n" if args.fetch(:newline, true)
171
+
172
+ if args[:internal] then
173
+ msg
174
+ else
175
+ if [:end, :right].include?(args[:type]) then
176
+ # Get screen width
177
+ @tty_width = `tput cols`.to_i if @tty_width < 0
178
+
179
+ # Get padding
180
+ pad = @tty_width - msg.inspect.gsub(/(\\e\[[0-9]+m)|(\")|(\\n)/, "").length
181
+
182
+ print "\033[A" if args[:up]
183
+ print "\033[0G\033[#{pad}C"
184
+ end
185
+
186
+ print(msg)
187
+ end
188
+
189
+ exit(args[:code] || 0) if args[:exit_after] || args[:fatal]
190
+ end
191
+
192
+ # Syntatic sugar
193
+ # Prints a warning message.
194
+ def warn(msg, args = nil)
195
+ args ||= {}
196
+
197
+ if msg.is_a?(Hash) then
198
+ args.merge!(msg)
199
+ else
200
+ args[:msg] = msg
201
+ end
202
+
203
+ args[:warn] = args[:msg]
204
+
205
+ self.write(args)
206
+ end
207
+
208
+ # Prints an error message.
209
+ def error(msg, args = nil)
210
+ args ||= {}
211
+
212
+ if msg.is_a?(Hash) then
213
+ args.merge!(msg)
214
+ else
215
+ args[:msg] = msg
216
+ end
217
+
218
+ args[:error] = args[:msg]
219
+
220
+ self.write(args)
221
+ end
222
+
223
+ # Prints and error message then abort.
224
+ def fatal(msg, args = nil)
225
+ args ||= {}
226
+
227
+ if msg.is_a?(Hash) then
228
+ args.merge!(msg)
229
+ else
230
+ args[:msg] = msg
231
+ end
232
+
233
+ args[:error] = args[:msg]
234
+ args[:exit_after] = true
235
+ args[:code] ||= 1
236
+
237
+ self.write(args)
238
+ end
239
+
240
+ # Prints an error status
241
+ def status(status, args = nil)
242
+ args ||= {}
243
+
244
+ if status.is_a?(Hash) then
245
+ args.merge!(status)
246
+ else
247
+ args[:status] = status
248
+ end
249
+
250
+ status = status.is_a?(Hash) ? status[:status] : status
251
+ args[:end] = @@statuses[status] || @@statuses[:ok]
252
+ args[:dots] = false
253
+ args[:up] = true if args[:up] == nil
254
+ self.write(args)
255
+ end
256
+
257
+ # Read input from the user.
258
+ #
259
+ # Arguments:
260
+ # * <em>msg</em>: The prompt to show
261
+ # * <em>valids</em>: A list of regexp to validate the input
262
+ # * <em>case_sensitive</em>: Wheter the validation is case_sensitive
263
+ #
264
+ # Returns: The read input
265
+ def read(args)
266
+ # Adjust prompt
267
+ msg = args[:msg] + ((msg !~ /([:?](\s*))$/) ? ":" : "")
268
+ msg += " " if msg !~ / ^/
269
+
270
+ # Turn choices into regular expressions
271
+ regexps = (args[:valids] || []).force_array.collect do |valid|
272
+ if !valid.is_a?(Regexp) then
273
+ valid = Regexp.new((valid !~ /^\^/ ? "^" : "") + valid + (valid !~ /\$$/ ? "$" : ""), Regexp::EXTENDED + (args[:case_sensitive] ? Regexp::IGNORECASE : 0), "U")
274
+ else
275
+ valid
276
+ end
277
+ end
278
+
279
+ rv = nil
280
+
281
+ # Read input
282
+ while true do
283
+ # Show message
284
+ print(msg)
285
+
286
+ # Get reply
287
+ bufs = gets.chop()
288
+
289
+ # If we don't have any regexp
290
+ if regexps.length == 0 then
291
+ rv = bufs
292
+ break
293
+ end
294
+
295
+ # Validate inputs
296
+ regexps.each do |re|
297
+ if bufs =~ re then
298
+ rv = bufs
299
+ break
300
+ end
301
+ end
302
+
303
+ break if rv
304
+ self.write(warn: "Sorry, your reply was not understood. Please try again")
305
+ end
306
+
307
+ rv
308
+ end
309
+
310
+ # Create a new Console.
311
+ def initialize
312
+ @indent_level = 0
313
+ @show_commands = false
314
+ @show_outputs = false
315
+ @skip_commands = false
316
+ @tty_width = -1
317
+ @@indentator= " "
318
+
319
+ @@styles = {
320
+ # Default color
321
+ "default" => "\33[0m",
322
+ # Text style
323
+ "bold" => "\33[1m", "underline" => "\33[4m", "blink" => "\33[5m", "reverse" => "\33[7m", "concealed" => "\33[8m",
324
+ # Foreground colors
325
+ "black" => "\33[30m", "red" => "\33[31m", "green" => "\33[32m", "yellow" => "\33[33m", "blue" => "\33[34m", "magenta" => "\33[35m", "cyan" => "\33[36m", "white" => "\33[37m",
326
+ # Background colors
327
+ "bg_black" => "\33[40m", "bg_red" => "\33[41m", "bg_green" => "\33[42m", "bg_yellow" => "\33[43m", "bg_blue" => "\33[44m", "bg_magenta" => "\33[45m", "bg_cyan" => "\33[46m", "bg_white" => "\33[47m"
328
+ }
329
+
330
+ @@statuses = {
331
+ ok: '<text style="bold blue">[ <text style="bold green">OK</text> ]</text> ',
332
+ pass: '<text style="bold blue">[<text style="bold cyan">PASS</text>]</text> ',
333
+ fail: '<text style="bold blue">[<text style="bold red">FAIL</text>]</text> ',
334
+ warn: '<text style="bold blue">[<text style="bold yellow">WARN</text>]</text> ',
335
+ }
336
+ end
337
+ end
338
+ end
360
339
  end