comp_tree 0.7.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
-