buzzware-buzzcore 0.2.2

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.
@@ -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
+