rant 0.3.8 → 0.4.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.
Files changed (55) hide show
  1. data/NEWS +19 -0
  2. data/README +51 -24
  3. data/Rantfile +7 -8
  4. data/doc/advanced.rdoc +3 -1
  5. data/doc/package.rdoc +280 -0
  6. data/doc/rantfile.rdoc +9 -19
  7. data/doc/rubyproject.rdoc +24 -16
  8. data/lib/rant/archive/minitar.rb +983 -0
  9. data/lib/rant/archive/rubyzip/ioextras.rb +122 -0
  10. data/lib/rant/archive/rubyzip/stdrubyext.rb +114 -0
  11. data/lib/rant/archive/rubyzip/tempfile_bugfixed.rb +195 -0
  12. data/lib/rant/archive/rubyzip.rb +1575 -0
  13. data/lib/rant/import/archive/tgz.rb +49 -0
  14. data/lib/rant/import/archive/zip.rb +67 -0
  15. data/lib/rant/import/archive.rb +312 -0
  16. data/lib/rant/import/autoclean.rb +2 -2
  17. data/lib/rant/import/c/dependencies.rb +3 -3
  18. data/lib/rant/import/clean.rb +1 -1
  19. data/lib/rant/import/directedrule.rb +1 -1
  20. data/lib/rant/import/package/tgz.rb +35 -0
  21. data/lib/rant/import/package/zip.rb +36 -0
  22. data/lib/rant/import/rubydoc.rb +1 -1
  23. data/lib/rant/import/rubypackage.rb +19 -77
  24. data/lib/rant/import/rubytest.rb +1 -1
  25. data/lib/rant/import/subfile.rb +28 -14
  26. data/lib/rant/import/win32/rubycmdwrapper.rb +1 -1
  27. data/lib/rant/import.rb +36 -16
  28. data/lib/rant/plugin/csharp.rb +1 -1
  29. data/lib/rant/rantenv.rb +2 -13
  30. data/lib/rant/rantfile.rb +11 -11
  31. data/lib/rant/rantlib.rb +7 -3
  32. data/lib/rant/rantsys.rb +53 -2
  33. data/lib/rant/rantvar.rb +62 -1
  34. data/misc/TODO +41 -0
  35. data/{devel-notes → misc/devel-notes} +6 -0
  36. data/misc/mt.rb +3 -0
  37. data/misc/t.rb +18 -0
  38. data/test/import/c/dependencies/test_c_dependencies.rb +18 -0
  39. data/test/import/package/MANIFEST +4 -0
  40. data/test/import/package/Rantfile +49 -0
  41. data/test/import/package/deep/sub/sub/f1 +1 -0
  42. data/test/import/package/sub/f1 +1 -0
  43. data/test/import/package/sub2/f1 +1 -0
  44. data/test/import/package/test_package.rb +425 -0
  45. data/test/import/subfile/Rantfile +8 -0
  46. data/test/import/subfile/test_subfile.rb +12 -0
  47. data/test/project_rb1/rantfile.rb +3 -4
  48. data/test/project_rb1/test_project_rb1.rb +16 -40
  49. data/test/rant-import/test_rant-import.rb +3 -3
  50. data/test/test_filelist.rb +39 -2
  51. data/test/tutil.rb +89 -3
  52. metadata +35 -6
  53. data/TODO +0 -21
  54. data/lib/rant/import/package.rb +0 -258
  55. /data/{rantmethods.rb → misc/rantmethods.rb} +0 -0
@@ -4,7 +4,7 @@ require 'rant/rantlib'
4
4
  class Rant::Generators::RubyPackage
5
5
 
6
6
  class << self
7
- def rant_generate(app, ch, args, &block)
7
+ def rant_gen(app, ch, args, &block)
8
8
  if !args || args.empty?
9
9
  self.new(:app => app, :__caller__ => ch, &block)
10
10
  elsif args.size == 1
@@ -109,7 +109,6 @@ class Rant::Generators::RubyPackage
109
109
  @app = opts[:app] || Rant.rantapp
110
110
  @pkg_dir = "pkg"
111
111
  @pkg_dir_task = nil
112
- @dist_dir_task = nil
113
112
  @gem_task = nil
114
113
  @tar_task = nil
115
114
  @zip_task = nil
@@ -185,6 +184,10 @@ class Rant::Generators::RubyPackage
185
184
  next if attr =~ /^gem\-./
