bovem 1.2.6 → 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.
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