buzzware-buzzcore 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +5 -0
- data/LICENSE +23 -0
- data/README.rdoc +46 -0
- data/Rakefile +63 -0
- data/VERSION +1 -0
- data/buzzcore.gemspec +77 -0
- data/buzzcore.vpj +93 -0
- data/buzzcore.vpw +6 -0
- data/lib/buzzcore.rb +2 -0
- data/lib/buzzcore/cap_utils.rb +181 -0
- data/lib/buzzcore/config.rb +210 -0
- data/lib/buzzcore/database_utils.rb +86 -0
- data/lib/buzzcore/enum.rb +50 -0
- data/lib/buzzcore/extend_base_classes.rb +320 -0
- data/lib/buzzcore/ftp_extra.rb +188 -0
- data/lib/buzzcore/logging.rb +159 -0
- data/lib/buzzcore/misc_utils.rb +382 -0
- data/lib/buzzcore/require_paths.rb +28 -0
- data/lib/buzzcore/shell_extras.rb +80 -0
- data/lib/buzzcore/string_utils.rb +53 -0
- data/lib/buzzcore/text_doc.rb +70 -0
- data/lib/buzzcore/thread_utils.rb +709 -0
- data/lib/buzzcore/xml_utils.rb +184 -0
- data/test/buzzcore_test.rb +7 -0
- data/test/config_test.rb +201 -0
- data/test/credentials_test.rb +71 -0
- data/test/shell_test.rb +54 -0
- data/test/test_helper.rb +10 -0
- metadata +95 -0
@@ -0,0 +1,188 @@
|
|
1
|
+
require 'net/ftp'
|
2
|
+
|
3
|
+
this_path = File.dirname(__FILE__)
|
4
|
+
require this_path+'/misc_utils'
|
5
|
+
|
6
|
+
String.class_eval do
|
7
|
+
def bite!(aValue=$/,aString=self)
|
8
|
+
if aString[0,aValue.length] == aValue
|
9
|
+
aString[0,aValue.length] = ''
|
10
|
+
return aString
|
11
|
+
else
|
12
|
+
return aString
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def bite(aValue=$/)
|
17
|
+
bite!(aValue,self.clone)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
module Net
|
23
|
+
class FTP
|
24
|
+
def FTP.with_connect(aHost,aUsername,aPassword,aDir=nil)
|
25
|
+
open(aHost,aUsername,aPassword) do |f|
|
26
|
+
f.passive = true
|
27
|
+
f.chdir(aDir) if aDir
|
28
|
+
yield f
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.crack_file_line(aString)
|
33
|
+
values = aString.scan(/(.{10}).{28}(.{13})(.*)$/).flatten
|
34
|
+
{
|
35
|
+
:bits => values[0],
|
36
|
+
:date => values[1],
|
37
|
+
:name => values[2]
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
# BEGIN BUGFIXES
|
42
|
+
|
43
|
+
#
|
44
|
+
# Returns the size of the given (remote) filename.
|
45
|
+
#
|
46
|
+
def size(filename)
|
47
|
+
voidcmd("TYPE I")
|
48
|
+
resp = sendcmd("SIZE " + filename)
|
49
|
+
code = resp[0, 3]
|
50
|
+
if code != "213" && code != "220"
|
51
|
+
raise FTPReplyError, resp
|
52
|
+
end
|
53
|
+
return resp[3..-1].strip.to_i
|
54
|
+
end
|
55
|
+
|
56
|
+
# END BUGFIXES
|
57
|
+
|
58
|
+
def subdirs(aPath)
|
59
|
+
list.delete_if {|line| line[0,1]=='d'}
|
60
|
+
return list
|
61
|
+
end
|
62
|
+
|
63
|
+
def files(aPath)
|
64
|
+
list.delete_if {|line| line[0,1]!='d'}
|
65
|
+
return list
|
66
|
+
end
|
67
|
+
|
68
|
+
def expand_dir(aPath,aBase=nil)
|
69
|
+
return aPath if aPath=='/'
|
70
|
+
return MiscUtils::path_relative?(aPath) ? File.expand_path(aPath,aBase || pwd()) : File.expand_path(aPath)
|
71
|
+
end
|
72
|
+
|
73
|
+
def dir_exists?(aPath)
|
74
|
+
aPath = expand_dir(aPath)
|
75
|
+
return true if aPath=='/'
|
76
|
+
dirname = File.basename(aPath)
|
77
|
+
parent = MiscUtils.path_parent(aPath)
|
78
|
+
dirname!='' && nlst(parent).include?(dirname)
|
79
|
+
end
|
80
|
+
|
81
|
+
def file_exists?(aPath)
|
82
|
+
aPath = expand_dir(aPath)
|
83
|
+
filename = File.basename(aPath)
|
84
|
+
parent = File.dirname(aPath)
|
85
|
+
filename!='' && nlst(parent).include?(filename)
|
86
|
+
end
|
87
|
+
|
88
|
+
def filelist_recurse(aPath=nil,aResult=nil,&block)
|
89
|
+
#puts "filelist_recurse: #{aPath.to_s} #{aResult.inspect}"
|
90
|
+
orig_dir = !aResult ? pwd : nil # assigned if called at top with aResult=nil
|
91
|
+
aResult ||= []
|
92
|
+
aPath ||= ''
|
93
|
+
chdir(aPath)
|
94
|
+
list('*').each do |f|
|
95
|
+
is_dir = f[0,1]=='d'
|
96
|
+
details = FTP::crack_file_line(f)
|
97
|
+
full = File.join(aPath,details[:name])
|
98
|
+
if !block_given? || yield(full)
|
99
|
+
if is_dir
|
100
|
+
filelist_recurse(full,aResult)
|
101
|
+
else
|
102
|
+
aResult << full
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
chdir(orig_dir) if orig_dir
|
107
|
+
return aResult
|
108
|
+
end
|
109
|
+
|
110
|
+
def get_files(aRemoteDir,aLocalDir,aFiles,aOptions=nil)
|
111
|
+
aOptions = {:overwrite => true}.merge(aOptions || {})
|
112
|
+
aFiles.each do |r|
|
113
|
+
relative = r.bite(MiscUtils::append_slash(aRemoteDir))
|
114
|
+
d = File.join(aLocalDir,relative)
|
115
|
+
puts "getting #{relative}"
|
116
|
+
getbinaryfile(r, d) unless !aOptions[:overwrite] && File.exists?(d)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def get_dir(aRemoteDir,aLocalDir,aOptions=nil,&block)
|
121
|
+
remote_files = block_given? ? filelist_recurse(aRemoteDir,nil,&block) : filelist_recurse(aRemoteDir)
|
122
|
+
get_files(aRemoteDir,aLocalDir,remote_files,aOptions)
|
123
|
+
end
|
124
|
+
|
125
|
+
def highest_existing(aPath)
|
126
|
+
sep = MiscUtils::sniff_seperator(aPath)
|
127
|
+
path = MiscUtils::path_parts(File.expand_path(aPath)) if aPath.is_a?(String)
|
128
|
+
# now assume path is an Array
|
129
|
+
depth = path.length-1
|
130
|
+
depth.downto(0) do |i| # from full path up to root
|
131
|
+
curr = (path[0]=='' && i==0) ? '/' : path[0..i].join(sep)
|
132
|
+
return curr if dir_exists?(curr)
|
133
|
+
end
|
134
|
+
return sep # root
|
135
|
+
end
|
136
|
+
|
137
|
+
def ensure_dir(aPath,aThorough=false)
|
138
|
+
if !aThorough
|
139
|
+
mkdir(aPath) unless dir_exists?(aPath)
|
140
|
+
else
|
141
|
+
return if dir_exists?(aPath)
|
142
|
+
path = expand_dir(aPath)
|
143
|
+
hi_existing = highest_existing(path)
|
144
|
+
# path to create under hi_existing
|
145
|
+
to_create = MiscUtils::path_debase(path,hi_existing)
|
146
|
+
parts = MiscUtils::path_parts(to_create)
|
147
|
+
curr_path = hi_existing
|
148
|
+
|
149
|
+
parts.each do |part|
|
150
|
+
curr_path = File.join(curr_path,part)
|
151
|
+
mkdir(curr_path)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def put_files(aLocalDir,aRemoteDir,aFiles,aOptions=nil)
|
157
|
+
aOptions = {:overwrite => true}.merge(aOptions || {})
|
158
|
+
|
159
|
+
# convert all files to relative to aLocalDir
|
160
|
+
aFiles = aFiles.map { |f| f.bite(MiscUtils::append_slash(aLocalDir)) }.sort
|
161
|
+
|
162
|
+
filelist = nil
|
163
|
+
this_dir = last_dir = nil
|
164
|
+
aFiles.each do |r|
|
165
|
+
d = File.expand_path(r,aRemoteDir)
|
166
|
+
this_dir = File.dirname(d)
|
167
|
+
if this_dir!=last_dir
|
168
|
+
ensure_dir(this_dir,true)
|
169
|
+
filelist = files(this_dir) - ['.','..','.svn']
|
170
|
+
end
|
171
|
+
if aOptions[:overwrite] || !filelist.member?(File.basename(r))
|
172
|
+
puts "Putting #{r}"
|
173
|
+
putbinaryfile(File.expand_path(r,aLocalDir), d)
|
174
|
+
else
|
175
|
+
puts "Skipping #{relative}"
|
176
|
+
end
|
177
|
+
last_dir = this_dir
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def put_dir(aLocalDir,aRemoteDir,&block)
|
182
|
+
local_files = block_given? ? MiscUtils::recursive_file_list(aLocalDir,true,&block) : MiscUtils::recursive_file_list(aLocalDir)
|
183
|
+
put_files(aLocalDir,aRemoteDir,local_files)
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
@@ -0,0 +1,159 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'buzzcore/misc_utils'
|
3
|
+
|
4
|
+
class Logger
|
5
|
+
attr_reader :logdev
|
6
|
+
end
|
7
|
+
|
8
|
+
module LogUtils
|
9
|
+
|
10
|
+
# eg.
|
11
|
+
# {
|
12
|
+
# 'destination' => 'STDERR|STDOUT|FILE',
|
13
|
+
# 'filename' => '/path/to/file.ext',
|
14
|
+
# 'level' => 'DEBUG|INFO|...',
|
15
|
+
#
|
16
|
+
# 'age' = 'daily|weekly|monthly',
|
17
|
+
# OR
|
18
|
+
# 'max_files' => 3,
|
19
|
+
# 'max_bytes' => 1024000
|
20
|
+
# }
|
21
|
+
def self.create_logger_from_config(aConfigHash)
|
22
|
+
if not aConfigHash
|
23
|
+
result = Logger.new(STDERR)
|
24
|
+
result.level = Logger::INFO
|
25
|
+
return result
|
26
|
+
end
|
27
|
+
|
28
|
+
result = nil
|
29
|
+
case aConfigHash['destination']
|
30
|
+
when 'STDERR' then
|
31
|
+
result = Logger.new(STDERR)
|
32
|
+
when 'STDOUT' then
|
33
|
+
result = Logger.new(STDOUT)
|
34
|
+
when 'FILE' then
|
35
|
+
result = aConfigHash['age'] ?
|
36
|
+
Logger.new(aConfigHash['filename'],aConfigHash['age']) :
|
37
|
+
Logger.new(
|
38
|
+
aConfigHash['filename'],
|
39
|
+
(aConfigHash['max_files'] || 3).to_i,
|
40
|
+
(aConfigHash['max_bytes'] || 1024000).to_i
|
41
|
+
)
|
42
|
+
else
|
43
|
+
result = Logger.new(STDERR)
|
44
|
+
end
|
45
|
+
puts valstr = "Logger::#{(aConfigHash['level'] || 'INFO').upcase}"
|
46
|
+
result.level = eval(valstr)
|
47
|
+
return result
|
48
|
+
end
|
49
|
+
|
50
|
+
# use this to trunc a log file to 0 bytes
|
51
|
+
def self.trunc(aFilename)
|
52
|
+
f = File.open(aFilename, "w")
|
53
|
+
f.close
|
54
|
+
end
|
55
|
+
|
56
|
+
class ReportFormatter < Logger::Formatter
|
57
|
+
def call(severity, time, progname, msg)
|
58
|
+
"|%s %1s %s\n" % [(time.strftime('%H%M%S.')<<"%03d" % (time.usec/1000)),severity[0..0],msg2str(msg)]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class Reporter < Logger
|
63
|
+
def initialize(logdev)
|
64
|
+
super(logdev)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
def self.create_reporter(aFilename=nil)
|
70
|
+
aFilename ||= MiscUtils::temp_file()
|
71
|
+
result = Logger.new(aFilename)
|
72
|
+
result.formatter = ReportFormatter.new
|
73
|
+
result
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
|
79
|
+
class MultiLogger < Logger
|
80
|
+
|
81
|
+
attr_reader :loggers
|
82
|
+
|
83
|
+
def initialize(aLoggers)
|
84
|
+
@loggers = aLoggers.is_a?(Array) ? aLoggers : [aLoggers]
|
85
|
+
end
|
86
|
+
|
87
|
+
def add(severity, message = nil, progname = nil, &block)
|
88
|
+
return true if !@loggers
|
89
|
+
severity ||= UNKNOWN
|
90
|
+
@loggers.each do |lr|
|
91
|
+
block_given? ? lr.add(severity,message,progname,&block) : lr.add(severity,message,progname)
|
92
|
+
end
|
93
|
+
true
|
94
|
+
end
|
95
|
+
alias log add
|
96
|
+
|
97
|
+
def <<(msg)
|
98
|
+
@loggers.each do |lr|
|
99
|
+
lr << msg
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def close
|
104
|
+
@loggers.each do |lr|
|
105
|
+
lr.close
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
#DEBUG D
|
112
|
+
#INFO
|
113
|
+
#WARN ?
|
114
|
+
#ERROR !
|
115
|
+
#FATAL F
|
116
|
+
#UNKNOWN U
|
117
|
+
|
118
|
+
# Logger that mostly works like a STDOUT logger, except that warnings and above get sent to STDERR instead
|
119
|
+
class ConsoleLogger < Logger
|
120
|
+
|
121
|
+
class ReportFormatter < Logger::Formatter
|
122
|
+
def call(severity, time, progname, msg)
|
123
|
+
msg2str(msg)+"\n"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def initialize(aErrLevel = Severity::WARN)
|
128
|
+
super(STDOUT)
|
129
|
+
self.formatter = ReportFormatter.new
|
130
|
+
self.level = Severity::INFO
|
131
|
+
self << "\n"
|
132
|
+
@err_logger = Logger.new(STDERR)
|
133
|
+
@err_level = aErrLevel
|
134
|
+
@err_logger.formatter = ReportFormatter.new
|
135
|
+
end
|
136
|
+
|
137
|
+
alias_method :orig_add, :add
|
138
|
+
def add(severity, message = nil, progname = nil, &block)
|
139
|
+
if severity >= @err_level
|
140
|
+
block_given? ? @err_logger.add(severity,message,progname,&block) : @err_logger.add(severity,message,progname)
|
141
|
+
else
|
142
|
+
block_given? ? orig_add(severity,message,progname,&block) : orig_add(severity,message,progname)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
alias log add
|
146
|
+
|
147
|
+
#
|
148
|
+
# Close the logging device.
|
149
|
+
#
|
150
|
+
def close
|
151
|
+
begin
|
152
|
+
@logdev.close if @logdev
|
153
|
+
ensure
|
154
|
+
@err_logger.close
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
@@ -0,0 +1,382 @@
|
|
1
|
+
require 'tmpdir'
|
2
|
+
require 'logger'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
require 'buzzcore/logging'
|
6
|
+
|
7
|
+
module MiscUtils
|
8
|
+
|
9
|
+
def self.logger
|
10
|
+
@logger || @logger = Logger.new(STDERR)
|
11
|
+
end
|
12
|
+
|
13
|
+
# applies the given block to key/value pairs included/excluded by the given parameters
|
14
|
+
# aSource must be a hash, and may contain hashes which will be recursively processed.
|
15
|
+
# aInclude/aExclude may be nil, an array of selected keys, or a hash containing arrays of keys for inner hashes of aSource
|
16
|
+
# When aInclude/aExclude are a hash, non-hash source values may be selected by passing a value of true. Hash source values
|
17
|
+
# will be selected as a whole (all keys) when true is passed.
|
18
|
+
# input = { 'a' => 1, 'b' => {'x' => 9, 'y' => 8}, 'c' => 3 }
|
19
|
+
# filter_multilevel_hash(input,['a','b'],{'b'=>['y']}) {|h,k,v| h[k] = true}
|
20
|
+
# input now = { 'a' => true, 'b' => {'x' => true, 'y' => 8}, 'c' => 3 }
|
21
|
+
def self.filter_multilevel_hash(aSource,aInclude=nil,aExclude=nil,&block)
|
22
|
+
aSource.each do |key,value|
|
23
|
+
next if aInclude.is_a?(Array) and !aInclude.include?(key) # skip if not in aInclude array
|
24
|
+
next if aExclude.is_a?(Array) and aExclude.include?(key) # skip if in aExclude array
|
25
|
+
next if aExclude.is_a?(Hash) and aExclude[key]==true # skip if in aExclude hash with value=true
|
26
|
+
next if aInclude.is_a?(Hash) and !aInclude.include?(key) # skip if not in aInclude hash at all
|
27
|
+
if value.is_a?(Hash) # value is hash so recursively apply filter
|
28
|
+
filter_multilevel_hash(
|
29
|
+
value,
|
30
|
+
aInclude.is_a?(Hash) && (f = aInclude[key]) ? f : nil, # pass include array if provided for key
|
31
|
+
aExclude.is_a?(Hash) && (f = aExclude[key]) ? f : nil, # pass exclude array if provided for key
|
32
|
+
&block
|
33
|
+
)
|
34
|
+
else
|
35
|
+
yield(aSource,key,value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# returns output string if succesful, or integer return code if not, or nil
|
41
|
+
def self.execute(aCommand,aWorkingDir=nil,aTimeout=nil,aTimeoutClass=nil)
|
42
|
+
return nil unless !aWorkingDir || File.exists?(aWorkingDir)
|
43
|
+
begin
|
44
|
+
orig_wd = Dir.getwd
|
45
|
+
pipe = nil
|
46
|
+
result = nil
|
47
|
+
Dir.chdir(aWorkingDir) if aWorkingDir
|
48
|
+
Timeout.timeout(aTimeout,aTimeoutClass || Timeout::Error) do # nil aTimeout will not time out
|
49
|
+
pipe = IO.popen(aCommand)
|
50
|
+
logger.debug "command PID:"+pipe.pid.to_s
|
51
|
+
result = pipe.read
|
52
|
+
end
|
53
|
+
ensure
|
54
|
+
pipe.close if pipe
|
55
|
+
Dir.chdir(orig_wd)
|
56
|
+
end
|
57
|
+
return result
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.execute_string(aCmdString,aWorkingDir=nil)
|
61
|
+
result = nil
|
62
|
+
begin
|
63
|
+
orig_dir = Dir.pwd
|
64
|
+
Dir.chdir(aWorkingDir) if aWorkingDir
|
65
|
+
result = `#{aCmdString}`
|
66
|
+
ensure
|
67
|
+
Dir.chdir(orig_dir) if aWorkingDir
|
68
|
+
end
|
69
|
+
return result
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.temp_file(aExt=nil,aDir=nil)
|
73
|
+
aExt ||= '.tmp'
|
74
|
+
File.expand_path(("%08X" % rand(0x3FFFFFFF)) + aExt, aDir||Dir.tmpdir)
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.make_temp_file(aName=nil,aDir=nil,aContent=nil)
|
78
|
+
filename = aName ? File.expand_path(aName,aDir || Dir.tmpdir) : temp_file(nil,aDir)
|
79
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
80
|
+
aContent ||= "content of "+filename
|
81
|
+
string_to_file(aContent,filename)
|
82
|
+
filename
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.make_temp_dir(aPrefix='')
|
86
|
+
new_dir = nil
|
87
|
+
begin
|
88
|
+
new_dir = File.join(Dir.tmpdir,aPrefix+("%08X" % rand(0x3FFFFFFF)))
|
89
|
+
end until new_dir && !File.exists?(new_dir)
|
90
|
+
Dir.mkdir new_dir
|
91
|
+
return new_dir
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.mkdir?(aPath,aPermissions)
|
95
|
+
if File.exists?(aPath)
|
96
|
+
File.chmod(aPermissions, aPath)
|
97
|
+
else
|
98
|
+
Dir.mkdir(aPath, aPermissions)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def self.string_to_file(aString,aFilename)
|
103
|
+
File.open(aFilename,'wb') {|file| file.puts aString }
|
104
|
+
end
|
105
|
+
|
106
|
+
def self.string_from_file(aFilename)
|
107
|
+
result = nil
|
108
|
+
File.open(aFilename, "rb") { |f| result = f.read }
|
109
|
+
return result && result[0..-2] # quick hack to stop returning false \n at end
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.sniff_seperator(aPath)
|
113
|
+
result = 0.upto(aPath.length-1) do |i|
|
114
|
+
char = aPath[i,1]
|
115
|
+
break char if char=='\\' || char=='/'
|
116
|
+
end
|
117
|
+
result = File::SEPARATOR if result==0
|
118
|
+
return result
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.append_slash(aPath,aSep=nil)
|
122
|
+
aSep = sniff_seperator(aPath) unless aSep
|
123
|
+
last_char = aPath[-1,1]
|
124
|
+
aPath += aSep unless last_char=='\\' || last_char=='/'
|
125
|
+
return aPath
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.remove_slash(aPath)
|
129
|
+
last_char = aPath[-1,1]
|
130
|
+
aPath = aPath[0..-2] if last_char=='\\' || last_char=='/'
|
131
|
+
return aPath
|
132
|
+
end
|
133
|
+
|
134
|
+
# Remove base dir from given path. Result will be relative to base dir and not have a leading or trailing slash
|
135
|
+
#'/a/b/c','/a' = 'b/c'
|
136
|
+
#'/a/b/c','/' = 'a/b/c'
|
137
|
+
#'/','/' = ''
|
138
|
+
def self.path_debase(aPath,aBase)
|
139
|
+
aBase = MiscUtils::append_slash(aBase)
|
140
|
+
aPath = MiscUtils::remove_slash(aPath) unless aPath=='/'
|
141
|
+
aPath[0,aBase.length]==aBase ? aPath[aBase.length,aPath.length-aBase.length] : aPath
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.path_rebase(aPath,aOldBase,aNewBase)
|
145
|
+
rel_path = path_debase(aPath,aOldBase)
|
146
|
+
append_slash(aNewBase)+rel_path
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.path_combine(aBasePath,aPath)
|
150
|
+
return aBasePath if !aPath
|
151
|
+
return aPath if !aBasePath
|
152
|
+
return path_relative?(aPath) ? File.join(aBasePath,aPath) : aPath
|
153
|
+
end
|
154
|
+
|
155
|
+
def self.path_parent(aPath)
|
156
|
+
MiscUtils.append_slash(File.dirname(MiscUtils.remove_slash(File.expand_path(aPath))))
|
157
|
+
end
|
158
|
+
|
159
|
+
def self.simple_dir_name(aPath)
|
160
|
+
File.basename(remove_slash(aPath))
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.path_parts(aPath)
|
164
|
+
sep = sniff_seperator(aPath)
|
165
|
+
aPath.split(sep)
|
166
|
+
end
|
167
|
+
|
168
|
+
def self.file_extension(aFile,aExtended=true)
|
169
|
+
f = File.basename(aFile)
|
170
|
+
dot = aExtended ? f.index('.') : f.rindex('.')
|
171
|
+
return dot ? f[dot+1..-1] : f
|
172
|
+
end
|
173
|
+
|
174
|
+
def self.file_no_extension(aFile,aExtended=true)
|
175
|
+
ext = file_extension(aFile,aExtended)
|
176
|
+
return aFile.chomp('.'+ext)
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.file_change_ext(aFile,aExt,aExtend=false)
|
180
|
+
file_no_extension(aFile,false)+(aExtend ? '.'+aExt+'.'+file_extension(aFile,false) : '.'+aExt)
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.platform
|
184
|
+
RUBY_PLATFORM.scan(/-(.+)$/).flatten.first
|
185
|
+
end
|
186
|
+
|
187
|
+
def self.windows_path(aPath)
|
188
|
+
aPath.gsub('/','\\')
|
189
|
+
end
|
190
|
+
|
191
|
+
def self.ruby_path(aPath)
|
192
|
+
aPath.gsub('\\','/')
|
193
|
+
end
|
194
|
+
|
195
|
+
def self.is_uri?(aString)
|
196
|
+
/^[a-zA-Z0-9+_]+\:\/\// =~ aString ? true : false
|
197
|
+
end
|
198
|
+
|
199
|
+
def self.native_path(aPath)
|
200
|
+
is_windows? ? windows_path(aPath) : ruby_path(aPath)
|
201
|
+
end
|
202
|
+
|
203
|
+
def self.path_relative?(aPath)
|
204
|
+
return false if aPath[0,1]=='/'
|
205
|
+
return false if aPath =~ /^[a-zA-Z]:/
|
206
|
+
return true
|
207
|
+
end
|
208
|
+
|
209
|
+
def self.path_absolute?(aPath)
|
210
|
+
!path_relative(aPath)
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.is_windows?
|
214
|
+
platform=='mswin32'
|
215
|
+
end
|
216
|
+
|
217
|
+
# takes a path and combines it with a root path (which defaults to Dir.pwd) unless it is absolute
|
218
|
+
# the final result is then expanded
|
219
|
+
def self.canonize_path(aPath,aRootPath=nil)
|
220
|
+
path = Pathname.new(aPath)
|
221
|
+
path = Pathname.new(aRootPath || Dir.pwd)+path if path.relative?
|
222
|
+
File.expand_path(path)
|
223
|
+
end
|
224
|
+
|
225
|
+
def self.get_files(aArray,aPath,aFullPath=true,aRootPath=nil,&block)
|
226
|
+
#puts "get_files: aPath='#{aPath}'"
|
227
|
+
if aRootPath
|
228
|
+
abssrcpath = path_combine(aRootPath,aPath)
|
229
|
+
else
|
230
|
+
abssrcpath = aRootPath = aPath
|
231
|
+
aPath = nil
|
232
|
+
end
|
233
|
+
return aArray if !File.exists?(abssrcpath)
|
234
|
+
#abssrcpath is real path to query
|
235
|
+
#aRootPath is highest level path
|
236
|
+
#aPath is current path relative to aRootPath
|
237
|
+
Dir.new(abssrcpath).to_a.each do |file|
|
238
|
+
next if ['.','..'].include? file
|
239
|
+
fullpath = File.join(abssrcpath,file)
|
240
|
+
resultpath = aFullPath ? fullpath : path_combine(aPath,file)
|
241
|
+
if !block_given? || yield(resultpath)
|
242
|
+
if FileTest.directory?(fullpath)
|
243
|
+
block_given? ? get_files(aArray,path_combine(aPath,file),aFullPath,aRootPath,&block) : get_files(aArray,path_combine(aPath,file),aFullPath,aRootPath)
|
244
|
+
else
|
245
|
+
aArray << resultpath
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
return aArray
|
250
|
+
end
|
251
|
+
|
252
|
+
def self.recursive_file_list(aPath,aFullPath=true,&block)
|
253
|
+
block_given? ? get_files([],aPath,aFullPath,nil,&block) : get_files([],aPath,aFullPath)
|
254
|
+
end
|
255
|
+
|
256
|
+
# returns true if aPath1 and aPath2 are the same path (doesn't query file system)
|
257
|
+
# both must be absolute or both relative
|
258
|
+
def self.path_same(aPath1,aPath2)
|
259
|
+
return nil unless path_relative?(aPath1) == path_relative?(aPath2)
|
260
|
+
remove_slash(aPath1) == remove_slash(aPath2)
|
261
|
+
end
|
262
|
+
|
263
|
+
# returns true if aPath is under aPathParent
|
264
|
+
# both must be absolute or both relative
|
265
|
+
def self.path_ancestor(aPathParent,aPath)
|
266
|
+
return nil unless path_relative?(aPathParent) == path_relative?(aPath)
|
267
|
+
aPath.index(append_slash(aPathParent))==0
|
268
|
+
end
|
269
|
+
|
270
|
+
# returns the lowest path containing all files (assumes aFiles contains only absolute paths)
|
271
|
+
def self.file_list_ancestor(aFiles)
|
272
|
+
files = aFiles.is_a?(Hash) ? aFiles.keys : aFiles
|
273
|
+
result = File.dirname(files.first)
|
274
|
+
files.each do |fp|
|
275
|
+
filedir = File.dirname(fp)
|
276
|
+
while path_same(result,filedir)==false && path_ancestor(result,filedir)==false
|
277
|
+
result = path_parent(result)
|
278
|
+
end
|
279
|
+
end
|
280
|
+
result
|
281
|
+
end
|
282
|
+
|
283
|
+
def self.path_match(aPath,aPatterns)
|
284
|
+
aPatterns = [aPatterns] unless aPatterns.is_a? Array
|
285
|
+
aPatterns.any? do |pat|
|
286
|
+
case pat
|
287
|
+
when String then aPath[0,pat.length] == pat
|
288
|
+
when Regexp then aPath =~ pat
|
289
|
+
else false
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
|
295
|
+
# takes a hash and returns a single closed tag containing the hash pairs as attributes, correctly encoded
|
296
|
+
def self.hash_to_xml_tag(aName,aHash)
|
297
|
+
atts = ''
|
298
|
+
aHash.each do |k,v|
|
299
|
+
atts += ' ' + k.to_s + "=\"#{v.to_s.to_xs}\""
|
300
|
+
end
|
301
|
+
"<#{aName}#{atts}/>"
|
302
|
+
end
|
303
|
+
|
304
|
+
def self.filelist_from_patterns(aPatterns,aBasePath)
|
305
|
+
return [] unless aPatterns
|
306
|
+
aPatterns = [aPatterns] unless aPatterns.is_a? Array
|
307
|
+
|
308
|
+
aPatterns.map do |fp|
|
309
|
+
fp = File.expand_path(fp,aBasePath) # relative to rails root
|
310
|
+
fp = FileList[fp] if fp['*'] || fp['?']
|
311
|
+
fp
|
312
|
+
end.flatten
|
313
|
+
end
|
314
|
+
|
315
|
+
#:host
|
316
|
+
#:port
|
317
|
+
#:helodomain
|
318
|
+
#:user
|
319
|
+
#:password
|
320
|
+
#:from
|
321
|
+
#:from_alias
|
322
|
+
#:to
|
323
|
+
#:to_alias
|
324
|
+
#:subject
|
325
|
+
#:message
|
326
|
+
#:auth : 'plain', 'login', 'cram_md5'
|
327
|
+
|
328
|
+
# send an email via an SMTP server
|
329
|
+
def self.send_email(aArgs)
|
330
|
+
msg = <<END_OF_MESSAGE
|
331
|
+
From: #{aArgs[:from_alias]} <#{aArgs[:from]}>
|
332
|
+
To: #{aArgs[:to_alias]} <#{aArgs[:to]}>
|
333
|
+
Subject: #{aArgs[:subject]}
|
334
|
+
|
335
|
+
#{aArgs[:message]}
|
336
|
+
END_OF_MESSAGE
|
337
|
+
|
338
|
+
Net::SMTP.start(
|
339
|
+
aArgs[:host],
|
340
|
+
aArgs[:port],
|
341
|
+
aArgs[:helodomain],
|
342
|
+
aArgs[:user],
|
343
|
+
aArgs[:password],
|
344
|
+
aArgs[:auth]
|
345
|
+
) do |smtp|
|
346
|
+
smtp.send_message msg, aArgs[:from], aArgs[:to]
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
350
|
+
end
|
351
|
+
|
352
|
+
# include this at the top of a class to protect it from baddies.
|
353
|
+
# eg.
|
354
|
+
# + nearly all ancestor public_instance_methods will be hidden
|
355
|
+
# + inspect will only return the class name
|
356
|
+
# + methods will return public methods
|
357
|
+
module SecureThisClass
|
358
|
+
def self.hack(aClass,aOptions={})
|
359
|
+
include_actions = (aOptions[:include] || aClass.public_instance_methods.clone)
|
360
|
+
exclude_actions = ['class','public_methods'] | (aOptions[:exclude] || [])
|
361
|
+
actions_to_hide = include_actions-exclude_actions
|
362
|
+
aClass.class_eval do
|
363
|
+
actions_to_hide.each { |m| protected m.to_sym }
|
364
|
+
|
365
|
+
def inspect
|
366
|
+
return self.class.name
|
367
|
+
end
|
368
|
+
|
369
|
+
def methods
|
370
|
+
public_methods
|
371
|
+
end
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
module ::Kernel
|
378
|
+
def secure_class(aOptions={})
|
379
|
+
SecureThisClass::hack(self,aOptions)
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|