boc 0.4.2-java
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/CHANGES.rdoc +21 -0
- data/README.rdoc +119 -0
- data/Rakefile +139 -0
- data/devel/levitate.rb +852 -0
- data/ext/boc/boc.c +136 -0
- data/ext/boc/extconf.rb +2 -0
- data/jext/boc/BocService.java +111 -0
- data/lib/boc.rb +108 -0
- data/lib/boc/binding_of_caller.rb +11 -0
- data/lib/boc/boc.jar +0 -0
- data/lib/boc/version.rb +3 -0
- data/test/basic_test.rb +209 -0
- data/test/jruby_trial.rb +20 -0
- data/test/main.rb +56 -0
- data/test/readme_test.rb +9 -0
- data/test/shim_test.rb +26 -0
- metadata +88 -0
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
|