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,85 @@
1
+ module NanDoc
2
+ module PathTardo
3
+ #
4
+ # This is like a really basic xpath for data structures
5
+ # that are composed of arrays and hashes
6
+ #
7
+ def tardo_array_index str
8
+ /\A\[(-?\d+)\]\Z/ =~ str ? $1.to_i : nil
9
+ end
10
+ module_function :tardo_array_index
11
+
12
+ def hash_to_paths hash, prefix=nil
13
+ paths = []
14
+ hash.each do |k,v|
15
+ ch_prefix = [prefix, k].compact.join('/')
16
+ if v.kind_of?(Hash)
17
+ paths.concat hash_to_paths(v, ch_prefix)
18
+ else
19
+ paths.push ch_prefix
20
+ end
21
+ end
22
+ paths
23
+ end
24
+
25
+ def path_tardo hash_or_array, path_tardo, prefix = ''
26
+ /\A([^\/]+)(?:\/(.+))?\Z/ =~ path_tardo or
27
+ fail("no parse: #{path_tardo}")
28
+ head, tail = $1, $2
29
+ value = nil
30
+ found = nil
31
+ if idx = tardo_array_index(head)
32
+ if idx > 0 && idx >= hash_or_array.size
33
+ found = false
34
+ elsif idx < 0 && (idx*-1) > hash_or_array.size
35
+ found = false
36
+ else
37
+ found = true
38
+ value = hash_or_array.slice(idx)
39
+ end
40
+ else
41
+ if hash_or_array.key?(head)
42
+ found = true
43
+ value = hash_or_array[head]
44
+ else
45
+ found = false
46
+ end
47
+ end
48
+ if ! found
49
+ Tardo::NotFound.new(prefix, head, hash_or_array)
50
+ elsif tail
51
+ local_full_path =
52
+ [ prefix.empty? ? nil : prefix, head ].compact.join('/')
53
+ path_tardo(value, tail, local_full_path)
54
+ else
55
+ Tardo::Found.new(value)
56
+ end
57
+ end
58
+ module Tardo
59
+ class NotFound < Struct.new(:prefix, :head, :hash_or_array)
60
+ def found?; false end
61
+ def error_message
62
+ sub_msg =
63
+ if idx = PathTardo.tardo_array_index(head)
64
+ "#{idx} is a nonexistant offset"
65
+ else
66
+ "a \"#{head}\" key does not exist"
67
+ end
68
+ context_msg =
69
+ if prefix.empty?
70
+ nil
71
+ elsif hash_or_array.kind_of?(Array)
72
+ "in \"#{prefix}\" array,"
73
+ else
74
+ "in hash \"#{prefix}\","
75
+ end
76
+ msg = [context_msg, sub_msg].compact.join(' ')
77
+ msg
78
+ end
79
+ end
80
+ class Found < Struct.new(:value)
81
+ def found?; true end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,76 @@
1
+ module NanDoc
2
+ module RegexpEnhance
3
+ #
4
+ # Just gives pre-1.9 regexes the ability to have named captures
5
+ #
6
+ # Usage:
7
+ #
8
+ # re = /(foo*)bar(baz*)/
9
+ # RegexpEnhance.names(re, :the_foo, :the_baz)
10
+ # md = re.match("fooobarbazzzz")
11
+ # md[:the_foo] # => 'fooo'
12
+ #
13
+ class << self
14
+ def names re, *list
15
+ to(re) do |re|
16
+ re.names(*list)
17
+ end
18
+ nil
19
+ end
20
+ def to re, &block
21
+ re.extend(self)
22
+ re.regexp_enhance_init
23
+ block.call(re)
24
+ nil
25
+ end
26
+ end
27
+ def regexp_enhance_init
28
+ @names ||= []
29
+ class << self
30
+ alias_method :orig_match, :match
31
+ def match str
32
+ md = super
33
+ MatchData.to(md, self) if md
34
+ md
35
+ end
36
+ end
37
+ end
38
+ def names *list
39
+ if list.any?
40
+ @names = list
41
+ else
42
+ @names
43
+ end
44
+ end
45
+ module MatchData
46
+ class << self
47
+ def to(md, re)
48
+ md.extend self
49
+ md.match_data_enhanced_init(re)
50
+ nil
51
+ end
52
+ end
53
+ def match_data_enhanced_init re
54
+ @names = re.names
55
+ class << self # @todo see if this doesn't break by moving defs out
56
+ alias_method :fetch_orig, :[]
57
+ attr_reader :names
58
+ def [](mixed)
59
+ return fetch_orig(mixed) unless mixed.kind_of?(Symbol)
60
+ fail("no such named capture: #{mixed.inspect}") unless
61
+ @names.include?(mixed)
62
+ offset = @names.index(mixed) + 1
63
+ fetch_orig offset
64
+ end
65
+ end
66
+ end
67
+
68
+ #
69
+ # @return [Hash] of the named captures in the MatchData
70
+ #
71
+ def to_hash
72
+ Hash[ names.map{ |n| [n, self[n] ] } ]
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,46 @@
1
+ require File.dirname(__FILE__)+'/diff-proxy.rb'
2
+
3
+ module NanDoc
4
+ class SiteDiff
5
+ #
6
+ # This is a reworking of what's in site-merge (which doesn't really
7
+ # do anything useful attotw, if it still even works.)
8
+ # it's a wrapper around diff proxy that knows what folder(s) & file(s) we
9
+ # do and don't want to take into account when showing a sitewide diff.
10
+ # Specifically it's expected use is for comparing <my-site>/(* ~ output)
11
+ # with its protoype.
12
+ #
13
+
14
+ include NanDoc::Config::Accessors # file_utils()
15
+ include NanDoc::PathTardo # debug-ging e.g. hash_to_paths()
16
+ NanDoc.persistent_delegate_to(self) # empty_tmpdir()
17
+ include Treebis::DirAsHash # dir_as_hash()
18
+ include Treebis::Capture3 # capture3()
19
+
20
+ def initialize src_path, dest_path
21
+ @skip_these = %w(
22
+ output
23
+ tmp
24
+ **/*.orig.rb
25
+ **/*.diff
26
+ treebis-task.rb
27
+ )
28
+ @src_path, @dst_path = src_path, dest_path
29
+ end
30
+
31
+ def get_diff_object
32
+ file_utils.notice('comparing these:',
33
+ "#{@src_path.inspect} -> #{@dst_path.inspect}"
34
+ )
35
+ src_hash = dir_as_hash(@src_path, :skip => @skip_these)
36
+ dst_hash = dir_as_hash(@dst_path, :skip => @skip_these)
37
+ dir = empty_tmpdir('site-diff')
38
+ src_path = dir + '/a'
39
+ dst_path = dir + '/b'
40
+ hash_to_dir src_hash, src_path, file_utils
41
+ hash_to_dir dst_hash, dst_path, file_utils
42
+ diff = NanDoc::DiffProxy.diff(src_path, dst_path, :relative_to=>dir)
43
+ diff
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,62 @@
1
+ require File.dirname(__FILE__)+'/diff-proxy.rb'
2
+
3
+ module NanDoc
4
+ class SiteMerge
5
+ include Treebis::DirAsHash
6
+ include Treebis::Capture3
7
+ NanDoc.persistent_delegate_to(self) # empty_tmpdir()
8
+
9
+ def initialize site_creator
10
+ @creator = site_creator
11
+ end
12
+ def site_merge opts, args
13
+ existing_site_path = args.first
14
+ task_abort("it's not exist: #{path}") unless
15
+ File.exist?(existing_site_path)
16
+ # nanoc writes to stdout so we do too, but here we want to write
17
+ # to stderr these notices, and write the diff to stdout
18
+ out, err, diff = capture3 do
19
+ $stdout.puts(
20
+ "#------------------ (this is for the merge hack:) -----------------")
21
+ @tmpdir = empty_tmpdir('ridiculous')
22
+ existing_subset_path = temp_site_subset(existing_site_path)
23
+ generated_site_path = temp_generated_site opts
24
+ order = [existing_subset_path, generated_site_path]
25
+ order.reverse! if opts[:merge_hack_reverse]
26
+ diff = DiffProxy.diff(order[0], order[1], :relative_to => @tmpdir)
27
+ # you could delete the tempdirs now. we will leave them there
28
+ diff
29
+ end
30
+ fail("hack failed: #{err.inspect}") unless err.empty?
31
+ $stderr.puts out # write out to err here
32
+ $stderr.puts diff.command
33
+ $stderr.puts <<-HERE.gsub(/^ +/,'')
34
+ #---------------- (above is stderr, below is stdout) ------------------
35
+ HERE
36
+ $stdout.puts diff.to_s
37
+ diff
38
+ end
39
+ private
40
+ def temp_site_subset path
41
+ subset_in_memory = dir_as_hash(path, :skip=>['output'])
42
+ @file_utils = NanDoc::Config.file_utils
43
+ subset_on_disk = @tmpdir+'/user-site'
44
+ hash_to_dir(subset_in_memory, subset_on_disk, @file_utils)
45
+ subset_on_disk
46
+ end
47
+ def temp_generated_site opts
48
+ put_it_here = @tmpdir+'/generated-site'
49
+ chops = opts.dup
50
+ chops.delete(:datasource)
51
+ @creator.run(chops, [put_it_here], :_merge=>false)
52
+ remove_output_directory(put_it_here)
53
+ put_it_here
54
+ end
55
+ def remove_output_directory put_it_here
56
+ dir = put_it_here + '/output'
57
+ fail("fail") unless File.directory?(dir)
58
+ fail("fail") unless Dir[dir+'/*'].empty?
59
+ @file_utils.remove_entry_secure(dir)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,69 @@
1
+ module NanDoc
2
+ module SiteMethods
3
+
4
+ def deduce_site_path_or_fail args
5
+ if args.any?
6
+ deduce_site_path_from_args args
7
+ else
8
+ deduce_site_path_from_persistent_data
9
+ end
10
+ end
11
+
12
+ private
13
+
14
+ def config_path_for_site_path path
15
+ path + '/config.yaml'
16
+ end
17
+
18
+ def deduce_site_path_from_args args
19
+ if File.exist?(args.first)
20
+ path = args.first
21
+ unless path == persistent_get('last_site_path')
22
+ persistent_set('last_site_path', path)
23
+ end
24
+ path
25
+ else
26
+ task_abort <<-D.gsub(/^ */,'')
27
+ site path not found: #{args.first.inspect}
28
+ usage: #{usage}
29
+ #{invite_to_more_command_help}
30
+ D
31
+ end
32
+ end
33
+
34
+ def deduce_site_path_from_persistent_data
35
+ if path = persistent_get('last_site_path')
36
+ if File.exist?(path)
37
+ path
38
+ else
39
+ persistent_set('last_site_path',false)
40
+ task_abort <<-D.gsub(/^ */,'')
41
+ previous site path is stale (#{path.inspect}) and no site provided
42
+ usage: #{usage}
43
+ #{invite_to_more_command_help}
44
+ D
45
+ end
46
+ else
47
+ task_abort(
48
+ 'no site path provided and no site path in persistent data file '<<
49
+ "(#{NanDoc.dotfile_path})\n"<<
50
+ <<-D.gsub(/^ */,'')
51
+ usage: #{usage}
52
+ #{invite_to_more_command_help}
53
+ D
54
+ )
55
+ end
56
+ end
57
+
58
+ #
59
+ # you just get the raw file data tree, it's not merged in with any
60
+ # DEFAULT_CONFIG stuff or anything
61
+ #
62
+ def parse_config_for_site_path path
63
+ config_path = config_path_for_site_path( path )
64
+ task_abort("config file for app not found: #{config_path}") unless
65
+ File.exist?(config_path)
66
+ YAML.load_file config_path
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,203 @@
1
+ module NanDoc
2
+ class StreamColorizer
3
+ module RuleList; end
4
+ module SecretParent; end
5
+ include RuleList;
6
+ def dup
7
+ other = self.class.new
8
+ other.stylesheet = stylesheet.dup
9
+ other.rule_list = rule_list.dup
10
+ other.state_set = state_set.dup
11
+ other
12
+ end
13
+ def initialize(*a, &b)
14
+ rule_list_init
15
+ @stylesheet = {}
16
+ if a.any? || block_given?
17
+ merge(*a, &b)
18
+ end
19
+ end
20
+ attr_writer :rule_list
21
+ def filter string, out
22
+ found = rule_list.detect{ |x| x.match(string) }
23
+ use_state = found ? found.state : :plain
24
+ state = get_state_or_fail(use_state)
25
+ string = string.dup
26
+ while next_state_name = state.process(string, out)
27
+ state = get_state_or_fail(next_state_name)
28
+ end
29
+ nil
30
+ end
31
+ def merge(&block)
32
+ yield(self) if block_given?
33
+ self
34
+ end
35
+ def spawn(*a, &b)
36
+ other = dup
37
+ other.merge(*a, &b)
38
+ other
39
+ end
40
+ attr_writer :state_set
41
+ attr_accessor :stylesheet
42
+ def stylesheet_merge other
43
+ @stylesheet.merge!(other)
44
+ end
45
+
46
+ #
47
+ # api private classes:
48
+ #
49
+
50
+ module RuleList
51
+ def rule_list_init
52
+ @rule_list = []
53
+ @state_set = {}
54
+ end
55
+ def add_regex_rule re, opts
56
+ fail("no") unless opts[:state]
57
+ @rule_list.push RegexRule.make(re, opts[:state], {})
58
+ end
59
+ def add_regex_rule_neg re, opts
60
+ fail("no") unless opts[:state]
61
+ @rule_list.push RegexRule.make(re, opts[:state], {:neg=>true})
62
+ end
63
+ def define_state name, &block
64
+ state = State.new(self, name, &block)
65
+ fail("no") if @state_set.key?(state.name)
66
+ @state_set[state.name] = state
67
+ end
68
+ def get_state_or_fail name
69
+ state = @state_set[name]
70
+ state or fail("no such state: #{name.inspect}")
71
+ state
72
+ end
73
+ attr_reader :rule_list
74
+ def when(re_or_symbol, opts=nil, &block)
75
+ if re_or_symbol.kind_of?(Regexp) && Hash===opts && ! block_given?
76
+ add_regex_rule(re_or_symbol, opts)
77
+ elsif re_or_symbol.kind_of?(Symbol) && opts.nil? && block_given?
78
+ define_state(re_or_symbol, &block)
79
+ else
80
+ fail("unrecongized signature: `#{self.class}#when("<<
81
+ "[#{re_or_symbol.class}],[#{opts.class}],[#{block.class}])")
82
+ end
83
+ end
84
+ def when_not re, opts
85
+ add_regex_rule_neg re, opts
86
+ end
87
+ attr_reader :state_set
88
+ end
89
+ class RegexRule
90
+ class << self
91
+ def make regex, state, opts
92
+ if opts[:neg]
93
+ RegexRuleNeg.new(regex, state)
94
+ else
95
+ RegexRule.new(regex, state)
96
+ end
97
+ end
98
+ end
99
+ def initialize regex, state
100
+ @regex = regex
101
+ @state = state or fail('no')
102
+ end
103
+ attr_reader :regex, :state
104
+ def match str
105
+ @regex =~ str
106
+ end
107
+ end
108
+ class RegexRuleNeg < RegexRule
109
+ def match str
110
+ ! super
111
+ end
112
+ end
113
+ class State
114
+ include RuleList, Treebis::Colorize, NanDoc::SecretParent
115
+ def initialize parent, name, &block
116
+ self.parent = parent
117
+ rule_list_init
118
+ fail('no') unless Symbol === name
119
+ @name = name
120
+ @style = nil
121
+ @trailing_whitespace_style = nil
122
+ block.call(self)
123
+ end
124
+ attr_accessor :name
125
+ def next_line str, alter=false
126
+ res = false
127
+ if str == ''
128
+ nil
129
+ elsif alter
130
+ if /\A([^\n]+)(?:\n?)(.*)\Z/m =~ str
131
+ res = $1
132
+ str.replace($2)
133
+ else
134
+ fail("fail: #{str.inspect}")
135
+ end
136
+ else
137
+ if /\A([^\n]+)/ =~ str
138
+ res = $1
139
+ else
140
+ fail("fail: #{str.inspect}")
141
+ end
142
+ end
143
+ res
144
+ end
145
+ def process string, out
146
+ ret = nil
147
+ while line = next_line(string)
148
+ if other = rule_list.detect{|x| x.match(string)}
149
+ next_state_name = other.state
150
+ ret = next_state_name
151
+ break
152
+ else
153
+ next_line(string, true) # alter string
154
+ use_line = nil
155
+ if @trailing_whitespace_style
156
+ /\A(|.*[^[:space:]])([[:space:]]*)\Z/ =~ line or fail('oops')
157
+ head, tail = $1, $2
158
+ colored_head = colorize(head, *colors)
159
+ use_line = colored_head.dup
160
+ unless tail.empty?
161
+ ws_style = parent.stylesheet[@trailing_whitespace_style] or
162
+ style_not_found_failure('@trailing_whitespace_style')
163
+ colored_tail = colorize(tail, *ws_style)
164
+ use_line.concat colored_tail
165
+ end
166
+ else
167
+ use_line = colorize(line, *colors)
168
+ end
169
+ out.puts use_line
170
+ end
171
+ end
172
+ ret
173
+ end
174
+ def colors
175
+ @colors ||= begin
176
+ if style.nil?
177
+ []
178
+ else
179
+ parent.stylesheet[style] or style_not_found_failure
180
+ end
181
+ end
182
+ end
183
+ def style *a
184
+ case a.size
185
+ when 0; @style
186
+ when 1; @style = a.first
187
+ else fail('no')
188
+ end
189
+ end
190
+ def trailing_whitespace_style *a
191
+ case a.size
192
+ when 0; @trailing_whitespace_style
193
+ when 1; @trailing_whitespace_style = a.first
194
+ else fail('no')
195
+ end
196
+ end
197
+ def style_not_found_failure which = '@style'
198
+ value = instance_variable_get(which)
199
+ fail("#{which} not found: #{value.inspect}")
200
+ end
201
+ end
202
+ end
203
+ end