bovem 1.2.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/.travis.yml +1 -1
  2. data/Gemfile +1 -1
  3. data/README.md +5 -2
  4. data/bovem.gemspec +11 -9
  5. data/doc/Bovem.html +7 -25
  6. data/doc/Bovem/Configuration.html +30 -43
  7. data/doc/Bovem/Console.html +225 -4360
  8. data/doc/Bovem/ConsoleMethods.html +125 -0
  9. data/doc/Bovem/ConsoleMethods/Interactions.html +575 -0
  10. data/doc/Bovem/ConsoleMethods/Interactions/ClassMethods.html +231 -0
  11. data/doc/Bovem/ConsoleMethods/Logging.html +2218 -0
  12. data/doc/Bovem/ConsoleMethods/Logging/ClassMethods.html +212 -0
  13. data/doc/Bovem/ConsoleMethods/Output.html +1213 -0
  14. data/doc/Bovem/ConsoleMethods/StyleHandling.html +274 -0
  15. data/doc/Bovem/ConsoleMethods/StyleHandling/ClassMethods.html +653 -0
  16. data/doc/Bovem/Errors.html +3 -3
  17. data/doc/Bovem/Errors/InvalidConfiguration.html +3 -3
  18. data/doc/Bovem/Errors/InvalidLogger.html +3 -3
  19. data/doc/Bovem/Logger.html +112 -12
  20. data/doc/Bovem/Shell.html +58 -1888
  21. data/doc/Bovem/ShellMethods.html +125 -0
  22. data/doc/Bovem/ShellMethods/Directories.html +484 -0
  23. data/doc/Bovem/ShellMethods/Execute.html +565 -0
  24. data/doc/Bovem/ShellMethods/General.html +450 -0
  25. data/doc/Bovem/ShellMethods/Read.html +451 -0
  26. data/doc/Bovem/ShellMethods/Write.html +676 -0
  27. data/doc/Bovem/Version.html +6 -6
  28. data/doc/_index.html +145 -4
  29. data/doc/class_list.html +1 -1
  30. data/doc/file.README.html +10 -7
  31. data/doc/frames.html +1 -1
  32. data/doc/index.html +10 -7
  33. data/doc/method_list.html +119 -79
  34. data/doc/top-level-namespace.html +3 -3
  35. data/lib/bovem.rb +1 -1
  36. data/lib/bovem/configuration.rb +24 -13
  37. data/lib/bovem/console.rb +566 -497
  38. data/lib/bovem/errors.rb +1 -1
  39. data/lib/bovem/logger.rb +4 -4
  40. data/lib/bovem/shell.rb +482 -305
  41. data/lib/bovem/version.rb +4 -4
  42. data/locales/en.yml +43 -0
  43. data/locales/it.yml +43 -0
  44. data/spec/bovem/configuration_spec.rb +3 -3
  45. data/spec/bovem/console_spec.rb +17 -10
  46. data/spec/bovem/logger_spec.rb +1 -1
  47. data/spec/bovem/shell_spec.rb +9 -5
  48. data/spec/coverage_helper.rb +1 -1
  49. data/spec/spec_helper.rb +1 -1
  50. metadata +32 -22
@@ -6,7 +6,7 @@
6
6
  <title>
7
7
  Top Level Namespace
8
8
 
9
- &mdash; Documentation by YARD 0.8.2.1
9
+ &mdash; Documentation by YARD 0.8.3
10
10
 
11
11
  </title>
12
12
 
@@ -103,9 +103,9 @@
103
103
  </div>
104
104
 
105
105
  <div id="footer">
106
- Generated on Mon Oct 22 09:30:26 2012 by
106
+ Generated on Fri Feb 1 23:40:17 2013 by
107
107
  <a href="http://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
108
- 0.8.2.1 (ruby-1.9.2).
108
+ 0.8.3 (ruby-1.9.3).
109
109
  </div>
110
110
 
111
111
  </body>
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
  #
3
- # This file is part of the bovem gem. Copyright (C) 2012 and above Shogun <shogun_panda@me.com>.
3
+ # This file is part of the bovem gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
4
  # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
5
  #
6
6
 
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
  #
3
- # This file is part of the bovem gem. Copyright (C) 2012 and above Shogun <shogun_panda@me.com>.
3
+ # This file is part of the bovem gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
4
  # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
5
  #
6
6
 
@@ -19,6 +19,8 @@ module Bovem
19
19
  # config.property = "VALUE"
20
20
  # ```
21
21
  class Configuration
22
+ include Lazier::I18n
23
+
22
24
  # Creates a new configuration.
23
25
  #
24
26
  # A configuration file is a plain Ruby file with a top-level {Configuration config} object.
@@ -47,20 +49,11 @@ module Bovem
47
49
  def parse(file = nil, overrides = {}, logger = nil)
48
50
  file = file.present? ? File.expand_path(file) : nil
49
51
 
50
- if file then # We have a file to parse
52
+ if file then
51
53
  if File.readable?(file) then
52
- begin
53
- # Open the file
54
- path = ::Pathname.new(file).realpath
55
- logger.info("Using configuration file #{path}.") if logger
56
- self.tap do |config|
57
- eval(::File.read(path))
58
- end
59
- rescue ::Exception => e
60
- raise Bovem::Errors::InvalidConfiguration.new("Config file #{file} is not valid.")
61
- end
54
+ read_configuration_file(file, logger)
62
55
  else
63
- raise Bovem::Errors::InvalidConfiguration.new("Config file #{file} is not existing or not readable.")
56
+ raise Bovem::Errors::InvalidConfiguration.new(self.i18n.configuration.not_found(file))
64
57
  end
65
58
  end
66
59
 
@@ -89,5 +82,23 @@ module Bovem
89
82
  self.instance_variable_set("@#{name}", value)
90
83
  end
91
84
  end
85
+
86
+ private
87
+ # Reads a configuration file.
88
+ #
89
+ # @param file [String] The file to read.
90
+ # @param logger [Logger] The logger to use for notifications.
91
+ def read_configuration_file(file, logger)
92
+ begin
93
+ # Open the file
94
+ path = ::Pathname.new(file).realpath
95
+ logger.info(self.i18n.using(path)) if logger
96
+ self.tap do |config|
97
+ eval(::File.read(path))
98
+ end
99
+ rescue ::Exception => e
100
+ raise Bovem::Errors::InvalidConfiguration.new(self.i18n.configuration.invalid(file))
101
+ end
102
+ end
92
103
  end
93
104
  end
@@ -1,575 +1,644 @@
1
1
  # encoding: utf-8
2
2
  #
3
- # This file is part of the bovem gem. Copyright (C) 2012 and above Shogun <shogun_panda@me.com>.
3
+ # This file is part of the bovem gem. Copyright (C) 2013 and above Shogun <shogun_panda@me.com>.
4
4
  # Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
