cowtech-lib 1.9.8.1 → 2.0.0

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.
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