pure 0.2.1 → 0.2.2
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 +5 -1
- data/README.rdoc +6 -23
- data/Rakefile +14 -10
- data/devel/levitate.rb +851 -0
- data/lib/pure/version.rb +1 -1
- data/spec/compute_timed_spec.rb +1 -1
- data/spec/pure_fun_spec.rb +3 -3
- data/spec/pure_spec.rb +2 -2
- data/spec/readme_spec.rb +4 -5
- metadata +34 -94
- data/MANIFEST +0 -55
- data/devel/jumpstart.rb +0 -987
- data/install.rb +0 -2
data/CHANGES.rdoc
CHANGED
data/README.rdoc
CHANGED
@@ -39,9 +39,9 @@ Language-level support for automatic parallelism and lazy evaluation.
|
|
39
39
|
|
40
40
|
% gem install pure
|
41
41
|
|
42
|
-
Or
|
42
|
+
Or from the (non-gem) .tgz package,
|
43
43
|
|
44
|
-
%
|
44
|
+
% rake install
|
45
45
|
|
46
46
|
== Description
|
47
47
|
|
@@ -59,9 +59,9 @@ jruby-1.4.
|
|
59
59
|
|
60
60
|
== Links
|
61
61
|
|
62
|
-
*
|
63
|
-
*
|
64
|
-
*
|
62
|
+
* Home: http://quix.github.com/pure
|
63
|
+
* Feature Requests, Bug Reports: http://github.com/quix/pure/issues
|
64
|
+
* Manual Download: http://github.com/quix/pure/archives/master
|
65
65
|
* Repository: http://github.com/quix/pure
|
66
66
|
|
67
67
|
== Terminology
|
@@ -574,30 +574,13 @@ may be reconstructed on another Ruby interpreter.
|
|
574
574
|
puts result.add # => 77
|
575
575
|
end
|
576
576
|
|
577
|
-
See http://
|
577
|
+
See http://tiamat.rubyforge.org for an example of a
|
578
578
|
multi-core/multi-machine worker plugin for Pure.
|
579
579
|
|
580
580
|
Pure::Compiler::RubyParser uses RubyParser and Ruby2Ruby together with
|
581
581
|
a code transformer (for instantiating +fun+ definitions) to compile a
|
582
582
|
function spec.
|
583
583
|
|
584
|
-
== Tools
|
585
|
-
|
586
|
-
Lobby your local ruby-core representative to add a built-in sexp
|
587
|
-
extractor and compiler. Lacking these tools, we are stuck this
|
588
|
-
inefficient roundabout process:
|
589
|
-
|
590
|
-
* Ruby interpreter parses the source
|
591
|
-
* RubyParser (redundantly) parses the source and converts it to an sexp
|
592
|
-
* Ruby2Ruby converts the sexp to a string containing Ruby code
|
593
|
-
* call +eval+ on the code string
|
594
|
-
|
595
|
-
However this could be reduced to (for example):
|
596
|
-
|
597
|
-
* Ruby interpreter parses the source
|
598
|
-
* call Proc#to_sexp
|
599
|
-
* call Proc.from_sexp
|
600
|
-
|
601
584
|
== Author
|
602
585
|
|
603
586
|
* James M. Lawrence <quixoticsycophant@gmail.com>
|
data/Rakefile
CHANGED
@@ -1,17 +1,21 @@
|
|
1
|
-
|
2
1
|
$LOAD_PATH.unshift "devel"
|
3
|
-
require "
|
2
|
+
require "levitate"
|
3
|
+
|
4
|
+
Levitate.new "pure" do |s|
|
5
|
+
s.developers << ["James M. Lawrence", "quixoticsycophant@gmail.com"]
|
6
|
+
s.username = "quix"
|
7
|
+
s.rubyforge_info = ["quix", "purefunctional"]
|
4
8
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
s.
|
11
|
-
s.extra_dev_deps = [
|
9
|
+
s.dependencies = [
|
10
|
+
["comp_tree", ">= 1.0.0"],
|
11
|
+
["ruby_parser", ">= 2.0.4"],
|
12
|
+
]
|
13
|
+
|
14
|
+
s.development_dependencies = [
|
12
15
|
["ruby2ruby", ">= 1.2.2"],
|
13
|
-
["rspec", "
|
16
|
+
["rspec", "~> 1.3"],
|
14
17
|
]
|
18
|
+
|
15
19
|
s.rdoc_files = %w[
|
16
20
|
lib/pure/pure.rb
|
17
21
|
lib/pure/pure_module.rb
|
data/devel/levitate.rb
ADDED
@@ -0,0 +1,851 @@
|
|
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).sort.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.homepage = url if url
|
176
|
+
dependencies.each { |dep|
|
177
|
+
g.add_dependency(*dep)
|
178
|
+
}
|
179
|
+
development_dependencies.each { |dep|
|
180
|
+
g.add_development_dependency(*dep)
|
181
|
+
}
|
182
|
+
g.authors = developers.map { |d| d[0] }
|
183
|
+
g.email = developers.map { |d| d[1] }
|
184
|
+
extra_gemspec.call(g)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
attribute :readme_contents do
|
189
|
+
File.read(readme_file) rescue "FIXME: readme_file"
|
190
|
+
end
|
191
|
+
|
192
|
+
attribute :sections do
|
193
|
+
begin
|
194
|
+
data = readme_contents.split(%r!^==\s*(.*?)\s*$!)
|
195
|
+
pairs = data[1..-1].each_slice(2).map { |section, contents|
|
196
|
+
[section.downcase, contents.strip]
|
197
|
+
}
|
198
|
+
Hash[*pairs.flatten]
|
199
|
+
rescue
|
200
|
+
nil
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
attribute :description_section do
|
205
|
+
"description"
|
206
|
+
end
|
207
|
+
|
208
|
+
attribute :summary_section do
|
209
|
+
"summary"
|
210
|
+
end
|
211
|
+
|
212
|
+
attribute :description_sentences do
|
213
|
+
1
|
214
|
+
end
|
215
|
+
|
216
|
+
attribute :summary_sentences do
|
217
|
+
1
|
218
|
+
end
|
219
|
+
|
220
|
+
[:summary, :description].each { |section|
|
221
|
+
attribute section do
|
222
|
+
begin
|
223
|
+
sections[send("#{section}_section")].
|
224
|
+
gsub("\n", " ").
|
225
|
+
split(%r!\.\s+!m).
|
226
|
+
first(send("#{section}_sentences")).
|
227
|
+
join(". ").
|
228
|
+
concat(".").
|
229
|
+
sub(%r!\.+\Z!, ".")
|
230
|
+
rescue
|
231
|
+
"FIXME: #{section}"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
}
|
235
|
+
|
236
|
+
attribute :url do
|
237
|
+
"http://#{username}.github.com/#{gem_name}"
|
238
|
+
end
|
239
|
+
|
240
|
+
attribute :username do
|
241
|
+
raise "username not set"
|
242
|
+
end
|
243
|
+
|
244
|
+
attribute :rubyforge_info do
|
245
|
+
nil
|
246
|
+
end
|
247
|
+
|
248
|
+
attribute :dependencies do
|
249
|
+
[]
|
250
|
+
end
|
251
|
+
|
252
|
+
attribute :development_dependencies do
|
253
|
+
[]
|
254
|
+
end
|
255
|
+
|
256
|
+
attribute :developers do
|
257
|
+
[]
|
258
|
+
end
|
259
|
+
|
260
|
+
attribute :remote_levitate do
|
261
|
+
url = ENV["LEVITATE"] ||
|
262
|
+
"https://github.com/quix/levitate/raw/master/levitate.rb"
|
263
|
+
IO.popen("curl -s #{url}") { |f| f.read }
|
264
|
+
end
|
265
|
+
|
266
|
+
attribute :local_levitate do
|
267
|
+
File.open(__FILE__, "rb") { |f| f.read }
|
268
|
+
end
|
269
|
+
|
270
|
+
#### tasks
|
271
|
+
|
272
|
+
def define_clean
|
273
|
+
require 'rake/clean'
|
274
|
+
task :clean do
|
275
|
+
Rake::Task[:clobber].invoke
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def define_package
|
280
|
+
if source_control?
|
281
|
+
require 'rubygems/package_task'
|
282
|
+
|
283
|
+
task :package => :clean
|
284
|
+
Gem::PackageTask.new(gemspec).define
|
285
|
+
end
|
286
|
+
end
|
287
|
+
|
288
|
+
def define_spec
|
289
|
+
unless spec_files.empty?
|
290
|
+
no_warnings {
|
291
|
+
require 'spec/rake/spectask'
|
292
|
+
}
|
293
|
+
|
294
|
+
desc "run specs"
|
295
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
296
|
+
t.spec_files = spec_files
|
297
|
+
end
|
298
|
+
|
299
|
+
desc "run specs with text output"
|
300
|
+
Spec::Rake::SpecTask.new('text_spec') do |t|
|
301
|
+
t.spec_files = spec_files
|
302
|
+
t.spec_opts = ['-fs']
|
303
|
+
end
|
304
|
+
|
305
|
+
desc "run specs with html output"
|
306
|
+
Spec::Rake::SpecTask.new('full_spec') do |t|
|
307
|
+
t.spec_files = spec_files
|
308
|
+
t.rcov = true
|
309
|
+
t.rcov_opts = rcov_options
|
310
|
+
t.spec_opts = ["-fh:#{spec_output}"]
|
311
|
+
end
|
312
|
+
|
313
|
+
suppress_task_warnings :spec, :full_spec, :text_spec
|
314
|
+
|
315
|
+
desc "run full_spec then open browser"
|
316
|
+
task :show_spec => :full_spec do
|
317
|
+
open_browser(spec_output, cov_dir + "/index.html")
|
318
|
+
end
|
319
|
+
|
320
|
+
desc "run specs individually"
|
321
|
+
task :spec_deps do
|
322
|
+
run_each_file(*spec_files)
|
323
|
+
end
|
324
|
+
|
325
|
+
task :prerelease => [:spec, :spec_deps]
|
326
|
+
task :default => :spec
|
327
|
+
|
328
|
+
CLEAN.add spec_output_dir
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
def define_test
|
333
|
+
unless test_files.empty?
|
334
|
+
desc "run tests"
|
335
|
+
task :test do
|
336
|
+
test_files.each { |file| require file }
|
337
|
+
|
338
|
+
# if we use at_exit hook instead, it won't run before :release
|
339
|
+
MiniTest::Unit.new.run ARGV
|
340
|
+
end
|
341
|
+
|
342
|
+
desc "run tests with coverage"
|
343
|
+
if ruby_18?
|
344
|
+
task :full_test do
|
345
|
+
verbose(false) {
|
346
|
+
sh("rcov", "-o", cov_dir, "--text-report",
|
347
|
+
*(test_files + rcov_options)
|
348
|
+
)
|
349
|
+
}
|
350
|
+
end
|
351
|
+
else
|
352
|
+
task :full_test do
|
353
|
+
rm_rf cov_dir
|
354
|
+
require 'simplecov'
|
355
|
+
SimpleCov.start do
|
356
|
+
add_filter "test/"
|
357
|
+
add_filter "devel/"
|
358
|
+
end
|
359
|
+
Rake::Task[:test].invoke
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
desc "run full_test then open browser"
|
364
|
+
task :show_test => :full_test do
|
365
|
+
show = lambda { open_browser(cov_dir + "/index.html") }
|
366
|
+
if ruby_18?
|
367
|
+
show.call
|
368
|
+
else
|
369
|
+
SimpleCov.at_exit do
|
370
|
+
SimpleCov.result.format!
|
371
|
+
show.call
|
372
|
+
end
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
desc "run tests individually"
|
377
|
+
task :test_deps do
|
378
|
+
run_each_file(*test_files)
|
379
|
+
end
|
380
|
+
|
381
|
+
task :prerelease => [:test, :test_deps]
|
382
|
+
task :default => :test
|
383
|
+
|
384
|
+
CLEAN.add cov_dir
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
def define_doc
|
389
|
+
desc "run rdoc"
|
390
|
+
task :doc => :clean_doc do
|
391
|
+
gem 'rdoc' rescue nil
|
392
|
+
require 'rdoc/rdoc'
|
393
|
+
args = (
|
394
|
+
gemspec.rdoc_options +
|
395
|
+
gemspec.require_paths.clone +
|
396
|
+
gemspec.extra_rdoc_files +
|
397
|
+
["-o", doc_dir]
|
398
|
+
).flatten.map { |t| t.to_s }
|
399
|
+
RDoc::RDoc.new.document args
|
400
|
+
end
|
401
|
+
|
402
|
+
task :clean_doc do
|
403
|
+
# normally rm_rf, but mimic rake/clean output
|
404
|
+
rm_r(doc_dir) rescue nil
|
405
|
+
end
|
406
|
+
|
407
|
+
desc "run rdoc then open browser"
|
408
|
+
task :show_doc => :doc do
|
409
|
+
open_browser(doc_dir + "/index.html")
|
410
|
+
end
|
411
|
+
|
412
|
+
task :rdoc => :doc
|
413
|
+
task :clean => :clean_doc
|
414
|
+
end
|
415
|
+
|
416
|
+
def define_publish
|
417
|
+
if source_control?
|
418
|
+
desc "publish docs"
|
419
|
+
task :publish => [:clean, :check_directory, :doc] do
|
420
|
+
current_branch = `git branch`[/^\* (\S+)$/, 1] or raise "??? branch"
|
421
|
+
if rubyforge_info
|
422
|
+
user, project = rubyforge_info
|
423
|
+
Dir.chdir(doc_dir) do
|
424
|
+
sh "scp", "-r",
|
425
|
+
".",
|
426
|
+
"#{user}@rubyforge.org:/var/www/gforge-projects/#{project}"
|
427
|
+
end
|
428
|
+
end
|
429
|
+
git "branch", "-D", "gh-pages"
|
430
|
+
git "checkout", "--orphan", "gh-pages"
|
431
|
+
FileUtils.rm ".git/index"
|
432
|
+
git "clean", "-fdx", "-e", "doc"
|
433
|
+
Dir["doc/*"].each { |path|
|
434
|
+
FileUtils.mv path, "."
|
435
|
+
}
|
436
|
+
FileUtils.rmdir "doc"
|
437
|
+
git "add", "."
|
438
|
+
git "commit", "-m", "generated by rdoc"
|
439
|
+
git "checkout", current_branch
|
440
|
+
git "push", "-f", "origin", "gh-pages"
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
def define_install
|
446
|
+
desc "direct install (no gem)"
|
447
|
+
task :install do
|
448
|
+
Installer.new.install
|
449
|
+
end
|
450
|
+
|
451
|
+
desc "direct uninstall (no gem)"
|
452
|
+
task :uninstall do
|
453
|
+
Installer.new.uninstall
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
def define_check_directory
|
458
|
+
task :check_directory do
|
459
|
+
unless `git status` =~ %r!nothing to commit \(working directory clean\)!
|
460
|
+
raise "directory not clean"
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
def define_ping
|
466
|
+
task :ping do
|
467
|
+
require 'rbconfig'
|
468
|
+
[
|
469
|
+
"github.com",
|
470
|
+
("rubyforge.org" if rubyforge_info),
|
471
|
+
].compact.each do |server|
|
472
|
+
cmd = "ping " + (
|
473
|
+
if RbConfig::CONFIG["host"] =~ %r!darwin!
|
474
|
+
"-c2 #{server}"
|
475
|
+
else
|
476
|
+
"#{server} 2 2"
|
477
|
+
end
|
478
|
+
)
|
479
|
+
unless `#{cmd}` =~ %r!0% packet loss!
|
480
|
+
raise "No ping for #{server}"
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def define_check_levitate
|
487
|
+
task :check_levitate do
|
488
|
+
unless local_levitate == remote_levitate
|
489
|
+
raise "levitate is out of date"
|
490
|
+
end
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
def define_update_levitate
|
495
|
+
task :update_levitate do
|
496
|
+
if local_levitate == remote_levitate
|
497
|
+
puts "Already up-to-date."
|
498
|
+
else
|
499
|
+
File.open(__FILE__, "w") { |f| f.print(remote_levitate) }
|
500
|
+
git "commit", __FILE__, "-m", "update levitate"
|
501
|
+
puts "Updated levitate."
|
502
|
+
end
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
def define_changes
|
507
|
+
task :changes do
|
508
|
+
if File.read(history_file).index version
|
509
|
+
raise "version not updated"
|
510
|
+
end
|
511
|
+
|
512
|
+
header = "\n\n== Version #{version}\n\n"
|
513
|
+
|
514
|
+
bullets = `git log --format=%s #{last_release}..HEAD`.lines.map { |line|
|
515
|
+
"* #{line}"
|
516
|
+
}.join.chomp
|
517
|
+
|
518
|
+
write_file(history_file) do
|
519
|
+
File.read(history_file).sub(/(?<=#{gem_name} Changes)/) {
|
520
|
+
header + bullets
|
521
|
+
}
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
def define_release
|
527
|
+
task :prerelease => [
|
528
|
+
:clean,
|
529
|
+
:check_directory,
|
530
|
+
:check_levitate,
|
531
|
+
:ping,
|
532
|
+
history_file
|
533
|
+
]
|
534
|
+
|
535
|
+
task :finish_release do
|
536
|
+
git "tag", "#{gem_name}-" + version.to_s
|
537
|
+
git "push", "--tags", "origin", "master"
|
538
|
+
sh "gem", "push", "pkg/#{gem_name}-#{version}.gem"
|
539
|
+
end
|
540
|
+
|
541
|
+
task :release => [:prerelease, :package, :finish_release, :publish]
|
542
|
+
end
|
543
|
+
|
544
|
+
def define_debug_gem
|
545
|
+
task :debug_gem do
|
546
|
+
puts gemspec.to_ruby
|
547
|
+
end
|
548
|
+
end
|
549
|
+
|
550
|
+
#### helpers
|
551
|
+
|
552
|
+
def files_in_require_paths
|
553
|
+
require_paths.inject([]) { |acc, dir|
|
554
|
+
acc + Dir.glob("#{dir}/**/*.rb")
|
555
|
+
}
|
556
|
+
end
|
557
|
+
|
558
|
+
def last_release
|
559
|
+
`git tag`.lines.select { |t| t.index(gem_name) == 0 }.last.chomp
|
560
|
+
end
|
561
|
+
|
562
|
+
def git(*args)
|
563
|
+
sh "git", *args
|
564
|
+
end
|
565
|
+
|
566
|
+
def open_browser(*files)
|
567
|
+
sh(*([browser].flatten + files))
|
568
|
+
end
|
569
|
+
|
570
|
+
def suppress_task_warnings(*task_names)
|
571
|
+
task_names.each { |task_name|
|
572
|
+
Rake::Task[task_name].actions.map! { |action|
|
573
|
+
lambda { |*args|
|
574
|
+
no_warnings {
|
575
|
+
action.call(*args)
|
576
|
+
}
|
577
|
+
}
|
578
|
+
}
|
579
|
+
}
|
580
|
+
end
|
581
|
+
|
582
|
+
def ruby_18?
|
583
|
+
RUBY_VERSION =~ %r!\A1\.8!
|
584
|
+
end
|
585
|
+
|
586
|
+
def source_control?
|
587
|
+
File.directory? ".git"
|
588
|
+
end
|
589
|
+
|
590
|
+
#### utility for instance and class
|
591
|
+
|
592
|
+
module Util
|
593
|
+
def ruby_bin
|
594
|
+
require 'rbconfig'
|
595
|
+
|
596
|
+
name = File.join(
|
597
|
+
RbConfig::CONFIG["bindir"],
|
598
|
+
RbConfig::CONFIG["RUBY_INSTALL_NAME"]
|
599
|
+
)
|
600
|
+
|
601
|
+
if RbConfig::CONFIG["host"] =~ %r!(mswin|cygwin|mingw)! and
|
602
|
+
File.basename(name) !~ %r!\.(exe|com|bat|cmd)\Z!i
|
603
|
+
name + RbConfig::CONFIG["EXEEXT"]
|
604
|
+
else
|
605
|
+
name
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
def ruby_command
|
610
|
+
[ruby_bin] + Levitate.ruby_opts.to_a
|
611
|
+
end
|
612
|
+
|
613
|
+
def ruby_command_string
|
614
|
+
ruby_command.join(" ")
|
615
|
+
end
|
616
|
+
|
617
|
+
def run(*args)
|
618
|
+
cmd = ruby_command + args
|
619
|
+
unless system(*cmd)
|
620
|
+
cmd_str = cmd.map { |t| "'#{t}'" }.join(", ")
|
621
|
+
raise "system(#{cmd_str}) failed with status #{$?.exitstatus}"
|
622
|
+
end
|
623
|
+
end
|
624
|
+
|
625
|
+
def run_each_file(*files)
|
626
|
+
files.each { |file|
|
627
|
+
run("-w", file)
|
628
|
+
}
|
629
|
+
end
|
630
|
+
|
631
|
+
def run_code_and_capture(code)
|
632
|
+
IO.popen(ruby_command_string, "r+") { |pipe|
|
633
|
+
pipe.print(code)
|
634
|
+
pipe.flush
|
635
|
+
pipe.close_write
|
636
|
+
pipe.read
|
637
|
+
}
|
638
|
+
end
|
639
|
+
|
640
|
+
def run_file_and_capture(file)
|
641
|
+
unless File.file? file
|
642
|
+
raise "file does not exist: `#{file}'"
|
643
|
+
end
|
644
|
+
IO.popen(ruby_command_string + " " + file, "r") { |pipe|
|
645
|
+
pipe.read
|
646
|
+
}
|
647
|
+
end
|
648
|
+
|
649
|
+
def with_warnings(value = true)
|
650
|
+
previous = $VERBOSE
|
651
|
+
$VERBOSE = value
|
652
|
+
begin
|
653
|
+
yield
|
654
|
+
ensure
|
655
|
+
$VERBOSE = previous
|
656
|
+
end
|
657
|
+
end
|
658
|
+
|
659
|
+
def no_warnings(&block)
|
660
|
+
with_warnings(nil, &block)
|
661
|
+
end
|
662
|
+
|
663
|
+
def to_camel_case(str)
|
664
|
+
str.split('_').map { |t| t.capitalize }.join
|
665
|
+
end
|
666
|
+
|
667
|
+
def write_file(file)
|
668
|
+
contents = yield
|
669
|
+
File.open(file, "wb") { |out|
|
670
|
+
out.print(contents)
|
671
|
+
}
|
672
|
+
contents
|
673
|
+
end
|
674
|
+
|
675
|
+
def instance_exec2(obj, *args, &block)
|
676
|
+
method_name = ["_", obj.object_id, "_", Thread.current.object_id].join
|
677
|
+
(class << obj ; self ; end).class_eval do
|
678
|
+
define_method method_name, &block
|
679
|
+
begin
|
680
|
+
obj.send(method_name, *args)
|
681
|
+
ensure
|
682
|
+
remove_method method_name
|
683
|
+
end
|
684
|
+
end
|
685
|
+
end
|
686
|
+
end
|
687
|
+
extend Util
|
688
|
+
include Util
|
689
|
+
|
690
|
+
#### public helpers for testing
|
691
|
+
|
692
|
+
class << self
|
693
|
+
# From 'minitest' by Ryan Davis.
|
694
|
+
def capture_io
|
695
|
+
require 'stringio'
|
696
|
+
|
697
|
+
orig_stdout, orig_stderr = $stdout, $stderr
|
698
|
+
captured_stdout, captured_stderr = StringIO.new, StringIO.new
|
699
|
+
$stdout, $stderr = captured_stdout, captured_stderr
|
700
|
+
|
701
|
+
yield
|
702
|
+
|
703
|
+
return captured_stdout.string, captured_stderr.string
|
704
|
+
ensure
|
705
|
+
$stdout = orig_stdout
|
706
|
+
$stderr = orig_stderr
|
707
|
+
end
|
708
|
+
|
709
|
+
def run_doc_code(code, expected, index, instance, &block)
|
710
|
+
lib = File.expand_path(File.dirname(__FILE__) + "/../lib")
|
711
|
+
header = %{
|
712
|
+
$LOAD_PATH.unshift "#{lib}"
|
713
|
+
begin
|
714
|
+
}
|
715
|
+
footer = %{
|
716
|
+
rescue Exception => __levitate_exception
|
717
|
+
puts "raises \#{__levitate_exception.class}"
|
718
|
+
end
|
719
|
+
}
|
720
|
+
final_code = header + code + footer
|
721
|
+
|
722
|
+
# Sometimes code is required to be inside a file.
|
723
|
+
actual = nil
|
724
|
+
require 'tempfile'
|
725
|
+
Tempfile.open("run-rdoc-code") { |temp_file|
|
726
|
+
temp_file.print(final_code)
|
727
|
+
temp_file.close
|
728
|
+
actual = run_file_and_capture(temp_file.path).chomp
|
729
|
+
}
|
730
|
+
|
731
|
+
instance_exec2(instance, expected, actual, index, &block)
|
732
|
+
end
|
733
|
+
|
734
|
+
def run_doc_section(file, section, instance, &block)
|
735
|
+
contents = File.read(file)
|
736
|
+
re = %r!^=+[ \t]#{Regexp.quote(section)}.*?\n(.*?)^=!m
|
737
|
+
if section_contents = contents[re, 1]
|
738
|
+
index = 0
|
739
|
+
section_contents.scan(%r!^( \S.*?)(?=(^\S|\Z))!m) { |indented, unused|
|
740
|
+
code_sections = indented.split(%r!^ \#\#\#\# output:\s*$!)
|
741
|
+
code, expected = (
|
742
|
+
case code_sections.size
|
743
|
+
when 1
|
744
|
+
[indented, indented.scan(%r!\# => (.*?)\n!).flatten.join("\n")]
|
745
|
+
when 2
|
746
|
+
code_sections
|
747
|
+
else
|
748
|
+
raise "parse error"
|
749
|
+
end
|
750
|
+
)
|
751
|
+
code.gsub!(/^\s*%.*$/, "") # ignore shell command examples
|
752
|
+
run_doc_code(code, expected, index, instance, &block)
|
753
|
+
index += 1
|
754
|
+
}
|
755
|
+
else
|
756
|
+
raise "couldn't find section `#{section}' of `#{file}'"
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
def doc_to_spec(file, *sections, &block)
|
761
|
+
levitate = self
|
762
|
+
describe file do
|
763
|
+
sections.each { |section|
|
764
|
+
describe "section `#{section}'" do
|
765
|
+
it "should run as claimed" do
|
766
|
+
if block
|
767
|
+
levitate.run_doc_section(file, section, self, &block)
|
768
|
+
else
|
769
|
+
levitate.run_doc_section(file, section, self) {
|
770
|
+
|expected, actual, index|
|
771
|
+
actual.should == expected
|
772
|
+
}
|
773
|
+
end
|
774
|
+
end
|
775
|
+
end
|
776
|
+
}
|
777
|
+
end
|
778
|
+
end
|
779
|
+
|
780
|
+
def doc_to_test(file, *sections, &block)
|
781
|
+
levitate = self
|
782
|
+
klass = Class.new MiniTest::Unit::TestCase do
|
783
|
+
sections.each { |section|
|
784
|
+
define_method "test_#{file}_#{section}" do
|
785
|
+
if block
|
786
|
+
levitate.run_doc_section(file, section, self, &block)
|
787
|
+
else
|
788
|
+
levitate.run_doc_section(file, section, self) {
|
789
|
+
|expected, actual, index|
|
790
|
+
assert_equal expected, actual
|
791
|
+
}
|
792
|
+
end
|
793
|
+
end
|
794
|
+
}
|
795
|
+
end
|
796
|
+
Object.const_set("Test#{file}".gsub(".", ""), klass)
|
797
|
+
end
|
798
|
+
|
799
|
+
def ruby_opts
|
800
|
+
@ruby_opts ||= []
|
801
|
+
end
|
802
|
+
attr_writer :ruby_opts
|
803
|
+
end
|
804
|
+
|
805
|
+
#### raw install, bypass gems
|
806
|
+
|
807
|
+
class Installer
|
808
|
+
def initialize
|
809
|
+
require 'fileutils'
|
810
|
+
require 'rbconfig'
|
811
|
+
require 'find'
|
812
|
+
|
813
|
+
@fu = FileUtils::Verbose
|
814
|
+
@spec = []
|
815
|
+
|
816
|
+
rb_root = RbConfig::CONFIG["sitelibdir"]
|
817
|
+
|
818
|
+
Find.find "lib" do |source|
|
819
|
+
next if source == "lib"
|
820
|
+
next unless File.directory?(source) || File.extname(source) == ".rb"
|
821
|
+
dest = File.join(rb_root, source.sub(%r!\Alib/!, ""))
|
822
|
+
@spec << [source, dest]
|
823
|
+
end
|
824
|
+
end
|
825
|
+
|
826
|
+
def install
|
827
|
+
@spec.each do |source, dest|
|
828
|
+
if File.directory?(source)
|
829
|
+
@fu.mkdir(dest) unless File.directory?(dest)
|
830
|
+
else
|
831
|
+
@fu.install(source, dest)
|
832
|
+
end
|
833
|
+
end
|
834
|
+
end
|
835
|
+
|
836
|
+
def uninstall
|
837
|
+
@spec.reverse.each do |source, dest|
|
838
|
+
if File.directory?(source)
|
839
|
+
@fu.rmdir(dest) if File.directory?(dest)
|
840
|
+
else
|
841
|
+
@fu.rm(dest) if File.file?(dest)
|
842
|
+
end
|
843
|
+
end
|
844
|
+
end
|
845
|
+
end
|
846
|
+
end
|
847
|
+
|
848
|
+
lambda do
|
849
|
+
config = File.join(File.dirname(__FILE__), "levitate_config.rb")
|
850
|
+
require config if File.file? config
|
851
|
+
end.call
|