5
5
  #
6
6
 
7
7
  module Bovem
8
8
  # List of valid terminal colors.
9
- TERM_COLORS = {
10
- :black => 0,
11
- :red => 1,
12
- :green => 2,
13
- :yellow => 3,
14
- :blue => 4,
15
- :magenta => 5,
16
- :cyan => 6,
17
- :white => 7,
18
- :default => 9,
19
- }
9
+ TERM_COLORS = { black: 0, red: 1, green: 2, yellow: 3, blue: 4, magenta: 5, cyan: 6, white: 7, default: 9}
20
10
 
21
11
  # List of valid terminal text effects.
22
- TERM_EFFECTS = {
23
- :reset => 0,
24
- :bright => 1,
25
- :italic => 3,
26
- :underline => 4,
27
- :blink => 5,
28
- :inverse => 7,
29
- :hide => 8,
30
- }
12
+ TERM_EFFECTS = { reset: 0, bright: 1, italic: 3, underline: 4, blink: 5, inverse: 7, hide: 8 }
13
+
14
+ # Methods of the {Console Console} class.
15
+ module ConsoleMethods
16
+ # Methods for handling styles in the terminal.
17
+ module StyleHandling
18
+ extend ActiveSupport::Concern
19
+
20
+ # Class methods for handling styles in the terminal.
21
+ module ClassMethods
22
+ # Parse a style and returns terminal codes.
23
+ #
24
+ # Supported styles and colors are those in {Bovem::TERM\_COLORS} and {Bovem::TERM\_EFFECTS}. You can also prefix colors with `bg_` (like `bg_red`) for background colors.
25
+ #
26
+ # @param style [String] The style to parse.
27
+ # @return [String] A string with ANSI color codes.
28
+ def parse_style(style)
29
+ style = style.ensure_string.strip.parameterize
30
+
31
+ if style.present? then
32
+ sym = style.to_sym
33
+
34
+ ::Bovem::Console.replace_term_code(Bovem::TERM_EFFECTS, style, 0) ||
35
+ ::Bovem::Console.replace_term_code(Bovem::TERM_COLORS, style, 30) ||
36
+ ::Bovem::Console.replace_term_code(Bovem::TERM_COLORS, style.gsub(/^bg_/, ""), 40) ||
37
+ ""
38
+ else
39
+ ""
40
+ end
41
+ end
31
42
 
32
- # This is a text utility wrapper console I/O.
33
- class Console
34
- # The line width. Default to `80`.
35
- attr_accessor :line_width
43
+ # Parses a set of styles and returns terminals codes.
44
+ # Supported styles and colors are those in {Bovem::TERM\_COLORS} and {Bovem::TERM\_EFFECTS}. You can also prefix colors with `bg_` (like `bg_red`) for background colors.
45
+ #
46
+ # @param styles [String] The styles to parse.
47
+ # @return [String] A string with ANSI color codes.
48
+ def parse_styles(styles)
49
+ styles.split(/\s*[\s,-]\s*/).collect { |s| self.parse_style(s) }.join("")
50
+ end
36
51
 
37
- # The current screen width.
38
- attr_accessor :screen_width
52
+ #
53
+ # Replaces a terminal code.
54
+ #
55
+ # @param codes [Array] The valid list of codes.
56
+ # @param code [String] The code to lookup.
57
+ # @param modifier [Fixnum] The modifier to apply to the code.
58
+ # @return [String|nil] The terminal code or `nil` if the code was not found.
59
+ def replace_term_code(codes, code, modifier = 0)
60
+ sym = code.to_sym
61
+ codes.include?(sym) ? "\e[#{modifier + codes[sym]}m" : nil
62
+ end
39
63
 
40
- # Current indentation width.
41
- attr_accessor :indentation
64
+ # Replaces colors markers in a string.
65
+ #
66
+ # You can specify markers by enclosing in `{mark=[style]}` and `{/mark}` tags. Separate styles with spaces, dashes or commas. Nesting markers is supported.
67
+ #
68
+ # Example:
69
+ #
70
+ # ```ruby
71
+ # Bovem::Console.new.replace_markers("{mark=bright bg_red}{mark=green}Hello world!{/mark}{/mark}")
72
+ # # => "\e[1m\e[41m\e[32mHello world!\e[1m\e[41m\e[0m"
73
+ # ```
74
+ #
75
+ # @param message [String] The message to analyze.
76
+ # @param plain [Boolean] If ignore (cleanify) color markers into the message.
77
+ # @return [String] The replaced message.
78
+ # @see #parse_style
79
+ def replace_markers(message, plain = false)
80
+ stack = []
81
+
82
+ message.ensure_string.gsub(/((\{mark=([a-z\-_\s,]+)\})|(\{\/mark\}))/mi) do
83
+ if $1 == "{/mark}" then # If it is a tag, pop from the latest opened.
84
+ stack.pop
85
+ plain || stack.blank? ? "" : ::Bovem::Console.parse_styles(stack.last)
86
+ else
87
+ styles = $3
88
+ replacement = plain ? "" : ::Bovem::Console.parse_styles(styles)
42
89
 
43
- # The string used for indentation.
44
- attr_accessor :indentation_string
90
+ if replacement.length > 0 then
91
+ stack << "reset" if stack.blank?
92
+ stack << styles
93
+ end
45
94
 
46
- # Whether to show executed commands.
47
- # attr_accessor :show_commands
95
+ replacement
96
+ end
97
+ end
98
+ end
99
+ end
48
100
 
49
- # Whether to show output of executed commands.
50
- # attr_accessor :show_outputs
101
+ # Replaces colors markers in a string.
102
+ #
103
+ # @see .replace_markers
104
+ #
105
+ # @param message [String] The message to analyze.
106
+ # @param plain [Boolean] If ignore (cleanify) color markers into the message.
107
+ # @return [String] The replaced message.
108
+ def replace_markers(message, plain = false)
109
+ ::Bovem::Console.replace_markers(message, plain)
110
+ end
111
+ end
51
112
 
52
- # Whether to simply print commands rather than executing them.
53
- # attr_accessor :skip_commands
113
+ # Methods for formatting output messages.
114
+ module Output
115
+ # Gets the current screen width.
116
+ #
117
+ # @return [Fixnum] The screen width.
118
+ def get_screen_width
119
+ ::Bovem::Console.execute("tput cols").to_integer(80)
120
+ end
54
121
 
55
- # Returns a unique instance for Console.
56
- #
57
- # @return [Console] A new instance.
58
- def self.instance
59
- @instance ||= ::Bovem::Console.new
60
- end
122
+ # Sets the new indentation width.
123
+ #
124
+ # @param width [Fixnum] The new width.
125
+ # @param is_absolute [Boolean] If the new width should not be added to the current one but rather replace it.
126
+ # @return [Fixnum] The new indentation width.
127
+ def set_indentation(width, is_absolute = false)
128
+ @indentation = [(!is_absolute ? @indentation : 0) + width, 0].max.to_i
129
+ @indentation
130
+ end
61
131
 