186
185
  next if mapped_attrs.include? attr
187
186
  setter = "#{attr}="
187
+ if attr == "files"
188
+ spec.send(setter, val.dup) if val
189
+ next
190
+ end
188
191
  spec.send(setter, val) if spec.respond_to? setter
189
192
  }
190
193
  # `gem-' attributes override others for gem spec
@@ -198,50 +201,9 @@ class Rant::Generators::RubyPackage
198
201
 
199
202
  def pkg_dir_task
200
203
  return if @pkg_dir_task
201
- if @dist_dir_task
202
- # not ideal but should work: If only the gem task will
203
- # be run, dist dir creation wouldn't be necessary
204
- return @pkg_dir_task = @dist_dir_task
205
- end
206
204
  @pkg_dir_task = @app.gen(Rant::Generators::Directory, @pkg_dir)
207
205
  end
208
206
 
209
- def dist_dir_task
210
- return if @dist_dir_task
211
- pkg_name = pkg_dist_dir
212
- dist_dir = pkg_dist_dir
213
- @dist_dir_task = @app.gen(Rant::Generators::Directory,
214
- dist_dir => files) { |t|
215
- # ensure to create new and empty destination directory
216
- if Dir.entries(dist_dir).size > 2 # "." and ".."
217
- @app.sys.rm_rf(dist_dir)
218
- @app.sys.mkdir(dist_dir)
219
- end
220
- # evaluate directory structure first
221
- dirs = []
222
- fl = []
223
- files.each { |e|
224
- if test(?d, e)
225
- dirs << e unless dirs.include? e
226
- else # assuming e is a file
227
- fl << e
228
- dir = File.dirname(e)
229
- dirs << dir unless dir == "." || dirs.include?(dir)
230
- end
231
- }
232
- # create directory structure
233
- dirs.each { |dir|
234
- dest = File.join(dist_dir, dir)
235
- @app.sys.mkpath(dest) unless test(?d, dest)
236
- }
237
- # link or copy files
238
- fl.each { |f|
239
- dest = File.join(dist_dir, f)
240
- @app.sys.safe_ln(f, dest)
241
- }
242
- }
243
- end
244
-
245
207
  # Create task for gem building. If tname is a true value, a
246
208
  # shortcut-task will be created.
247
209
  def gem_task(tname = :gem)
@@ -302,16 +264,15 @@ class Rant::Generators::RubyPackage
302
264
  pkg_files = files
303
265
  if tname
304
266
  # shortcut task
305
- @app.task({:__caller__ => @ch, tname => pkg_name})
267
+ @app.cx.task({:__caller__ => @ch, tname => pkg_name})
306
268
  end
307
269
  # actual tar-creating task
