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.
Files changed (65) hide show
  1. data/README +124 -0
  2. data/Rakefile +53 -0
  3. data/bin/nandoc +6 -0
  4. data/doc/CREDITS.md +6 -0
  5. data/doc/FAQ/why-not-wiki.md +20 -0
  6. data/doc/FAQ.md +68 -0
  7. data/doc/TODOs-and-BUGs.md +15 -0
  8. data/doc/bar/baz.md +4 -0
  9. data/doc/bar/bliff.md +8 -0
  10. data/doc/foo.md +5 -0
  11. data/doc/getting-started.rb +13 -0
  12. data/doc/svg/less-fonts.svg +21 -0
  13. data/lib/nandoc/commands/create-nandoc-site.rb +225 -0
  14. data/lib/nandoc/commands/diff.rb +279 -0
  15. data/lib/nandoc/config.rb +58 -0
  16. data/lib/nandoc/cri-hacks.rb +13 -0
  17. data/lib/nandoc/data-source.rb +239 -0
  18. data/lib/nandoc/filters.rb +661 -0
  19. data/lib/nandoc/helpers/menu-bouncy.rb +109 -0
  20. data/lib/nandoc/helpers/site-map.rb +157 -0
  21. data/lib/nandoc/helpers/top-nav.rb +47 -0
  22. data/lib/nandoc/helpers.rb +42 -0
  23. data/lib/nandoc/item-class-hacks.rb +57 -0
  24. data/lib/nandoc/nandoc.persistent.json +3 -0
  25. data/lib/nandoc/parse-readme.rb +95 -0
  26. data/lib/nandoc/spec-doc/mini-test/spec-instance-methods.rb +0 -0
  27. data/lib/nandoc/spec-doc/mini-test.rb +105 -0
  28. data/lib/nandoc/spec-doc/mock-prompt.rb +121 -0
  29. data/lib/nandoc/spec-doc/support-modules.rb +158 -0
  30. data/lib/nandoc/spec-doc/test-case-agent.rb +57 -0
  31. data/lib/nandoc/spec-doc/test-framework-dispatcher.rb +15 -0
  32. data/lib/nandoc/spec-doc/test-framework-proxy.rb +78 -0
  33. data/lib/nandoc/spec-doc.rb +46 -0
  34. data/lib/nandoc/support/diff-proxy.rb +113 -0
  35. data/lib/nandoc/support/orphanage.rb +77 -0
  36. data/lib/nandoc/support/path-tardo.rb +85 -0
  37. data/lib/nandoc/support/regexp-enhance.rb +76 -0
  38. data/lib/nandoc/support/site-diff.rb +46 -0
  39. data/lib/nandoc/support/site-merge.rb +62 -0
  40. data/lib/nandoc/support/site-methods.rb +69 -0
  41. data/lib/nandoc/support/stream-colorizer.rb +203 -0
  42. data/lib/nandoc/support-modules.rb +270 -0
  43. data/lib/nandoc/test/diff-to-string.rb +251 -0
  44. data/lib/nandoc/test/minitest-extlib.rb +53 -0
  45. data/lib/nandoc/treebis/NOGIT-DOCS/NEWS.md +5 -0
  46. data/lib/nandoc/treebis/NOGIT-README.md +65 -0
  47. data/lib/nandoc/treebis/nandoc.persistent.json +3 -0
  48. data/lib/nandoc.rb +48 -0
  49. data/proto/README.md +31 -0
  50. data/proto/default/Rakefile +1 -0
  51. data/proto/default/Rules +46 -0
  52. data/proto/default/config.yaml +57 -0
  53. data/proto/default/content/css/nanoc-dist-altered.css +213 -0
  54. data/proto/default/content/css/trollop-subset.css +116 -0
  55. data/proto/default/content/js/menu-bouncy.js +126 -0
  56. data/proto/default/content/stylesheet.css.diff +20 -0
  57. data/proto/default/content/vendor/jquery-1.3.js +4241 -0
  58. data/proto/default/content/vendor/jquery.easing.1.3.js +205 -0
  59. data/proto/default/layouts/default.html +70 -0
  60. data/proto/default/lib/default.orig.rb +2 -0
  61. data/proto/default/lib/default.rb +5 -0
  62. data/proto/default/treebis-task.rb +28 -0
  63. data/proto/misc/orphan-surrogate.md +6 -0
  64. data/test/test.rb +102 -0
  65. 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