62
- # Parse a style and returns terminal codes.
63
- #
64
- # Supported styles and colors are those in {Bovem::TERM\_COLORS} and {Bovem::TERM\_EFFECTS}. You can also prefix colors with `bg_` (like `bg_red`) for background colors.
65
- #
66
- # @param style [String] The style to parse.
67
- # @return [String] A string with ANSI color codes.
68
- def self.parse_style(style)
69
- rv = ""
70
- style = style.ensure_string.strip.parameterize
71
-
72
- if style.present? && style !~ /^[,-]$/ then
73
- style = style.ensure_string
74
- sym = style.to_sym
75
-
76
- if ::Bovem::TERM_EFFECTS.include?(sym) then
77
- rv = "\e[#{Bovem::TERM_EFFECTS[sym]}m"
78
- elsif style.index("bg_") == 0 then
79
- sym = style[3, style.length].to_sym
80
- rv = "\e[#{40 + ::Bovem::TERM_COLORS[sym]}m" if ::Bovem::TERM_COLORS.include?(sym)
81
- elsif style != "reset" then
82
- rv = "\e[#{30 + ::Bovem::TERM_COLORS[sym]}m" if ::Bovem::TERM_COLORS.include?(sym)
83
- end
132
+ # Resets indentation width to `0`.
133
+ #
134
+ # @return [Fixnum] The new indentation width.
135
+ def reset_indentation
136
+ @indentation = 0
84
137
  end
85
138
 
86
- rv
87
- end
139
+ # Starts a indented region of text.
140
+ #
141
+ # @param width [Fixnum] The new width.
142
+ # @param is_absolute [Boolean] If the new width should not be added to the current one but rather replace it.
143
+ # @return [Fixnum] The new indentation width.
144
+ def with_indentation(width = 3, is_absolute = false)
145
+ old = @indentation
146
+ self.set_indentation(width, is_absolute)
147
+ yield
148
+ self.set_indentation(old, true)
149
+
150
+ @indentation
151
+ end
88
152
 
89
- # Replaces colors markers in a string.
90
- #
91
- # You can specify markers by enclosing in `{mark=[style]}` and `{/mark}` tags. Separate styles with spaces, dashes or commas. Nesting markers is supported.
92
- #
93
- # Example:
94
- #
95
- # ```ruby
96
- # Bovem::Console.new.replace_markers("{mark=bright bg_red}{mark=green}Hello world!{/mark}{/mark}")
97
- # # => "\e[1m\e[41m\e[32mHello world!\e[1m\e[41m\e[0m"
98
- # ```
99
- #
100
- # @param message [String] The message to analyze.
101
- # @param plain [Boolean] If ignore (cleanify) color markers into the message.
102
- # @return [String] The replaced message.
103
- # @see #parse_style
104
- def self.replace_markers(message, plain = false)
105
- stack = []
106
- mark_regexp = /((\{mark=([a-z\-_\s,]+)\})|(\{\/mark\}))/mi
107
- split_regex = /\s*[\s,-]\s*/
108
-
109
- message = message.ensure_string.gsub(mark_regexp) do
110
- tag = $1
111
- styles = $3
112
- replacement = ""
113
-
114
- if tag == "{/mark}" then # If it is a tag, pop from the latest opened.
115
- stack.pop
116
- styles = stack.last
117
- replacement = plain || stack.blank? ? "" : styles.split(split_regex).collect { |s| self.parse_style(s) }.join("")
153
+ # Wraps a message in fixed line width.
154
+ #
155
+ # @param message [String] The message to wrap.
156
+ # @param width [Fixnum] The maximum width of a line. Default to the current line width.
157
+ # @return [String] The wrapped message.
158
+ def wrap(message, width = nil)
159
+ if width.to_integer <= 0 then
160
+ message
118
161
  else
119
- replacement = plain ? "" : styles.split(split_regex).collect { |s| self.parse_style(s) }.join("")
162
+ width = (width == true || width.to_integer < 0 ? self.get_screen_width : width.to_integer)
120
163
 
