bovem 0.8.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|