comp_tree 0.7.6 → 1.0.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.
data/devel/jumpstart.rb CHANGED
@@ -1,281 +1,543 @@
1
- $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
2
- $LOAD_PATH.unshift File.dirname(__FILE__)
3
-
4
- require 'rubygems'
5
- require 'ostruct'
6
- require 'rbconfig'
7
-
8
- require 'rake/gempackagetask'
9
- require 'rake/contrib/sshpublisher'
10
- require 'rake/clean'
11
-
12
- require 'rdoc/rdoc'
13
-
14
- require 'jumpstart/ruby'
15
- require 'jumpstart/lazy_attribute'
16
- require 'jumpstart/simple_installer'
17
1
 
18
2
  class Jumpstart
19
- include LazyAttribute
20
-
21
- def initialize(project_name)
22
- attribute :name do
23
- project_name
24
- end
25
-
26
- attribute :version do
27
- const = "VERSION"
28
- begin
29
- require name
30
- if mod = Object.const_get(to_camel_case(name))
31
- candidates = mod.constants.grep(%r!#{const}!)
32
- result = (
33
- if candidates.size == 1
34
- candidates.first
35
- elsif candidates.include? const
36
- const
3
+ class SimpleInstaller
4
+ def initialize
5
+ require 'fileutils'
6
+ require 'rbconfig'
7
+ require 'find'
8
+ dest_root = Config::CONFIG["sitelibdir"]
9
+ sources = []
10
+ Find.find("./lib") { |source|
11
+ if install_file?(source)
12
+ sources << source
13
+ end
14
+ }
15
+ @spec = sources.inject(Array.new) { |acc, source|
16
+ if source == "./lib"
17
+ acc
18
+ else
19
+ dest = File.join(dest_root, source.sub(%r!\A\./lib!, ""))
20
+
21
+ install = lambda {
22
+ if File.directory?(source)
23
+ unless File.directory?(dest)
24
+ puts "mkdir #{dest}"
25
+ FileUtils.mkdir(dest)
26
+ end
37
27
  else
38
- raise
28
+ puts "install #{source} --> #{dest}"
29
+ FileUtils.install(source, dest)
39
30
  end
40
- )
41
- mod.const_get(result)
42
- else
43
- raise
31
+ }
32
+
33
+ uninstall = lambda {
34
+ if File.directory?(source)
35
+ if File.directory?(dest)
36
+ puts "rmdir #{dest}"
37
+ FileUtils.rmdir(dest)
38
+ end
39
+ else
40
+ if File.file?(dest)
41
+ puts "rm #{dest}"
42
+ FileUtils.rm(dest)
43
+ end
44
+ end
45
+ }
46
+
47
+ acc << {
48
+ :source => source,
49
+ :dest => dest,
50
+ :install => install,
51
+ :uninstall => uninstall,
52
+ }
44
53
  end
45
- rescue Exception
46
- "0.0.0"
47
- end
54
+ }
48
55
  end
49
-
50
- attribute :rubyforge_name do
51
- name.gsub('_', '')
56
+
57
+ def install_file?(source)
58
+ File.directory?(source) or
59
+ (File.file?(source) and File.extname(source) == ".rb")
52
60
  end
53
61
 
54
- attribute :rubyforge_user do
55
- email.first[%r!^.*(?=@)!]
62
+ def install
63
+ @spec.each { |entry|
64
+ entry[:install].call
65
+ }
56
66
  end
57
-
58
- attribute :readme_file do
59
- "README.rdoc"
67
+
68
+ def uninstall
69
+ @spec.reverse.each { |entry|
70
+ entry[:uninstall].call
71
+ }
60
72
  end
61
73
 
62
- attribute :history_file do
63
- "CHANGES.rdoc"
74
+ def run(args = ARGV)
75
+ if args.empty?
76
+ install
77
+ elsif args.size == 1 and args.first == "--uninstall"
78
+ uninstall
79
+ else
80
+ raise "unrecognized arguments: #{args.inspect}"
81
+ end
64
82
  end
83
+ end
65
84
 
66
- attribute :doc_dir do
67
- "documentation"
85
+ module AttrLazy
86
+ def attr_lazy(name, &block)
87
+ AttrLazy.define_reader(class << self ; self ; end, name, &block)
68
88
  end
69
89
 
70
- attribute :spec_files do
71
- Dir["spec/*_{spec,example}.rb"]
90
+ def attr_lazy_accessor(name, &block)
91
+ attr_lazy(name, &block)
92
+ AttrLazy.define_writer(class << self ; self ; end, name, &block)
72
93
  end
73
94
 
74
- attribute :test_files do
75
- Dir["test/test_*.rb"]
76
- end
95
+ class << self
96
+ def included(mod)
97
+ (class << mod ; self ; end).class_eval do
98
+ def attr_lazy(name, &block)
99
+ AttrLazy.define_reader(self, name, &block)
100
+ end
77
101
 
78
- attribute :rcov_dir do
79
- "coverage"
80
- end
102
+ def attr_lazy_accessor(name, &block)
103
+ attr_lazy(name, &block)
104
+ AttrLazy.define_writer(self, name, &block)
105
+ end
106
+ end
107
+ end
81
108
 
82
- attribute :spec_output do
83
- "spec.html"
84
- end
109
+ def define_evaluated_reader(instance, name, value)
110
+ (class << instance ; self ; end).class_eval do
111
+ remove_method name rescue nil
112
+ define_method name do
113
+ value
114
+ end
115
+ end
116
+ end
85
117
 
86
- %w[gem tgz].map { |ext|
87
- attribute ext.to_sym do
88
- "pkg/#{name}-#{version}.#{ext}"
118
+ def define_reader(klass, name, &block)
119
+ klass.class_eval do
120
+ remove_method name rescue nil
121
+ define_method name do
122
+ value = instance_eval(&block)
123
+ AttrLazy.define_evaluated_reader(self, name, value)
124
+ value
125
+ end
126
+ end
89
127
  end
90
- }
91
128
 
92
- attribute :rcov_options do
93
- # workaround for the default rspec task
94
- Dir["*"].select { |f| File.directory? f }.inject(Array.new) { |acc, dir|
95
- if dir == "lib"
96
- acc
97
- else
98
- acc + ["--exclude", dir + "/"]
129
+ def define_writer(klass, name, &block)
130
+ klass.class_eval do
131
+ writer = "#{name}="
132
+ remove_method writer rescue nil
133
+ define_method writer do |value|
134
+ AttrLazy.define_evaluated_reader(self, name, value)
135
+ value
136
+ end
99
137
  end
100
- } + ["--text-report"]
138
+ end
101
139
  end
140
+ end
102
141
 
103
- attribute :readme_file do
104
- "README.rdoc"
105
- end
106
-
107
- attribute :manifest_file do
108
- "MANIFEST"
109
- end
142
+ module Ruby
143
+ module_function
144
+
145
+ def executable
146
+ require 'rbconfig'
110
147
 
111
- attribute :files do
112
- if File.exist?(manifest_file)
113
- File.read(manifest_file).split("\n")
148
+ name = File.join(
149
+ Config::CONFIG["bindir"],
150
+ Config::CONFIG["RUBY_INSTALL_NAME"]
151
+ )
152
+
153
+ if Config::CONFIG["host"] =~ %r!(mswin|cygwin|mingw)! and
154
+ File.basename(name) !~ %r!\.(exe|com|bat|cmd)\Z!i
155
+ name + Config::CONFIG["EXEEXT"]
114
156
  else
115
- [manifest_file] + `git ls-files`.split("\n")
157
+ name
158
+ end
159
+ end
160
+
161
+ def run(*args)
162
+ cmd = [executable, *args]
163
+ unless system(*cmd)
164
+ cmd_str = cmd.map { |t| "'#{t}'" }.join(", ")
165
+ raise "system(#{cmd_str}) failed with status #{$?.exitstatus}"
116
166
  end
117
167
  end
118
168
 
119
- attribute :rdoc_files do
120
- Dir["lib/**/*.rb"]
169
+ def run_code_and_capture(code)
170
+ IO.popen(%{"#{executable}"}, "r+") { |pipe|
171
+ pipe.print(code)
172
+ pipe.flush
173
+ pipe.close_write
174
+ pipe.read
175
+ }
121
176
  end
122
-
123
- attribute :extra_rdoc_files do
124
- if File.exist?(readme_file)
125
- [readme_file]
126
- else
127
- []
177
+
178
+ def run_file_and_capture(file)
179
+ unless File.file? file
180
+ raise "file does not exist: `#{file}'"
128
181
  end
182
+ IO.popen(%{"#{executable}" "#{file}"}, "r") { |pipe|
183
+ pipe.read
184
+ }
129
185
  end
186
+
187
+ def with_warnings(value = true)
188
+ previous = $VERBOSE
189
+ $VERBOSE = value
190
+ begin
191
+ yield
192
+ ensure
193
+ $VERBOSE = previous
194
+ end
195
+ end
196
+
197
+ def no_warnings(&block)
198
+ with_warnings(nil, &block)
199
+ end
200
+ end
130
201
 
131
- attribute :rdoc_options do
132
- if File.exist?(readme_file)
133
- ["--main", readme_file]
134
- else
135
- []
136
- end + [
137
- "--title", "#{name}: #{summary}",
138
- ] + (files - rdoc_files).inject(Array.new) { |acc, file|
139
- acc + ["--exclude", file]
202
+ module Util
203
+ module_function
204
+
205
+ def run_ruby_on_each(*files)
206
+ files.each { |file|
207
+ Ruby.run("-w", file)
140
208
  }
141
209
  end
142
210
 
143
- attribute :browser do
144
- if Config::CONFIG["host"] =~ %r!darwin!
145
- app = %w[Firefox Safari].map { |t|
146
- "/Applications/#{t}.app"
147
- }.select { |t|
148
- File.exist? t
149
- }.first
150
- if app
151
- ["open", app]
152
- else
153
- raise "need to set `browser'"
154
- end
155
- else
156
- "firefox"
157
- end
158
- end
159
-
160
- attribute :gemspec do
161
- Gem::Specification.new { |g|
162
- g.has_rdoc = true
163
- %w[
164
- name
165
- authors
166
- email
167
- summary
168
- version
169
- description
170
- files
171
- extra_rdoc_files
172
- rdoc_options
173
- ].each { |param|
174
- value = send(param) and (
175
- g.send("#{param}=", value)
176
- )
211
+ def to_camel_case(str)
212
+ str.split('_').map { |t| t.capitalize }.join
213
+ end
214
+
215
+ def write_file(file)
216
+ contents = yield
217
+ File.open(file, "wb") { |out|
218
+ out.print(contents)
219
+ }
220
+ contents
221
+ end
222
+
223
+ def replace_file(file)
224
+ old_contents = File.read(file)
225
+ new_contents = yield(old_contents)
226
+ if old_contents != new_contents
227
+ File.open(file, "wb") { |output|
228
+ output.print(new_contents)
177
229
  }
230
+ end
231
+ new_contents
232
+ end
233
+ end
178
234
 
179
- if rubyforge_name
180
- g.rubyforge_project = rubyforge_name
181
- end
235
+ module InstanceEvalWithArgs
236
+ module_function
182
237
 
183
- if url
184
- g.homepage = url
238
+ def with_temp_method(instance, method_name, method_block)
239
+ (class << instance ; self ; end).class_eval do
240
+ define_method(method_name, &method_block)
241
+ begin
242
+ yield method_name
243
+ ensure
244
+ remove_method(method_name)
185
245
  end
246
+ end
247
+ end
186
248
 
187
- extra_deps.each { |dep|
188
- g.add_dependency(*dep)
189
- }
190
-
191
- extra_dev_deps.each { |dep|
192
- g.add_development_dependency(*dep)
193
- }
249
+ def call_temp_method(instance, method_name, *args, &method_block)
250
+ with_temp_method(instance, method_name, method_block) {
251
+ instance.send(method_name, *args)
194
252
  }
195
253
  end
196
254
 
197
- attribute :readme_contents do
198
- File.read(readme_file) rescue "FIXME: readme_file"
255
+ def instance_eval_with_args(instance, *args, &block)
256
+ call_temp_method(instance, :__temp_method, *args, &block)
199
257
  end
200
-
201
- attribute :sections do
202
- begin
203
- pairs = Hash[*readme_contents.split(%r!^== (\w+).*?$!)[1..-1]].map {
204
- |section, contents|
205
- [section.downcase, contents.strip]
206
- }
207
- Hash[*pairs.flatten]
208
- rescue
209
- nil
258
+ end
259
+
260
+ include AttrLazy
261
+ include Util
262
+
263
+ def initialize(project_name)
264
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
265
+ $LOAD_PATH.unshift File.dirname(__FILE__)
266
+
267
+ require 'rake/gempackagetask'
268
+ require 'rake/clean'
269
+
270
+ @project_name = project_name
271
+
272
+ yield self
273
+
274
+ self.class.instance_methods(false).select { |t|
275
+ t.to_s =~ %r!\Adefine_!
276
+ }.each { |method_name|
277
+ send(method_name)
278
+ }
279
+ end
280
+
281
+ class << self
282
+ alias_method :attribute, :attr_lazy_accessor
283
+ end
284
+
285
+ attribute :name do
286
+ @project_name
287
+ end
288
+
289
+ attribute :version_constant_name do
290
+ "VERSION"
291
+ end
292
+
293
+ attribute :version do
294
+ require name
295
+ mod_name = to_camel_case(name)
296
+ begin
297
+ mod = Kernel.const_get(mod_name)
298
+ if mod.constants.include?(version_constant_name)
299
+ mod.const_get(version_constant_name)
300
+ else
301
+ raise
210
302
  end
303
+ rescue
304
+ "0.0.0"
211
305
  end
306
+ end
307
+
308
+ attribute :rubyforge_name do
309
+ name.gsub('_', '')
310
+ end
311
+
312
+ attribute :rubyforge_user do
313
+ email.first[%r!^.*(?=@)!]
314
+ end
315
+
316
+ attribute :readme_file do
317
+ "README.rdoc"
318
+ end
319
+
320
+ attribute :history_file do
321
+ "CHANGES.rdoc"
322
+ end
323
+
324
+ attribute :doc_dir do
325
+ "documentation"
326
+ end
327
+
328
+ attribute :spec_files do
329
+ Dir["./spec/*_{spec,example}.rb"]
330
+ end
331
+
332
+ attribute :test_files do
333
+ (Dir["./test/test_*.rb"] + Dir["./test/*_test.rb"]).uniq
334
+ end
335
+
336
+ attribute :rcov_dir do
337
+ "coverage"
338
+ end
339
+
340
+ attribute :spec_output_dir do
341
+ "rspec_output"
342
+ end
343
+
344
+ attribute :spec_output_file do
345
+ "spec.html"
346
+ end
212
347
 
213
- attribute :description_section do
214
- "description"
348
+ attr_lazy :spec_output do
349
+ "#{spec_output_dir}/#{spec_output_file}"
350
+ end
351
+
352
+ [:gem, :tgz].each { |ext|
353
+ attribute ext do
354
+ "pkg/#{name}-#{version}.#{ext}"
215
355
  end
356
+ }
357
+
358
+ attribute :rcov_options do
359
+ # workaround for the default rspec task
360
+ Dir["*"].select { |f| File.directory? f }.inject(Array.new) { |acc, dir|
361
+ if dir == "lib"
362
+ acc
363
+ else
364
+ acc + ["--exclude", dir + "/"]
365
+ end
366
+ } + ["--text-report"]
367
+ end
368
+
369
+ attribute :readme_file do
370
+ "README.rdoc"
371
+ end
372
+
373
+ attribute :manifest_file do
374
+ "MANIFEST"
375
+ end
376
+
377
+ attribute :generated_files do
378
+ []
379
+ end
216
380
 
217
- attribute :summary_section do
218
- "summary"
381
+ attribute :files do
382
+ if File.exist?(manifest_file)
383
+ File.read(manifest_file).split("\n")
384
+ else
385
+ `git ls-files`.split("\n") + [manifest_file] + generated_files
219
386
  end
387
+ end
220
388
 
221
- attribute :description_sentences do
222
- 1
389
+ attribute :rdoc_files do
390
+ Dir["lib/**/*.rb"]
391
+ end
392
+
393
+ attribute :extra_rdoc_files do
394
+ if File.exist?(readme_file)
395
+ [readme_file]
396
+ else
397
+ []
223
398
  end
399
+ end
224
400
 
225
- attribute :summary_sentences do
226
- 1
401
+ attribute :rdoc_options do
402
+ if File.exist?(readme_file)
403
+ ["--main", readme_file]
404
+ else
405
+ []
406
+ end + [
407
+ "--title", "#{name}: #{summary}",
408
+ ] + (files - rdoc_files).inject(Array.new) { |acc, file|
409
+ acc + ["--exclude", file]
410
+ }
411
+ end
412
+
413
+ attribute :browser do
414
+ require 'rbconfig'
415
+ if Config::CONFIG["host"] =~ %r!darwin!
416
+ app = %w[Firefox Safari].map { |t|
417
+ "/Applications/#{t}.app"
418
+ }.select { |t|
419
+ File.exist? t
420
+ }.first
421
+ if app
422
+ ["open", app]
423
+ else
424
+ raise "need to set `browser'"
425
+ end
426
+ else
427
+ "firefox"
227
428
  end
228
-
229
- [:summary, :description].each { |section|
230
- attribute section do
231
- begin
232
- sections[send("#{section}_section")].
233
- gsub("\n", " ").
234
- split(%r!\.\s*!m).
235
- first(send("#{section}_sentences")).
236
- join(". ") << "."
237
- rescue
238
- "FIXME: #{section}"
239
- end
429
+ end
430
+
431
+ attribute :gemspec do
432
+ Gem::Specification.new { |g|
433
+ g.has_rdoc = true
434
+ %w[
435
+ name
436
+ authors
437
+ email
438
+ summary
439
+ version
440
+ description
441
+ files
442
+ extra_rdoc_files
443
+ rdoc_options
444
+ ].each { |param|
445
+ value = send(param) and (
446
+ g.send("#{param}=", value)
447
+ )
448
+ }
449
+
450
+ if rubyforge_name
451
+ g.rubyforge_project = rubyforge_name
452
+ end
453
+
454
+ if url
455
+ g.homepage = url
240
456
  end
457
+
458
+ extra_deps.each { |dep|
459
+ g.add_dependency(*dep)
460
+ }
461
+
462
+ extra_dev_deps.each { |dep|
463
+ g.add_development_dependency(*dep)
464
+ }
241
465
  }
466
+ end
467
+
468
+ attribute :readme_contents do
469
+ File.read(readme_file) rescue "FIXME: readme_file"
470
+ end
471
+
472
+ attribute :sections do
473
+ begin
474
+ pairs = Hash[*readme_contents.split(%r!^== (\w+).*?$!)[1..-1]].map {
475
+ |section, contents|
476
+ [section.downcase, contents.strip]
477
+ }
478
+ Hash[*pairs.flatten]
479
+ rescue
480
+ nil
481
+ end
482
+ end
483
+
484
+ attribute :description_section do
485
+ "description"
486
+ end
487
+
488
+ attribute :summary_section do
489
+ "summary"
490
+ end
242
491
 
243
- attribute :url do
492
+ attribute :description_sentences do
493
+ 1
494
+ end
495
+
496
+ attribute :summary_sentences do
497
+ 1
498
+ end
499
+
500
+ [:summary, :description].each { |section|
501
+ attribute section do
244
502
  begin
245
- readme_contents.match(%r!^\*.*?(http://\S+)!)[1]
503
+ sections[send("#{section}_section")].
504
+ gsub("\n", " ").
505
+ split(%r!\.\s*!m).
506
+ first(send("#{section}_sentences")).
507
+ join(". ") << "."
246
508
  rescue
247
- "http://#{rubyforge_name}.rubyforge.org"
509
+ "FIXME: #{section}"
248
510
  end
249
511
  end
512
+ }
250
513
 
251
- attribute :extra_deps do
252
- []
253
- end
254
-
255
- attribute :extra_dev_deps do
256
- []
514
+ attribute :url do
515
+ begin
516
+ readme_contents.match(%r!^\*.*?(http://\S+)!)[1]
517
+ rescue
518
+ "http://#{rubyforge_name}.rubyforge.org"
257
519
  end
520
+ end
258
521
 
259
- yield self
522
+ attribute :extra_deps do
523
+ []
524
+ end
260
525
 
261
- self.class.instance_methods(false).select { |t|
262
- t.to_s =~ %r!\Adefine_!
263
- }.each { |method_name|
264
- send(method_name)
265
- }
526
+ attribute :extra_dev_deps do
527
+ []
266
528
  end
267
529
 
268
- def developer(name, email)
269
- authors << name
270
- self.email << email
530
+ attribute :authors do
531
+ Array.new
271
532
  end
272
533
 
273
- def authors
274
- @authors ||= Array.new
534
+ attribute :email do
535
+ Array.new
275
536
  end
276
537
 
277
- def email
278
- @email ||= Array.new
538
+ def developer(name, email)
539
+ authors << name
540
+ self.email << email
279
541
  end
280
542
 
281
543
  def dependency(name, version)
@@ -335,7 +597,7 @@ class Jumpstart
335
597
  task :prerelease => [:spec, :spec_deps]
336
598
  task :default => :spec
337
599
 
338
- CLEAN.include spec_output
600
+ CLEAN.include spec_output_dir
339
601
  end
340
602
  end
341
603
 
@@ -377,6 +639,7 @@ class Jumpstart
377
639
  def define_doc
378
640
  desc "run rdoc"
379
641
  task :doc => :clean_doc do
642
+ require 'rdoc/rdoc'
380
643
  args = (
381
644
  gemspec.rdoc_options +
382
645
  gemspec.require_paths.clone +
@@ -403,6 +666,7 @@ class Jumpstart
403
666
  def define_publish
404
667
  desc "upload docs"
405
668
  task :publish => [:clean_doc, :doc] do
669
+ require 'rake/contrib/sshpublisher'
406
670
  Rake::SshDirPublisher.new(
407
671
  "#{rubyforge_user}@rubyforge.org",
408
672
  "/var/www/gforge-projects/#{rubyforge_name}",
@@ -445,9 +709,10 @@ class Jumpstart
445
709
  end
446
710
 
447
711
  def debug_info(enable)
712
+ require 'find'
448
713
  Find.find("lib", "test") { |path|
449
714
  if path =~ %r!\.rb\Z!
450
- Jumpstart.replace_file(path) { |contents|
715
+ replace_file(path) { |contents|
451
716
  result = comment_regions(!enable, contents, "debug")
452
717
  comment_lines(!enable, result, "trace")
453
718
  }
@@ -485,13 +750,13 @@ class Jumpstart
485
750
  task :comments do
486
751
  file = "comments.txt"
487
752
  write_file(file) {
488
- Array.new.tap { |result|
489
- (["Rakefile"] + Dir["**/*.{rb,rake}"]).each { |file|
490
- File.read(file).scan(%r!\#[^\{].*$!) { |match|
491
- result << match
492
- }
753
+ result = Array.new
754
+ (["Rakefile"] + Dir["**/*.{rb,rake}"]).each { |f|
755
+ File.read(f).scan(%r!\#[^\{].*$!) { |match|
756
+ result << match
493
757
  }
494
- }.join("\n")
758
+ }
759
+ result.join("\n")
495
760
  }
496
761
  CLEAN.include file
497
762
  end
@@ -507,6 +772,7 @@ class Jumpstart
507
772
 
508
773
  def define_ping
509
774
  task :ping do
775
+ require 'rbconfig'
510
776
  %w[github.com rubyforge.org].each { |server|
511
777
  cmd = "ping " + (
512
778
  if Config::CONFIG["host"] =~ %r!darwin!
@@ -548,28 +814,29 @@ class Jumpstart
548
814
  }
549
815
  end
550
816
 
551
- def rubyforge(command, file)
552
- sh(
553
- "rubyforge",
554
- command,
817
+ def rubyforge(mode, file, *options)
818
+ command = ["rubyforge", mode] + options + [
555
819
  rubyforge_name,
556
820
  rubyforge_name,
557
821
  version.to_s,
558
- file
559
- )
822
+ file,
823
+ ]
824
+ sh(*command)
560
825
  end
561
826
 
562
827
  def define_release
563
- task :prerelease => [:clean, :check_directory, :ping]
828
+ task :prerelease => [:clean, :check_directory, :ping, history_file]
564
829
 
565
830
  task :finish_release do
566
831
  gem_md5, tgz_md5 = [gem, tgz].map { |file|
567
- "#{file}.md5".tap { |md5|
568
- sh("md5sum #{file} > #{md5}")
569
- }
832
+ md5 = "#{file}.md5"
833
+ sh("md5sum #{file} > #{md5}")
834
+ md5
570
835
  }
571
836
 
572
- rubyforge("add_release", gem)
837
+ rubyforge(
838
+ "add_release", gem, "--release_changes", history_file, "--preformatted"
839
+ )
573
840
  [gem_md5, tgz, tgz_md5].each { |file|
574
841
  rubyforge("add_file", file)
575
842
  }
@@ -591,44 +858,113 @@ class Jumpstart
591
858
  sh(*([browser].flatten + files))
592
859
  end
593
860
 
594
- def write_file(file)
595
- yield.tap { |contents|
596
- File.open(file, "wb") { |out|
597
- out.print(contents)
598
- }
599
- }
600
- end
861
+ class << self
862
+ include Util
863
+ include InstanceEvalWithArgs
601
864
 
602
- def run_ruby_on_each(*files)
603
- files.each { |file|
604
- Ruby.run("-w", file)
605
- }
606
- end
865
+ # From minitest, part of the Ruby source; by Ryan Davis.
866
+ def capture_io
867
+ require 'stringio'
607
868
 
608
- def to_camel_case(str)
609
- str.split('_').map { |t| t.capitalize }.join
610
- end
869
+ orig_stdout, orig_stderr = $stdout, $stderr
870
+ captured_stdout, captured_stderr = StringIO.new, StringIO.new
871
+ $stdout, $stderr = captured_stdout, captured_stderr
611
872
 
612
- class << self
613
- def replace_file(file)
614
- old_contents = File.read(file)
615
- yield(old_contents).tap { |new_contents|
616
- if old_contents != new_contents
617
- File.open(file, "wb") { |output|
618
- output.print(new_contents)
619
- }
873
+ yield
874
+
875
+ return captured_stdout.string, captured_stderr.string
876
+ ensure
877
+ $stdout = orig_stdout
878
+ $stderr = orig_stderr
879
+ end
880
+
881
+ def run_doc_code(code, expected, index, instance, &block)
882
+ lib = File.expand_path(File.dirname(__FILE__) + "/../lib")
883
+ header = %{
884
+ $LOAD_PATH.unshift "#{lib}"
885
+ require 'rubygems'
886
+ begin
887
+ }
888
+ footer = %{
889
+ rescue Exception => __jumpstart_exception
890
+ puts "raises \#{__jumpstart_exception.class}"
620
891
  end
621
892
  }
893
+ final_code = header + code + footer
894
+
895
+ # Sometimes code is required to be inside a file.
896
+ actual = nil
897
+ require 'tempfile'
898
+ Tempfile.open("run-rdoc-code") { |temp_file|
899
+ temp_file.print(final_code)
900
+ temp_file.close
901
+ actual = Ruby.run_file_and_capture(temp_file.path).chomp
902
+ }
903
+
904
+ instance_eval_with_args(instance, expected, actual, index, &block)
905
+ end
906
+
907
+ def run_doc_section(file, section, instance, &block)
908
+ contents = File.read(file)
909
+ if section_contents = contents[%r!^=+[ \t]#{section}.*?\n(.*?)^=!m, 1]
910
+ index = 0
911
+ section_contents.scan(%r!^( \S.*?)(?=(^\S|\Z))!m) { |indented, unused|
912
+ code_sections = indented.split(%r!^ \#\#\#\# output:\s*$!)
913
+ code, expected = (
914
+ case code_sections.size
915
+ when 1
916
+ [indented, indented.scan(%r!\# => (.*?)\n!).flatten.join("\n")]
917
+ when 2
918
+ code_sections
919
+ else
920
+ raise "parse error"
921
+ end
922
+ )
923
+ run_doc_code(code, expected, index, instance, &block)
924
+ index += 1
925
+ }
926
+ else
927
+ raise "couldn't find section `#{section}' of `#{file}'"
928
+ end
929
+ end
930
+
931
+ def doc_to_spec(file, *sections, &block)
932
+ jump = self
933
+ describe file do
934
+ sections.each { |section|
935
+ describe "section `#{section}'" do
936
+ it "should run as claimed" do
937
+ if block
938
+ jump.run_doc_section(file, section, self, &block)
939
+ else
940
+ jump.run_doc_section(file, section, self) {
941
+ |expected, actual, index|
942
+ actual.should == expected
943
+ }
944
+ end
945
+ end
946
+ end
947
+ }
948
+ end
622
949
  end
623
- end
624
- end
625
950
 
626
- unless respond_to? :tap
627
- class Object
628
- def tap
629
- yield self
630
- self
951
+ def doc_to_test(file, *sections, &block)
952
+ jump = self
953
+ klass = Class.new Test::Unit::TestCase do
954
+ sections.each { |section|
955
+ define_method "test_#{file}_#{section}" do
956
+ if block
957
+ jump.run_doc_section(file, section, self, &block)
958
+ else
959
+ jump.run_doc_section(file, section, self) {
960
+ |expected, actual, index|
961
+ assert_equal expected, actual
962
+ }
963
+ end
964
+ end
965
+ }
966
+ end
967
+ Object.const_set("Test#{file}".gsub(".", ""), klass)
631
968
  end
632
969
  end
633
970
  end
634
-