121
- if replacement.length > 0 then
122
- stack << "reset" if stack.blank?
123
- stack << styles
124
- end
164
+ message.split("\n").collect { |line|
165
+ line.length > width ? line.gsub(/(.{1,#{width}})(\s+|$)/, "\\1\n").strip : line
166
+ }.join("\n")
125
167
  end
126
-
127
- replacement
128
168
  end
129
169
 
130
- message
131
- end
170
+ # Indents a message.
171
+ #
172
+ # @param message [String] The message to indent.
173
+ # @param width [Fixnum] The indentation width. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces. `nil` or `false` will skip indentation.
174
+ # @param newline_separator [String] The character used for newlines.
175
+ # @return [String] The indentend message.
176
+ def indent(message, width = true, newline_separator = "\n")
177
+ if width.to_integer != 0 then
178
+ width = (width == true ? 0 : width.to_integer)
179
+ width = width < 0 ? -width : @indentation + width
180
+
181
+ message = message.split(newline_separator).collect {|line|
182
+ (@indentation_string * width) + line
183
+ }.join(newline_separator)
184
+ end
132
185
 
133
- # Returns the minimum length of a banner, not including brackets and leading spaces.
134
- # @return [Fixnum] The minimum length of a banner.
135
- def self.min_banner_length
136
- 1
137
- end
186
+ message
187
+ end
138
188
 
139
- # Executes a command and returns its output.
140
- #
141
- # @param command [String] The command to execute.
142
- # @return [String] The command's output.
143
- def self.execute(command)
144
- %x{#{command}}
145
- end
189
+ # Formats a message.
190
+ #
191
+ # You can style text by using `{mark}` and `{/mark}` syntax.
192
+ #
193
+ # @see #replace_markers
194
+ #
195
+ # @param message [String] The message to format.
196
+ # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
197
+ # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
198
+ # @param wrap [Object] If not `nil` or `false`, the maximum length of a line. `true` means the current line width.
199
+ # @param plain [Boolean] If ignore color markers into the message.
200
+ # @return [String] The formatted message.
201
+ def format(message, suffix = "\n", indent = true, wrap = true, plain = false)
202
+ rv = message
203
+
204
+ rv = self.replace_markers(rv, plain) # Replace markers
205
+
206
+ # Compute the real width available for the screen, if we both indent and wrap
207
+ if wrap == true then
208
+ wrap = @line_width
209
+
210
+ if indent == true then
211
+ wrap -= @indentation
212
+ else
213
+ indent_i = indent.to_integer
214
+ wrap -= (indent_i > 0 ? @indentation : 0) + indent_i
215
+ end
216
+ end
146
217
 
147
- # Initializes a new Console.
148
- def initialize
149
- @line_width = self.get_screen_width
150
- @indentation = 0
151
- @indentation_string = " "
152
- end
218
+ rv = self.wrap(rv, wrap) # Wrap
219
+ rv = self.indent(rv, indent) # Indent
153
220
 
154
- # Gets the current screen width.
155
- #
156
- # @return [Fixnum] The screen width.
157
- def get_screen_width
158
- ::Bovem::Console.execute("tput cols").to_integer(80)
159
- end
221
+ rv += suffix.ensure_string if suffix # Add the suffix
222
+ rv
223
+ end
160
224
 
161
- # Sets the new indentation width.
162
- #
163
- # @param width [Fixnum] The new width.
164
- # @param is_absolute [Boolean] If the new width should not be added to the current one but rather replace it.
165
- # @return [Fixnum] The new indentation width.
166
- def set_indentation(width, is_absolute = false)
167
- self.indentation = [(!is_absolute ? self.indentation : 0) + width, 0].max.to_i
168
- self.indentation
169
- end
225
+ # Formats a message to be written right-aligned.
226
+ #
227
+ # @param message [String] The message to format.
228
+ # @param width [Fixnum] The screen width. If `true`, it is automatically computed.
229
+ # @param go_up [Boolean] If go up one line before formatting.
230
+ # @param plain [Boolean] If ignore color markers into the message.
231
+ # @return [String] The formatted message.
232
+ def format_right(message, width = true, go_up = true, plain = false)
233
+ message = self.replace_markers(message, plain)
170
234
 
171
- # Resets indentation width to `0`.
172
- #
173
- # @return [Fixnum] The new indentation width.
174
- def reset_indentation
175
- self.indentation = 0
176
- self.indentation
177
- end
235
+ rv = go_up ? "\e[A" : ""
178
236
 
179
- # Starts a indented region of text.
180
- #
181
- # @param width [Fixnum] The new width.
182
- # @param is_absolute [Boolean] If the new width should not be added to the current one but rather replace it.
183
- # @return [Fixnum] The new indentation width.
184
- def with_indentation(width = 3, is_absolute = false)
185
- old = self.indentation
186
- self.set_indentation(width, is_absolute)
187
- yield
188
- self.set_indentation(old, true)
189
-
190
- self.indentation
191
- end
237
+ @screen_width ||= self.get_screen_width
238
+ width = (width == true || width.to_integer < 1 ? @screen_width : width.to_integer)
192
239
 
193
- # Wraps a message in fixed line width.
194
- #
195
- # @param message [String] The message to wrap.
196
- # @param width [Fixnum] The maximum width of a line. Default to the current line width.
197
- # @return [String] The wrapped message.
198
- def wrap(message, width = nil)
199
- if width.to_integer <= 0 then
200
- message
201
- else
202
- width = (width == true || width.to_integer < 0 ? self.get_screen_width : width.to_integer)
240
+ # Get padding
241
+ padding = width - message.to_s.gsub(/(\e\[[0-9]*[a-z]?)|(\\n)/i, "").length
203
242
 
204
- message.split("\n").collect { |line|
205
- line.length > width ? line.gsub(/(.{1,#{width}})(\s+|$)/, "\\1\n").strip : line
206
- }.join("\n")
243
+ # Return
244
+ rv + "\e[0G\e[#{padding}C" + message
207
245
  end
208
246
  end
209
247
 
210
- # Indents a message.
211
- #
212
- # @param message [String] The message to indent.
213
- # @param width [Fixnum] The indentation width. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces. `nil` or `false` will skip indentation.
214
- # @param newline_separator [String] The character used for newlines.
215
- # @return [String] The indentend message.
216
- def indent(message, width = true, newline_separator = "\n")
217
- if width.to_integer != 0 then
218
- width = (width == true ? 0 : width.to_integer)
219
- width = width < 0 ? -width : @indentation + width
220
-
221
- message = message.split(newline_separator).collect {|line|
222
- (@indentation_string * width) + line
223
- }.join(newline_separator)
248
+ # Methods for logging activities to the user.
249
+ module Logging
250
+ extend ActiveSupport::Concern
251
+
252
+ # Class methods for logging activities to the user.
253
+ module ClassMethods
254
+ # Returns the minimum length of a banner, not including brackets and leading spaces.
255
+ # @return [Fixnum] The minimum length of a banner.
256
+ def min_banner_length
257
+ 1
258
+ end
224
259
  end
225
260
 
226
- message
227
- end
261
+ # Writes a message.
262
+ #
263
+ # @param message [String] The message to format.
264
+ # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
265
+ # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
266
+ # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
267
+ # @param plain [Boolean] If ignore color markers into the message.
268
+ # @param print [Boolean] If `false`, the result will be returned instead of be printed.
269
+ # @return [String] The printed message.
270
+ #
271
+ # @see #format
272
+ def write(message, suffix = "\n", indent = true, wrap = false, plain = false, print = true)
273
+ rv = self.format(message, suffix, indent, wrap, plain)
274
+ Kernel.puts(rv) if print
275
+ rv
276
+ end
228
277
 
229
- # Replaces colors markers in a string.
230
- #
231
- # @see .replace_markers
232
- #
233
- # @param message [String] The message to analyze.
234
- # @param plain [Boolean] If ignore (cleanify) color markers into the message.
235
- # @return [String] The replaced message.
236
- def replace_markers(message, plain = false)
237
- ::Bovem::Console.replace_markers(message, plain)
238
- end
278
+ # Writes a message, aligning to a call with an empty banner.
279
+ #
280
+ # @param message [String] The message to format.
281
+ # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
282
+ # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
283
+ # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
284
+ # @param plain [Boolean] If ignore color markers into the message.
285
+ # @param print [Boolean] If `false`, the result will be returned instead of be printed.
286
+ # @return [String] The printed message.
287
+ #
288
+ # @see #format
289
+ def write_banner_aligned(message, suffix = "\n", indent = true, wrap = false, plain = false, print = true)
290
+ self.write((" " * (::Bovem::Console.min_banner_length + 3)) + message.ensure_string, suffix, indent, wrap, plain, print)
291
+ end
239
292
 
240
- # Formats a message.
241
- #
242
- # You can style text by using `{mark}` and `{/mark}` syntax.
243
- #
244
- # @see #replace_markers
245
- #
246
- # @param message [String] The message to format.
247
- # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
248
- # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
249
- # @param wrap [Object] If not `nil` or `false`, the maximum length of a line. `true` means the current line width.
250
- # @param plain [Boolean] If ignore color markers into the message.
251
- # @return [String] The formatted message.
252
- def format(message, suffix = "\n", indent = true, wrap = true, plain = false)
253
- rv = message
254
-
255
- rv = self.replace_markers(rv, plain) # Replace markers
256
-
257
- # Compute the real width available for the screen, if we both indent and wrap
258
- if wrap == true then
259
- wrap = @line_width
260
-
261
- if indent == true then
262
- wrap -= self.indentation
263
- else
264
- indent_i = indent.to_integer
265
- wrap -= (indent_i > 0 ? self.indentation : 0) + indent_i
293
+ # Writes a status to the output. Valid values are `:ok`, `:pass`, `:fail`, `:warn`.
294
+ #
295
+ # @param status [Symbol] The status to write.
296
+ # @param plain [Boolean] If not use colors.
297
+ # @param go_up [Boolean] If go up one line before formatting.
298
+ # @param right [Boolean] If to print results on the right.
299
+ # @param print [Boolean] If `false`, the result will be returned instead of be printed.
300
+ # @return [Array] An dictionary with `:label` and `:color` keys for the status.
301
+ def status(status, plain = false, go_up = true, right = true, print = true)
302
+ statuses = {
303
+ ok: {label: " OK ", color: "bright green"},
304
+ pass: {label: "PASS", color: "bright cyan"},
305
+ warn: {label: "WARN", color: "bright yellow"},
306
+ fail: {label: "FAIL", color: "bright red"}
307
+ }
308
+ statuses.default = statuses[:ok]
309
+
310
+ rv = statuses[status]
311
+
312
+ if print then
313
+ banner = self.get_banner(rv[:label], rv[:color])
314
+
315
+ if right then
316
+ Kernel.puts self.format_right(banner + " ", true, go_up, plain)
317
+ else
318
+ Kernel.puts self.format(banner + " ", "\n", true, true, plain)
319
+ end
266
320
  end
267
- end
268
321
 
269
- rv = self.wrap(rv, wrap) # Wrap
270
- rv = self.indent(rv, indent) # Indent
322
+ rv
323
+ end
271
324
 
272
- rv += suffix.ensure_string if suffix # Add the suffix
273
- rv
274
- end
325
+ # Gets a banner for the messages.
326
+ #
327
+ # @param label [String] The label for the banner.
328
+ # @param base_color [String] The color for the label.
329
+ # @param full_colored [String] If all the message should be of the label color.
330
+ # @param bracket_color [String] The color of the brackets.
331
+ # @param brackets [Array] An array of dimension 2 to use for brackets.
332
+ # @return [String] The banner.
333
+ # @see #format
334
+ def get_banner(label, base_color, full_colored = false, bracket_color = "blue", brackets = ["[", "]"])
335
+ label = label.rjust(Bovem::Console.min_banner_length, " ")
336
+ brackets = brackets.ensure_array
337
+ bracket_color = base_color if full_colored
338
+ "{mark=%s}%s{mark=%s}%s{/mark}%s{/mark}" % [bracket_color.parameterize, brackets[0], base_color.parameterize, label, brackets[1]]
339
+ end
275
340
 
276
- # Formats a message to be written right-aligned.
277
- #
278
- # @param message [String] The message to format.
279
- # @param width [Fixnum] The screen width. If `true`, it is automatically computed.
280
- # @param go_up [Boolean] If go up one line before formatting.
281
- # @param plain [Boolean] If ignore color markers into the message.
282
- # @return [String] The formatted message.
283
- def format_right(message, width = true, go_up = true, plain = false)
284
- message = self.replace_markers(message, plain)
341
+ # Writes a message prepending a green banner.
342
+ #
343
+ # @param message [String] The message to format.
344
+ # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
345
+ # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
346
+ # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
347
+ # @param plain [Boolean] If ignore color markers into the message.
348
+ # @param indented_banner [Boolean] If also the banner should be indented.
349
+ # @param full_colored [Boolean] If the banner should be fully colored.
350
+ # @param print [Boolean] If `false`, the result will be returned instead of be printed.
351
+ #
352
+ # @see #format
353
+ def begin(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, print = true)
354
+ banner = self.get_banner("*", "bright green")
355
+ message = self.indent(message, indented_banner ? 0 : indent)
356
+ self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
357
+ end
285
358
 
286
- rv = go_up ? "\e[A" : ""
359
+ # Writes a message prepending a red banner and then quits the application.
360
+ #
361
+ # @param message [String] The message to format.
362
+ # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
363
+ # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
364
+ # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
365
+ # @param plain [Boolean] If ignore color markers into the message.
366
+ # @param indented_banner [Boolean] If also the banner should be indented.
367
+ # @param full_colored [Boolean] If the banner should be fully colored.
368
+ # @param return_code [Fixnum] The code to return to the shell.
369
+ # @param print [Boolean] If `false`, the result will be returned instead of be printed.
370
+ #
371
+ # @see #format
372
+ def fatal(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, return_code = -1, print = true)
373
+ self.error(message, suffix, indent, wrap, plain, indented_banner, full_colored, print)
374
+ Kernel.exit(return_code.to_integer(-1))
375
+ end
287
376
 
288
- @screen_width ||= self.get_screen_width
289
- width = (width == true || width.to_integer < 1 ? @screen_width : width.to_integer)
377
+ # Writes a message prepending a cyan banner.
378
+ #
379
+ # @param message [String] The message to format.
380
+ # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
381
+ # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
382
+ # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
383
+ # @param plain [Boolean] If ignore color markers into the message.
384
+ # @param indented_banner [Boolean] If also the banner should be indented.
385
+ # @param full_colored [Boolean] If the banner should be fully colored.
386
+ # @param print [Boolean] If `false`, the result will be returned instead of be printed.
387
+ # @param banner [Array] An array with at last letter and style to use for the banner.
388
+ #
389
+ # @see #format
390
+ def info(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, print = true, *banner)
391
+ banner = banner.ensure_array.flatten
392
+ banner = ["I", "bright cyan"] if banner.blank?
393
+ banner = self.get_banner(banner[0], banner[1], full_colored)
394
+ message = self.indent(message, indented_banner ? 0 : indent)
395
+ self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
396
+ end
290
397
 
291
- # Get padding
292
- padding = width - message.to_s.gsub(/(\e\[[0-9]*[a-z]?)|(\\n)/i, "").length
398
+ # Writes a message prepending a magenta banner.
399
+ #
400
+ # @param message [String] The message to format.
401
+ # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
402
+ # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
403
+ # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
404
+ # @param plain [Boolean] If ignore color markers into the message.
405
+ # @param indented_banner [Boolean] If also the banner should be indented.
406
+ # @param full_colored [Boolean] If the banner should be fully colored.
407
+ # @param print [Boolean] If `false`, the result will be returned instead of be printed.
408
+ #
409
+ # @see #format
410
+ def debug(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, print = true)
411
+ self.info(message, suffix, indent, wrap, plain, indented_banner, full_colored, print, ["D", "bright magenta"])
412
+ end
293
413
 
294
- # Return
295
- rv + "\e[0G\e[#{padding}C" + message
296
- end
414
+ # Writes a message prepending a yellow banner.
415
+ #
416
+ # @param message [String] The message to format.
417
+ # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
418
+ # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
419
+ # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
420
+ # @param plain [Boolean] If ignore color markers into the message.
421
+ # @param indented_banner [Boolean] If also the banner should be indented.
422
+ # @param full_colored [Boolean] If the banner should be fully colored.
423
+ # @param print [Boolean] If `false`, the result will be returned instead of be printed.
424
+ #
425
+ # @see #format
426
+ def warn(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, print = true)
427
+ warn_banner = ["W", "bright yellow"]
428
+ self.info(message, suffix, indent, wrap, plain, indented_banner, full_colored, print, warn_banner)
429
+ end
297
430
 
298
- # Writes a message.
299
- #
300
- # @param message [String] The message to format.
301
- # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
302
- # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
303
- # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
304
- # @param plain [Boolean] If ignore color markers into the message.
305
- # @param print [Boolean] If `false`, the result will be returned instead of be printed.
306
- # @return [String] The printed message.
307
- #
308
- # @see #format
309
- def write(message, suffix = "\n", indent = true, wrap = false, plain = false, print = true)
310
- rv = self.format(message, suffix, indent, wrap, plain)
311
- Kernel.puts(rv) if print
312
- rv
431
+ # Writes a message prepending a red banner.
432
+ #
433
+ # @param message [String] The message to format.
434
+ # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
435
+ # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
436
+ # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
437
+ # @param plain [Boolean] If ignore color markers into the message.
438
+ # @param indented_banner [Boolean] If also the banner should be indented.
439
+ # @param full_colored [Boolean] If the banner should be fully colored.
440
+ # @param print [Boolean] If `false`, the result will be returned instead of be printed.
441
+ #
442
+ # @see #format
443
+ def error(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, print = true)
444
+ self.info(message, suffix, indent, wrap, plain, indented_banner, full_colored, print, "E", "bright red")
445
+ end
313
446
  end
314
447
 
315
- # Writes a message, aligning to a call with an empty banner.
316
- #
317
- # @param message [String] The message to format.
318
- # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
319
- # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
320
- # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
321
- # @param plain [Boolean] If ignore color markers into the message.
322
- # @param print [Boolean] If `false`, the result will be returned instead of be printed.
323
- # @return [String] The printed message.
324
- #
325
- # @see #format
326
- def write_banner_aligned(message, suffix = "\n", indent = true, wrap = false, plain = false, print = true)
327
- self.write((" " * (::Bovem::Console.min_banner_length + 3)) + message.ensure_string, suffix, indent, wrap, plain, print)
328
- end
448
+ # Methods to interact with the user and other processes.
449
+ module Interactions
450
+ extend ActiveSupport::Concern
451
+
452
+ # Class methods to interact with the user and other processes.
453
+ module ClassMethods
454
+ # Executes a command and returns its output.
455
+ #
456
+ # @param command [String] The command to execute.
457
+ # @return [String] The command's output.
458
+ def execute(command)
459
+ %x{#{command}}
460
+ end
461
+ end
329
462
 
330
- # Writes a status to the output. Valid values are `:ok`, `:pass`, `:fail`, `:warn`.
331
- #
332
- # @param status [Symbol] The status to write.
333
- # @param plain [Boolean] If not use colors.
334
- # @param go_up [Boolean] If go up one line before formatting.
335
- # @param right [Boolean] If to print results on the right.
336
- # @param print [Boolean] If `false`, the result will be returned instead of be printed.
337
- # @return [Array] An dictionary with `:label` and `:color` keys for the status.
338
- def status(status, plain = false, go_up = true, right = true, print = true)
339
- statuses = {
340
- :ok => {:label => " OK ", :color => "bright green"},
341
- :pass => {:label => "PASS", :color => "bright cyan"},
342
- :warn => {:label => "WARN", :color => "bright yellow"},
343
- :fail => {:label => "FAIL", :color => "bright red"}
344
- }
345
- statuses.default = statuses[:ok]
346
-
347
- rv = statuses[status]
348
-
349
- if print then
350
- banner = self.get_banner(rv[:label], rv[:color])
351
-
352
- if right then
353
- Kernel.puts self.format_right(banner + " ", true, go_up, plain)
354
- else
355
- Kernel.puts self.format(banner + " ", "\n", true, true, plain)
463
+ # Reads a string from the console.
464
+ #
465
+ # @param prompt [String|Boolean] A prompt to show. If `true`, `Please insert a value:` will be used, if `nil` or `false` no prompt will be shown.
466
+ # @param default_value [String] Default value if user simply pressed the enter key.
467
+ # @param validator [Array|Regexp] An array of values or a Regexp to match the submitted value against.
468
+ # @param echo [Boolean] If to show submitted text to the user.
469
+ def read(prompt = true, default_value = nil, validator = nil, echo = true)
470
+ prompt = sanitize_prompt(prompt)
471
+
472
+ # Adjust validator
473
+ validator = sanitize_validator(validator)
474
+
475
+ with_echo_handling(echo) do
476
+ begin
477
+ catch(:reply) do
478
+ while true do
479
+ reply = validate_input_value(read_input_value(prompt, default_value), validator)
480
+ handle_reply(reply)
481
+ end
482
+ end
483
+ rescue Interrupt => e
484
+ default_value
485
+ end
356
486
  end
357
487
  end
358
488
 
359
- rv
360
- end
489
+ # Executes a block of code in a indentation region and then prints out and ending status message.
490
+ #
491
+ # @param message [String] The message to format.
492
+ # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
493
+ # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
494
+ # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
495
+ # @param plain [Boolean] If ignore color markers into the message.
496
+ # @param indented_banner [Boolean] If also the banner should be indented.
497
+ # @param full_colored [Boolean] If the banner should be fully colored.
498
+ # @param block_indentation [Fixnum] The new width for the indented region.
499
+ # @param block_indentation_absolute [Boolean] If the new width should not be added to the current one but rather replace it.
500
+ # @return [Symbol] The exit status for the block.
501
+ def task(message = nil, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, block_indentation = 2, block_indentation_absolute = false)
502
+ status = nil
503
+
504
+ self.begin(message, suffix, indent, wrap, plain, indented_banner, full_colored) if message.present?
505
+ self.with_indentation(block_indentation, block_indentation_absolute) do
506
+ rv = block_given? ? yield.ensure_array : [:ok] # Execute block
507
+ exit_task(message, rv, plain) # Handle task exit
508
+ status = rv[0] # Return value
361
509
 
362
- # Gets a banner for the messages.
363
- #
364
- # @param label [String] The label for the banner.
365
- # @param base_color [String] The color for the label.
366
- # @param full_colored [String] If all the message should be of the label color.
367
- # @param bracket_color [String] The color of the brackets.
368
- # @param brackets [Array] An array of dimension 2 to use for brackets.
369
- # @return [String] The banner.
370
- # @see #format
371
- def get_banner(label, base_color, full_colored = false, bracket_color = "blue", brackets = ["[", "]"])
372
- label = label.rjust(Bovem::Console.min_banner_length, " ")
373
- brackets = brackets.ensure_array
374
- bracket_color = base_color if full_colored
375
- "{mark=%s}%s{mark=%s}%s{/mark}%s{/mark}" % [bracket_color.parameterize, brackets[0], base_color.parameterize, label, brackets[1]]
376
- end
510
+ end
377
511
 
378
- # Writes a message prepending a cyan banner.
379
- #
380
- # @param message [String] The message to format.
381
- # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
382
- # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
383
- # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
384
- # @param plain [Boolean] If ignore color markers into the message.
385
- # @param indented_banner [Boolean] If also the banner should be indented.
386
- # @param full_colored [Boolean] If the banner should be fully colored.
387
- # @param print [Boolean] If `false`, the result will be returned instead of be printed.
388
- #
389
- # @see #format
390
- def info(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, print = true)
391
- banner = self.get_banner("I", "bright cyan", full_colored)
392
- message = self.indent(message, indented_banner ? 0 : indent)
393
- self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
394
- end
512
+ status
513
+ end
395
514
 
396
- # Writes a message prepending a green banner.
397
- #
398
- # @param message [String] The message to format.
399
- # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
400
- # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
401
- # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
402
- # @param plain [Boolean] If ignore color markers into the message.
403
- # @param indented_banner [Boolean] If also the banner should be indented.
404
- # @param full_colored [Boolean] If the banner should be fully colored.
405
- # @param print [Boolean] If `false`, the result will be returned instead of be printed.
406
- #
407
- # @see #format
408
- def begin(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, print = true)
409
- banner = self.get_banner("*", "bright green")
410
- message = self.indent(message, indented_banner ? 0 : indent)
411
- self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
412
- end
515
+ private
516
+ # Handles task exit.
517
+ #
518
+ # @param message [String] The message to format.
519
+ # @param rv [Array] An array with exit status and exit code.
520
+ # @param plain [Boolean] If ignore color markers into the message.
521
+ def exit_task(message, rv, plain)
522
+ if rv[0] == :fatal then
523
+ self.status(:fail, plain)
524
+ exit(rv.length > 1 ? rv[1].to_integer : -1)
525
+ else
526
+ self.status(rv[0], plain) if message.present?
527
+ end
528
+ end
413
529
 
414
- # Writes a message prepending a yellow banner.
415
- #
416
- # @param message [String] The message to format.
417
- # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
418
- # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
419
- # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
420
- # @param plain [Boolean] If ignore color markers into the message.
421
- # @param indented_banner [Boolean] If also the banner should be indented.
422
- # @param full_colored [Boolean] If the banner should be fully colored.
423
- # @param print [Boolean] If `false`, the result will be returned instead of be printed.
424
- #
425
- # @see #format
426
- def warn(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, print = true)
427
- banner = self.get_banner("W", "bright yellow", full_colored)
428
- message = self.indent(message, indented_banner ? 0 : indent)
429
- self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
430
- end
530
+ # Returns a prompt for input prompting.
531
+ #
532
+ # @param prompt [String]
533
+ # @return [String|nil] The prompt to use or `nil`, if no message must be prompted.
534
+ def sanitize_prompt(prompt)
535
+ if prompt.present?
536
+ (prompt == true ? self.i18n.console.prompt : prompt).gsub(/:?\s*$/, "") + ": "
537
+ else
538
+ nil
539
+ end
540
+ end
431
541
 
432
- # Writes a message prepending a red banner.
433
- #
434
- # @param message [String] The message to format.
435
- # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
436
- # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
437
- # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
438
- # @param plain [Boolean] If ignore color markers into the message.
439
- # @param indented_banner [Boolean] If also the banner should be indented.
440
- # @param full_colored [Boolean] If the banner should be fully colored.
441
- # @param print [Boolean] If `false`, the result will be returned instead of be printed.
442
- #
443
- # @see #format
444
- def error(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, print = true)
445
- banner = self.get_banner("E", "bright red", full_colored)
446
- message = self.indent(message, indented_banner ? 0 : indent)
447
- self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
448
- end
542
+ # Make sure that the validators are an array of string if not a regexp.
543
+ #
544
+ # @param validator [String|Regexp] The validator to sanitize.
545
+ # @return [Object] A list of strings, a Regexp or nil.
546
+ def sanitize_validator(validator)
547
+ validator.present? && !validator.is_a?(::Regexp) ? validator.ensure_array.collect {|v| v.ensure_string} : validator
548
+ end
449
549
 
450
- # Writes a message prepending a red banner and then quits the application.
451
- #
452
- # @param message [String] The message to format.
453
- # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
454
- # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
455
- # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
456
- # @param plain [Boolean] If ignore color markers into the message.
457
- # @param indented_banner [Boolean] If also the banner should be indented.
458
- # @param full_colored [Boolean] If the banner should be fully colored.
459
- # @param return_code [Fixnum] The code to return to the shell.
460
- # @param print [Boolean] If `false`, the result will be returned instead of be printed.
461
- #
462
- # @see #format
463
- def fatal(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, return_code = -1, print = true)
464
- self.error(message, suffix, indent, wrap, plain, indented_banner, full_colored, print)
465
- Kernel.exit(return_code.to_integer(-1))
466
- end
550
+ # Handle terminal echoing.
551
+ #
552
+ # @param echo [Boolean] If disabled echoing
553
+ def with_echo_handling(echo = true)
554
+ rv = nil
467
555
 
468
- # Writes a message prepending a magenta banner.
469
- #
470
- # @param message [String] The message to format.
471
- # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
472
- # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
473
- # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
474
- # @param plain [Boolean] If ignore color markers into the message.
475
- # @param indented_banner [Boolean] If also the banner should be indented.
476
- # @param full_colored [Boolean] If the banner should be fully colored.
477
- # @param print [Boolean] If `false`, the result will be returned instead of be printed.
478
- #
479
- # @see #format
480
- def debug(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, print = true)
481
- banner = self.get_banner("D", "bright magenta", full_colored)
482
- message = self.indent(message, indented_banner ? 0 : indent)
483
- self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
484
- end
556
+ disable_echo = !echo && @stty.present? && /-echo\b/mix.match(::Bovem::Console.execute(@stty)).nil?
557
+ ::Bovem::Console.execute("#{@stty} -echo") if disable_echo
558
+ rv = yield
559
+ ::Bovem::Console.execute("#{@stty} echo") if disable_echo
485
560
 
486
- # Reads a string from the console.
487
- #
488
- # @param prompt [String|Boolean] A prompt to show. If `true`, `Please insert a value:` will be used, if `nil` or `false` no prompt will be shown.
489
- # @param default_value [String] Default value if user simply pressed the enter key.
490
- # @param validator [Array|Regexp] An array of values or a Regexp to match the submitted value against.
491
- # @param echo [Boolean] If to show submitted text to the user.
492
- def read(prompt = true, default_value = nil, validator = nil, echo = true)
493
- # Write the prompt
494
- prompt = "Please insert a value" if prompt == true
495
- final_prompt = !prompt.nil? ? prompt.gsub(/:?\s*$/, "") + ": " : nil
496
-
497
- # Adjust validator
498
- validator = validator.ensure_array.collect {|v| v.ensure_string} if validator.present? && !validator.is_a?(::Regexp)
499
-
500
- # Handle echo
501
- stty = ::Bovem::Console.execute("which stty").strip
502
- disable_echo = !echo && stty.present? && /-echo\b/mix.match(::Bovem::Console.execute(stty)).nil?
503
-
504
- # Disable echo
505
- ::Bovem::Console.execute("#{stty} -echo") if disable_echo
506
-
507
- begin
508
- catch(:reply) do
509
- while true do
510
- valid = true
511
-
512
- if final_prompt then
513
- Kernel.print self.format(final_prompt, false, false)
514
- $stdout.flush
515
- end
561
+ rv
562
+ end
516
563
 
517
- reply = $stdin.gets.chop
518
- reply = default_value if reply.empty?
564
+ # Read an input from the terminal.
565
+ #
566
+ # @param prompt [String] A message to show to the user.
567
+ # @param default_value [Object] A default value to enter if the user just pressed the enter key.
568
+ # @return [Object] The read value.
569
+ def read_input_value(prompt, default_value = nil)
570
+ if prompt then
571
+ Kernel.print self.format(prompt, false, false)
572
+ $stdout.flush
573
+ end
519
574
 
520
- # Match against the validator
521
- if validator.present? then
522
- if validator.is_a?(Array) then
523
- valid = false if validator.length > 0 && !validator.include?(reply)
524
- elsif validator.is_a?(Regexp) then
525
- valid = false if !validator.match(reply)
526
- end
527
- end
575
+ reply = $stdin.gets.chop
576
+ reply.present? ? reply : default_value
577
+ end
528
578
 
529
- if !valid then
530
- self.write("Sorry, your reply was not understood. Please try again.", false, false)
531
- else
532
- throw(:reply, reply)
579
+ # Validates a read value from the terminal.
580
+ #
581
+ # @param reply [String] The value to validate.
582
+ # @param validator [Array|Regexp] An array of values or a Regexp to match the submitted value against.
583
+ # @return [String|nil] The validated value or `nil`, if the value is invalid.
584
+ def validate_input_value(reply, validator)
585
+ # Match against the validator
586
+ if validator.present? then
587
+ if validator.is_a?(Array) then
588
+ reply = nil if validator.length > 0 && !validator.include?(reply)
589
+ elsif validator.is_a?(Regexp) then
590
+ reply = nil if !validator.match(reply)
533
591
  end
534
592
  end
593
+
594
+ reply
595
+ end
596
+
597
+ # Handles a read value from the terminal.
598
+ #
599
+ # @param reply [String] The value to handle.
600
+ def handle_reply(reply)
601
+ if reply then
602
+ throw(:reply, reply)
603
+ else
604
+ self.write(self.i18n.console.unknown_reply, false, false)
605
+ end
535
606
  end
536
- rescue Interrupt
537
- default_value
538
- ensure
539
- ::Bovem::Console.execute("#{stty} echo") if disable_echo
540
- end
541
607
  end
608
+ end
542
609
 
543
- # Executes a block of code in a indentation region and then prints out and ending status message.
610
+ # This is a text utility wrapper console I/O.
611
+ #
612
+ # @attr [Fixnum] line_width The line width. Default to `80`.
613
+ # @attr [Fixnum] screen_width The current screen width.
614
+ # @attr [Fixnum] indentation Current indentation width.
615
+ # @attr [String] indentation_string The string used for indentation.
616
+ class Console
617
+ attr_accessor :line_width
618
+ attr_accessor :screen_width
619
+ attr_accessor :indentation
620
+ attr_accessor :indentation_string
621
+
622
+ include Lazier::I18n
623
+ include Bovem::ConsoleMethods::StyleHandling
624
+ include Bovem::ConsoleMethods::Output
625
+ include Bovem::ConsoleMethods::Logging
626
+ include Bovem::ConsoleMethods::Interactions
627
+
628
+ # Returns a unique instance for Console.
544
629
  #
545
- # @param message [String] The message to format.
546
- # @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
547
- # @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use the current indentation, a negative value of `-x` will indent of `x` absolute spaces.
548
- # @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
549
- # @param plain [Boolean] If ignore color markers into the message.
550
- # @param indented_banner [Boolean] If also the banner should be indented.
551
- # @param full_colored [Boolean] If the banner should be fully colored.
552
- # @param block_indentation [Fixnum] The new width for the indented region.
553
- # @param block_indentation_absolute [Boolean] If the new width should not be added to the current one but rather replace it.
554
- # @return [Symbol] The exit status for the block.
555
- def task(message = nil, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, block_indentation = 2, block_indentation_absolute = false)
556
- status = nil
557
-
558
- self.begin(message, suffix, indent, wrap, plain, indented_banner, full_colored) if message.present?
559
-
560
- self.with_indentation(block_indentation, block_indentation_absolute) do
561
- rv = block_given? ? yield.ensure_array : [:ok] # Execute block
562
- status = rv[0] # Return value
563
-
564
- if status == :fatal then
565
- self.status(:fail, plain)
566
- exit(rv.length > 1 ? rv[1].to_integer : -1)
567
- else
568
- self.status(status, plain) if message.present?
569
- end
570
- end
630
+ # @return [Console] A new instance.
631
+ def self.instance
632
+ @instance ||= ::Bovem::Console.new
633
+ end
571
634
 
572
- status
635
+ # Initializes a new Console.
636
+ def initialize
637
+ @line_width = self.get_screen_width
638
+ @indentation = 0
639
+ @indentation_string = " "
640
+ @stty = ::Bovem::Console.execute("which stty").strip
641
+ self.i18n_setup(:bovem, ::File.absolute_path(::Pathname.new(::File.dirname(__FILE__)).to_s + "/../../locales/"))
573
642
  end
574
643
  end
575
644
  end