core_ex 0.1.0

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,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