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,378 @@
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
+ # Revision:: $Id: require.rb 252 2005-05-31 23:41:42Z ertai $
5
+
6
+ require 'core_ex/pathname'
7
+ require 'set'
8
+
9
+ module Kernel
10
+ class << self
11
+ alias :__require__ :require
12
+ alias :__load__ :load
13
+ end
14
+ end # module Kernel
15
+
16
+
17
+ class RequireSystem
18
+
19
+
20
+ EXTENSIONS = %w[ .rb .so .bundle .o .dll ] << ''
21
+
22
+
23
+ attr_reader :include_dirs, :loaded, :required
24
+
25
+ def initialize
26
+ @include_dirs = Set.new
27
+ @loaded = Set.new
28
+ # @autoloads = {} FIXME AUTOLOAD
29
+ @required = Set.new
30
+ self.class.enable
31
+ Kernel.kernel_require_system = self
32
+ Module.module_require_system = self
33
+ $".each do |file|
34
+ feature = file.to_path.expand_path_with($:, EXTENSIONS)
35
+ raise "file not found #{file}" if feature.nil?
36
+ @required << feature
37
+ end
38
+ feature = $0.to_path.expand_path_with($:, EXTENSIONS)
39
+ @required << feature unless feature.nil?
40
+ end
41
+
42
+
43
+ def check_pathname ( aPathname )
44
+ unless aPathname.is_a? Pathname
45
+ raise LoadError, "need a Pathname not a #{aPathname.class}"
46
+ end
47
+ end
48
+ protected :check_pathname
49
+
50
+
51
+ def cannot_found ( aPathname )
52
+ raise LoadError, "CoreEx: Cannot found #{aPathname} in $:"
53
+ end
54
+ private :cannot_found
55
+
56
+
57
+ def load ( aPathname )
58
+ check_pathname(aPathname)
59
+ abs = aPathname.expand_path_with($:, EXTENSIONS)
60
+ cannot_found(aPathname) if abs.nil?
61
+ @loaded << abs
62
+ Kernel.__load__(abs)
63
+ end
64
+
65
+
66
+ # FIXME This autoload doesn't work for these reasons:
67
+ # - |
68
+ # # file1.rb
69
+ # module B
70
+ # class A
71
+ # end
72
+ # end
73
+ #
74
+ # # file2.rb
75
+ # class A # where ::A != B::A
76
+ # end
77
+ #
78
+ # module B
79
+ # autoload :A, 'file1'
80
+ # A # this doesn't work because ::A exists so no const_missing is reached
81
+ # end
82
+ #
83
+ # - |
84
+ # # file1.rb
85
+ # module B
86
+ # class A
87
+ # end
88
+ # end
89
+ #
90
+ # # file2.rb
91
+ # module B
92
+ # autoload :A, 'file1'
93
+ # class ::C
94
+ # A # this doesn't work because A is search in ::C which is not
95
+ # # included in B.
96
+ # end
97
+ # end
98
+ # etc.
99
+ # Const missing was (it's obvious now) a very bad idea to make an autoload.
100
+ # We need to hook a const_get which is call every time.
101
+ def autoload ( aModule, aConst, aPathname )
102
+ raise ArgumentError, "need a module not #{cst}" unless aModule.is_a? Module
103
+ raise ArgumentError, "need a symbol not #{cst}" unless aConst.is_a? Symbol
104
+ raise if aConst.to_s =~ /::/
105
+ abs = aPathname.expand_path_with($:, EXTENSIONS)
106
+ abs = aPathname if abs.nil? # FIXME
107
+ # cannot_found(aPathname) if abs.nil?
108
+ aModule.__autoload__(aConst, abs.to_s)
109
+ # @autoloads[aModule] ||= {}
110
+ # @autoloads[aModule][aConst] ||= []
111
+ # @autoloads[aModule][aConst] << aPathname
112
+ self
113
+ end
114
+
115
+
116
+ # def const_missing ( aModule, aConst )
117
+ # raise ArgumentError, "need a module not #{cst}" unless aModule.is_a? Module
118
+ # raise ArgumentError, "need a symbol not #{cst}" unless aConst.is_a? Symbol
119
+ # raise if aConst.to_s =~ /::/
120
+ # @autoloads[aModule] ||= {}
121
+ # if @autoloads[aModule][aConst].nil?
122
+ # mods = aModule.to_s.split(/::/)
123
+ # if mods.size == 1
124
+ # p "CONSTMISSING"
125
+ # aModule.__const_missing__(aConst)
126
+ # end
127
+ # return const_missing(eval(mods[0..-2].join('::')), aConst)
128
+ # end
129
+ # @autoloads[aModule][aConst] ||= []
130
+ # @autoloads[aModule][aConst].each do |req|
131
+ # req.require
132
+ # end
133
+ # @autoloads[aModule].delete aConst
134
+ # if aModule.const_defined?(aConst)
135
+ # aModule.const_get(aConst)
136
+ # else
137
+ # p "CONSTMISSING"
138
+ # aModule.__const_missing__(aConst)
139
+ # end
140
+ # end
141
+
142
+ def require ( aPathname )
143
+ check_pathname(aPathname)
144
+ abs = aPathname.expand_path_with($:, EXTENSIONS)
145
+ cannot_found(aPathname) if abs.nil?
146
+ feature = aPathname.to_s
147
+ feature += abs.extname if aPathname.extname.empty?
148
+ if $".include? feature or @required.include? abs
149
+ return false
150
+ else
151
+ @required << abs
152
+ if abs.extname =~ /^(\.rb)?$/
153
+ $" << feature
154
+ res = Kernel.__load__(abs)
155
+ else
156
+ res = Kernel.__require__(abs.extsplit.first.to_s)
157
+ $" << feature unless $".include? feature
158
+ end
159
+ return true
160
+ end
161
+ end
162
+
163
+
164
+ def include_dir ( aPathname )
165
+ unless abs = include_dir?(aPathname)
166
+ $: << aPathname.to_s
167
+ $: << abs.to_s
168
+ @include_dirs << abs
169
+ end
170
+ self
171
+ end
172
+
173
+
174
+ def include_dir? ( aPathname )
175
+ check_pathname(aPathname)
176
+ abs = aPathname.expand_path.cleanpath
177
+ return ($:.include?(aPathname.to_s) ||
178
+ $:.include?(abs.to_s) ||
179
+ @include_dirs.include?(abs))? abs : nil
180
+ end
181
+
182
+
183
+ def required? ( aPathname )
184
+ check_pathname(aPathname)
185
+ abs = aPathname.expand_path_with($:, EXTENSIONS)
186
+ cannot_found(aPathname) if abs.nil?
187
+ feature = aPathname.to_s
188
+ feature += abs.extname if aPathname.extname.empty?
189
+ return $".include?(feature) || @required.include?(abs)
190
+ end
191
+
192
+
193
+ def self.instance
194
+ @@instance = new
195
+ class << self
196
+ undef :instance
197
+ def instance
198
+ @@instance
199
+ end
200
+ end
201
+ @@instance
202
+ end
203
+
204
+ def self.enable
205
+ class << self
206
+ module ::Kernel
207
+ def kernel_require_system= ( arg )
208
+ @@require_system = arg
209
+ end
210
+ alias :__require__ :require
211
+ def require ( aString )
212
+ @@require_system.require(aString.to_path)
213
+ end
214
+ alias :__load__ :load
215
+ def load ( aString )
216
+ @@require_system.load(aString.to_path)
217
+ end
218
+ end # module ::Kernel
219
+ class ::Module
220
+ def module_require_system= ( arg )
221
+ @@require_system = arg
222
+ end
223
+ alias :__autoload__ :autoload
224
+ def autoload ( aConst, anObject )
225
+ @@require_system.autoload(self, aConst, anObject.to_path)
226
+ end
227
+ # alias :__const_missing__ :const_missing
228
+ # def const_missing ( aConst )
229
+ # @@require_system.const_missing(self, aConst)
230
+ # end
231
+ end # module ::Module
232
+ undef :enable
233
+ def enable
234
+ end
235
+ end
236
+ end
237
+
238
+ end # class RequireSystem
239
+
240
+
241
+
242
+ test_section __FILE__ do
243
+
244
+ class RequireSystemTest < Test::Unit::TestCase
245
+
246
+ class MockRequirable < Pathname
247
+ @@requires = []
248
+ def initialize ( name, url )
249
+ @name, @url = name, url
250
+ end
251
+ def require ( *a )
252
+ @url.require
253
+ @@requires << @name
254
+ end
255
+ def self.requires
256
+ @@requires
257
+ end
258
+ def inspect
259
+ "#<#{self.class}: url=#@url, name=#@name>"
260
+ end
261
+ def self.[] ( name, url )
262
+ new(name, url)
263
+ end
264
+ end
265
+
266
+ @@save_dirs = $:.dup
267
+ @@save_files = $".dup
268
+
269
+ def setup
270
+ @rs = RequireSystem.new
271
+ @test = __FILE__.to_path.dirname.parent.parent + 'test'
272
+ @res = @test + 'resources'
273
+ @req = @res + 'require'
274
+ @s = 'test_require'
275
+ @s_dne = 'test_require_dne'
276
+ @s_so = 'test_require_so.so'
277
+ @s_rb = 'test_require_rb.rb'
278
+ @p = @s.to_path
279
+ @p_dne = @s_dne.to_path
280
+ @p_so = @s_so.to_path
281
+ @p_rb = @s_rb.to_path
282
+ @ls = [ @s, @s_dne, @s_so, @s_rb ]
283
+ @lp = [ @p, @p_dne, @p_so, @p_rb ]
284
+ @l = @ls + @lp
285
+ $:.replace @@save_dirs.dup
286
+ $".replace @@save_files.dup
287
+ @mr = MockRequirable
288
+ end
289
+
290
+ def test_0_initialize
291
+ %w[
292
+ load require
293
+ include_dir include_dirs
294
+ loaded required
295
+ ].each do |m|
296
+ assert_respond_to(@rs, m, m)
297
+ end
298
+ end
299
+
300
+ def assert_search ( inp, ref )
301
+ assert_nothing_raised do
302
+ @my = inp.expand_path_with($:, RequireSystem::EXTENSIONS)
303
+ ref = ref.expand_path.cleanpath unless ref.nil?
304
+ assert_equal(ref, @my, inp)
305
+ end
306
+ unless ref.nil?
307
+ assert_kind_of(Pathname, @my)
308
+ assert(@my.exist?)
309
+ end
310
+ end
311
+
312
+ def test_1_search
313
+ @ls.each do |x|
314
+ assert_raise(LoadError) do
315
+ x.expand_path_with($:, RequireSystem::EXTENSIONS)
316
+ end
317
+ end
318
+ @lp.each { |x| assert_search(x, nil) }
319
+ end
320
+
321
+ def test_2_include_dir
322
+ assert(! @rs.include_dir?(@test))
323
+ assert_nothing_raised do
324
+ assert_equal(@rs, @rs.include_dir(@test))
325
+ end
326
+ assert(@rs.include_dir?(@test))
327
+ $: << @res.to_s
328
+ assert(@rs.include_dir?(@res))
329
+ end
330
+
331
+ def test_2_search
332
+ @rs.include_dir(@req)
333
+ @lp.delete(@p_dne)
334
+ @lp.each { |x| assert_search(x, @req + x) }
335
+ @lp.each { |x| assert_search(@req + x, @req + x) }
336
+ assert_search(@p_dne, nil)
337
+ end
338
+
339
+ def test_load
340
+ end
341
+
342
+ def assert_require ( aPathname, ref_bool, ref_cst )
343
+ $REQUIRE_SYSTEM_TEST_COUNTER = 0
344
+ assert_nothing_raised { @my = aPathname.require }
345
+ assert_equal(ref_bool, @my)
346
+ assert_equal(ref_cst, $REQUIRE_SYSTEM_TEST_COUNTER)
347
+ end
348
+
349
+ def test_require
350
+ @lp.each do |x|
351
+ assert_raise(LoadError) { x.require }
352
+ end
353
+ @rs.include_dir(@req)
354
+ @lp.delete(@p_dne)
355
+ @lp.each { |x| assert_require(x, true, 1) }
356
+ assert_raise(LoadError) { @p_dne.require }
357
+ end
358
+
359
+ module ::AutoloadTree
360
+ end
361
+
362
+ def t_e_s_t_autoload # FIXME AUTOLOAD
363
+ @rs.include_dir(@res + 'autoload_tree')
364
+ [[:A,'A'], [:B,'B'], [:Foo, 'foo/C']].each do |cst,url|
365
+ assert_nothing_raised { AutoloadTree.autoload cst, @mr[cst,url] }
366
+ end
367
+ assert_nothing_raised do
368
+ assert(AutoloadTree)
369
+ assert_equal('AutoloadTree::A', ::AutoloadTree::A.name)
370
+ assert_equal('AutoloadTree::B', ::AutoloadTree::B.name)
371
+ assert_equal('AutoloadTree::Foo::C', ::AutoloadTree::Foo::C.name)
372
+ end
373
+ assert_equal([:A, :B, :Foo], @mr.requires)
374
+ end
375
+
376
+ end # RequireSystemTest
377
+ end
378
+
@@ -0,0 +1,47 @@
1
+ # Author:: Nicolas Pouillard <ertai@lrde.epita.fr>.
2
+ # Copyright:: Copyright (c) 2004, 2005 Nicolas Pouillard. All rights reserved.
3
+ # License:: GNU General Public License (GPL).
4
+ # Revision:: $Id$
5
+
6
+ class String
7
+
8
+ #
9
+ # Provide an helper to cut strings.
10
+ #
11
+ # Example:
12
+ #
13
+ # puts "
14
+ # | A very complex
15
+ # | string
16
+ # | with a specific indentation.
17
+ # |
18
+ # |I prefer that instead of a HERE doc
19
+ # | because I keep my indentation.
20
+ # |".head_cut!
21
+ #
22
+ def head_cut! ( sep='\|' )
23
+ gsub!(/^\s*#{sep}/, '')
24
+ self
25
+ end
26
+
27
+ def require
28
+ to_path.require
29
+ end
30
+
31
+ def to_path
32
+ Pathname.new(self)
33
+ end
34
+
35
+ def autoload ( aConst )
36
+ to_path.autoload(aConst)
37
+ end
38
+
39
+ # XXX
40
+ # Do not implement to_io.
41
+ # It breaks File.exists?
42
+ # def to_io
43
+ # end
44
+ # XXX
45
+
46
+ end # class String
47
+
@@ -0,0 +1,194 @@
1
+ # Copyright: Copyright (c) 2004 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: temp_path.rb 252 2005-05-31 23:41:42Z ertai $
7
+
8
+ require 'core_ex'
9
+ require 'tempfile'
10
+ require 'tmpdir'
11
+ require 'set'
12
+ require 'thread'
13
+
14
+ class TempPath < Pathname
15
+
16
+ @@tmps = Set.new
17
+ @@mutex = Mutex.new
18
+ @@progname = Pathname.new($0).basename
19
+ @@tmpdir = Pathname.new(Dir.tmpdir) + "#{@@progname}.#{$$}"
20
+ @@tmpdir.freeze
21
+ @@initialized = false
22
+ @@clean_planned = false
23
+ @@auto_clean = true
24
+
25
+ # You can use a temporary pathname like that:
26
+ #
27
+ # - # No argument are mandatory.
28
+ # tmp = TempPath.new
29
+ # tmp.open('w') { |f| f << 'foo' }
30
+ # tmp.clean
31
+ #
32
+ # - # You can choose the basename and an extension.
33
+ # TmpPath.new('the_base_file_name', 'rb')
34
+ #
35
+ # - # You can supply a block (recomended).
36
+ # TmpPath.new('foo') do |tmp|
37
+ # tmp.open('w') { |f| << 'foo' }
38
+ # tmp.exist? == true
39
+ # $tmp = tmp
40
+ # end
41
+ # $tmp.exist? == false
42
+ #
43
+ # - # You can make a temporary directory.
44
+ # TmpPath.new('adirectory') do |tmpdir|
45
+ # tmpdir.mkpath
46
+ # $file = tmpdir + 'foo'
47
+ # $file.open('w') { |f| f << 'foo' }
48
+ # end
49
+ # $file.exist? == false
50
+ #
51
+ # The file name follow this scheme:
52
+ #
53
+ # TempPath.new('foo', 'rb')
54
+ # => 'foo.111811.432.rb'
55
+ # TempPath.new('bar')
56
+ # => 'bar.111811.134'
57
+ #
58
+ # which follow this format:
59
+ # => 'base.pid.uniq.ext
60
+ #
61
+ def initialize ( base=@@progname, ext='', &block )
62
+ if base.to_s =~ /\//
63
+ raise ArgumentError, "bad basename, you give me a pathname #{base}"
64
+ end
65
+ self.class.init
66
+ ext = ".#{ext}" unless ext.empty? or ext[0] == ?.
67
+ res = nil
68
+ @@mutex.synchronize do
69
+ id_tmp = object_id
70
+ while (res = @@tmpdir + "#{base}.#{id_tmp}#{ext}").exist? \
71
+ and not @@tmps.include? res
72
+ id_tmp += 1
73
+ end
74
+ super(res)
75
+ @@tmps << self
76
+ end
77
+ if block_given?
78
+ begin
79
+ block[self.dup]
80
+ ensure
81
+ clean
82
+ end
83
+ end
84
+ end
85
+
86
+ # This method remove your temporary pathname.
87
+ # You do not need to call this method if you provide
88
+ # a block when you create a tempfile.
89
+ def clean
90
+ if exist?
91
+ if directory?
92
+ rmtree
93
+ else
94
+ unlink
95
+ end
96
+ end
97
+ end
98
+
99
+ def temp?
100
+ true
101
+ end
102
+
103
+ def self.init #:nodoc:
104
+ return if @@initialized
105
+ @@mutex.synchronize do
106
+ return if @@initialized
107
+ @@tmpdir.mkpath
108
+ @@initialized = true
109
+ at_exit { clean if @@auto_clean }
110
+ end
111
+ end
112
+
113
+ # By default the autoclean is on.
114
+ # But in some case (if you use your temppaths in at_exit)
115
+ # You must disable the autoclean.
116
+ # And manually call TempPath.clean at the very of the program.
117
+ def self.clean ( &block )
118
+ @@mutex.synchronize do
119
+ return if @@clean_planned
120
+ @@clean_planned = true
121
+ end
122
+ begin
123
+ block[] if block_given?
124
+ ensure
125
+ if @@tmpdir.exist?
126
+ @@tmpdir.rmtree
127
+ @@initialized = false
128
+ end
129
+ end
130
+ end
131
+
132
+ def self.auto_clean= ( aBool )
133
+ @@auto_clean = aBool
134
+ end
135
+
136
+ def self.tmpdir
137
+ init
138
+ @@tmpdir
139
+ end
140
+
141
+ end # class TempPath
142
+
143
+
144
+ class Pathname
145
+
146
+ def temp?
147
+ false
148
+ end
149
+
150
+ end # class Pathname
151
+
152
+
153
+
154
+ test_section __FILE__ do
155
+
156
+ class MkTempTest < Test::Unit::TestCase
157
+
158
+ def setup
159
+ assert_nothing_raised { @foo = TempPath.new('foo') }
160
+ assert_nothing_raised { @foobar = TempPath.new('foo', 'bar') }
161
+ @list = []
162
+ end
163
+
164
+ def teardown
165
+ @list.each { |l| l.clean }
166
+ [@foo, @foobar].each { |l| l.clean }
167
+ end
168
+
169
+ def test_interface
170
+ assert_match(/\.#{$$}\/foo\.\d+$/, @foo.to_s)
171
+ assert_match(/\.#{$$}\/foo\.\d+\.bar$/, @foobar.to_s)
172
+ assert_nothing_raised { @foo.open('w') { |f| f.puts 'FooFoo' } }
173
+ end
174
+
175
+ def test_many
176
+ (0 .. 100).each do |i|
177
+ tmp = TempPath.new('many')
178
+ tmp.open('w') { |f| f.puts "i: #{i}" }
179
+ assert(tmp.exist?)
180
+ assert_equal("i: #{i}\n", tmp.readlines.join)
181
+ @list << tmp
182
+ end
183
+ end
184
+
185
+ def test_temp?
186
+ assert(@foo.temp?, 'not tmp.temp?')
187
+ assert_nothing_raised do
188
+ assert(!Pathname.new(@foo.to_s).temp?)
189
+ end
190
+ end
191
+
192
+ end # class MkTempTest
193
+
194
+ end