core_ex 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,464 @@
1
+ # Revision:: $Id: filelist.rb 249 2005-05-31 13:23:42Z ertai $
2
+
3
+ #--
4
+ # Copyright (c) 2003, 2004 Jim Weirich
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining
7
+ # a copy of this software and associated documentation files (the
8
+ # "Software"), to deal in the Software without restriction, including
9
+ # without limitation the rights to use, copy, modify, merge, publish,
10
+ # distribute, sublicense, and/or sell copies of the Software, and to
11
+ # permit persons to whom the Software is furnished to do so, subject to
12
+ # the following conditions:
13
+ #
14
+ # The above copyright notice and this permission notice shall be
15
+ # included in all copies or substantial portions of the Software.
16
+ #
17
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
21
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
22
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
23
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24
+ #++
25
+ #
26
+
27
+ # Contributons by Nicolas Pouillard <ertai@lrde.epita.fr>.
28
+
29
+ # Some objects are dupable, some are not. So we define a version of
30
+ # dup (called rake_dup) that returns self on the handful of classes
31
+ # that are not dupable.
32
+
33
+ module Kernel
34
+ # Duplicate an object if it can be duplicated. If it can not be
35
+ # cloned or duplicated, then just return the original object.
36
+ def rake_dup()
37
+ dup
38
+ end
39
+ end
40
+
41
+ [NilClass, FalseClass, TrueClass, Fixnum, Symbol].each do |clazz|
42
+ clazz.class_eval {
43
+ # Duplicate an object if it can be duplicated. If it can not be
44
+ # cloned or duplicated, then just return the original object.
45
+ def rake_dup() self end
46
+ }
47
+ end
48
+
49
+ ######################################################################
50
+ # User defined methods to be added to String.
51
+ #
52
+ class String
53
+ unless instance_methods.include? "ext"
54
+ # Replace the file extension with +newext+. If there is no
55
+ # extenson on the string, append the new extension to the end. If
56
+ # the new extension is not given, or is the empty string, remove
57
+ # any existing extension.
58
+ #
59
+ # +ext+ is a user added method for the String class.
60
+ def ext(newext='')
61
+ return self.dup if ['.', '..'].include? self
62
+ if newext != ''
63
+ newext = (newext =~ /^\./) ? newext : ("." + newext)
64
+ end
65
+ dup.sub!(%r(([^/\\])\.[^./\\]*$)) { $1 + newext } || self + newext
66
+ end
67
+ end
68
+ end
69
+
70
+
71
+ ####################################################################
72
+ # A FileList is essentially an array with a few helper methods
73
+ # defined to make file manipulation a bit easier.
74
+ #
75
+ # FileLists are lazy. When given a list of glob patterns for
76
+ # possible files to be included in the file list, instead of
77
+ # searching the file structures to find the files, a FileList holds
78
+ # the pattern for latter use.
79
+ #
80
+ # This allows us to define a number of FileList to match any number of
81
+ # files, but only search out the actual files when then FileList
82
+ # itself is actually used. The key is that the first time an
83
+ # element of the FileList/Array is requested, the pending patterns
84
+ # are resolved into a real list of file names.
85
+ #
86
+ class FileList
87
+
88
+ def clone
89
+ sibling = self.class.new
90
+ instance_variables.each do |ivar|
91
+ value = self.instance_variable_get(ivar)
92
+ sibling.instance_variable_set(ivar, value.rake_dup)
93
+ end
94
+ sibling
95
+ end
96
+ alias dup clone
97
+
98
+ # == Method Delegation
99
+ #
100
+ # The lazy evaluation magic of FileLists happens by implementing
101
+ # all the array specific methods to call +resolve+ before
102
+ # delegating the heavy lifting to an embedded array object
103
+ # (@items).
104
+ #
105
+ # In addition, there are two kinds of delegation calls. The
106
+ # regular kind delegates to the @items array and returns the
107
+ # result directly. Well, almost directly. It checks if the
108
+ # returned value is the @items object itself, and if so will
109
+ # return the FileList object instead.
110
+ #
111
+ # The second kind of delegation call is used in methods that
112
+ # normally return a new Array object. We want to capture the
113
+ # return value of these methods and wrap them in a new FileList
114
+ # object. We enumerate these methods in the +SPECIAL_RETURN+ list
115
+ # below.
116
+
117
+ # List of array methods (that are not in +Object+) that need to be
118
+ # delegated.
119
+ ARRAY_METHODS = Array.instance_methods - Object.instance_methods
120
+
121
+ # List of additional methods that must be delegated.
122
+ MUST_DEFINE = %w[to_a inspect]
123
+
124
+ # List of methods that should not be delegated here (we define
125
+ # special versions of them explicitly below).
126
+ MUST_NOT_DEFINE = %w[to_a to_ary partition *]
127
+
128
+ # List of delegated methods that return new array values which
129
+ # need wrapping.
130
+ SPECIAL_RETURN = %w[
131
+ map collect sort sort_by select find_all reject grep
132
+ compact flatten uniq values_at
133
+ + - & |
134
+ ]
135
+
136
+ DELEGATING_METHODS = (ARRAY_METHODS + MUST_DEFINE - MUST_NOT_DEFINE).sort.uniq
137
+
138
+ # Now do the delegation.
139
+ DELEGATING_METHODS.each_with_index do |sym, i|
140
+ if SPECIAL_RETURN.include?(sym)
141
+ ln = __LINE__+1
142
+ class_eval %{
143
+ def #{sym}(*args, &block)
144
+ resolve if @pending
145
+ result = @items.send(:#{sym}, *args, &block)
146
+ FileList.new.import(result)
147
+ end
148
+ }, __FILE__, ln
149
+ else
150
+ ln = __LINE__+1
151
+ class_eval %{
152
+ def #{sym}(*args, &block)
153
+ resolve if @pending
154
+ result = @items.send(:#{sym}, *args, &block)
155
+ result.object_id == @items.object_id ? self : result
156
+ end
157
+ }, __FILE__, ln
158
+ end
159
+ end
160
+
161
+ # Create a file list from the globbable patterns given. If you
162
+ # wish to perform multiple includes or excludes at object build
163
+ # time, use the "yield self" pattern.
164
+ #
165
+ # Example:
166
+ # file_list = FileList.new['lib/**/*.rb', 'test/test*.rb']
167
+ #
168
+ # pkg_files = FileList.new['lib/**/*'] do |fl|
169
+ # fl.exclude(/\bCVS\b/)
170
+ # end
171
+ #
172
+ def initialize(*patterns)
173
+ @pending_add = []
174
+ @pending = false
175
+ @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
176
+ @exclude_re = nil
177
+ @items = []
178
+ patterns.each { |pattern| include(pattern) }
179
+ yield self if block_given?
180
+ end
181
+
182
+ # Add file names defined by glob patterns to the file list. If an
183
+ # array is given, add each element of the array.
184
+ #
185
+ # Example:
186
+ # file_list.include("*.java", "*.cfg")
187
+ # file_list.include %w( math.c lib.h *.o )
188
+ #
189
+ def include(*filenames)
190
+ # TODO: check for pending
191
+ filenames.each do |fn|
192
+ if fn.respond_to? :to_ary
193
+ include(*fn.to_ary)
194
+ else
195
+ @pending_add << fn
196
+ end
197
+ end
198
+ @pending = true
199
+ self
200
+ end
201
+ alias :add :include
202
+
203
+ # Register a list of file name patterns that should be excluded
204
+ # from the list. Patterns may be regular expressions, glob
205
+ # patterns or regular strings.
206
+ #
207
+ # Note that glob patterns are expanded against the file system.
208
+ # If a file is explicitly added to a file list, but does not exist
209
+ # in the file system, then an glob pattern in the exclude list
210
+ # will not exclude the file.
211
+ #
212
+ # Examples:
213
+ # FileList['a.c', 'b.c'].exclude("a.c") => ['b.c']
214
+ # FileList['a.c', 'b.c'].exclude(/^a/) => ['b.c']
215
+ #
216
+ # If "a.c" is a file, then ...
217
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['b.c']
218
+ #
219
+ # If "a.c" is not a file, then ...
220
+ # FileList['a.c', 'b.c'].exclude("a.*") => ['a.c', 'b.c']
221
+ #
222
+ def exclude(*patterns)
223
+ patterns.each do |pat| @exclude_patterns << pat end
224
+ if ! @pending
225
+ calculate_exclude_regexp
226
+ reject! { |fn| fn.to_s =~ @exclude_re }
227
+ end
228
+ self
229
+ end
230
+
231
+
232
+
233
+
234
+ # Clear all the exclude patterns so that we exclude nothing.
235
+ def clear_exclude
236
+ @exclude_patterns = []
237
+ calculate_exclude_regexp if ! @pending
238
+ end
239
+
240
+ # Define equality.
241
+ def ==(array)
242
+ to_ary == array
243
+ end
244
+
245
+ # Return the internal array object.
246
+ def to_a
247
+ resolve
248
+ @items
249
+ end
250
+
251
+ # Return the internal array object.
252
+ def to_ary
253
+ resolve
254
+ @items
255
+ end
256
+
257
+ # Redefine * to return either a string or a new file list.
258
+ def *(other)
259
+ result = @items * other
260
+ case result
261
+ when Array
262
+ FileList.new.import(result)
263
+ else
264
+ result
265
+ end
266
+ end
267
+
268
+ # Resolve all the pending adds now.
269
+ def resolve
270
+ if @pending
271
+ @pending = false
272
+ @pending_add.each do |fn| resolve_add(fn) end
273
+ @pending_add = []
274
+ resolve_exclude
275
+ end
276
+ self
277
+ end
278
+
279
+ def calculate_exclude_regexp
280
+ ignores = []
281
+ @exclude_patterns.each do |pat|
282
+ case pat
283
+ when Regexp
284
+ ignores << pat
285
+ when /[*.]/
286
+ Dir[pat].each do |p| ignores << p end
287
+ else
288
+ ignores << Regexp.quote(pat)
289
+ end
290
+ end
291
+ if ignores.empty?
292
+ @exclude_re = /^$/
293
+ else
294
+ re_str = ignores.collect { |p| "(" + p.to_s + ")" }.join("|")
295
+ @exclude_re = Regexp.new(re_str)
296
+ end
297
+ end
298
+
299
+ def resolve_add(fn)
300
+ case fn
301
+ when Array
302
+ fn.each { |f| self.resolve_add(f) }
303
+ when %r{[*?]}
304
+ add_matching(fn)
305
+ else
306
+ # >>>>
307
+ self << fn.to_path
308
+ # <<<<
309
+ end
310
+ end
311
+
312
+ def resolve_exclude
313
+ @exclude_patterns.each do |pat|
314
+ case pat
315
+ when Regexp
316
+ # <<<<
317
+ reject! { |fn| fn.to_s =~ pat }
318
+ # >>>>
319
+ when /[*.]/
320
+ # <<<<
321
+ reject_list = Pathname.glob(pat)
322
+ # >>>>
323
+ reject! { |fn| reject_list.include?(fn) }
324
+ else
325
+ # <<<<
326
+ reject! { |fn| fn.to_s == pat.to_s }
327
+ # >>>>
328
+ end
329
+ end
330
+ self
331
+ end
332
+
333
+ # Return a new FileList with the results of running +sub+ against
334
+ # each element of the oringal list.
335
+ #
336
+ # Example:
337
+ # FileList['a.c', 'b.c'].sub(/\.c$/, '.o') => ['a.o', 'b.o']
338
+ #
339
+ def sub(pat, rep)
340
+ inject(FileList.new) { |res, fn| res << fn.sub(pat,rep) }
341
+ end
342
+
343
+ # Return a new FileList with the results of running +gsub+ against
344
+ # each element of the original list.
345
+ #
346
+ # Example:
347
+ # FileList['lib/test/file', 'x/y'].gsub(/\//, "\\")
348
+ # => ['lib\\test\\file', 'x\\y']
349
+ #
350
+ def gsub(pat, rep)
351
+ inject(FileList.new) { |res, fn| res << fn.gsub(pat,rep) }
352
+ end
353
+
354
+ # Same as +sub+ except that the oringal file list is modified.
355
+ def sub!(pat, rep)
356
+ each_with_index { |fn, i| self[i] = fn.sub(pat,rep) }
357
+ self
358
+ end
359
+
360
+ # Same as +gsub+ except that the original file list is modified.
361
+ def gsub!(pat, rep)
362
+ each_with_index { |fn, i| self[i] = fn.gsub(pat,rep) }
363
+ self
364
+ end
365
+
366
+ # Return a new array with <tt>String#ext</tt> method applied to
367
+ # each member of the array.
368
+ #
369
+ # This method is a shortcut for:
370
+ #
371
+ # array.collect { |item| item.ext(newext) }
372
+ #
373
+ # +ext+ is a user added method for the Array class.
374
+ def ext(newext='')
375
+ collect { |fn| fn.ext(newext) }
376
+ end
377
+
378
+
379
+ # FileList version of partition. Needed because the nested arrays
380
+ # should be FileLists in this version.
381
+ def partition(&block) # :nodoc:
382
+ resolve
383
+ result = @items.partition(&block)
384
+ [
385
+ FileList.new.import(result[0]),
386
+ FileList.new.import(result[1]),
387
+ ]
388
+ end
389
+
390
+ # Convert a FileList to a string by joining all elements with a space.
391
+ def to_s
392
+ resolve if @pending
393
+ self.join(' ')
394
+ end
395
+
396
+ # Add matching glob patterns.
397
+ def add_matching(pattern)
398
+ # <<<<
399
+ Pathname.glob(pattern) do |fn|
400
+ # >>>>
401
+ self << fn unless exclude?(fn)
402
+ end
403
+ end
404
+ private :add_matching
405
+
406
+ # Should the given file name be excluded?
407
+ def exclude?(fn)
408
+ calculate_exclude_regexp unless @exclude_re
409
+ # <<<<
410
+ fn.to_s =~ @exclude_re
411
+ # >>>>
412
+ end
413
+
414
+
415
+ DEFAULT_IGNORE_PATTERNS = [
416
+ /(^|[\/\\])CVS([\/\\]|$)/,
417
+ /(^|[\/\\])\.svn([\/\\]|$)/,
418
+ /\.bak$/,
419
+ /~$/,
420
+ /(^|[\/\\])core$/
421
+ ]
422
+ @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
423
+
424
+ def import(array)
425
+ @items = array
426
+ self
427
+ end
428
+
429
+ class << self
430
+ # Create a new file list including the files listed. Similar to:
431
+ #
432
+ # FileList.new(*args)
433
+ def [](*args)
434
+ new(*args)
435
+ end
436
+
437
+ # Set the ignore patterns back to the default value. The
438
+ # default patterns will ignore files
439
+ # * containing "CVS" in the file path
440
+ # * containing ".svn" in the file path
441
+ # * ending with ".bak"
442
+ # * ending with "~"
443
+ # * named "core"
444
+ #
445
+ # Note that file names beginning with "." are automatically
446
+ # ignored by Ruby's glob patterns and are not specifically
447
+ # listed in the ignore patterns.
448
+ def select_default_ignore_patterns
449
+ @exclude_patterns = DEFAULT_IGNORE_PATTERNS.dup
450
+ end
451
+
452
+ # Clear the ignore patterns.
453
+ def clear_ignore_patterns
454
+ @exclude_patterns = [ /^$/ ]
455
+ end
456
+ end
457
+
458
+ # <<<<
459
+ def require
460
+ each { |x| x.require }
461
+ end
462
+ # >>>>
463
+ end
464
+
@@ -0,0 +1,44 @@
1
+ # Copyright: Copyright (c) 2005 Nicolas Pouillard. All rights reserved.
2
+ # Author: Nicolas Pouillard <ertai@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: ertai $
6
+ # $Id: fileutils.rb 249 2005-05-31 13:23:42Z ertai $
7
+
8
+ require 'fileutils'
9
+
10
+ module FileUtils
11
+
12
+ alias remove_dir_without_chmod remove_dir
13
+
14
+ def remove_dir (dir, force = false) #:nodoc:
15
+ dir = dir.sub(%r</\z>, '')
16
+ first_time_p = true
17
+ begin
18
+ Dir.foreach(dir) do |file|
19
+ next if /\A\.\.?\z/ =~ file
20
+ path = "#{dir}/#{file.untaint}"
21
+ if File.symlink?(path)
22
+ remove_file path, force
23
+ elsif File.directory?(path)
24
+ remove_dir path, force
25
+ else
26
+ remove_file path, force
27
+ end
28
+ end
29
+ begin
30
+ Dir.rmdir dir
31
+ rescue Errno::ENOENT
32
+ raise unless force
33
+ end
34
+ rescue
35
+ if first_time_p
36
+ first_time_p = false
37
+ File.chmod 0777, dir
38
+ retry
39
+ end
40
+ raise
41
+ end
42
+ end
43
+
44
+ end
@@ -0,0 +1,196 @@
1
+ # Copyright: Copyright (c) 2004 Nicolas Despres. All rights reserved.
2
+ # Author: Nicolas Despres <polrop@lrde.epita.fr>.
3
+ # License: Gnu General Public License.
4
+
5
+ # $LastChangedBy: ertai $
6
+ # $Id: pathname.rb 252 2005-05-31 23:41:42Z ertai $
7
+
8
+
9
+ require 'pathname'
10
+ require 'core_ex/embedded_tests'
11
+ require 'core_ex/string'
12
+
13
+
14
+ class Pathname
15
+
16
+ def ensure_mkdir
17
+ (mkdir) rescue Errno::EEXIST
18
+ end
19
+
20
+ def ensure_mkpath
21
+ (mkpath) rescue Errno::EEXIST
22
+ end
23
+
24
+ def extsplit ( aChar='.' )
25
+ raise ArgumentError, "#{aChar} is not just a char" if aChar.size != 1
26
+ aChar = Regexp.escape(aChar)
27
+ to_s =~ /^(.*?)(#{aChar}[^#{aChar}]*)?$/
28
+ [Pathname.new($1), $2 || '']
29
+ end
30
+
31
+ def cp ( aPath )
32
+ FileUtils.cp self.to_s, aPath.to_s
33
+ end
34
+
35
+ def cp_r ( aPath )
36
+ FileUtils.cp_r self.to_s, aPath.to_s
37
+ end
38
+
39
+ def rm
40
+ FileUtils.rm self.to_s
41
+ end
42
+
43
+ def rm_r
44
+ FileUtils.rm_r self.to_s
45
+ end
46
+
47
+ def rm_rf
48
+ FileUtils.rm_rf self.to_s
49
+ end
50
+
51
+ def rm_f
52
+ FileUtils.rm_f self.to_s
53
+ end
54
+
55
+ # The current ruby's unlink implementation has a bug.
56
+ def unlink()
57
+ if FileTest.directory? @path and not FileTest.symlink? @path
58
+ Dir.unlink @path
59
+ else
60
+ File.unlink @path
61
+ end
62
+ end
63
+
64
+ # Allow this kind of things:
65
+ #
66
+ # root = Pathname.new('/tmp/test')
67
+ # foo, bar = 'foo', 'bar'
68
+ #
69
+ # ...
70
+ #
71
+ # (root/foo/bar).each_line do |line|
72
+ # ...
73
+ # end
74
+ alias :/ :+
75
+
76
+ def to_io
77
+ @open_mode ||= 'r'
78
+ open(@open_mode)
79
+ end
80
+
81
+ def to_path
82
+ self
83
+ end
84
+
85
+ attr_accessor :open_mode
86
+
87
+ def require ( *a )
88
+ if a.empty?
89
+ RequireSystem.instance.require(self)
90
+ else
91
+ Kernel.__require__(*a)
92
+ end
93
+ end
94
+
95
+ def load ( *a )
96
+ if a.empty?
97
+ RequireSystem.instance.load(self)
98
+ else
99
+ Kernel.__load__(*a)
100
+ end
101
+ end
102
+
103
+ def expand_path_with ( directories, extensions=nil )
104
+ ext = extname
105
+ exts, dirs = [''], ['']
106
+ unless extensions.nil?
107
+ if ext.empty?
108
+ exts = extensions
109
+ else
110
+ unless extensions.include? ext
111
+ raise ArgumentError, "bad extension `#{ext}' for #{self}"
112
+ end
113
+ end
114
+ end
115
+
116
+ dirs = directories unless absolute?
117
+
118
+ exts.each do |ext|
119
+ dirs.each do |dir|
120
+ dir = dir.to_path
121
+ file = dir + "#{self}#{ext}"
122
+ return file.expand_path.cleanpath if file.exist?
123
+ end
124
+ end
125
+ return nil
126
+ end
127
+
128
+ module ShortCut
129
+
130
+ # Allow to use this sort of constructions:
131
+ #
132
+ # `/path/to/a/file`.open do |f|
133
+ # ...
134
+ # end
135
+ def ` ( path )
136
+ Pathname.new(path)
137
+ end
138
+ end
139
+
140
+ end # class Pathname
141
+
142
+
143
+
144
+ #
145
+ # Unit test suite
146
+ #
147
+ test_section __FILE__ do
148
+
149
+
150
+ require 'tempfile'
151
+
152
+
153
+ class PathnameExTest < Test::Unit::TestCase
154
+
155
+ #
156
+ # Tests
157
+ #
158
+ def test_ensure_dir
159
+ p = nil
160
+ begin
161
+ name = Tempfile.new('pathname')
162
+ p = Pathname.new(name.path)
163
+ name.delete
164
+ assert(! p.directory?, 'no directory')
165
+ assert_nothing_raised { p.ensure_mkdir }
166
+ assert(p.directory?, 'directory')
167
+ assert_nothing_raised { p.ensure_mkdir }
168
+ assert(p.directory?, 'still directory')
169
+ ensure
170
+ p.rmdir unless p.nil?
171
+ end
172
+ end
173
+
174
+ include Pathname::ShortCut
175
+
176
+ def test_simple
177
+ assert_equal([`path.ext1`, '.ext2'], `path.ext1.ext2`.extsplit)
178
+ assert_equal([`path`, ''], `path`.extsplit)
179
+ assert_equal([`path`, '.'], `path.`.extsplit)
180
+ assert_equal([`path`, '-ext'], `path-ext`.extsplit('-'))
181
+ assert_equal([`path-ext1`, '-ext2'], `path-ext1-ext2`.extsplit('-'))
182
+ end
183
+
184
+ def test_slash
185
+ assert_equal(`path/to/a/file`, `path`/'to'/'a'/'file')
186
+ path, to, a, file = `/path`, 'to', 'a', 'file'
187
+ assert_equal(`/path/to/a/file`, path/to/a/file)
188
+ end
189
+
190
+ # def test_expand_path_with
191
+ # See core_ex/require RequireSystemTest#test_*_search
192
+ # end
193
+
194
+ end # class PathnameExTest
195
+
196
+ end