bovem 0.8.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile.lock +3 -1
- data/bovem.gemspec +1 -0
- data/lib/bovem.rb +4 -1
- data/lib/bovem/configuration.rb +3 -3
- data/lib/bovem/console.rb +42 -32
- data/lib/bovem/logger.rb +3 -2
- data/lib/bovem/shell.rb +377 -0
- data/lib/bovem/version.rb +3 -3
- data/spec/bovem/console_spec.rb +43 -40
- data/spec/bovem/shell_spec.rb +473 -0
- data/spec/coverage_helper.rb +3 -2
- metadata +155 -145
data/Gemfile.lock
CHANGED
@@ -1,8 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
bovem (0.
|
4
|
+
bovem (1.0.0)
|
5
5
|
lazier (~> 1.0)
|
6
|
+
open4 (~> 1.3.0)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: http://rubygems.org/
|
@@ -38,6 +39,7 @@ GEM
|
|
38
39
|
tzinfo (~> 0.3.0)
|
39
40
|
method_source (0.8)
|
40
41
|
multi_json (1.3.6)
|
42
|
+
open4 (1.3.0)
|
41
43
|
pry (0.9.10)
|
42
44
|
coderay (~> 1.0.5)
|
43
45
|
method_source (~> 0.8)
|
data/bovem.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.require_paths = ["lib"]
|
23
23
|
|
24
24
|
gem.add_dependency("lazier", "~> 1.0")
|
25
|
+
gem.add_dependency("open4", "~> 1.3.0")
|
25
26
|
|
26
27
|
gem.add_development_dependency("rspec", "~> 2.11.0")
|
27
28
|
gem.add_development_dependency("rake", "~> 0.9.0")
|
data/lib/bovem.rb
CHANGED
@@ -6,6 +6,8 @@
|
|
6
6
|
|
7
7
|
require "logger"
|
8
8
|
require "lazier"
|
9
|
+
require "open4"
|
10
|
+
require "find"
|
9
11
|
|
10
12
|
Lazier.load!(:object)
|
11
13
|
|
@@ -13,4 +15,5 @@ require "bovem/version" if !defined?(Bovem::Version)
|
|
13
15
|
require "bovem/errors"
|
14
16
|
require "bovem/configuration"
|
15
17
|
require "bovem/logger"
|
16
|
-
require "bovem/console"
|
18
|
+
require "bovem/console"
|
19
|
+
require "bovem/shell"
|
data/lib/bovem/configuration.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
#
|
6
6
|
|
7
7
|
module Bovem
|
8
|
-
# This class holds the configuration of
|
8
|
+
# This class holds the configuration of an applicaton.
|
9
9
|
#
|
10
10
|
# Extend this class and add valid properties via {property property} method.
|
11
11
|
# Example:
|
@@ -31,7 +31,7 @@ module Bovem
|
|
31
31
|
self.parse(file, overrides, logger)
|
32
32
|
end
|
33
33
|
|
34
|
-
#
|
34
|
+
# Parses a configuration file.
|
35
35
|
#
|
36
36
|
# A configuration file is a plain Ruby file with a top-level {Configuration config} object.
|
37
37
|
#
|
@@ -69,7 +69,7 @@ module Bovem
|
|
69
69
|
self
|
70
70
|
end
|
71
71
|
|
72
|
-
# Defines a new property for the configuration
|
72
|
+
# Defines a new property for the configuration.
|
73
73
|
#
|
74
74
|
# @param name [Symbol] The name of the property.
|
75
75
|
# @param options [Hash] A set of options for the property. Currently, only `:default` (which holds the default value) is supported.
|
data/lib/bovem/console.rb
CHANGED
@@ -61,14 +61,15 @@ module Bovem
|
|
61
61
|
|
62
62
|
# Parse a style and returns terminal codes.
|
63
63
|
#
|
64
|
-
# Supported styles and colors are those in {Bovem::
|
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
65
|
#
|
66
66
|
# @param style [String] The style to parse.
|
67
67
|
# @return [String] A string with ANSI color codes.
|
68
68
|
def self.parse_style(style)
|
69
69
|
rv = ""
|
70
|
+
style = style.ensure_string.strip.parameterize
|
70
71
|
|
71
|
-
if style.present? then
|
72
|
+
if style.present? && style !~ /^[,-]$/ then
|
72
73
|
style = style.ensure_string
|
73
74
|
sym = style.to_sym
|
74
75
|
|
@@ -87,16 +88,25 @@ module Bovem
|
|
87
88
|
|
88
89
|
# Replaces colors markers in a string.
|
89
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
|
+
#
|
90
100
|
# @param message [String] The message to analyze.
|
91
101
|
# @param plain [Boolean] If ignore (cleanify) color markers into the message.
|
92
102
|
# @return [String] The replaced message.
|
93
103
|
# @see #parse_style
|
94
104
|
def self.replace_markers(message, plain = false)
|
95
105
|
stack = []
|
106
|
+
mark_regexp = /((\{mark=([a-z\-_\s,]+)\})|(\{\/mark\}))/mi
|
107
|
+
split_regex = /\s*[\s,-]\s*/
|
96
108
|
|
97
|
-
|
98
|
-
|
99
|
-
message = message.gsub(regexp) do
|
109
|
+
message = message.gsub(mark_regexp) do
|
100
110
|
tag = $1
|
101
111
|
styles = $3
|
102
112
|
replacement = ""
|
@@ -104,9 +114,9 @@ module Bovem
|
|
104
114
|
if tag == "{/mark}" then # If it is a tag, pop from the latest opened.
|
105
115
|
stack.pop
|
106
116
|
styles = stack.last
|
107
|
-
replacement = plain || stack.blank? ? "" : styles.split(
|
117
|
+
replacement = plain || stack.blank? ? "" : styles.split(split_regex).collect { |s| self.parse_style(s) }.join("")
|
108
118
|
else
|
109
|
-
replacement = plain ? "" : styles.split(
|
119
|
+
replacement = plain ? "" : styles.split(split_regex).collect { |s| self.parse_style(s) }.join("")
|
110
120
|
|
111
121
|
if replacement.length > 0 then
|
112
122
|
stack << "reset" if stack.blank?
|
@@ -165,7 +175,7 @@ module Bovem
|
|
165
175
|
# @param width [Fixnum] The new width.
|
166
176
|
# @param is_absolute [Boolean] If the new width should not be added to the current one but rather replace it.
|
167
177
|
# @return [Fixnum] The new indentation width.
|
168
|
-
def with_indentation(width, is_absolute = false)
|
178
|
+
def with_indentation(width = 3, is_absolute = false)
|
169
179
|
old = self.indentation
|
170
180
|
self.set_indentation(width, is_absolute)
|
171
181
|
yield
|
@@ -194,7 +204,7 @@ module Bovem
|
|
194
204
|
# Indents a message.
|
195
205
|
#
|
196
206
|
# @param message [String] The message to indent.
|
197
|
-
# @param width [Fixnum] The indentation width. `true` means to use
|
207
|
+
# @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.
|
198
208
|
# @param newline_separator [String] The character used for newlines.
|
199
209
|
# @return [String] The indentend message.
|
200
210
|
def indent(message, width = true, newline_separator = "\n")
|
@@ -212,10 +222,10 @@ module Bovem
|
|
212
222
|
|
213
223
|
# Replaces colors markers in a string.
|
214
224
|
#
|
215
|
-
#
|
225
|
+
# @see .replace_markers
|
216
226
|
#
|
217
227
|
# @param message [String] The message to analyze.
|
218
|
-
# @param plain [Boolean] If ignore (cleanify
|
228
|
+
# @param plain [Boolean] If ignore (cleanify) color markers into the message.
|
219
229
|
# @return [String] The replaced message.
|
220
230
|
def replace_markers(message, plain = false)
|
221
231
|
::Bovem::Console.replace_markers(message, plain)
|
@@ -223,11 +233,13 @@ module Bovem
|
|
223
233
|
|
224
234
|
# Formats a message.
|
225
235
|
#
|
226
|
-
# You can style text by using `{
|
236
|
+
# You can style text by using `{mark}` and `{/mark}` syntax.
|
237
|
+
#
|
238
|
+
# @see #replace_markers
|
227
239
|
#
|
228
240
|
# @param message [String] The message to format.
|
229
241
|
# @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
|
230
|
-
# @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use
|
242
|
+
# @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.
|
231
243
|
# @param wrap [Object] If not `nil` or `false`, the maximum length of a line. `true` means the current line width.
|
232
244
|
# @param plain [Boolean] If ignore color markers into the message.
|
233
245
|
# @return [String] The formatted message.
|
@@ -268,7 +280,7 @@ module Bovem
|
|
268
280
|
#
|
269
281
|
# @param message [String] The message to format.
|
270
282
|
# @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
|
271
|
-
# @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use
|
283
|
+
# @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.
|
272
284
|
# @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
|
273
285
|
# @param plain [Boolean] If ignore color markers into the message.
|
274
286
|
# @param print [Boolean] If `false`, the result will be returned instead of be printed.
|
@@ -296,7 +308,7 @@ module Bovem
|
|
296
308
|
:warn => {:label => "WARN", :color => "bright yellow"},
|
297
309
|
:fail => {:label => "FAIL", :color => "bright red"}
|
298
310
|
}
|
299
|
-
statuses.default = statuses[:
|
311
|
+
statuses.default = statuses[:ok]
|
300
312
|
|
301
313
|
rv = statuses[status]
|
302
314
|
|
@@ -328,11 +340,11 @@ module Bovem
|
|
328
340
|
"{mark=%s}%s{mark=%s}%s{/mark}%s{/mark}" % [bracket_color.parameterize, brackets[0], base_color.parameterize, label, brackets[1]]
|
329
341
|
end
|
330
342
|
|
331
|
-
# Writes a message prepending a cyan
|
343
|
+
# Writes a message prepending a cyan banner.
|
332
344
|
#
|
333
345
|
# @param message [String] The message to format.
|
334
346
|
# @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
|
335
|
-
# @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use
|
347
|
+
# @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.
|
336
348
|
# @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
|
337
349
|
# @param plain [Boolean] If ignore color markers into the message.
|
338
350
|
# @param indented_banner [Boolean] If also the banner should be indented.
|
@@ -346,11 +358,11 @@ module Bovem
|
|
346
358
|
self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
|
347
359
|
end
|
348
360
|
|
349
|
-
# Writes a message prepending a green
|
361
|
+
# Writes a message prepending a green banner.
|
350
362
|
#
|
351
363
|
# @param message [String] The message to format.
|
352
364
|
# @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
|
353
|
-
# @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use
|
365
|
+
# @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.
|
354
366
|
# @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
|
355
367
|
# @param plain [Boolean] If ignore color markers into the message.
|
356
368
|
# @param indented_banner [Boolean] If also the banner should be indented.
|
@@ -364,11 +376,11 @@ module Bovem
|
|
364
376
|
self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
|
365
377
|
end
|
366
378
|
|
367
|
-
# Writes a message prepending a yellow
|
379
|
+
# Writes a message prepending a yellow banner.
|
368
380
|
#
|
369
381
|
# @param message [String] The message to format.
|
370
382
|
# @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
|
371
|
-
# @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use
|
383
|
+
# @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.
|
372
384
|
# @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
|
373
385
|
# @param plain [Boolean] If ignore color markers into the message.
|
374
386
|
# @param indented_banner [Boolean] If also the banner should be indented.
|
@@ -382,11 +394,11 @@ module Bovem
|
|
382
394
|
self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
|
383
395
|
end
|
384
396
|
|
385
|
-
# Writes a message prepending a red
|
397
|
+
# Writes a message prepending a red banner.
|
386
398
|
#
|
387
399
|
# @param message [String] The message to format.
|
388
400
|
# @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
|
389
|
-
# @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use
|
401
|
+
# @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.
|
390
402
|
# @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
|
391
403
|
# @param plain [Boolean] If ignore color markers into the message.
|
392
404
|
# @param indented_banner [Boolean] If also the banner should be indented.
|
@@ -400,11 +412,11 @@ module Bovem
|
|
400
412
|
self.write(banner + " " + message, suffix, indented_banner ? indent : 0, wrap, plain, print)
|
401
413
|
end
|
402
414
|
|
403
|
-
# Writes a message prepending a red
|
415
|
+
# Writes a message prepending a red banner and then quits the application.
|
404
416
|
#
|
405
417
|
# @param message [String] The message to format.
|
406
418
|
# @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
|
407
|
-
# @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use
|
419
|
+
# @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.
|
408
420
|
# @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
|
409
421
|
# @param plain [Boolean] If ignore color markers into the message.
|
410
422
|
# @param indented_banner [Boolean] If also the banner should be indented.
|
@@ -415,14 +427,14 @@ module Bovem
|
|
415
427
|
# @see #format
|
416
428
|
def fatal(message, suffix = "\n", indent = true, wrap = false, plain = false, indented_banner = false, full_colored = false, return_code = -1, print = true)
|
417
429
|
self.error(message, suffix, indent, wrap, plain, indented_banner, full_colored, print)
|
418
|
-
Kernel.
|
430
|
+
Kernel.exit(return_code.to_integer(-1))
|
419
431
|
end
|
420
432
|
|
421
|
-
# Writes a message prepending a magenta
|
433
|
+
# Writes a message prepending a magenta banner.
|
422
434
|
#
|
423
435
|
# @param message [String] The message to format.
|
424
436
|
# @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
|
425
|
-
# @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use
|
437
|
+
# @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.
|
426
438
|
# @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
|
427
439
|
# @param plain [Boolean] If ignore color markers into the message.
|
428
440
|
# @param indented_banner [Boolean] If also the banner should be indented.
|
@@ -443,8 +455,6 @@ module Bovem
|
|
443
455
|
# @param validator [Array|Regexp] An array of values or a Regexp to match the submitted value against.
|
444
456
|
# @param echo [Boolean] If to show submitted text to the user.
|
445
457
|
def read(prompt = true, default_value = nil, validator = nil, echo = true)
|
446
|
-
# TODO: Echo and print prompt without newline
|
447
|
-
|
448
458
|
# Write the prompt
|
449
459
|
prompt = "Please insert a value" if prompt == true
|
450
460
|
final_prompt = !prompt.nil? ? prompt.gsub(/:?\s*$/, "") + ": " : nil
|
@@ -495,11 +505,11 @@ module Bovem
|
|
495
505
|
end
|
496
506
|
end
|
497
507
|
|
498
|
-
#
|
508
|
+
# Executes a block of code in a indentation region and then prints out and ending status message.
|
499
509
|
#
|
500
510
|
# @param message [String] The message to format.
|
501
511
|
# @param suffix [Object] If not `nil` or `false`, a suffix to add to the message. `true` means to add `\n`.
|
502
|
-
# @param indent [Object] If not `nil` or `false`, the width to use for indentation. `true` means to use
|
512
|
+
# @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.
|
503
513
|
# @param wrap [Object] If not `nil` or `false`, the maximum length of a line for wrapped text. `true` means the current line width.
|
504
514
|
# @param plain [Boolean] If ignore color markers into the message.
|
505
515
|
# @param indented_banner [Boolean] If also the banner should be indented.
|
data/lib/bovem/logger.rb
CHANGED
@@ -13,7 +13,8 @@ module Bovem
|
|
13
13
|
# The file or device to log messages to.
|
14
14
|
attr_reader :device
|
15
15
|
|
16
|
-
# Creates a new logger
|
16
|
+
# Creates a new logger.
|
17
|
+
#
|
17
18
|
# @see http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html
|
18
19
|
#
|
19
20
|
# @param logdev [String|IO] The log device. This is a filename (String) or IO object (typically STDOUT, STDERR, or an open file).
|
@@ -24,7 +25,7 @@ module Bovem
|
|
24
25
|
super(logdev, shift_age, shift_size)
|
25
26
|
end
|
26
27
|
|
27
|
-
# Creates a new logger
|
28
|
+
# Creates a new logger.
|
28
29
|
#
|
29
30
|
# @param file [String|IO] The log device. This is a filename (String) or IO object (typically STDOUT, STDERR, or an open file).
|
30
31
|
# @param level [Fixnum] The minimum severity to log. See http://www.ruby-doc.org/stdlib-1.9.3/libdoc/logger/rdoc/Logger.html for valid levels.
|
data/lib/bovem/shell.rb
ADDED
@@ -0,0 +1,377 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
#
|
3
|
+
# This file is part of the bovem gem. Copyright (C) 2012 and above Shogun <shogun_panda@me.com>.
|
4
|
+
# Licensed under the MIT license, which can be found at http://www.opensource.org/licenses/mit-license.php.
|
5
|
+
#
|
6
|
+
|
7
|
+
module Bovem
|
8
|
+
# A utility class for most common shell operation.
|
9
|
+
class Shell
|
10
|
+
# A {Console Console} instance.
|
11
|
+
attr_accessor :console
|
12
|
+
|
13
|
+
# Returns a unique instance for Shell.
|
14
|
+
#
|
15
|
+
# @return [Shell] A new instance.
|
16
|
+
def self.instance
|
17
|
+
@instance ||= ::Bovem::Shell.new
|
18
|
+
end
|
19
|
+
|
20
|
+
# Initializes a new Shell.
|
21
|
+
def initialize
|
22
|
+
@console = ::Bovem::Console.instance
|
23
|
+
end
|
24
|
+
|
25
|
+
# Runs a command into the shell.
|
26
|
+
#
|
27
|
+
# @param command [String] The string to run.
|
28
|
+
# @param message [String] A message to show before running.
|
29
|
+
# @param run [Boolean] If `false`, it will just print a message with the full command that will be run.
|
30
|
+
# @param show_exit [Boolean] If show the exit status.
|
31
|
+
# @param show_output [Boolean] If show command output.
|
32
|
+
# @param fatal [Boolean] If quit in case of fatal errors.
|
33
|
+
# @return [Hash] An hash with `status` and `output` keys.
|
34
|
+
def run(command, message = nil, run = true, show_exit = true, show_output = false, fatal = true)
|
35
|
+
rv = {:status => 0, :output => ""}
|
36
|
+
command = command.ensure_string
|
37
|
+
|
38
|
+
# Show the command
|
39
|
+
self.console.begin(message) if message.present?
|
40
|
+
|
41
|
+
|
42
|
+
if !run then # Print a message
|
43
|
+
self.console.warn("Will run command: {mark=bright}\"#{command}\"{/mark}...")
|
44
|
+
self.console.status(:ok) if show_exit
|
45
|
+
else # Run
|
46
|
+
output = ""
|
47
|
+
|
48
|
+
rv[:status] = ::Open4::open4(command + " 2>&1") { |pid, stdin, stdout, stderr|
|
49
|
+
stdout.each_line do |line|
|
50
|
+
output << line
|
51
|
+
Kernel.print line if show_output
|
52
|
+
end
|
53
|
+
}.exitstatus
|
54
|
+
|
55
|
+
rv[:output] = output
|
56
|
+
end
|
57
|
+
|
58
|
+
# Return
|
59
|
+
self.console.status(rv[:status] == 0 ? :ok : :fail) if show_exit
|
60
|
+
exit(rv[:status]) if fatal && rv[:status] != 0
|
61
|
+
rv
|
62
|
+
end
|
63
|
+
|
64
|
+
# Tests a path against a list of test.
|
65
|
+
#
|
66
|
+
# Valid tests are every method available in http://www.ruby-doc.org/core-1.9.3/FileTest.html (plus `read`, `write`, `execute`, `exec`, `dir`). Trailing question mark can be omitted.
|
67
|
+
#
|
68
|
+
#
|
69
|
+
# @param path [String] The path to test.
|
70
|
+
# @param tests [Array] The list of tests to perform.
|
71
|
+
def check(path, tests)
|
72
|
+
path = path.ensure_string
|
73
|
+
|
74
|
+
tests.ensure_array.all? {|test|
|
75
|
+
# Adjust test name
|
76
|
+
test = test.ensure_string.strip
|
77
|
+
|
78
|
+
test = case test
|
79
|
+
when "read" then "readable"
|
80
|
+
when "write" then "writable"
|
81
|
+
when "execute", "exec" then "executable"
|
82
|
+
when "dir" then "directory"
|
83
|
+
else test
|
84
|
+
end
|
85
|
+
|
86
|
+
# Execute test
|
87
|
+
test += "?" if test !~ /\?$/
|
88
|
+
FileTest.respond_to?(test) ? FileTest.send(test, path) : nil
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
# Deletes a list of files.
|
93
|
+
#
|
94
|
+
# @param files [Array] The list of files to remove
|
95
|
+
# @param run [Boolean] If `false`, it will just print a list of message that would be deleted.
|
96
|
+
# @param show_errors [Boolean] If show errors.
|
97
|
+
# @param fatal [Boolean] If quit in case of fatal errors.
|
98
|
+
# @return [Boolean] `true` if operation succeeded, `false` otherwise.
|
99
|
+
def delete(files, run = true, show_errors = false, fatal = true)
|
100
|
+
rv = true
|
101
|
+
files = files.ensure_array.compact.collect {|f| File.expand_path(f.ensure_string) }
|
102
|
+
|
103
|
+
if !run then
|
104
|
+
self.console.warn("Will remove file(s):")
|
105
|
+
self.console.with_indentation(11) do
|
106
|
+
files.each do |file| self.console.write(file) end
|
107
|
+
end
|
108
|
+
else
|
109
|
+
rv = catch(:rv) do
|
110
|
+
begin
|
111
|
+
FileUtils.rm_r(files, {:noop => false, :verbose => false, :secure => true})
|
112
|
+
throw(:rv, true)
|
113
|
+
rescue Errno::EACCES => e
|
114
|
+
self.console.send(fatal ? :fatal : :error, "Cannot remove following non writable file: {mark=bright}#{e.message.gsub(/.+ - (.+)/, "\\1")}{/mark}")
|
115
|
+
rescue Errno::ENOENT => e
|
116
|
+
self.console.send(fatal ? :fatal : :error, "Cannot remove following non existent file: {mark=bright}#{e.message.gsub(/.+ - (.+)/, "\\1")}{/mark}")
|
117
|
+
rescue Exception => e
|
118
|
+
if show_errors then
|
119
|
+
self.console.error("Cannot remove following file(s):")
|
120
|
+
self.console.with_indentation(11) do
|
121
|
+
files.each do |file| self.console.write(file) end
|
122
|
+
end
|
123
|
+
self.console.write("due to this error: [#{e.class.to_s}] #{e}.", "\n", 5)
|
124
|
+
Kernel.exit(-1) if fatal
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
false
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
rv
|
133
|
+
end
|
134
|
+
|
135
|
+
# Copies or moves a set of files or directory to another location.
|
136
|
+
#
|
137
|
+
# @param src [String|Array] The entries to copy or move. If is an Array, `dst` is assumed to be a directory.
|
138
|
+
# @param dst [String] The destination. **Any existing entries will be overwritten.** Any required directory will be created.
|
139
|
+
# @param operation [Symbol] The operation to perform. Valid values are `:copy` or `:move`.
|
140
|
+
# @param run [Boolean] If `false`, it will just print a list of message that would be copied or moved.
|
141
|
+
# @param show_errors [Boolean] If show errors.
|
142
|
+
# @param fatal [Boolean] If quit in case of fatal errors.
|
143
|
+
# @return [Boolean] `true` if operation succeeded, `false` otherwise.
|
144
|
+
def copy_or_move(src, dst, operation, run = true, show_errors = false, fatal = true)
|
145
|
+
rv = true
|
146
|
+
operation = :copy if operation != :move
|
147
|
+
single = !src.is_a?(Array)
|
148
|
+
|
149
|
+
if single then
|
150
|
+
src = File.expand_path(src)
|
151
|
+
else
|
152
|
+
src = src.collect {|s| File.expand_path(s) }
|
153
|
+
end
|
154
|
+
|
155
|
+
dst = File.expand_path(dst.ensure_string)
|
156
|
+
|
157
|
+
if !run then
|
158
|
+
if single then
|
159
|
+
self.console.warn("Will #{operation} a file:")
|
160
|
+
self.console.write("From: {mark=bright}#{File.expand_path(src.ensure_string)}{/mark}", "\n", 11)
|
161
|
+
self.console.write("To: {mark=bright}#{dst}{/mark}", "\n", 11)
|
162
|
+
else
|
163
|
+
self.console.warn("Will #{operation} following entries:")
|
164
|
+
self.console.with_indentation(11) do
|
165
|
+
src.each do |s| self.console.write(s) end
|
166
|
+
end
|
167
|
+
self.console.write("to directory: {mark=bright}#{dst}{/mark}", "\n", 5)
|
168
|
+
end
|
169
|
+
else
|
170
|
+
rv = catch(:rv) do
|
171
|
+
dst_dir = single ? File.dirname(dst) : dst
|
172
|
+
has_dir = self.check(dst_dir, :dir)
|
173
|
+
|
174
|
+
# Create directory
|
175
|
+
has_dir = self.create_directories(dst_dir, 0755, true, show_errors, fatal) if !has_dir
|
176
|
+
throw(:rv, false) if !has_dir
|
177
|
+
|
178
|
+
if single && self.check(dst, :dir) then
|
179
|
+
@console.send(fatal ? :fatal : :error, "Cannot #{operation} file {mark=bright}#{src}{/mark} to {mark=bright}#{dst}{/mark} because it is currently a directory.")
|
180
|
+
throw(:rv, false)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Check that every file is existing
|
184
|
+
src.ensure_array.each do |s|
|
185
|
+
if !self.check(s, :exists) then
|
186
|
+
@console.send(fatal ? :fatal : :error, "Cannot #{operation} non existent file {mark=bright}#{s}{/mark}.")
|
187
|
+
throw(:rv, false)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Do operation
|
192
|
+
begin
|
193
|
+
FileUtils.send(operation == :move ? :move : :cp_r, src, dst, {:noop => false, :verbose => false})
|
194
|
+
rescue Errno::EACCES => e
|
195
|
+
if single then
|
196
|
+
@console.send(fatal ? :fatal : :error, "Cannot #{operation} file {mark=bright}#{src}{/mark} to non writable directory {mark=bright}#{dst_dir}{/mark}.")
|
197
|
+
else
|
198
|
+
self.console.error("Cannot #{operation} following file(s) to non writable directory {mark=bright}#{dst}{/mark}:")
|
199
|
+
self.console.with_indentation(11) do
|
200
|
+
src.each do |s| self.console.write(s) end
|
201
|
+
end
|
202
|
+
Kernel.exit(-1) if fatal
|
203
|
+
end
|
204
|
+
|
205
|
+
throw(:rv, false)
|
206
|
+
rescue Exception => e
|
207
|
+
if single then
|
208
|
+
@console.send(fatal ? :fatal : :error, "Cannot #{operation} file {mark=bright}#{src}{/mark} to directory {mark=bright}#{dst_dir}{/mark} due to this error: [#{e.class.to_s}] #{e}.", "\n", 5)
|
209
|
+
else
|
210
|
+
self.console.error("Cannot #{operation} following entries to {mark=bright}#{dst}{/mark}:")
|
211
|
+
self.console.with_indentation(11) do
|
212
|
+
src.each do |s| self.console.write(s) end
|
213
|
+
end
|
214
|
+
self.console.write("due to this error: [#{e.class.to_s}] #{e}.", "\n", 5)
|
215
|
+
Kernel.exit(-1) if fatal
|
216
|
+
end
|
217
|
+
|
218
|
+
throw(:rv, false)
|
219
|
+
end
|
220
|
+
|
221
|
+
true
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
rv
|
226
|
+
end
|
227
|
+
|
228
|
+
# Copies a set of files or directory to another location.
|
229
|
+
#
|
230
|
+
# @param src [String|Array] The entries to copy. If is an Array, `dst` is assumed to be a directory.
|
231
|
+
# @param dst [String] The destination. **Any existing entries will be overwritten.** Any required directory will be created.
|
232
|
+
# @param run [Boolean] If `false`, it will just print a list of message that would be copied or moved.
|
233
|
+
# @param show_errors [Boolean] If show errors.
|
234
|
+
# @param fatal [Boolean] If quit in case of fatal errors.
|
235
|
+
# @return [Boolean] `true` if operation succeeded, `false` otherwise.
|
236
|
+
def copy(src, dst, run = true, show_errors = false, fatal = true)
|
237
|
+
self.copy_or_move(src, dst, :copy, run, show_errors, fatal)
|
238
|
+
end
|
239
|
+
|
240
|
+
# Moves a set of files or directory to another location.
|
241
|
+
#
|
242
|
+
# @param src [String|Array] The entries to move. If is an Array, `dst` is assumed to be a directory.
|
243
|
+
# @param dst [String] The destination. **Any existing entries will be overwritten.** Any required directory will be created.
|
244
|
+
# @param run [Boolean] If `false`, it will just print a list of message that would be deleted.
|
245
|
+
# @param show_errors [Boolean] If show errors.
|
246
|
+
# @param fatal [Boolean] If quit in case of fatal errors.
|
247
|
+
# @return [Boolean] `true` if operation succeeded, `false` otherwise.
|
248
|
+
def move(src, dst, run = true, show_errors = false, fatal = true)
|
249
|
+
self.copy_or_move(src, dst, :move, run, show_errors, fatal)
|
250
|
+
end
|
251
|
+
|
252
|
+
# Executes a block of code in another directory.
|
253
|
+
#
|
254
|
+
# @param directory [String] The new working directory.
|
255
|
+
# @param restore [Boolean] If to restore the original working directory.
|
256
|
+
# @param show_messages [Boolean] Show informative messages about working directory changes.
|
257
|
+
# @return [Boolean] `true` if the directory was valid and the code executed, `false` otherwise.
|
258
|
+
def within_directory(directory, restore = true, show_messages = false)
|
259
|
+
rv = false
|
260
|
+
original = Dir.pwd
|
261
|
+
directory = File.expand_path(directory.ensure_string)
|
262
|
+
|
263
|
+
if self.check(directory, [:directory, :executable]) then
|
264
|
+
begin
|
265
|
+
self.console.info("Moving into directory {mark=bright}#{directory}{/mark}")
|
266
|
+
Dir.chdir(directory)
|
267
|
+
rv = true
|
268
|
+
rescue Exception => e
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
yield if rv && block_given?
|
273
|
+
|
274
|
+
if rv && original then
|
275
|
+
begin
|
276
|
+
self.console.info("Moving back into directory {mark=bright}#{original}{/mark}")
|
277
|
+
Dir.chdir(original) if restore
|
278
|
+
rescue Exception => e
|
279
|
+
rv = false
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
rv
|
284
|
+
end
|
285
|
+
|
286
|
+
# Creates a list of directories, included missing parent directories.
|
287
|
+
#
|
288
|
+
# @param directories [Array] The list of directories to create.
|
289
|
+
# @param mode [Fixnum] Initial permissions for the new directories.
|
290
|
+
# @param run [Boolean] If `false`, it will just print a list of directories that would be created.
|
291
|
+
# @param show_errors [Boolean] If show errors.
|
292
|
+
# @param fatal [Boolean] If quit in case of fatal errors.
|
293
|
+
# @return [Boolean] `true` if operation succeeded, `false` otherwise.
|
294
|
+
def create_directories(directories, mode = 0755, run = true, show_errors = false, fatal = true)
|
295
|
+
rv = true
|
296
|
+
|
297
|
+
# Adjust directory
|
298
|
+
directories = directories.ensure_array.compact {|d| File.expand_path(d.ensure_string) }
|
299
|
+
|
300
|
+
if !run then # Just print
|
301
|
+
self.console.warn("Will create directories:")
|
302
|
+
self.console.with_indentation(11) do
|
303
|
+
directories.each do |directory| self.console.write(directory) end
|
304
|
+
end
|
305
|
+
else
|
306
|
+
directories.each do |directory|
|
307
|
+
rv = catch(:rv) do
|
308
|
+
# Perform tests
|
309
|
+
if self.check(directory, :directory) then
|
310
|
+
self.console.send(fatal ? :fatal : :error, "The directory {mark=bright}#{directory}{/mark} already exists.")
|
311
|
+
elsif self.check(directory, :exist) then
|
312
|
+
self.console.send(fatal ? :fatal : :error, "Path {mark=bright}#{directory}{/mark} is currently a file.")
|
313
|
+
else
|
314
|
+
begin # Create directory
|
315
|
+
FileUtils.mkdir_p(directory, {:mode => mode, :noop => false, :verbose => false})
|
316
|
+
throw(:rv, true)
|
317
|
+
rescue Errno::EACCES => e
|
318
|
+
self.console.send(fatal ? :fatal : :error, "Cannot create following directory due to permission denied: {mark=bright}#{e.message.gsub(/.+ - (.+)/, "\\1")}{/mark}.")
|
319
|
+
rescue Exception => e
|
320
|
+
if show_errors then
|
321
|
+
self.console.error("Cannot create following directories:")
|
322
|
+
self.console.with_indentation(11) do
|
323
|
+
directories.each do |directory| self.console.write(directory) end
|
324
|
+
end
|
325
|
+
self.console.write("due to this error: [#{e.class.to_s}] #{e}.", "\n", 5)
|
326
|
+
Kernel.exit(-1) if fatal
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
false
|
332
|
+
end
|
333
|
+
|
334
|
+
break if !rv
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
rv
|
339
|
+
end
|
340
|
+
|
341
|
+
# Find a list of files in directories matching given regexps or patterns.
|
342
|
+
#
|
343
|
+
# @param directories [String] A list of directories where to search files.
|
344
|
+
# @param patterns [Array] A list of regexps or patterns to match files. If empty, every file is returned.
|
345
|
+
# @param by_extension [Boolean] If to only search in extensions.
|
346
|
+
# @param case_sensitive [Boolean] If the search is case sensitive. Only meaningful for string patterns.
|
347
|
+
def find(directories, patterns = [], by_extension = false, case_sensitive = false)
|
348
|
+
rv = []
|
349
|
+
|
350
|
+
# Adjust directory
|
351
|
+
directories = directories.ensure_array.compact {|d| File.expand_path(d.ensure_string) }
|
352
|
+
|
353
|
+
# Adjust patterns
|
354
|
+
patterns = patterns.ensure_array.compact.collect {|p| p.is_a?(::Regexp) ? p : Regexp.new(Regexp.quote(p.ensure_string)) }
|
355
|
+
patterns = patterns.collect {|p| /(#{p.source})$/ } if by_extension
|
356
|
+
patterns = patterns.collect {|p| /#{p.source}/i } if !case_sensitive
|
357
|
+
|
358
|
+
directories.each do |directory|
|
359
|
+
if self.check(directory, [:directory, :readable, :executable]) then
|
360
|
+
Find.find(directory) do |entry|
|
361
|
+
found = patterns.blank? ? true : catch(:found) do
|
362
|
+
patterns.each do |pattern|
|
363
|
+
throw(:found, true) if pattern.match(entry) && (!by_extension || !File.directory?(entry))
|
364
|
+
end
|
365
|
+
|
366
|
+
false
|
367
|
+
end
|
368
|
+
|
369
|
+
rv << entry if found
|
370
|
+
end
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
rv
|
375
|
+
end
|
376
|
+
end
|
377
|
+
end
|