308
- @tar_task = @app.file(:__caller__ => @ch,
309
- pkg_name => [pkg_dist_dir] + pkg_files) { |t|
310
- @app.sys.cd(@pkg_dir) {
311
- @app.sys %W(tar zcf #{tar_pkg_name} #{pkg_base_name})
312
- }
313
- }
314
- dist_dir_task
270
+ @app.cx.import "package/tgz"
271
+ @tar_task =
272
+ Rant::Generators::Package::Tgz.rant_gen(@app, @ch,
273
+ ["#@pkg_dir/#{pkg_base_name}",
274
+ # we use tar.gz extension here for backwards compatibility
275
+ {:files => pkg_files, :extension => ".tar.gz"}])
315
276
  end
316
277
 
317
278
  def zip_task(tname = :zip)
@@ -325,17 +286,10 @@ class Rant::Generators::RubyPackage
325
286
  @app.task({:__caller__ => @ch, tname => pkg_name})
326
287
  end
327
288
  # actual zip-creating task
328
- @zip_task = @app.file(:__caller__ => @ch,
329
- pkg_name => [pkg_dist_dir] + pkg_files) { |t|
330
- @app.sys.cd(@pkg_dir) {
331
- # zip options:
332
- # y: store symlinks instead of referenced files
333
- # r: recurse into directories
334
- # q: quiet operation
335
- @app.sys %W(zip -yqr #{zip_pkg_name} #{pkg_base_name})
336
- }
337
- }
338
- dist_dir_task
289
+ @app.cx.import "package/zip"
290
+ @zip_task =
291
+ Rant::Generators::Package::Zip.rant_gen(@app, @ch,
292
+ ["#@pkg_dir/#{pkg_base_name}", {:files => pkg_files}])
339
293
  end
340
294
 
341
295
  # Create a task which runs gem/zip/tar tasks.
@@ -361,23 +315,15 @@ class Rant::Generators::RubyPackage
361
315
 
362
316
  # Returns true if at least one task was defined.
363
317
  def def_available_tasks
364
- defined = false
365
- if Rant::Env.have_tar?
366
- # we don't create shortcut tasks, hence nil as argument
367
- self.tar_task(nil)
368
- defined = true
369
- end
370
- if Rant::Env.have_zip?
371
- self.zip_task(nil)
372
- defined = true
373
- end
318
+ self.tar_task(nil)
319
+ self.zip_task(nil)
374
320
  begin
375
321
  require 'rubygems'
376
322
  self.gem_task(nil)
377
323
  defined = true
378
324
  rescue LoadError
379
325
  end
380
- defined
326
+ true
381
327
  end
382
328
 
383
329
  def pkg_base_name
@@ -392,10 +338,6 @@ class Rant::Generators::RubyPackage
392
338
  pkg_dist_dir + ".gem"
393
339
  end
394
340
 
395
- #--
396
- # Arghhh... tar makes me feel angry
397
- #++
398
-
399
341
  def tar_pkg_name
400
342
  pkg_base_name + ".tar.gz"
401
343
  end
@@ -6,7 +6,7 @@ module Rant
6
6
 
7
7
  class << self
8
8
 
9
- def rant_generate(app, ch, args, &block)
9
+ def rant_gen(app, ch, args, &block)
10
10
  if !args || args.empty?
11
11
  self.new(app, ch, &block)
12
12
  elsif args.size == 1
@@ -6,33 +6,47 @@
6
6
  require 'rant/rantlib'
7
7
 
8
8
  class Rant::Generators::SubFile
9
- def self.rant_generate(rac, ch, args, &block)
9
+ def self.rant_gen(rac, ch, args, &block)
10
10
  case args.size
11
11
  when 1
12
- basedir, fine = nil, args.first
13
- path = fine
12
+ fine, basedir = args
14
13
  when 2
15
14
  basedir, fine = args
16
- path = File.join(basedir, fine)
17
15
  else
18
16
  rac.abort_at(ch, "SubFile takes one or two arguments.")
19
17
  end
18
+ deps = []
19
+ if fine.respond_to? :to_hash
20
+ # hash should contain only one element
21
+ fine = fine.to_hash
22
+ fine.each { |k, v|
23
+ ch = v && next if k == :__caller__
24
+ fine = k
25
+ if Rant::FileList === v
26
+ deps = v.dup
27
+ elsif v.respond_to? :to_ary
28
+ deps.concat(v.to_ary)
29
+ else
30
+ deps << v
31
+ end
32
+ }
33
+ end
34
+ path = basedir ? File.join(basedir, fine) : fine
20
35
  file_desc = rac.pop_desc
21
- rac.prepare_task(path, block, ch) { |name,pre,blk|
36
+ rac.prepare_task({path => deps}, block, ch) { |name,pre,blk|
22
37
  dir, file = File.split(fine.to_s)
23
38
  dirp = basedir ? File.join(basedir, dir) : dir
24
39
  unless dir == "."
25
- dt = rac.resolve(dirp)
26
- if dt.empty?
27
- dt = [if basedir
28
- rac.cx.gen(
29
- ::Rant::Generators::Directory, basedir, dir)
40
+ unless rac.tasks.include? dirp
41
+ if basedir
42
+ ::Rant::Generators::Directory.rant_gen(
43
+ rac, ch, [basedir, dir])
30
44
  else
31
- rac.cx.gen(
32
- ::Rant::Generators::Directory, dir)
33
- end]
45
+ ::Rant::Generators::Directory.rant_gen(
46
+ rac, ch, [dir])
47
+ end
34
48
  end
35
- pre.concat(dt)
49
+ pre << dirp
36
50
  end
37
51
  rac.cx.desc file_desc
38
52
  ::Rant::FileTask.new(rac, name, pre, &blk)
@@ -8,7 +8,7 @@ require 'rant/rantlib'
8
8
  module Rant::Generators
9
9
  module Win32
10
10
  module RubyCmdWrapper
11
- def self.rant_generate(rac, ch, args, &block)
11
+ def self.rant_gen(rac, ch, args, &block)
12
12
  fl = args.first
13
13
  unless args.size == 1 and fl.respond_to? :to_ary
14
14
  rac.abort_at(ch,
data/lib/rant/import.rb CHANGED
@@ -78,10 +78,10 @@ module Rant
78
78
  @mono_fn = nil
79
79
  @force = false
80
80
  @rantapp = nil
81
- @core_imports = []
82
- @lib_imports = []
83
81
  @included_plugins = []
84
82
  @included_imports = []
83
+ # contains all filenames as given to +require+
84
+ @included_files = []
85
85
  @skip_comments = true
86
86
  @reduce_whitespace = false
87
87
  @auto = false
@@ -234,7 +234,7 @@ EOF
234
234
  "This file should contains the core of rant, so import is impossible.",
235
235
  "Please check your rant installation!")
236
236
  end
237
- @core_imports << "rantlib"
237
+ @included_files << "rant/rantlib"
238
238
  resolve_requires rantlib
239
239
  end
240
240
 
@@ -242,13 +242,19 @@ EOF
242
242
  rs = ""
243
243
  @imports.each { |name|
244
244
  next if @included_imports.include? name
245
- path = get_lib_rant_path "import/#{name}.rb"
245
+ lib_name = "import/#{name}"
246
+ lib_fn = "#{lib_name}.rb"
247
+ rfn = "rant/#{lib_name}"
248
+ path = get_lib_rant_path lib_fn
246
249
  unless path
247
250
  abort("No such import - #{name}")
248
251
  end
249
- msg "Including import `#{name}'", path
250
- @included_imports << name.dup
251
- rs << resolve_requires(File.read(path))
252
+ @included_imports << name.dup
253
+ unless @included_files.include? rfn
254
+ @included_files << rfn
255
+ msg "Including import `#{name}'", path
256
+ rs << resolve_requires(File.read(path))
257
+ end
252
258
  }
253
259
  rs
254
260
  end
@@ -258,13 +264,19 @@ EOF
258
264
  @plugins.each { |name|
259
265
  lc_name = name.downcase
260
266
  next if @included_plugins.include? lc_name
261
- path = get_lib_rant_path "plugin/#{lc_name}.rb"
267
+ plugin_name = "plugin/#{lc_name}"
268
+ plugin_fn = "#{plugin_name}.rb"
269
+ rfn = "rant/#{plugin_name}"
270
+ path = get_lib_rant_path plugin_fn
262
271
  unless File.exist? path
263
272
  abort("No such plugin - #{name}")
264
273
  end
265
- msg "Including plugin `#{lc_name}'", path
266
274
  @included_plugins << lc_name
267
- rs << resolve_requires(File.read(path))
275
+ unless @included_files.include? rfn
276
+ @included_files << rfn
277
+ msg "Including plugin `#{lc_name}'", path
278
+ rs << resolve_requires(File.read(path))
279
+ end
268
280
  }
269
281
  rs
270
282
  end
@@ -283,6 +295,12 @@ EOF
283
295
  end
284
296
  # skip shebang line
285
297
  next if line =~ /^#! ?(\/|\\)?\w/
298
+ # uncomment line if +uncomment+ directive given
299
+ if line =~ /^\s*#.*#\s?rant-import:\s?uncomment\s*$/
300
+ line.sub! '#', ''
301
+ end
302
+ # skip line if +remove+ directive given
303
+ next if line =~ /#\s?rant-import:\s?remove\s*$/
286
304
  # skip pure comment lines
287
305
  next if line =~ /^\s*#/ if @skip_comments
288
306
  if line =~ /^=begin\s/
@@ -291,10 +309,11 @@ EOF
291
309
  end
292
310
  name = nil
293
311
  lib_file = nil
294
- if line =~ /\s*(require|load)\s+('|")rant\/(\w+)(\.rb)?('|")/
312
+ if line =~ /\s*(require|load)\s+('|")rant\/([\w\/]+)(\.rb)?('|")/
295
313
  # Rant library code
296
314
  name = $3
297
- elsif line =~ /\s*(require|load)\s+('|")rant\/(import\/\w+)(\.rb)?('|")/
315
+ elsif line =~
316
+ /\s*(require|load)\s+('|")rant\/(import\/[\w\/]+)(\.rb)?('|")/
298
317
  # some "import" code
299
318
  name = $3
300
319
  elsif line =~ /\s*(require|load)\s+('|")([^\2]+)\2[^r]*rant-import/
@@ -302,16 +321,17 @@ EOF
302
321
  lib_file = $3
303
322
  end
304
323
  if name
305
- next if @core_imports.include? name
324
+ rfn = "rant/#{name}"
325
+ next if @included_files.include? rfn
306
326
  path = get_lib_rant_path "#{name}.rb"
307
327
  msg "Including `#{name}'", path
308
- @core_imports << name
328
+ @included_files << rfn
309
329
  rs << resolve_requires(File.read(path))
310
330
  elsif lib_file
311
- next if @lib_imports.include? lib_file
331
+ next if @included_files.include? lib_file
312
332
  path = get_lib_path "#{lib_file}.rb"
313
333
  msg "Including `#{lib_file}'", path
314
- @lib_imports << lib_file
334
+ @included_files << lib_file
315
335
  rs << resolve_requires(File.read(path))
316
336
  else
317
337
  line.sub!(/^\s+/, '') if @reduce_whitespace
@@ -9,7 +9,7 @@ module Rant
9
9
  class Generators::Assembly < CsCompiler
10
10
  class << self
11
11
 
12
- def rant_generate(app, clr, args, &block)
12
+ def rant_gen(app, clr, args, &block)
13
13
  assembly = self.new(&block)
14
14
  if args.size == 1
15
15
  targ = args.first
data/lib/rant/rantenv.rb CHANGED
@@ -4,8 +4,8 @@ require 'rbconfig'
4
4
 
5
5
  module Rant end
6
6
 
7
- # This module provides some platform indenpendant
8
- # (let's hope) environment information.
7
+ # This module interfaces with the environment to provide
8
+ # information/conversion methods in a portable manner.
9
9
  module Rant::Env
10
10
  OS = ::Config::CONFIG['target']
11
11
  RUBY = ::Config::CONFIG['ruby_install_name']
@@ -176,14 +176,3 @@ module Rant::Console
176
176
 
177
177
  extend self
178
178
  end
179
-
180
- class Rant::CustomConsole
181
- include Rant::Console
182
-
183
- def initialize msg_prefix = RANT_PREFIX
184
- @msg_prefix = msg_prefix || ""
185
- end
186
- def msg_prefix=(str)
187
- @msg_prefix = str || ""
188
- end
189
- end
data/lib/rant/rantfile.rb CHANGED
@@ -170,7 +170,7 @@ module Rant
170
170
  include Node
171
171
 
172
172
  class << self
173
- def rant_generate(rac, ch, args, &block)
173
+ def rant_gen(rac, ch, args, &block)
174
174
  unless args.size == 1
175
175
  rac.abort("LightTask takes only one argument " +
176
176
  "which has to be the taskname (string or symbol)")
@@ -242,9 +242,9 @@ module Rant
242
242
  T0 = Time.at(0).freeze
243
243
 
244
244
  class << self
245
- def rant_generate(rac, ch, args, &block)
245
+ def rant_gen(rac, ch, args, &block)
246
246
  if args.size == 1
247
- UserTask.rant_generate(rac, ch, args, &block)
247
+ UserTask.rant_gen(rac, ch, args, &block)
248
248
  else
249
249
  rac.abort("Task generator currently takes only one" +
250
250
  " argument. (generates a UserTask)")
@@ -459,7 +459,7 @@ module Rant
459
459
  class UserTask < Task
460
460
 
461
461
  class << self
462
- def rant_generate(rac, ch, args, &block)
462
+ def rant_gen(rac, ch, args, &block)
463
463
  unless args.size == 1
464
464
  rac.abort("UserTask takes only one argument " +
465
465
  "which has to be like one given to the " +
@@ -594,7 +594,7 @@ module Rant
594
594
  # block will be called after complete directory creation.
595
595
  # After the block execution, the modification time of the
596
596
  # directory will be updated.
597
- def rant_generate(rac, ch, args, &block)
597
+ def rant_gen(rac, ch, args, &block)
598
598
  case args.size
599
599
  when 1
600
600
  name, pre, file, ln = rac.normalize_task_arg(args.first, ch)
@@ -630,11 +630,11 @@ module Rant
630
630
  task_block = nil
631
631
  desc_for_last = rac.pop_desc
632
632
  dirs.each { |dir|
633
- pre = [path]
634
- pre.compact!
633
+ pre = [path]
634
+ pre.compact!
635
635
  if dir.equal?(dirs.last)
636
636
  rac.cx.desc desc_for_last
637
- pre.concat prerequisites if prerequisites
637
+ pre = prerequisites + pre
638
638
  task_block = block
639
639
  end
640
640
  path = path.nil? ? dir : File.join(path, dir)
@@ -713,7 +713,7 @@ module Rant
713
713
  class SourceNode
714
714
  include Node
715
715
 
716
- def self.rant_generate(rac, ch, args)
716
+ def self.rant_gen(rac, ch, args)
717
717
  unless args.size == 1
718
718
  rac.abort_at(ch, "SourceNode takes one argument.")
719
719
  end
@@ -800,7 +800,7 @@ module Rant
800
800
  class Rule < ::Proc
801
801
  # Generate a rule by installing an at_resolve hook for
802
802
  # +rac+.
803
- def self.rant_generate(rac, ch, args, &block)
803
+ def self.rant_gen(rac, ch, args, &block)
804
804
  unless args.size == 1
805
805
  rac.abort_at(ch, "Rule takes only one argument.")
806
806
  end
@@ -863,7 +863,7 @@ module Rant
863
863
  end # class Rule
864
864
 
865
865
  class Action
866
- def self.rant_generate(rac, ch, args, &block)
866
+ def self.rant_gen(rac, ch, args, &block)
867
867
  unless args.empty?
868
868
  rac.warn_msg(rac.pos_text(ch[:file], ch[:ln]),
869
869
  "Action doesn't take arguments.")
data/lib/rant/rantlib.rb CHANGED
@@ -539,12 +539,12 @@ class Rant::RantApp
539
539
  pre = []
540
540
  # validate args
541
541
  generator = args.shift
542
- unless generator.respond_to? :rant_generate
542
+ unless generator.respond_to? :rant_gen
543
543
  abort_at(ch,
544
544
  "First argument to `gen' has to be a task-generator.")
545
545
  end
546
546
  # ask generator to produce a task for this application
547
- generator.rant_generate(self, ch, args, &block)
547
+ generator.rant_gen(self, ch, args, &block)
548
548
  end
549
549
 
550
550
  # Currently ignores block.
@@ -938,6 +938,10 @@ class Rant::RantApp
938
938
  end
939
939
  public :resolve
940
940
 
941
+ # This hook will be invoked when no matching task is found for a
942
+ # target. It may create one or more tasks for the target, which is
943
+ # given as argument, on the fly and return an array of the created
944
+ # tasks or nil.
941
945
  def at_resolve &block
942
946
  @resolve_hooks << block if block
943
947
  end
@@ -1244,7 +1248,7 @@ class Rant::RantApp
1244
1248
  end
1245
1249
  elsif Rant::CommandError === orig
1246
1250
  msg << orig.message if @opts[:err_commands]
1247
- elsif !(Rant::RantAbortException === orig)
1251
+ elsif orig && !(Rant::RantAbortException === orig)
1248
1252
  msg << orig.message << orig.backtrace[0..4]
1249
1253
  end
1250
1254
  end
data/lib/rant/rantsys.rb CHANGED
@@ -57,6 +57,7 @@ module Rant
57
57
  end
58
58
 
59
59
  def to_ary
60
+ #puts caller
60
61
  resolve if @pending
61
62
  @files
62
63
  end
@@ -68,7 +69,9 @@ module Rant
68
69
  def +(other)
69
70
  case other
70
71
  when Array
71
- dup.files.concat(other)
72
+ c = dup
73
+ c.files.concat(other)
74
+ c
72
75
  when FileList
73
76
  c = other.dup
74
77
  c.actions.concat(@actions)
@@ -80,11 +83,22 @@ module Rant
80
83
  end
81
84
  end
82
85
 
83
- def <<(file)
86
+ # Add file to filelist unless it matches an exclude pattern.
87
+ # Take care: Don't rely on any order when inserting a file
88
+ # with this method!
89
+ def add(file)
84
90
  @files << file unless file =~ ignore_rx
85
91
  self
86
92
  end
87
93
 
94
+ # Like #add but doesn't honor exclude patterns.
95
+ # Returns self.
96
+ def <<(file)
97
+ #STDERR.puts caller unless String === file
98
+ @actions << [:apply_ary_method_1, :push, file]
99
+ self
100
+ end
101
+
88
102
  def concat(ary)
89
103
  resolve if @pending
90
104
  ix = ignore_rx
@@ -316,10 +330,47 @@ module Rant
316
330
  def arglist
317
331
  to_ary.arglist
318
332
  end
333
+
334
+ # Same as #uniq! but evaluation is delayed until the next read
335
+ # access (e.g. by calling #each). Always returns self.
336
+ def lazy_uniq!
337
+ @actions << [:apply_ary_method, :uniq!]
338
+ @pending = true
339
+ self
340
+ end
341
+
342
+ # Same as #sort! but evaluation is delayed until the next read
343
+ # access (e.g. by calling #each). Always returns self.
344
+ def lazy_sort!
345
+ @actions << [:apply_ary_method, :sort!]
346
+ @pending = true
347
+ self
348
+ end
349
+
350
+ private
351
+ def apply_ary_method(meth)
352
+ @files.send meth
353
+ end
354
+ def apply_ary_method_1(meth, arg1)
355
+ @files.send meth, arg1
356
+ end
319
357
  end # class FileList
320
358
 
321
359
  class RacFileList < FileList
322
360
 
361
+ # Returns files if <tt>FileList === files</tt>, otherwise
362
+ # a new RacFileList with <tt>file.to_ary</tt> as initial set
363
+ # of files.
364
+ def self.filelist(rac, files)
365
+ return files if FileList === files
366
+ fl = self.new(rac)
367
+ fl.instance_eval {
368
+ @pending = false
369
+ @files = files.to_ary
370
+ }
371
+ fl
372
+ end
373
+
323
374
  attr_reader :subdir
324
375
  attr_reader :basedir
325
376
 
data/lib/rant/rantvar.rb CHANGED
@@ -16,7 +16,7 @@
16
16
  # If you're looking for general info about Rant, read the
17
17
  # README[link:files/README.html].
18
18
  module Rant
19
- VERSION = '0.3.8'
19
+ VERSION = '0.4.0'
20
20
 
21
21
  # Those are the filenames for rantfiles.
22
22
  # Case matters!
@@ -46,6 +46,67 @@ module Rant
46
46
  module Generators
47
47
  end
48
48
 
49
+ @__rant_no_value__ = Object.new.freeze
50
+ def self.__rant_no_value__
51
+ @__rant_no_value__
52
+ end
53
+
54
+ module MetaUtils
55
+ # Creates two accessor methods:
56
+ # obj.attr_name:: Return value of instance variable
57
+ # @attr_name
58
+ # obj.attr_name = val:: Set value instance variable
59
+ # @attr_name to val
60
+ # obj.attr_name val:: same as above
61
+ def rant_attr attr_name
62
+ attr_name = valid_attr_name attr_name
63
+ attr_writer attr_name
64
+ module_eval <<-EOD
65
+ def #{attr_name} val=Rant.__rant_no_value__
66
+ if val.equal? Rant.__rant_no_value__
67
+ @#{attr_name}
68
+ else
69
+ @#{attr_name} = val
70
+ end
71
+ end
72
+ EOD
73
+ nil
74
+ end
75
+ # Creates accessor methods like #rant_attr for the attribute
76
+ # attr_name. Additionally, values are converted with to_str
77
+ # before assignment to instance variables happens.
78
+ def string_attr attr_name
79
+ attr_name = valid_attr_name attr_name
80
+ module_eval <<-EOD
81
+ def #{attr_name}=(val)
82
+ if val.respond_to? :to_str
83
+ @#{attr_name} = val.to_str
84
+ else
85
+ raise ArgumentError,
86
+ "string (#to_str) value required", caller
87
+ end
88
+ end
89
+ def #{attr_name} val=Rant.__rant_no_value__
90
+ if val.equal? Rant.__rant_no_value__
91
+ @#{attr_name}
92
+ else
93
+ self.__send__(:#{attr_name}=, val)
94
+ end
95
+ end
96
+ EOD
97
+ nil
98
+ end
99
+ # attr_name is converted to a string with #to_s and has to
100
+ # match /^\w+$/. Returns attr_name.to_s.
101
+ def valid_attr_name attr_name
102
+ attr_name = attr_name.to_s
103
+ attr_name =~ /^\w+$/ or
104
+ raise ArgumentError,
105
+ "argument has to match /^\w+$/", caller
106
+ attr_name
107
+ end
108
+ end # module MetaUtils
109
+
49
110
  module RantVar
50
111
 
51
112
  class Error < RantError