nandoc 0.0.1
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/README +124 -0
- data/Rakefile +53 -0
- data/bin/nandoc +6 -0
- data/doc/CREDITS.md +6 -0
- data/doc/FAQ/why-not-wiki.md +20 -0
- data/doc/FAQ.md +68 -0
- data/doc/TODOs-and-BUGs.md +15 -0
- data/doc/bar/baz.md +4 -0
- data/doc/bar/bliff.md +8 -0
- data/doc/foo.md +5 -0
- data/doc/getting-started.rb +13 -0
- data/doc/svg/less-fonts.svg +21 -0
- data/lib/nandoc/commands/create-nandoc-site.rb +225 -0
- data/lib/nandoc/commands/diff.rb +279 -0
- data/lib/nandoc/config.rb +58 -0
- data/lib/nandoc/cri-hacks.rb +13 -0
- data/lib/nandoc/data-source.rb +239 -0
- data/lib/nandoc/filters.rb +661 -0
- data/lib/nandoc/helpers/menu-bouncy.rb +109 -0
- data/lib/nandoc/helpers/site-map.rb +157 -0
- data/lib/nandoc/helpers/top-nav.rb +47 -0
- data/lib/nandoc/helpers.rb +42 -0
- data/lib/nandoc/item-class-hacks.rb +57 -0
- data/lib/nandoc/nandoc.persistent.json +3 -0
- data/lib/nandoc/parse-readme.rb +95 -0
- data/lib/nandoc/spec-doc/mini-test/spec-instance-methods.rb +0 -0
- data/lib/nandoc/spec-doc/mini-test.rb +105 -0
- data/lib/nandoc/spec-doc/mock-prompt.rb +121 -0
- data/lib/nandoc/spec-doc/support-modules.rb +158 -0
- data/lib/nandoc/spec-doc/test-case-agent.rb +57 -0
- data/lib/nandoc/spec-doc/test-framework-dispatcher.rb +15 -0
- data/lib/nandoc/spec-doc/test-framework-proxy.rb +78 -0
- data/lib/nandoc/spec-doc.rb +46 -0
- data/lib/nandoc/support/diff-proxy.rb +113 -0
- data/lib/nandoc/support/orphanage.rb +77 -0
- data/lib/nandoc/support/path-tardo.rb +85 -0
- data/lib/nandoc/support/regexp-enhance.rb +76 -0
- data/lib/nandoc/support/site-diff.rb +46 -0
- data/lib/nandoc/support/site-merge.rb +62 -0
- data/lib/nandoc/support/site-methods.rb +69 -0
- data/lib/nandoc/support/stream-colorizer.rb +203 -0
- data/lib/nandoc/support-modules.rb +270 -0
- data/lib/nandoc/test/diff-to-string.rb +251 -0
- data/lib/nandoc/test/minitest-extlib.rb +53 -0
- data/lib/nandoc/treebis/NOGIT-DOCS/NEWS.md +5 -0
- data/lib/nandoc/treebis/NOGIT-README.md +65 -0
- data/lib/nandoc/treebis/nandoc.persistent.json +3 -0
- data/lib/nandoc.rb +48 -0
- data/proto/README.md +31 -0
- data/proto/default/Rakefile +1 -0
- data/proto/default/Rules +46 -0
- data/proto/default/config.yaml +57 -0
- data/proto/default/content/css/nanoc-dist-altered.css +213 -0
- data/proto/default/content/css/trollop-subset.css +116 -0
- data/proto/default/content/js/menu-bouncy.js +126 -0
- data/proto/default/content/stylesheet.css.diff +20 -0
- data/proto/default/content/vendor/jquery-1.3.js +4241 -0
- data/proto/default/content/vendor/jquery.easing.1.3.js +205 -0
- data/proto/default/layouts/default.html +70 -0
- data/proto/default/lib/default.orig.rb +2 -0
- data/proto/default/lib/default.rb +5 -0
- data/proto/default/treebis-task.rb +28 -0
- data/proto/misc/orphan-surrogate.md +6 -0
- data/test/test.rb +102 -0
- metadata +166 -0
@@ -0,0 +1,121 @@
|
|
1
|
+
# @todo figure out how to get the symlink hack to work and avoid
|
2
|
+
# this nonsense all the while letting this be a valid requireable file
|
3
|
+
require(File.expand_path('../../../nandoc.rb',__FILE__)) unless
|
4
|
+
Object.const_defined?('NanDoc')
|
5
|
+
require(File.expand_path('../../spec-doc.rb',__FILE__)) unless
|
6
|
+
NanDoc.const_defined?('SpecDoc')
|
7
|
+
require(File.dirname(__FILE__)+'/mini-test.rb') unless
|
8
|
+
NanDoc::SpecDoc.const_defined?('MiniTest')
|
9
|
+
|
10
|
+
module NanDoc
|
11
|
+
class MockPrompt
|
12
|
+
#
|
13
|
+
# This is a bit of a misnomer. it's actually just a wrapper
|
14
|
+
# around the real shell. It's for testing. SpecDoc.
|
15
|
+
# @todo - Move this class to under SpecDoc
|
16
|
+
#
|
17
|
+
|
18
|
+
include Treebis::Sopen2
|
19
|
+
include SpecDoc::AgentInstanceMethods
|
20
|
+
|
21
|
+
|
22
|
+
def initialize test_case=nil
|
23
|
+
@last_both = @last_out = @last_err = nil
|
24
|
+
@record = false
|
25
|
+
@test_case = test_case
|
26
|
+
end
|
27
|
+
|
28
|
+
# wrapper around FileUtils cd that takes block form
|
29
|
+
# the tail portion of the path if any that you want to go documented
|
30
|
+
# with SpecDoc is the second arg. The first arg if any is the part
|
31
|
+
# of the path that will go undocumented.
|
32
|
+
def cd actual_basedir, specdoc_subdir=nil, &block
|
33
|
+
actual_path = File.join( * [actual_basedir, specdoc_subdir].compact)
|
34
|
+
if specdoc_subdir && @record
|
35
|
+
@recordings.add(:cd, specdoc_subdir)
|
36
|
+
end
|
37
|
+
FileUtils.cd(actual_path) do
|
38
|
+
block.call(self)
|
39
|
+
end
|
40
|
+
if specdoc_subdir && @record
|
41
|
+
@recordings.add(:cd_end)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def cosmetic_ellipsis str
|
46
|
+
@record && @recordings.add(:cosmetic_ellipsis, str)
|
47
|
+
end
|
48
|
+
|
49
|
+
def enter2 cmd
|
50
|
+
@last_both = nil
|
51
|
+
@record && @recordings.add(:command, cmd)
|
52
|
+
@last_out, @last_err = sopen2(cmd)
|
53
|
+
end
|
54
|
+
|
55
|
+
def err string, opts={}
|
56
|
+
exp = reindent string
|
57
|
+
assert_equal_strings exp, @last_err, opts
|
58
|
+
end
|
59
|
+
|
60
|
+
def note(*a,&b)
|
61
|
+
@record && @recordings.note(*a, &b)
|
62
|
+
end
|
63
|
+
|
64
|
+
def out string, opts={}
|
65
|
+
exp = reindent(string)
|
66
|
+
@record && @recordings.add(:out, exp)
|
67
|
+
assert_equal_strings exp, @last_out, opts
|
68
|
+
end
|
69
|
+
|
70
|
+
def out_begin exp_begin
|
71
|
+
@exp_begin = reindent(exp_begin)
|
72
|
+
@record && @recordings.add(:out_begin, @exp_begin)
|
73
|
+
# we don't do the assert until we get to (any?) end
|
74
|
+
end
|
75
|
+
|
76
|
+
def out_end exp_end, opts={}
|
77
|
+
@exp_begin or fail("no begin found for end")
|
78
|
+
exp_end2 = reindent(exp_end)
|
79
|
+
@record && @recordings.add(:out_end, exp_end2)
|
80
|
+
act_begin = @last_out[0..@exp_begin.length-1]
|
81
|
+
assert_equal_strings @exp_begin, act_begin, opts
|
82
|
+
act_end = @last_out[(exp_end2.length * -1)..-1]
|
83
|
+
assert_equal_strings exp_end2, act_end, opts
|
84
|
+
end
|
85
|
+
|
86
|
+
def record story_name=nil
|
87
|
+
method = method_name_to_record(caller)
|
88
|
+
@record = true
|
89
|
+
recordings
|
90
|
+
@recordings.add(:method, method)
|
91
|
+
@recordings.add(:story, story_name) if story_name
|
92
|
+
end
|
93
|
+
|
94
|
+
def record_stop
|
95
|
+
@record = false
|
96
|
+
@recordings = nil
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def assert_equal_strings exp, act, opts
|
102
|
+
# @test_case.assert_equal exp, @last_out
|
103
|
+
@test_case.assert_no_diff exp, act, nil, opts
|
104
|
+
end
|
105
|
+
|
106
|
+
# this is different than the dozens of similar ones
|
107
|
+
def reindent str
|
108
|
+
these = str.scan(/^[\t ]*/).each.with_index.map
|
109
|
+
string, idx = these.min_by{ |x| x[0].length }
|
110
|
+
if string.length == 0 && str.index("\n") # exp
|
111
|
+
s2, _ =
|
112
|
+
these.reject{ |x| x[0].length == 0 }.min_by{ |x| x[0].length }
|
113
|
+
string = s2 if s2
|
114
|
+
end
|
115
|
+
re = /^#{Regexp.escape(string)}/
|
116
|
+
str.gsub(re, '')
|
117
|
+
end
|
118
|
+
|
119
|
+
attr_reader :test_case
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module NanDoc
|
2
|
+
module SpecDoc
|
3
|
+
module AgentInstanceMethods
|
4
|
+
# share things between MockPrompt and TestCaseAgent
|
5
|
+
|
6
|
+
# we used to use first method, now we use first test_ method
|
7
|
+
def method_name_to_record caller
|
8
|
+
line = caller.detect{ |x| x =~ /in `test_/ } or fail('hack fail')
|
9
|
+
method = line =~ /in `(.+)'\Z/ && $1 or fail("hack fail")
|
10
|
+
method
|
11
|
+
end
|
12
|
+
|
13
|
+
def recordings
|
14
|
+
@recordings ||= NanDoc::SpecDoc::Recordings.get(test_case)
|
15
|
+
end
|
16
|
+
|
17
|
+
def story story_name
|
18
|
+
method = method_name_to_record(caller)
|
19
|
+
rec = recordings
|
20
|
+
rec.add(:method, method)
|
21
|
+
rec.add(:story, story_name)
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
class CodeSnippet
|
28
|
+
#
|
29
|
+
# internally this does deferred parsing of the thing
|
30
|
+
# a code snippet holds meta information (or maybe content)
|
31
|
+
# for record_ruby nandoc commands in tests.
|
32
|
+
#
|
33
|
+
|
34
|
+
def initialize matches_hash
|
35
|
+
@start_at = matches_hash
|
36
|
+
@stop_at = nil
|
37
|
+
@lines_proc = nil
|
38
|
+
end
|
39
|
+
attr_reader :start_at
|
40
|
+
%w(method line file).each do |meth|
|
41
|
+
sym = meth.to_sym
|
42
|
+
define_method(meth){ || @start_at[sym] }
|
43
|
+
end
|
44
|
+
def describe
|
45
|
+
last = method ? ":in `#{method}'" : ''
|
46
|
+
"#{file}:#{line}#{tail}"
|
47
|
+
end
|
48
|
+
# just hide all the lines from dumps to make irb debugging prettier
|
49
|
+
def file_lines
|
50
|
+
@lines_proc ||= begin
|
51
|
+
stop_at_assert # not really appropriate here
|
52
|
+
same_file_assert # not really appropriate here
|
53
|
+
all_lines = File.open(@start_at[:file],'r').lines.map # sure why not
|
54
|
+
proc{ all_lines }
|
55
|
+
end
|
56
|
+
@lines_proc.call
|
57
|
+
end
|
58
|
+
def line_start
|
59
|
+
@start_at[:line]
|
60
|
+
end
|
61
|
+
def line_stop
|
62
|
+
@stop_at[:line]
|
63
|
+
end
|
64
|
+
# @todo maybe get rid of this. we interpolate inspects elsewhere
|
65
|
+
def ruby_string_raw
|
66
|
+
@ruby_string_raw ||= begin
|
67
|
+
these = all_lines[line_start..(line_stop-2)]
|
68
|
+
these.join('') # they have newlines already
|
69
|
+
end
|
70
|
+
end
|
71
|
+
def stop_at data=nil
|
72
|
+
data ? (@stop_at = data) : @stop_at
|
73
|
+
end
|
74
|
+
private
|
75
|
+
def same_file_assert
|
76
|
+
@stop_at[:file] == @start_at[:file] or fail("I want life to be"<<
|
77
|
+
" simple. start and stop files must be the same: "<<
|
78
|
+
([@stop_at, @start_at].map{ |x| File.basename(x)}*' and '))
|
79
|
+
end
|
80
|
+
def stop_at_assert
|
81
|
+
stop_at or fail("no record_ruby_stop() found in method "<<
|
82
|
+
"after #{describe}")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
module ParseTrace
|
87
|
+
#
|
88
|
+
# @return [Regexp] enhanced regex that parses a stack trace line
|
89
|
+
#
|
90
|
+
def parse_trace
|
91
|
+
@parse_trace_re ||= begin
|
92
|
+
re = /\A(.*):(\d+)(?::in `([^']+)')?\Z/
|
93
|
+
RegexpEnhance.names(re, :file, :line, :method)
|
94
|
+
re
|
95
|
+
end
|
96
|
+
end
|
97
|
+
def parse_trace_assert line
|
98
|
+
md = parse_trace.match(line) or
|
99
|
+
fail("couldn't parse trace line: #{line}")
|
100
|
+
h = md.to_hash
|
101
|
+
/\A\d+\Z/ =~ h[:line] or fail("not line: #{h[:line]}.inspect")
|
102
|
+
h[:line] = h[:line].to_i
|
103
|
+
h
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
class Recordings < Array
|
109
|
+
#
|
110
|
+
# everything that nandoc does during test runs gets written
|
111
|
+
# to one of these. It's like a Sexp structure.
|
112
|
+
#
|
113
|
+
|
114
|
+
|
115
|
+
@for_test_case = {}
|
116
|
+
|
117
|
+
class << self
|
118
|
+
attr_accessor :for_test_case
|
119
|
+
def get test_case
|
120
|
+
@for_test_case[test_case.class] ||= new(test_case.class)
|
121
|
+
end
|
122
|
+
def report_test_case_not_found tc
|
123
|
+
msgs = ["no recordings found for #{tc}"]
|
124
|
+
msgs.join(' ')
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def add name, *data
|
129
|
+
# this might change if we need to group by method name
|
130
|
+
push [name, *data]
|
131
|
+
end
|
132
|
+
|
133
|
+
def get_first_sexp_for_test_method meth
|
134
|
+
first = index([:method, meth]) or return nil
|
135
|
+
last = (first+1..length-1).detect do |i|
|
136
|
+
self[i].first == :method && self[i][1] != meth
|
137
|
+
end
|
138
|
+
last = last ? (last - 1) : (length - 1)
|
139
|
+
ret = self[first..last]
|
140
|
+
ret
|
141
|
+
end
|
142
|
+
|
143
|
+
def initialize test_case
|
144
|
+
@test_case = test_case
|
145
|
+
end
|
146
|
+
|
147
|
+
def note &block
|
148
|
+
push [:note, block]
|
149
|
+
end
|
150
|
+
|
151
|
+
def report_recording_not_found meth_name
|
152
|
+
"no recordings found for #{meth_name}"
|
153
|
+
end
|
154
|
+
|
155
|
+
attr_reader :test_case
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module NanDoc::SpecDoc
|
2
|
+
class TestCaseAgent
|
3
|
+
include ParseTrace, AgentInstanceMethods, ::Treebis::Capture3
|
4
|
+
def initialize test_case
|
5
|
+
@test_case = test_case
|
6
|
+
end
|
7
|
+
|
8
|
+
# @param [block] gets called, and whatever is written to $stdout
|
9
|
+
# will get asserted against whatever is in
|
10
|
+
# @param [String] exp.
|
11
|
+
# if it passes (or fails?) one of the strings is written to an
|
12
|
+
# :out node in the recording
|
13
|
+
# raises something if $stderr is written to.
|
14
|
+
#
|
15
|
+
# warning - blah blah @todo
|
16
|
+
#
|
17
|
+
#
|
18
|
+
def out exp, &block
|
19
|
+
trace = parse_trace_assert(caller.first)
|
20
|
+
out, err = capture3(&block)
|
21
|
+
fail("no: #{err.inspect}") unless err == ""
|
22
|
+
@test_case.assert_no_diff(exp, out)
|
23
|
+
if out == exp
|
24
|
+
recordings.add(:out, out, trace)
|
25
|
+
end
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
def inspect mixed, exp_str = nil
|
29
|
+
act_str = mixed.inspect
|
30
|
+
line = caller.first
|
31
|
+
trace = parse_trace_assert(line)
|
32
|
+
if exp_str
|
33
|
+
@test_case.assert_no_diff(exp_str, act_str, "at #{line}")
|
34
|
+
end
|
35
|
+
recordings.add(:inspect, act_str, trace)
|
36
|
+
end
|
37
|
+
def record_ruby
|
38
|
+
md = parse_trace_assert(caller.first)
|
39
|
+
snip = CodeSnippet.new(md)
|
40
|
+
recordings.add(:method, snip.method)
|
41
|
+
recordings.add(:record_ruby, snip)
|
42
|
+
@last_snip = snip
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
def record_ruby_stop
|
46
|
+
line = caller.first
|
47
|
+
md = parse_trace_assert(caller.first)
|
48
|
+
@last_snip or fail("no record_start in method before "<<
|
49
|
+
"record_stop at #{line}")
|
50
|
+
@last_snip.stop_at md
|
51
|
+
end
|
52
|
+
private
|
53
|
+
def recordings
|
54
|
+
Recordings.get(@test_case)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__)+'/test-framework-proxy.rb'
|
2
|
+
|
3
|
+
module NanDoc
|
4
|
+
module SpecDoc
|
5
|
+
class TestFrameworkDispatcher
|
6
|
+
def initialize gem_root
|
7
|
+
require File.dirname(__FILE__)+'/mini-test.rb'
|
8
|
+
@the_only_proxy = SpecDoc::MiniTest::Proxy.new(gem_root)
|
9
|
+
end
|
10
|
+
def get_sexp *a
|
11
|
+
@the_only_proxy.get_sexp(*a)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module NanDoc::SpecDoc
|
2
|
+
class TestFrameworkProxy
|
3
|
+
# abstract baseclass, an agent that runs tests
|
4
|
+
|
5
|
+
def initialize gem_root
|
6
|
+
@gem_root = gem_root
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_sexp testfile, testname
|
10
|
+
TestFrameworkProxy.sexp_cache[testfile][testname] ||= begin
|
11
|
+
build_sexp testfile, testname
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def build_sexp testfile, testname
|
16
|
+
load_file testfile
|
17
|
+
test_case, meth_name = find_test testname
|
18
|
+
run_test_case_method testfile, testname, test_case, meth_name
|
19
|
+
recs = ::NanDoc::SpecDoc::Recordings.for_test_case[test_case] or
|
20
|
+
fail(::NanDoc::SpecDoc::Recordings.
|
21
|
+
report_test_case_not_found(test_case))
|
22
|
+
sexp = recs.get_first_sexp_for_test_method(meth_name) or
|
23
|
+
fail recs.report_recording_not_found(meth_name)
|
24
|
+
sexp
|
25
|
+
end
|
26
|
+
|
27
|
+
@sexp_cache = Hash.new{ |h,k| h[k] = {} }
|
28
|
+
class << self
|
29
|
+
attr_reader :sexp_cache
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
#
|
35
|
+
# the stream to write to when things like a specdoc test run fails
|
36
|
+
#
|
37
|
+
def err
|
38
|
+
$stderr
|
39
|
+
end
|
40
|
+
|
41
|
+
def handle_failed_tests runner, testfile, testname
|
42
|
+
err.print "can't SpecDoc. at least one test failed when trying to run"
|
43
|
+
err.puts " #{testfile.inspect} #{testname.inspect} - "
|
44
|
+
runner.report.each do |line|
|
45
|
+
err.puts line
|
46
|
+
end
|
47
|
+
err.puts "Please get your tests green and re-run."
|
48
|
+
exit(1);
|
49
|
+
end
|
50
|
+
|
51
|
+
def load_file testfile
|
52
|
+
path = testdir + '/' + testfile
|
53
|
+
fail("test file not found: #{path.inspect}") unless File.file?(path)
|
54
|
+
# if you need to, do a diff
|
55
|
+
require path
|
56
|
+
end
|
57
|
+
|
58
|
+
#
|
59
|
+
# default way to read what the test run wrote to stdout
|
60
|
+
#
|
61
|
+
def testout_str
|
62
|
+
@testout.rewind
|
63
|
+
@testout.read
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# default way to deduce the root directory that holds the tests
|
68
|
+
#
|
69
|
+
def testdir
|
70
|
+
@testdir ||= begin
|
71
|
+
tries = [@gem_root+'/test', @gem_root+'/spec']
|
72
|
+
found = tries.detect{ |path| File.directory?(path) }
|
73
|
+
fail("Couldn't find test dir for gem at (#{tries*', '})") unless found
|
74
|
+
found
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# little hack to a) let clients just include this file for test runs
|
2
|
+
# and b) allow us to do the symlink hack for developing this.
|
3
|
+
unless Object.const_defined?('NanDoc')
|
4
|
+
require File.expand_path('../../nandoc.rb', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
me = File.dirname(__FILE__)+'/spec-doc'
|
8
|
+
require me + '/support-modules.rb'
|
9
|
+
require me + '/test-case-agent.rb'
|
10
|
+
require me + '/test-framework-dispatcher.rb'
|
11
|
+
|
12
|
+
module NanDoc
|
13
|
+
module SpecDoc
|
14
|
+
class << self
|
15
|
+
|
16
|
+
#
|
17
|
+
# enhance a test framework spec or test case module
|
18
|
+
# for e.g. a minitest spec class.
|
19
|
+
#
|
20
|
+
def include_to mod
|
21
|
+
if Object.const_defined?('MiniTest') &&
|
22
|
+
mod.ancestors.include?(::MiniTest::Spec)
|
23
|
+
require File.dirname(__FILE__)+'/spec-doc/mini-test.rb'
|
24
|
+
::NanDoc::SpecDoc::MiniTest::SpecInstanceMethods.include_to mod
|
25
|
+
else
|
26
|
+
fail("don't know how to enhance test module: #{mod}")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize gem_root
|
32
|
+
@sexp_cache = Hash.new{|h,k| h[k] = {}}
|
33
|
+
@test_framework_dispatcher = TestFrameworkDispatcher.new(gem_root)
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# only run any test method at most once, just to keep recordings clean
|
38
|
+
#
|
39
|
+
def get_sexp testfile, testname
|
40
|
+
sexp = @sexp_cache[testfile][testname] ||= begin
|
41
|
+
@test_framework_dispatcher.get_sexp testfile, testname
|
42
|
+
end
|
43
|
+
sexp
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module NanDoc
|
2
|
+
module DiffProxy
|
3
|
+
include Treebis::Sopen2
|
4
|
+
extend self
|
5
|
+
def diff path_a, path_b, opts={}
|
6
|
+
fail('no') unless File.exist?(path_a) && File.exist?(path_b)
|
7
|
+
rel_to = opts.delete(:relative_to)
|
8
|
+
path_a, path_b = relativize(rel_to, path_a, path_b) if rel_to
|
9
|
+
opts = {'--unified=3'=>nil, '--recursive'=>nil}.merge(opts)
|
10
|
+
args = ['diff'] + opts.each.map.flatten.compact + [path_a, path_b]
|
11
|
+
out = err = nil
|
12
|
+
block = proc do
|
13
|
+
out, err = sopen2(*args)
|
14
|
+
end
|
15
|
+
if rel_to
|
16
|
+
FileUtils.cd(rel_to, :verbose=>true, &block)
|
17
|
+
else
|
18
|
+
block.call
|
19
|
+
end
|
20
|
+
diff = Diff.new(out, err, args)
|
21
|
+
if diff.error?
|
22
|
+
return fail(diff.full_error_message){|f| f.diff = diff }
|
23
|
+
end
|
24
|
+
diff
|
25
|
+
end
|
26
|
+
private
|
27
|
+
def fail(*a, &b)
|
28
|
+
raise Fail.new(*a, &b)
|
29
|
+
end
|
30
|
+
def relativize base, path_a, path_b
|
31
|
+
fail("KISS") unless [0,0]==[path_a, path_b].map{|x| x.index(base)}
|
32
|
+
tail_a, tail_b = [path_a, path_b].map{|x| '.'+x[base.length..-1]}
|
33
|
+
[tail_a, tail_b]
|
34
|
+
end
|
35
|
+
class Diff
|
36
|
+
class << self
|
37
|
+
def default_diff_stylesheet
|
38
|
+
@default_diff_stylesheet ||= {
|
39
|
+
:header => [:bold, :yellow],
|
40
|
+
:add => [:bold, :green],
|
41
|
+
:remove => [:bold, :red],
|
42
|
+
:range => [:bold, :magenta],
|
43
|
+
:trailing_whitespace => [:background, :red]
|
44
|
+
}
|
45
|
+
end
|
46
|
+
def stream_colorizer_prototype
|
47
|
+
@stream_colorizer_prototype ||= begin
|
48
|
+
require File.dirname(__FILE__)+'/stream-colorizer.rb'
|
49
|
+
NanDoc::StreamColorizer.new do |sc|
|
50
|
+
sc.stylesheet_merge(default_diff_stylesheet)
|
51
|
+
sc.when %r(\Adiff ), :state=>:header
|
52
|
+
sc.when(:header) do |o|
|
53
|
+
o.style :header
|
54
|
+
o.when %r(\A@@), :state=>:range
|
55
|
+
end
|
56
|
+
sc.when(:range) do |o|
|
57
|
+
o.style :range
|
58
|
+
o.when_not %r(\A@@), :state=>:plain
|
59
|
+
end
|
60
|
+
sc.when(:plain) do |o|
|
61
|
+
o.style nil
|
62
|
+
o.when %r(\Adiff ), :state=>:header
|
63
|
+
o.when %r(\A\+), :state=>:add
|
64
|
+
o.when %r(\A\-), :state=>:remove
|
65
|
+
end
|
66
|
+
sc.when(:add) do |o|
|
67
|
+
o.style :add
|
68
|
+
o.trailing_whitespace_style :trailing_whitespace
|
69
|
+
o.when_not %r(\A\+), :state=>:plain
|
70
|
+
end
|
71
|
+
sc.when(:remove) do |o|
|
72
|
+
o.style :remove
|
73
|
+
o.trailing_whitespace_style :trailing_whitespace
|
74
|
+
o.when_not %r(\A\-), :state=>:plain
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
def initialize out, err, args
|
81
|
+
@out = out
|
82
|
+
@error = err
|
83
|
+
@args = args
|
84
|
+
end
|
85
|
+
attr_reader :error
|
86
|
+
def command
|
87
|
+
Shellwords.join(@args)
|
88
|
+
end
|
89
|
+
def error?; ! @error.empty? end
|
90
|
+
def full_error_message
|
91
|
+
"diff failed: #{command}\ngot error: #{error}"
|
92
|
+
end
|
93
|
+
def ok?; ! error? end
|
94
|
+
def to_s
|
95
|
+
@out
|
96
|
+
end
|
97
|
+
def colorize out, opts={}
|
98
|
+
colorizer = self.class.stream_colorizer_prototype.spawn do |c|
|
99
|
+
c.stylesheet_merge(opts[:styles] || {})
|
100
|
+
end
|
101
|
+
colorizer.filter(to_s, out)
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
end
|
105
|
+
class Fail < RuntimeError;
|
106
|
+
def initialize(*a,&b)
|
107
|
+
super(*a)
|
108
|
+
yield self if block_given?
|
109
|
+
end
|
110
|
+
attr_accessor :diff
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module NanDoc
|
2
|
+
class DataSource # reopen, not really necessary
|
3
|
+
class Orphanage
|
4
|
+
#
|
5
|
+
# @api private implementation for experimental orphan_rescue()
|
6
|
+
#
|
7
|
+
include ItemMethods
|
8
|
+
class << self
|
9
|
+
def rescue_orphans config, items
|
10
|
+
new(config, items).rescue_orphans
|
11
|
+
end
|
12
|
+
private :new
|
13
|
+
end
|
14
|
+
|
15
|
+
def rescue_orphans
|
16
|
+
fail("must have site root to continue hacking") unless site_root
|
17
|
+
items = @items.map # we are going to add to it in loop below
|
18
|
+
items.each do |item|
|
19
|
+
next unless is_orphan? item
|
20
|
+
id = item.identifier
|
21
|
+
parent_id = parent_identifier(id)
|
22
|
+
bare_root = identifier_bare_rootname_assert(id)
|
23
|
+
bare_parent = slash_strip_assert(parent_id)
|
24
|
+
new_id = nil
|
25
|
+
@renamer = nil
|
26
|
+
if @basenames.include?(bare_root)
|
27
|
+
# then we need to hack a rename to the identifier
|
28
|
+
@renamer = /\A\/#{Regexp.escape(bare_root)}\/(.+)\Z/
|
29
|
+
new_id = rename(id)
|
30
|
+
end
|
31
|
+
if (!new_id || bare_parent!=bare_root) && is_orphan?(item, new_id)
|
32
|
+
make_surrogate_parent parent_id
|
33
|
+
end
|
34
|
+
item.identifier = new_id if new_id
|
35
|
+
end
|
36
|
+
end
|
37
|
+
private
|
38
|
+
def initialize config, items
|
39
|
+
@basenames = config[:source_file_basenames] || []
|
40
|
+
@items = items
|
41
|
+
end
|
42
|
+
|
43
|
+
def is_orphan? item, using_identifier=nil
|
44
|
+
item.nandoc_content_leaf? or return false
|
45
|
+
using_identifier ||= item.identifier
|
46
|
+
parent = find_parent using_identifier
|
47
|
+
parent.nil? && using_identifier != '/'
|
48
|
+
end
|
49
|
+
|
50
|
+
def make_surrogate_parent parent_id
|
51
|
+
use_id = @renamer ? rename(parent_id) : parent_id
|
52
|
+
use_path = @renamer ? "../#{parent_id}" : parent_id
|
53
|
+
content = surrogate_content
|
54
|
+
fake_parent = Nanoc3::Item.new(
|
55
|
+
content,
|
56
|
+
{:filename => use_path, :content_filename => use_path },
|
57
|
+
use_id
|
58
|
+
)
|
59
|
+
@items.unshift(fake_parent)
|
60
|
+
end
|
61
|
+
|
62
|
+
# very private
|
63
|
+
def rename str
|
64
|
+
@renamer =~ str or
|
65
|
+
fail("rename fail: #{str.inspect} against #{@renamer}")
|
66
|
+
renamed = "/#{$1}"
|
67
|
+
renamed
|
68
|
+
end
|
69
|
+
|
70
|
+
def surrogate_content
|
71
|
+
@surrogate_content ||= begin
|
72
|
+
File.read(NanDoc::Config.orphan_surrogate_filename)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|