ratch 0.1
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/LICENSE.txt +344 -0
- data/README.txt +10 -0
- data/bin/lt +4 -0
- data/bin/ludo +4 -0
- data/bin/ratch +8 -0
- data/data/mint/ratch/announce +224 -0
- data/data/mint/ratch/install +49 -0
- data/data/mint/ratch/notes +183 -0
- data/data/mint/ratch/publish +44 -0
- data/data/mint/ratch/rdoc +40 -0
- data/data/mint/ratch/setup +1616 -0
- data/data/mint/ratch/stats +138 -0
- data/demo/README +8 -0
- data/demo/doc/rdoc/created.rid +1 -0
- data/demo/doc/rdoc/files/README.html +112 -0
- data/demo/doc/rdoc/files/lib/foo/foo_rb.html +145 -0
- data/demo/doc/rdoc/fr_class_index.html +26 -0
- data/demo/doc/rdoc/fr_file_index.html +28 -0
- data/demo/doc/rdoc/fr_method_index.html +27 -0
- data/demo/doc/rdoc/index.html +24 -0
- data/demo/doc/rdoc/rdoc-style.css +208 -0
- data/demo/lib/foo/foo.rb +7 -0
- data/demo/util/conf/rdoc +4 -0
- data/demo/util/one +6 -0
- data/demo/util/rdoc +39 -0
- data/demo/util/tryme +10 -0
- data/dev/taskable-simple.rb +42 -0
- data/dev/taskable.rb +573 -0
- data/lib/ratch/batch.rb +43 -0
- data/lib/ratch/cli/lt.rb +56 -0
- data/lib/ratch/cli/ludo.rb +14 -0
- data/lib/ratch/cli/ratch.rb +47 -0
- data/lib/ratch/configutils.rb +100 -0
- data/lib/ratch/consoleutils.rb +88 -0
- data/lib/ratch/emailutils.rb +88 -0
- data/lib/ratch/fileutils.rb +173 -0
- data/lib/ratch/options.rb +57 -0
- data/lib/ratch/runnable.rb +117 -0
- data/lib/ratch/taskable.rb +105 -0
- data/lib/ratch/taskutils.rb +44 -0
- data/lib/ratch/uploadutils.rb +413 -0
- data/meta/manifest.txt +70 -0
- data/meta/project.yaml +24 -0
- data/misc/original.rb +308 -0
- data/task/setup +1616 -0
- data/task/stats +138 -0
- metadata +114 -0
data/lib/ratch/batch.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#require 'shellwords'
|
2
|
+
require 'rbconfig' # replace with facets/rbsystem in future ?
|
3
|
+
require 'ratch/taskutils'
|
4
|
+
|
5
|
+
module Ratch
|
6
|
+
|
7
|
+
# Batch File class, is used as an executionm context for a
|
8
|
+
# ratch script.
|
9
|
+
|
10
|
+
class BatchFile < Module
|
11
|
+
|
12
|
+
include TaskUtils
|
13
|
+
|
14
|
+
# Quick start, equivalent to calling new.run(file).
|
15
|
+
|
16
|
+
#def self.start(file)
|
17
|
+
# new(file).call
|
18
|
+
#end
|
19
|
+
|
20
|
+
# New Batch File
|
21
|
+
|
22
|
+
def initialize(file)
|
23
|
+
abort "missing batch file -- #{file}" unless File.file?(file)
|
24
|
+
@file = file
|
25
|
+
end
|
26
|
+
|
27
|
+
# TODO What todo about arguments?
|
28
|
+
|
29
|
+
def call(arguments=nil)
|
30
|
+
script = File.read($0 = @file)
|
31
|
+
eval(script, binding, $0) #instance_eval(script)
|
32
|
+
@main.call if @main
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
# Load TaskUtils directly into to main runspace.
|
40
|
+
|
41
|
+
#class << self
|
42
|
+
# include TaskUtils
|
43
|
+
#end
|
data/lib/ratch/cli/lt.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
#! /usr/bin/ruby1.8
|
2
|
+
|
3
|
+
# scan task scripts for descriptions
|
4
|
+
|
5
|
+
def script_desc( dir )
|
6
|
+
help = {}
|
7
|
+
files = Dir.glob( File.join( dir, '*' ) )
|
8
|
+
files.each do |fname|
|
9
|
+
next if FileTest.directory?( fname )
|
10
|
+
next unless FileTest.executable?( fname )
|
11
|
+
desc = ''
|
12
|
+
File.open(fname) do |f|
|
13
|
+
line = ''
|
14
|
+
until f.eof?
|
15
|
+
line = f.gets
|
16
|
+
case line
|
17
|
+
when /^(#!|\s*$)/
|
18
|
+
next
|
19
|
+
when /^\s*#(.*)/
|
20
|
+
desc = $1.strip; break
|
21
|
+
else
|
22
|
+
desc = nil; break
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
help[File.basename(fname)] = desc
|
27
|
+
end
|
28
|
+
help
|
29
|
+
end
|
30
|
+
|
31
|
+
def show( dir )
|
32
|
+
tasks = script_desc( dir )
|
33
|
+
max = tasks.keys.max{ |a,b| a.size <=> b.size }.size
|
34
|
+
if dir == ''
|
35
|
+
max += 4 + 2
|
36
|
+
else
|
37
|
+
max += dir.size + 2
|
38
|
+
end
|
39
|
+
tasks.each do |name, sum|
|
40
|
+
#sum = Sake.help_summary( type )
|
41
|
+
if dir == ''
|
42
|
+
cmd = "sake #{name}"
|
43
|
+
else
|
44
|
+
cmd = File.join( dir.chomp('/'), name )
|
45
|
+
end
|
46
|
+
puts "%-#{max}s # %s" % [cmd, sum]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
dir = ARGV[0] || '.'
|
51
|
+
|
52
|
+
if File.directory?( dir )
|
53
|
+
show( dir )
|
54
|
+
else
|
55
|
+
puts "#{dir} is not a directory"
|
56
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
#! /usr/bin/ruby1.8
|
2
|
+
|
3
|
+
require 'facets/filetest'
|
4
|
+
|
5
|
+
name = ARGV[0]
|
6
|
+
|
7
|
+
if name
|
8
|
+
Dir.chdir '..' until FileTest.executable?(name) or FileTest.root?(Dir.pwd)
|
9
|
+
if FileTest.executable?( name )
|
10
|
+
system name
|
11
|
+
end
|
12
|
+
else
|
13
|
+
puts "Script #{name} not found."
|
14
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#require 'shellwords'
|
2
|
+
require 'rbconfig' # replace with facets/rbsystem in future ?
|
3
|
+
require 'ratch/batch'
|
4
|
+
|
5
|
+
module Ratch
|
6
|
+
|
7
|
+
class RatchCommand
|
8
|
+
|
9
|
+
# Quick start.
|
10
|
+
|
11
|
+
def self.start(file)
|
12
|
+
new.run(file)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Run task.
|
16
|
+
|
17
|
+
def run(file)
|
18
|
+
if file
|
19
|
+
BatchFile.new(file).call
|
20
|
+
else
|
21
|
+
help
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Dispaly help.
|
26
|
+
|
27
|
+
def help
|
28
|
+
help = <<-END
|
29
|
+
USAGE:
|
30
|
+
|
31
|
+
ratch [options] <taskfile>
|
32
|
+
|
33
|
+
OPTIONS:
|
34
|
+
|
35
|
+
--dryrun --nohram
|
36
|
+
|
37
|
+
--trace
|
38
|
+
|
39
|
+
--debug
|
40
|
+
END
|
41
|
+
puts help.gsub(/^\s+/, '')
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
|
2
|
+
module Ratch
|
3
|
+
|
4
|
+
module ConfigUtils
|
5
|
+
|
6
|
+
DEFAULT_CONFIG_FILE = 'config'
|
7
|
+
|
8
|
+
#
|
9
|
+
|
10
|
+
def configuration(file=nil)
|
11
|
+
@configuration ||= {}
|
12
|
+
if file = config_file(file)
|
13
|
+
@configuration[file] ||= config_read(file)
|
14
|
+
else
|
15
|
+
@configuration[file] ||= Hash.new{ |h,k| h[k] = {} }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
|
21
|
+
def config_file(path=nil)
|
22
|
+
path ||= DEFAULT_CONFIG_FILE
|
23
|
+
find = "{#{utility_directory}/,}#{path}{.yaml,.yml,}"
|
24
|
+
file = Dir.glob(find)[0]
|
25
|
+
return file
|
26
|
+
end
|
27
|
+
|
28
|
+
# Load configuration data from a file. THe file will
|
29
|
+
# be looked for in the current script directory then
|
30
|
+
# from the project root.
|
31
|
+
#
|
32
|
+
# Since they are YAML files, they can optionally
|
33
|
+
# end with '.yaml' or '.yml'.
|
34
|
+
|
35
|
+
def config_read(path)
|
36
|
+
find = "{#{utility_directory}/,}#{path}{.yaml,.yml,}"
|
37
|
+
if file = Dir.glob(find)[0]
|
38
|
+
YAML::load(File.open(file))
|
39
|
+
else
|
40
|
+
raise LoadError, "Missing file -- #{path}"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# TODO Better name? Better definition? (Won't handle task subdirs well).
|
45
|
+
|
46
|
+
def utility_directory
|
47
|
+
File.dirname($0)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Create an argument vector from a set of config options.
|
51
|
+
# TODO Deprecate in favor of Hash extension.
|
52
|
+
|
53
|
+
def config_vector(config, args_field=nil)
|
54
|
+
config.command_vector(args_field)
|
55
|
+
end
|
56
|
+
|
57
|
+
# # FIXME ProjectInfo, if available.
|
58
|
+
#
|
59
|
+
# def projectinfo
|
60
|
+
# require 'box/project'
|
61
|
+
# @projectinfo
|
62
|
+
#
|
63
|
+
# #begin
|
64
|
+
# # @projectinfo = ProjectInfo.open
|
65
|
+
# #rescue LoadError
|
66
|
+
# # @projectinfo = nil
|
67
|
+
# #end
|
68
|
+
# end
|
69
|
+
|
70
|
+
# def config_file
|
71
|
+
# @config_file ||= (
|
72
|
+
# dir = utility_directory
|
73
|
+
# name = Dir.basename(dir)
|
74
|
+
# Dir.glob("{#{dir},meta,info,}/{#{name},config}{.yaml,.yml}")
|
75
|
+
# )
|
76
|
+
# end
|
77
|
+
|
78
|
+
# # Load task configuration if any.
|
79
|
+
#
|
80
|
+
# def config_load(*names) #, defaults=nil)
|
81
|
+
#
|
82
|
+
# names.inject({}) do |memo, name|
|
83
|
+
# name = name.to_s
|
84
|
+
# #defaults = defaults || {}
|
85
|
+
#
|
86
|
+
# if file = config_file(name)
|
87
|
+
# config = YAML.load(File.open(file))
|
88
|
+
# elsif file = Dir.glob("#{utility_directory}/config{,.yaml,.yml}")[0]
|
89
|
+
# config = YAML.load(File.open(file))[name.to_s]
|
90
|
+
# else
|
91
|
+
# config = {}
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# config.update(memo)
|
95
|
+
# end
|
96
|
+
# #return defaults.update(config || {})
|
97
|
+
# end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Ratch
|
2
|
+
|
3
|
+
module ConsoleUtils
|
4
|
+
|
5
|
+
# Convenient method to get simple console reply.
|
6
|
+
|
7
|
+
def ask(question, answers=nil)
|
8
|
+
print "#{question}"
|
9
|
+
print " [#{answers}] " if answers
|
10
|
+
until inp = $stdin.gets ; sleep 1 ; end
|
11
|
+
inp
|
12
|
+
end
|
13
|
+
|
14
|
+
# Ask for a password. (FIXME: only for unix so far)
|
15
|
+
|
16
|
+
def password(prompt=nil)
|
17
|
+
msg ||= "Enter Password: "
|
18
|
+
inp = ''
|
19
|
+
|
20
|
+
print "#{prompt} "
|
21
|
+
|
22
|
+
begin
|
23
|
+
system "stty -echo"
|
24
|
+
#inp = gets.chomp
|
25
|
+
until inp = $stdin.gets
|
26
|
+
sleep 1
|
27
|
+
end
|
28
|
+
ensure
|
29
|
+
system "stty echo"
|
30
|
+
end
|
31
|
+
|
32
|
+
return inp.chomp
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
class Array
|
41
|
+
|
42
|
+
# Convert an array into command line parameters.
|
43
|
+
# The array is accepted in the format of Ruby
|
44
|
+
# method arguments --ie. [arg1, arg2, ..., hash]
|
45
|
+
|
46
|
+
def to_params
|
47
|
+
flags = (Hash===last ? pop : {})
|
48
|
+
flags = flags.collect do |f,v|
|
49
|
+
m = f.to_s.size == 1 ? '-' : '--'
|
50
|
+
case v
|
51
|
+
when Array
|
52
|
+
v.collect{ |e| "#{m}#{f} '#{e}'" }.join(' ')
|
53
|
+
when true
|
54
|
+
"#{m}#{f}"
|
55
|
+
when false, nil
|
56
|
+
''
|
57
|
+
else
|
58
|
+
"#{m}#{f} '#{v}'"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
return (flags + self).join(" ")
|
62
|
+
end
|
63
|
+
|
64
|
+
# Not empty?
|
65
|
+
|
66
|
+
def not_empty?
|
67
|
+
!empty?
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
class Hash
|
74
|
+
|
75
|
+
# Setup a console arguments vector from a set of config options.
|
76
|
+
|
77
|
+
def command_vector(args_field=nil)
|
78
|
+
config = dup
|
79
|
+
if args_field
|
80
|
+
args = [config.delete(args_field)].flatten.compact
|
81
|
+
else
|
82
|
+
args = []
|
83
|
+
end
|
84
|
+
args << config
|
85
|
+
return args
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
begin
|
2
|
+
require 'facets/net/smtp_tls'
|
3
|
+
rescue LoadError
|
4
|
+
require 'net/smtp'
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
module Ratch
|
9
|
+
|
10
|
+
module EmailUtils
|
11
|
+
|
12
|
+
module_function
|
13
|
+
|
14
|
+
# Email function to easily send out an email.
|
15
|
+
#
|
16
|
+
# Settings:
|
17
|
+
#
|
18
|
+
# subject Subject of email message.
|
19
|
+
# from Message FROM address [email].
|
20
|
+
# to Email address to send announcemnt.
|
21
|
+
# server Email server to route message.
|
22
|
+
# port Email server's port.
|
23
|
+
# domain Email server's domain name.
|
24
|
+
# account Email account name.
|
25
|
+
# login Login type: plain, cram_md5 or login [plain].
|
26
|
+
# secure Uses TLS security, true or false? [false]
|
27
|
+
# message Mesage to send -or-
|
28
|
+
# file File that contains message.
|
29
|
+
#
|
30
|
+
# (Square brackets indicate defaults taken from Project information.
|
31
|
+
# if used via Project class.)
|
32
|
+
|
33
|
+
def email(message, settings)
|
34
|
+
server = settings['server']
|
35
|
+
account = settings['account']
|
36
|
+
login = settings['login'].to_sym
|
37
|
+
subject = settings['subject']
|
38
|
+
mail_to = settings['to'] || settings['mail_to']
|
39
|
+
mail_from = settings['from'] || settings['mail_from']
|
40
|
+
secure = settings['secure']
|
41
|
+
domain = settings['domain'] || server
|
42
|
+
|
43
|
+
port ||= (secure ? 465 : 25)
|
44
|
+
account ||= mail_from
|
45
|
+
login ||= :plain
|
46
|
+
|
47
|
+
#mail_to = nil if mail_to.empty?
|
48
|
+
|
49
|
+
raise ArgumentError, "missing email field -- server" unless server
|
50
|
+
raise ArgumentError, "missing email field -- account" unless account
|
51
|
+
raise ArgumentError, "missing email field -- subject" unless subject
|
52
|
+
raise ArgumentError, "missing email field -- to" unless mail_to
|
53
|
+
raise ArgumentError, "missing email field -- from" unless mail_from
|
54
|
+
|
55
|
+
passwd = password(account)
|
56
|
+
|
57
|
+
mail_to = [mail_to].flatten.compact
|
58
|
+
|
59
|
+
msg = ""
|
60
|
+
msg << "From: #{mail_from}\n"
|
61
|
+
msg << "To: #{mail_to.join(';')}\n"
|
62
|
+
msg << "Subject: #{subject}\n"
|
63
|
+
msg << ""
|
64
|
+
msg << message
|
65
|
+
|
66
|
+
begin
|
67
|
+
Net::SMTP.enable_tls if Net::SMTP.respond_to?(:enable_tls) and secure
|
68
|
+
Net::SMTP.start(server, port, domain, account, passwd, login) do |s|
|
69
|
+
s.send_message( msg, mail_from, mail_to )
|
70
|
+
end
|
71
|
+
puts "Email sent successfully to #{mail_to.join(';')}."
|
72
|
+
return true
|
73
|
+
rescue => e
|
74
|
+
if trace?
|
75
|
+
raise e
|
76
|
+
else
|
77
|
+
abort "Email delivery failed."
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#def password( account )
|
83
|
+
# @password || ENV['PASSWORD'] || ask("Password for #{account}: ")
|
84
|
+
#end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
|
4
|
+
module Ratch
|
5
|
+
|
6
|
+
module FileUtils
|
7
|
+
|
8
|
+
# Glob files.
|
9
|
+
|
10
|
+
def glob(*args, &blk)
|
11
|
+
Dir.glob(*args, &blk)
|
12
|
+
end
|
13
|
+
|
14
|
+
# Read file.
|
15
|
+
|
16
|
+
def file_read(path)
|
17
|
+
File.read(path)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Write file.
|
21
|
+
|
22
|
+
def file_write(path, text)
|
23
|
+
if dryrun?
|
24
|
+
puts "write #{path}"
|
25
|
+
else
|
26
|
+
File.open(path, 'w'){ |f| f << text }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
##########################
|
31
|
+
# Add FileUtils Features #
|
32
|
+
##########################
|
33
|
+
|
34
|
+
::FileUtils.private_instance_methods(false).each do |meth|
|
35
|
+
next if meth =~ /^fu_/
|
36
|
+
module_eval %{
|
37
|
+
def #{meth}(*a,&b)
|
38
|
+
fileutils.#{meth}(*a,&b)
|
39
|
+
end
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
# Delegate access to FileUtils.
|
44
|
+
|
45
|
+
def fileutils
|
46
|
+
dryrun? ? ::FileUtils::DryRun : ::FileUtils
|
47
|
+
end
|
48
|
+
|
49
|
+
# Bonus FileUtils features.
|
50
|
+
|
51
|
+
def cd(*a,&b)
|
52
|
+
puts "cd #{a}" if dryrun?
|
53
|
+
fileutils.chdir(*a,&b)
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
#########################
|
58
|
+
# Add FileTest Features #
|
59
|
+
#########################
|
60
|
+
|
61
|
+
FileTest.private_instance_methods(false).each do |meth|
|
62
|
+
next if meth =~ /^fu_/
|
63
|
+
module_eval %{
|
64
|
+
def #{meth}(*a,&b)
|
65
|
+
FileTest.#{meth}(*a,&b)
|
66
|
+
end
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
# Is a given path a regular file? If +path+ is a glob
|
71
|
+
# then checks to see if all matches are refular files.
|
72
|
+
|
73
|
+
def file?(path)
|
74
|
+
paths = Dir.glob(path)
|
75
|
+
paths.not_empty? && paths.all?{ |f| FileTest.file?(f) }
|
76
|
+
end
|
77
|
+
|
78
|
+
# Assert that a given path is a file.
|
79
|
+
|
80
|
+
def file!(*paths)
|
81
|
+
abort "file not found #{path}" unless paths.any?{|path| file?(path)}
|
82
|
+
end
|
83
|
+
|
84
|
+
# Is a given path a directory? If +path+ is a glob
|
85
|
+
# checks to see if all matches are directories.
|
86
|
+
|
87
|
+
def dir?(path)
|
88
|
+
paths = Dir.glob(path)
|
89
|
+
paths.not_empty? && paths.all?{ |f| FileTest.directory?(f) }
|
90
|
+
end
|
91
|
+
alias_method :directory?, :dir? ; module_function :directory?
|
92
|
+
|
93
|
+
# Assert that a given path is a directory.
|
94
|
+
|
95
|
+
def dir!(*paths)
|
96
|
+
paths.each do |path|
|
97
|
+
abort "Directory not found: '#{path}'." unless dir?(path)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
alias_method :directory!, :dir! ; module_function :directory!
|
101
|
+
|
102
|
+
# # Okay, I'm being a dork, but 'fold' seems like a better word
|
103
|
+
# # then 'dir', 'folder', or 'directory'.
|
104
|
+
#
|
105
|
+
# def fold?(path)
|
106
|
+
# paths = Dir.glob(path)
|
107
|
+
# paths.not_empty? && paths.all?{ |f| FileTest.directory?(f) }
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# # Assert that a given path is a fold (ie. a folder).
|
111
|
+
#
|
112
|
+
# def fold!(*paths)
|
113
|
+
# abort "fold not found #{path}" unless paths.any?{|path| fold?(path)}
|
114
|
+
# end
|
115
|
+
|
116
|
+
# Assert that a path exists.
|
117
|
+
|
118
|
+
def exists?(path)
|
119
|
+
paths = Dir.glob(path)
|
120
|
+
paths.not_empty?
|
121
|
+
end
|
122
|
+
alias_method :exist?, :exists? ; module_function :exist?
|
123
|
+
alias_method :path?, :exists? ; module_function :path?
|
124
|
+
|
125
|
+
# Assert that a path exists.
|
126
|
+
|
127
|
+
def exists!(*paths)
|
128
|
+
abort "path not found #{path}" unless paths.any?{|path| exists?(path)}
|
129
|
+
end
|
130
|
+
alias_method :exist!, :exists! ; module_function :exist!
|
131
|
+
alias_method :path!, :exists! ; module_function :path!
|
132
|
+
|
133
|
+
# Is a file a task?
|
134
|
+
|
135
|
+
def task?(path)
|
136
|
+
task = File.dirname($0) + "/#{path}"
|
137
|
+
task.chomp!('!')
|
138
|
+
task if FileTest.file?(task) && FileTest.executable?(task)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Is a file a command executable?
|
142
|
+
#
|
143
|
+
# TODO Probably needs to be fixed for Windows.
|
144
|
+
|
145
|
+
def bin?(path)
|
146
|
+
is_bin = command_paths.any? do |f|
|
147
|
+
FileTest.exist?(File.join(f, path))
|
148
|
+
end
|
149
|
+
is_bin ? File.basename(path) : false
|
150
|
+
end
|
151
|
+
|
152
|
+
# This is a support method of #bin?
|
153
|
+
|
154
|
+
def command_paths
|
155
|
+
@command_paths ||= ENV['PATH'].split(':')
|
156
|
+
end
|
157
|
+
|
158
|
+
# TODO Make more robust.
|
159
|
+
|
160
|
+
UNSAFE = [ '/', '/*', '/**/*' ]
|
161
|
+
|
162
|
+
# Is a path considered reasonably "safe"?
|
163
|
+
|
164
|
+
def safe?(path)
|
165
|
+
case path
|
166
|
+
when *UNSAFE
|
167
|
+
return false
|
168
|
+
end
|
169
|
+
true
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
end
|