boc 0.4.2-java

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES.rdoc ADDED
@@ -0,0 +1,21 @@
1
+
2
+ = boc Changes
3
+
4
+ == Version 0.4.2
5
+
6
+ * add jruby support
7
+ * add AlreadyEnabledError
8
+
9
+ == Version 0.3.2
10
+
11
+ * "rake test" now works without rake-compiler on Windows
12
+ * additions to readme
13
+
14
+ == Version 0.3.1
15
+
16
+ * add prototype for rb_funcall_passing block
17
+ (http://redmine.ruby-lang.org/issues/4504)
18
+
19
+ == Version 0.3.0
20
+
21
+ * birthday
data/README.rdoc ADDED
@@ -0,0 +1,119 @@
1
+
2
+ = boc
3
+
4
+ == Summary
5
+
6
+ Binding of caller.
7
+
8
+ == Synopsis
9
+
10
+ require 'boc'
11
+
12
+ class A
13
+ def f
14
+ p eval("x", Boc.value)
15
+ end
16
+ end
17
+
18
+ Boc.enable A, :f
19
+ x = 33
20
+ A.new.f # => 33
21
+
22
+ == Install
23
+
24
+ % gem install boc
25
+
26
+ Or from inside an unpacked .tgz download, <code>rake install</code> /
27
+ <code>rake uninstall</code>.
28
+
29
+ == Description
30
+
31
+ Binding of caller: obtain a caller's binding.
32
+
33
+ MRI 1.9.2 or JRuby 1.6+ is required. Support for other Ruby platforms
34
+ is a goal.
35
+
36
+ == <code>Binding.of_caller</code>
37
+
38
+ <code>require 'boc/binding_of_caller'</code> will define
39
+ <code>Binding.of_caller</code> (not present by default).
40
+
41
+ require 'boc/binding_of_caller'
42
+
43
+ class A
44
+ def f
45
+ Binding.of_caller do |bind|
46
+ p eval("x", bind)
47
+ end
48
+ end
49
+ end
50
+
51
+ Boc.enable A, :f
52
+ x = 33
53
+ A.new.f # => 33
54
+
55
+ This is not an actual compatibility layer since the call to +enable+
56
+ is still necessary. <code>Binding.of_caller</code> is merely a
57
+ convenience method for existing 1.8 code.
58
+
59
+ == Links
60
+
61
+ * Home: http://quix.github.com/boc
62
+ * Feature Requests, Bug Reports: http://github.com/quix/boc/issues
63
+ * Manual Download: http://github.com/quix/boc/archives/master
64
+ * Repository: http://github.com/quix/boc
65
+
66
+ == Implementation
67
+
68
+ When <code>Boc.enable(A, :f)</code> is called it first makes an alias,
69
+
70
+ class A
71
+ alias_method :f__impl, :f
72
+ end
73
+
74
+ Following that, +f+ is replaced by native method whose only tasks are
75
+ to set <code>Boc.value</code> and then call +f__impl+.
76
+
77
+ == Background
78
+
79
+ After adapting the old continuation-based
80
+ <code>Binding.of_caller</code> to 1.9.2
81
+ (http://quix.github.com/binding_of_caller), I found the result
82
+ unsatisfying. There were syntax restrictions surrounding the use of
83
+ it, and though workaroundable they raised practical problems.
84
+
85
+ While a caller's binding might be obtained by accessesing VM innards,
86
+ this approach would be subject to future breakage. The implementation
87
+ presented herein is a compromise. In exchange for restricting
88
+ functionality (the additional requirement of +enable+),
89
+ binding-of-caller may be implemented straightforwardly with only the
90
+ public C API, meaning that it should work in future MRI releases.
91
+
92
+ == Author
93
+
94
+ * James M. Lawrence < quixoticsycophant@gmail.com >
95
+
96
+ == License
97
+
98
+ Copyright (c) 2011 James M. Lawrence. All rights reserved.
99
+
100
+ Permission is hereby granted, free of charge, to any person
101
+ obtaining a copy of this software and associated documentation files
102
+ (the "Software"), to deal in the Software without restriction,
103
+ including without limitation the rights to use, copy, modify, merge,
104
+ publish, distribute, sublicense, and/or sell copies of the Software,
105
+ and to permit persons to whom the Software is furnished to do so,
106
+ subject to the following conditions:
107
+
108
+ The above copyright notice and this permission notice shall be
109
+ included in all copies or substantial portions of the Software.
110
+
111
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
112
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
113
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
114
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
115
+ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
116
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
117
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
118
+ SOFTWARE.
119
+
data/Rakefile ADDED
@@ -0,0 +1,139 @@
1
+ require_relative 'devel/levitate'
2
+ require 'rbconfig'
3
+
4
+ def config
5
+ @s.developers << ["James M. Lawrence", "quixoticsycophant@gmail.com"]
6
+ @s.username = "quix"
7
+ @s.required_ruby_version = ">= 1.9.2"
8
+ @s.development_dependencies << ["rake-compiler", "~> 0.7.6"]
9
+ @s.rdoc_files = %w[
10
+ lib/boc.rb
11
+ lib/boc/version.rb
12
+ ]
13
+ end
14
+
15
+ def mri_ext
16
+ require 'rake/extensiontask'
17
+
18
+ Rake::ExtensionTask.new @s.gem_name, @s.gemspec do |ext|
19
+ ext.cross_compile = true
20
+ ext.cross_platform = 'i386-mswin32'
21
+ ext.lib_dir = "lib/#{@s.gem_name}"
22
+ end
23
+
24
+ # compensate for strange rake-compiler invocation
25
+ task :cross_native_gem do
26
+ Rake::Task[:gem].reenable
27
+ Rake.application.top_level_tasks.replace %w[cross native gem]
28
+ Rake.application.top_level
29
+ end
30
+
31
+ task :gem => :cross_native_gem
32
+
33
+ task :test => so_file
34
+
35
+ @s.gemspec.extensions = ["Rakefile"]
36
+ end
37
+
38
+ def jruby_ext
39
+ require "rake/javaextensiontask"
40
+
41
+ Levitate.no_warnings do
42
+ Rake::JavaExtensionTask.new @s.gem_name, @s.gemspec do |ext|
43
+ ext.ext_dir = "jext"
44
+ ext.lib_dir = "lib/#{@s.gem_name}"
45
+ end
46
+ end
47
+
48
+ task :jar => jar_file
49
+
50
+ case RUBY_ENGINE
51
+ when "jruby"
52
+ task :test => jar_file
53
+ when "ruby"
54
+ # building/releasing from MRI
55
+
56
+ task :jruby_gem => jar_file do
57
+ # compensate for strange rake-compiler invocation
58
+ Rake::Task[:gem].reenable
59
+ Rake.application.top_level_tasks.replace %w[java gem]
60
+ Rake.application.top_level
61
+ end
62
+
63
+ task :gem => :jruby_gem
64
+
65
+ task :test_jruby do
66
+ run_jruby = ENV["RUN_JRUBY"] || "jruby"
67
+ cmd = run_jruby + " --1.9 -J-Djruby.astInspector.enabled=0 -S rake"
68
+ sh(cmd + " test")
69
+ sh(cmd + " test_deps")
70
+ end
71
+
72
+ task :prerelease => :test_jruby
73
+
74
+ CLEAN.add jar_file
75
+ end
76
+ end
77
+
78
+ def jar_file
79
+ # the only path jruby recognizes
80
+ File.join("lib", @s.gem_name, @s.gem_name + ".jar")
81
+ end
82
+
83
+ def so_file
84
+ File.join("lib", @s.gem_name, @s.gem_name + "." + RbConfig::CONFIG["DLEXT"])
85
+ end
86
+
87
+ def native_file
88
+ case RUBY_ENGINE
89
+ when "ruby"
90
+ so_file
91
+ when "jruby"
92
+ jar_file
93
+ end
94
+ end
95
+
96
+ def append_installer
97
+ fu = FileUtils::Verbose
98
+
99
+ source = native_file
100
+ dir = File.join RbConfig::CONFIG["sitearchdir"], @s.gem_name
101
+ dest = File.join dir, File.basename(source)
102
+
103
+ task :install => source do
104
+ fu.mkdir dir unless File.directory? dir
105
+ fu.install source, dest
106
+ end
107
+
108
+ task :uninstall do
109
+ fu.rm dest if File.file? dest
110
+ fu.rmdir dir if File.directory? dir
111
+ end
112
+ end
113
+
114
+ def config_extension
115
+ case RUBY_ENGINE
116
+ when "jruby"
117
+ jruby_ext
118
+ when "ruby"
119
+ mri_ext
120
+ else
121
+ raise "sorry, platform `#{RUBY_ENGINE}' not supported"
122
+ end
123
+ end
124
+
125
+ Levitate.new "boc" do |s|
126
+ @s = s
127
+ config
128
+ if File.directory? ".git"
129
+ mri_ext if RUBY_ENGINE == "ruby"
130
+ jruby_ext
131
+ elsif not File.file?(native_file)
132
+ config_extension
133
+ end
134
+ append_installer
135
+ task :finish_release do
136
+ sh "gem", "push", "pkg/#{@s.gem_name}-#{@s.version}-java.gem"
137
+ sh "gem", "push", "pkg/#{@s.gem_name}-#{@s.version}-x86-mswin32.gem"
138
+ end
139
+ end
data/devel/levitate.rb ADDED
@@ -0,0 +1,852 @@
1
+
2
+ class Levitate
3
+ def initialize(gem_name)
4
+ @gem_name = gem_name
5
+
6
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
7
+
8
+ yield self
9
+
10
+ self.class.instance_methods(false).each do |name|
11
+ if name.to_s =~ %r!\Adefine_!
12
+ send(name)
13
+ end
14
+ end
15
+ end
16
+
17
+ attr_reader :gem_name
18
+
19
+ def self.attribute(name, &block)
20
+ var = :"@#{name}"
21
+ define_method name do
22
+ if instance_variable_defined?(var)
23
+ instance_variable_get(var)
24
+ else
25
+ instance_variable_set(var, instance_eval(&block))
26
+ end
27
+ end
28
+ attr_writer name
29
+ end
30
+
31
+ attribute :version_constant_name do
32
+ "VERSION"
33
+ end
34
+
35
+ attribute :camel_name do
36
+ to_camel_case(gem_name)
37
+ end
38
+
39
+ attribute :version do
40
+ catch :bail do
41
+ if File.file?(version_file = "./lib/#{gem_name}/version.rb")
42
+ require version_file
43
+ elsif File.file?("./lib/#{gem_name}.rb")
44
+ require gem_name
45
+ else
46
+ throw :bail
47
+ end
48
+ mod = Kernel.const_get(camel_name)
49
+ constants = mod.constants.map { |t| t.to_sym }
50
+ unless constants.include?(version_constant_name.to_sym)
51
+ throw :bail
52
+ end
53
+ mod.const_get(version_constant_name)
54
+ end or "0.0.0"
55
+ end
56
+
57
+ attribute :required_ruby_version do
58
+ ">= 0"
59
+ end
60
+
61
+ attribute :readme_file do
62
+ "README.rdoc"
63
+ end
64
+
65
+ attribute :history_file do
66
+ "CHANGES.rdoc"
67
+ end
68
+
69
+ attribute :doc_dir do
70
+ "doc"
71
+ end
72
+
73
+ attribute :spec_files do
74
+ Dir["./spec/*_{spec,example}.rb"]
75
+ end
76
+
77
+ attribute :test_files do
78
+ (Dir["./test/test_*.rb"] + Dir["./test/*_test.rb"]).uniq
79
+ end
80
+
81
+ attribute :cov_dir do
82
+ "coverage"
83
+ end
84
+
85
+ attribute :spec_output_dir do
86
+ "rspec_output"
87
+ end
88
+
89
+ attribute :spec_output_file do
90
+ "spec.html"
91
+ end
92
+
93
+ attribute :spec_output do
94
+ "#{spec_output_dir}/#{spec_output_file}"
95
+ end
96
+
97
+ attribute :rcov_options do
98
+ # workaround for the default rspec task
99
+ Dir["*"].select { |f| File.directory? f }.inject(Array.new) { |acc, dir|
100
+ if dir == "lib"
101
+ acc
102
+ else
103
+ acc + ["--exclude", dir + "/"]
104
+ end
105
+ } + ["--text-report"]
106
+ end
107
+
108
+ attribute :generated_files do
109
+ []
110
+ end
111
+
112
+ attribute :extra_gemspec do
113
+ lambda { |spec| }
114
+ end
115
+
116
+ attribute :files do
117
+ if source_control?
118
+ IO.popen("git ls-files") { |pipe| pipe.read.split "\n" }
119
+ end.to_a + generated_files
120
+ end
121
+
122
+ attribute :rdoc_files do
123
+ files_in_require_paths
124
+ end
125
+
126
+ attribute :rdoc_title do
127
+ "#{gem_name}: #{summary}".sub(/\.\Z/, "")
128
+ end
129
+
130
+ attribute :require_paths do
131
+ ["lib"]
132
+ end
133
+
134
+ attribute :rdoc_options do
135
+ if File.file?(readme_file)
136
+ ["--main", readme_file]
137
+ else
138
+ []
139
+ end + [
140
+ "--title", rdoc_title,
141
+ ] + (files_in_require_paths - rdoc_files).inject(Array.new) {
142
+ |acc, file|
143
+ acc + ["--exclude", file]
144
+ }
145
+ end
146
+
147
+ attribute :extra_rdoc_files do
148
+ [readme_file, history_file].select { |file| File.file?(file) }
149
+ end
150
+
151
+ attribute :browser do
152
+ require 'rbconfig'
153
+ if RbConfig::CONFIG["host"] =~ %r!darwin!
154
+ "open"
155
+ else
156
+ "firefox"
157
+ end
158
+ end
159
+
160
+ attribute :gemspec do
161
+ Gem::Specification.new do |g|
162
+ %w[
163
+ summary
164
+ version
165
+ description
166
+ files
167
+ rdoc_options
168
+ extra_rdoc_files
169
+ require_paths
170
+ required_ruby_version
171
+ ].each do |param|
172
+ t = send(param) and g.send("#{param}=", t)
173
+ end
174
+ g.name = gem_name
175
+ g.has_rdoc = true
176
+ g.homepage = url if url
177
+ dependencies.each { |dep|
178
+ g.add_dependency(*dep)
179
+ }
180
+ development_dependencies.each { |dep|
181
+ g.add_development_dependency(*dep)
182
+ }
183
+ g.authors = developers.map { |d| d[0] }
184
+ g.email = developers.map { |d| d[1] }
185
+ extra_gemspec.call(g)
186
+ end
187
+ end
188
+
189
+ attribute :readme_contents do
190
+ File.read(readme_file) rescue "FIXME: readme_file"
191
+ end
192
+
193
+ attribute :sections do
194
+ begin
195
+ data = readme_contents.split(%r!^==\s*(.*?)\s*$!)
196
+ pairs = data[1..-1].each_slice(2).map { |section, contents|
197
+ [section.downcase, contents.strip]
198
+ }
199
+ Hash[*pairs.flatten]
200
+ rescue
201
+ nil
202
+ end
203
+ end
204
+
205
+ attribute :description_section do
206
+ "description"
207
+ end
208
+
209
+ attribute :summary_section do
210
+ "summary"
211
+ end
212
+
213
+ attribute :description_sentences do
214
+ 1
215
+ end
216
+
217
+ attribute :summary_sentences do
218
+ 1
219
+ end
220
+
221
+ [:summary, :description].each { |section|
222
+ attribute section do
223
+ begin
224
+ sections[send("#{section}_section")].
225
+ gsub("\n", " ").
226
+ split(%r!\.\s+!m).
227
+ first(send("#{section}_sentences")).
228
+ join(". ").
229
+ concat(".").
230
+ sub(%r!\.+\Z!, ".")
231
+ rescue
232
+ "FIXME: #{section}"
233
+ end
234
+ end
235
+ }
236
+
237
+ attribute :url do
238
+ "http://#{username}.github.com/#{gem_name}"
239
+ end
240
+
241
+ attribute :username do
242
+ raise "username not set"
243
+ end
244
+
245
+ attribute :rubyforge_info do
246
+ nil
247
+ end
248
+
249
+ attribute :dependencies do
250
+ []
251
+ end
252
+
253
+ attribute :development_dependencies do
254
+ []
255
+ end
256
+
257
+ attribute :developers do
258
+ []
259
+ end
260
+
261
+ attribute :remote_levitate do
262
+ url = ENV["LEVITATE"] ||
263
+ "https://github.com/quix/levitate/raw/master/levitate.rb"
264
+ IO.popen("curl -s #{url}") { |f| f.read }
265
+ end
266
+
267
+ attribute :local_levitate do
268
+ File.open(__FILE__, "rb") { |f| f.read }
269
+ end
270
+
271
+ #### tasks
272
+
273
+ def define_clean
274
+ require 'rake/clean'
275
+ task :clean do
276
+ Rake::Task[:clobber].invoke
277
+ end
278
+ end
279
+
280
+ def define_package
281
+ if source_control?
282
+ require 'rubygems/package_task'
283
+
284
+ task :package => :clean
285
+ Gem::PackageTask.new(gemspec).define
286
+ end
287
+ end
288
+
289
+ def define_spec
290
+ unless spec_files.empty?
291
+ no_warnings {
292
+ require 'spec/rake/spectask'
293
+ }
294
+
295
+ desc "run specs"
296
+ Spec::Rake::SpecTask.new('spec') do |t|
297
+ t.spec_files = spec_files
298
+ end
299
+
300
+ desc "run specs with text output"
301
+ Spec::Rake::SpecTask.new('text_spec') do |t|
302
+ t.spec_files = spec_files
303
+ t.spec_opts = ['-fs']
304
+ end
305
+
306
+ desc "run specs with html output"
307
+ Spec::Rake::SpecTask.new('full_spec') do |t|
308
+ t.spec_files = spec_files
309
+ t.rcov = true
310
+ t.rcov_opts = rcov_options
311
+ t.spec_opts = ["-fh:#{spec_output}"]
312
+ end
313
+
314
+ suppress_task_warnings :spec, :full_spec, :text_spec
315
+
316
+ desc "run full_spec then open browser"
317
+ task :show_spec => :full_spec do
318
+ open_browser(spec_output, cov_dir + "/index.html")
319
+ end
320
+
321
+ desc "run specs individually"
322
+ task :spec_deps do
323
+ run_each_file(*spec_files)
324
+ end
325
+
326
+ task :prerelease => [:spec, :spec_deps]
327
+ task :default => :spec
328
+
329
+ CLEAN.add spec_output_dir
330
+ end
331
+ end
332
+
333
+ def define_test
334
+ unless test_files.empty?
335
+ desc "run tests"
336
+ task :test do
337
+ test_files.each { |file| require file }
338
+
339
+ # if we use at_exit hook instead, it won't run before :release
340
+ MiniTest::Unit.new.run ARGV
341
+ end
342
+
343
+ desc "run tests with coverage"
344
+ if ruby_18?
345
+ task :full_test do
346
+ verbose(false) {
347
+ sh("rcov", "-o", cov_dir, "--text-report",
348
+ *(test_files + rcov_options)
349
+ )
350
+ }
351
+ end
352
+ else
353
+ task :full_test do
354
+ rm_rf cov_dir
355
+ require 'simplecov'
356
+ SimpleCov.start do
357
+ add_filter "test/"
358
+ add_filter "devel/"
359
+ end
360
+ Rake::Task[:test].invoke
361
+ end
362
+ end
363
+
364
+ desc "run full_test then open browser"
365
+ task :show_test => :full_test do
366
+ show = lambda { open_browser(cov_dir + "/index.html") }
367
+ if ruby_18?
368
+ show.call
369
+ else
370
+ SimpleCov.at_exit do
371
+ SimpleCov.result.format!
372
+ show.call
373
+ end
374
+ end
375
+ end
376
+
377
+ desc "run tests individually"
378
+ task :test_deps do
379
+ run_each_file(*test_files)
380
+ end
381
+
382
+ task :prerelease => [:test, :test_deps]
383
+ task :default => :test
384
+
385
+ CLEAN.add cov_dir
386
+ end
387
+ end
388
+
389
+ def define_doc
390
+ desc "run rdoc"
391
+ task :doc => :clean_doc do
392
+ gem 'rdoc' rescue nil
393
+ require 'rdoc/rdoc'
394
+ args = (
395
+ gemspec.rdoc_options +
396
+ gemspec.require_paths.clone +
397
+ gemspec.extra_rdoc_files +
398
+ ["-o", doc_dir]
399
+ ).flatten.map { |t| t.to_s }
400
+ RDoc::RDoc.new.document args
401
+ end
402
+
403
+ task :clean_doc do
404
+ # normally rm_rf, but mimic rake/clean output
405
+ rm_r(doc_dir) rescue nil
406
+ end
407
+
408
+ desc "run rdoc then open browser"
409
+ task :show_doc => :doc do
410
+ open_browser(doc_dir + "/index.html")
411
+ end
412
+
413
+ task :rdoc => :doc
414
+ task :clean => :clean_doc
415
+ end
416
+
417
+ def define_publish
418
+ if source_control?
419
+ desc "publish docs"
420
+ task :publish => [:clean, :check_directory, :doc] do
421
+ current_branch = `git branch`[/^\* (\S+)$/, 1] or raise "??? branch"
422
+ if rubyforge_info
423
+ user, project = rubyforge_info
424
+ Dir.chdir(doc_dir) do
425
+ sh "scp", "-r",
426
+ ".",
427
+ "#{user}@rubyforge.org:/var/www/gforge-projects/#{project}"
428
+ end
429
+ end
430
+ git "branch", "-D", "gh-pages"
431
+ git "checkout", "--orphan", "gh-pages"
432
+ FileUtils.rm ".git/index"
433
+ git "clean", "-fdx", "-e", "doc"
434
+ Dir["doc/*"].each { |path|
435
+ FileUtils.mv path, "."
436
+ }
437
+ FileUtils.rmdir "doc"
438
+ git "add", "."
439
+ git "commit", "-m", "generated by rdoc"
440
+ git "checkout", current_branch
441
+ git "push", "-f", "origin", "gh-pages"
442
+ end
443
+ end
444
+ end
445
+
446
+ def define_install
447
+ desc "direct install (no gem)"
448
+ task :install do
449
+ Installer.new.install
450
+ end
451
+
452
+ desc "direct uninstall (no gem)"
453
+ task :uninstall do
454
+ Installer.new.uninstall
455
+ end
456
+ end
457
+
458
+ def define_check_directory
459
+ task :check_directory do
460
+ unless `git status` =~ %r!nothing to commit \(working directory clean\)!
461
+ raise "directory not clean"
462
+ end
463
+ end
464
+ end
465
+
466
+ def define_ping
467
+ task :ping do
468
+ require 'rbconfig'
469
+ [
470
+ "github.com",
471
+ ("rubyforge.org" if rubyforge_info),
472
+ ].compact.each do |server|
473
+ cmd = "ping " + (
474
+ if RbConfig::CONFIG["host"] =~ %r!darwin!
475
+ "-c2 #{server}"
476
+ else
477
+ "#{server} 2 2"
478
+ end
479
+ )
480
+ unless `#{cmd}` =~ %r!0% packet loss!
481
+ raise "No ping for #{server}"
482
+ end
483
+ end
484
+ end
485
+ end
486
+
487
+ def define_check_levitate
488
+ task :check_levitate do
489
+ unless local_levitate == remote_levitate
490
+ raise "levitate is out of date"
491
+ end
492
+ end
493
+ end
494
+
495
+ def define_update_levitate
496
+ task :update_levitate do
497
+ if local_levitate == remote_levitate
498
+ puts "Already up-to-date."
499
+ else
500
+ File.open(__FILE__, "w") { |f| f.print(remote_levitate) }
501
+ git "commit", __FILE__, "-m", "update levitate"
502
+ puts "Updated levitate."
503
+ end
504
+ end
505
+ end
506
+
507
+ def define_changes
508
+ task :changes do
509
+ if File.read(history_file).index version
510
+ raise "version not updated"
511
+ end
512
+
513
+ header = "\n\n== Version #{version}\n\n"
514
+
515
+ bullets = `git log --format=%s #{last_release}..HEAD`.lines.map { |line|
516
+ "* #{line}"
517
+ }.join.chomp
518
+
519
+ write_file(history_file) do
520
+ File.read(history_file).sub(/(?<=#{gem_name} Changes)/) {
521
+ header + bullets
522
+ }
523
+ end
524
+ end
525
+ end
526
+
527
+ def define_release
528
+ task :prerelease => [
529
+ :clean,
530
+ :check_directory,
531
+ :check_levitate,
532
+ :ping,
533
+ history_file
534
+ ]
535
+
536
+ task :finish_release do
537
+ git "tag", "#{gem_name}-" + version.to_s
538
+ git "push", "--tags", "origin", "master"
539
+ sh "gem", "push", "pkg/#{gem_name}-#{version}.gem"
540
+ end
541
+
542
+ task :release => [:prerelease, :package, :finish_release, :publish]
543
+ end
544
+
545
+ def define_debug_gem
546
+ task :debug_gem do
547
+ puts gemspec.to_ruby
548
+ end
549
+ end
550
+
551
+ #### helpers
552
+
553
+ def files_in_require_paths
554
+ require_paths.inject([]) { |acc, dir|
555
+ acc + Dir.glob("#{dir}/**/*.rb")
556
+ }
557
+ end
558
+
559
+ def last_release
560
+ `git tag`.lines.select { |t| t.index(gem_name) == 0 }.last.chomp
561
+ end
562
+
563
+ def git(*args)
564
+ sh "git", *args
565
+ end
566
+
567
+ def open_browser(*files)
568
+ sh(*([browser].flatten + files))
569
+ end
570
+
571
+ def suppress_task_warnings(*task_names)
572
+ task_names.each { |task_name|
573
+ Rake::Task[task_name].actions.map! { |action|
574
+ lambda { |*args|
575
+ no_warnings {
576
+ action.call(*args)
577
+ }
578
+ }
579
+ }
580
+ }
581
+ end
582
+
583
+ def ruby_18?
584
+ RUBY_VERSION =~ %r!\A1\.8!
585
+ end
586
+
587
+ def source_control?
588
+ File.directory? ".git"
589
+ end
590
+
591
+ #### utility for instance and class
592
+
593
+ module Util
594
+ def ruby_bin
595
+ require 'rbconfig'
596
+
597
+ name = File.join(
598
+ RbConfig::CONFIG["bindir"],
599
+ RbConfig::CONFIG["RUBY_INSTALL_NAME"]
600
+ )
601
+
602
+ if RbConfig::CONFIG["host"] =~ %r!(mswin|cygwin|mingw)! and
603
+ File.basename(name) !~ %r!\.(exe|com|bat|cmd)\Z!i
604
+ name + RbConfig::CONFIG["EXEEXT"]
605
+ else
606
+ name
607
+ end
608
+ end
609
+
610
+ def ruby_command
611
+ [ruby_bin] + Levitate.ruby_opts.to_a
612
+ end
613
+
614
+ def ruby_command_string
615
+ ruby_command.join(" ")
616
+ end
617
+
618
+ def run(*args)
619
+ cmd = ruby_command + args
620
+ unless system(*cmd)
621
+ cmd_str = cmd.map { |t| "'#{t}'" }.join(", ")
622
+ raise "system(#{cmd_str}) failed with status #{$?.exitstatus}"
623
+ end
624
+ end
625
+
626
+ def run_each_file(*files)
627
+ files.each { |file|
628
+ run("-w", file)
629
+ }
630
+ end
631
+
632
+ def run_code_and_capture(code)
633
+ IO.popen(ruby_command_string, "r+") { |pipe|
634
+ pipe.print(code)
635
+ pipe.flush
636
+ pipe.close_write
637
+ pipe.read
638
+ }
639
+ end
640
+
641
+ def run_file_and_capture(file)
642
+ unless File.file? file
643
+ raise "file does not exist: `#{file}'"
644
+ end
645
+ IO.popen(ruby_command_string + " " + file, "r") { |pipe|
646
+ pipe.read
647
+ }
648
+ end
649
+
650
+ def with_warnings(value = true)
651
+ previous = $VERBOSE
652
+ $VERBOSE = value
653
+ begin
654
+ yield
655
+ ensure
656
+ $VERBOSE = previous
657
+ end
658
+ end
659
+
660
+ def no_warnings(&block)
661
+ with_warnings(nil, &block)
662
+ end
663
+
664
+ def to_camel_case(str)
665
+ str.split('_').map { |t| t.capitalize }.join
666
+ end
667
+
668
+ def write_file(file)
669
+ contents = yield
670
+ File.open(file, "wb") { |out|
671
+ out.print(contents)
672
+ }
673
+ contents
674
+ end
675
+
676
+ def instance_exec2(obj, *args, &block)
677
+ method_name = ["_", obj.object_id, "_", Thread.current.object_id].join
678
+ (class << obj ; self ; end).class_eval do
679
+ define_method method_name, &block
680
+ begin
681
+ obj.send(method_name, *args)
682
+ ensure
683
+ remove_method method_name
684
+ end
685
+ end
686
+ end
687
+ end
688
+ extend Util
689
+ include Util
690
+
691
+ #### public helpers for testing
692
+
693
+ class << self
694
+ # From 'minitest' by Ryan Davis.
695
+ def capture_io
696
+ require 'stringio'
697
+
698
+ orig_stdout, orig_stderr = $stdout, $stderr
699
+ captured_stdout, captured_stderr = StringIO.new, StringIO.new
700
+ $stdout, $stderr = captured_stdout, captured_stderr
701
+
702
+ yield
703
+
704
+ return captured_stdout.string, captured_stderr.string
705
+ ensure
706
+ $stdout = orig_stdout
707
+ $stderr = orig_stderr
708
+ end
709
+
710
+ def run_doc_code(code, expected, index, instance, &block)
711
+ lib = File.expand_path(File.dirname(__FILE__) + "/../lib")
712
+ header = %{
713
+ $LOAD_PATH.unshift "#{lib}"
714
+ begin
715
+ }
716
+ footer = %{
717
+ rescue Exception => __levitate_exception
718
+ puts "raises \#{__levitate_exception.class}"
719
+ end
720
+ }
721
+ final_code = header + code + footer
722
+
723
+ # Sometimes code is required to be inside a file.
724
+ actual = nil
725
+ require 'tempfile'
726
+ Tempfile.open("run-rdoc-code") { |temp_file|
727
+ temp_file.print(final_code)
728
+ temp_file.close
729
+ actual = run_file_and_capture(temp_file.path).chomp
730
+ }
731
+
732
+ instance_exec2(instance, expected, actual, index, &block)
733
+ end
734
+
735
+ def run_doc_section(file, section, instance, &block)
736
+ contents = File.read(file)
737
+ re = %r!^=+[ \t]#{Regexp.quote(section)}.*?\n(.*?)^=!m
738
+ if section_contents = contents[re, 1]
739
+ index = 0
740
+ section_contents.scan(%r!^( \S.*?)(?=(^\S|\Z))!m) { |indented, unused|
741
+ code_sections = indented.split(%r!^ \#\#\#\# output:\s*$!)
742
+ code, expected = (
743
+ case code_sections.size
744
+ when 1
745
+ [indented, indented.scan(%r!\# => (.*?)\n!).flatten.join("\n")]
746
+ when 2
747
+ code_sections
748
+ else
749
+ raise "parse error"
750
+ end
751
+ )
752
+ code.gsub!(/^\s*%.*$/, "") # ignore shell command examples
753
+ run_doc_code(code, expected, index, instance, &block)
754
+ index += 1
755
+ }
756
+ else
757
+ raise "couldn't find section `#{section}' of `#{file}'"
758
+ end
759
+ end
760
+
761
+ def doc_to_spec(file, *sections, &block)
762
+ levitate = self
763
+ describe file do
764
+ sections.each { |section|
765
+ describe "section `#{section}'" do
766
+ it "should run as claimed" do
767
+ if block
768
+ levitate.run_doc_section(file, section, self, &block)
769
+ else
770
+ levitate.run_doc_section(file, section, self) {
771
+ |expected, actual, index|
772
+ actual.should == expected
773
+ }
774
+ end
775
+ end
776
+ end
777
+ }
778
+ end
779
+ end
780
+
781
+ def doc_to_test(file, *sections, &block)
782
+ levitate = self
783
+ klass = Class.new MiniTest::Unit::TestCase do
784
+ sections.each { |section|
785
+ define_method "test_#{file}_#{section}" do
786
+ if block
787
+ levitate.run_doc_section(file, section, self, &block)
788
+ else
789
+ levitate.run_doc_section(file, section, self) {
790
+ |expected, actual, index|
791
+ assert_equal expected, actual
792
+ }
793
+ end
794
+ end
795
+ }
796
+ end
797
+ Object.const_set("Test#{file}".gsub(".", ""), klass)
798
+ end
799
+
800
+ def ruby_opts
801
+ @ruby_opts ||= []
802
+ end
803
+ attr_writer :ruby_opts
804
+ end
805
+
806
+ #### raw install, bypass gems
807
+
808
+ class Installer
809
+ def initialize
810
+ require 'fileutils'
811
+ require 'rbconfig'
812
+ require 'find'
813
+
814
+ @fu = FileUtils::Verbose
815
+ @spec = []
816
+
817
+ rb_root = RbConfig::CONFIG["sitelibdir"]
818
+
819
+ Find.find "lib" do |source|
820
+ next if source == "lib"
821
+ next unless File.directory?(source) || File.extname(source) == ".rb"
822
+ dest = File.join(rb_root, source.sub(%r!\Alib/!, ""))
823
+ @spec << [source, dest]
824
+ end
825
+ end
826
+
827
+ def install
828
+ @spec.each do |source, dest|
829
+ if File.directory?(source)
830
+ @fu.mkdir(dest) unless File.directory?(dest)
831
+ else
832
+ @fu.install(source, dest)
833
+ end
834
+ end
835
+ end
836
+
837
+ def uninstall
838
+ @spec.reverse.each do |source, dest|
839
+ if File.directory?(source)
840
+ @fu.rmdir(dest) if File.directory?(dest)
841
+ else
842
+ @fu.rm(dest) if File.file?(dest)
843
+ end
844
+ end
845
+ end
846
+ end
847
+ end
848
+
849
+ lambda do
850
+ config = File.join(File.dirname(__FILE__), "levitate_config.rb")
851
+ require config if File.file? config
852
+ end.call