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,279 @@
1
+ support = File.expand_path('../../support', __FILE__)
2
+ require support + '/diff-proxy.rb'
3
+ require support + '/path-tardo.rb'
4
+ require support + '/site-methods.rb'
5
+
6
+ module NanDoc::Commands
7
+ class Diff < ::Cri::Command
8
+ NanDoc.persistent_delegate_to(self) # persistent_set(), persistent_get()
9
+ include NanDoc::CliCommandHelpers, NanDoc::PathTardo, NanDoc::SiteMethods
10
+
11
+ def name; 'diff' end
12
+
13
+ def aliases; [ 'd' ] end
14
+
15
+ def short_desc; 'maybe push and pull some stuff (nanDoc hack)' end
16
+
17
+ def long_desc
18
+ <<-LONG_DESC.gsub(/\n +/,' ')
19
+ Patch a subtree of <my-site>/content with the same subtree in
20
+ <my-site>/output with (--content-to-output|-c). (default). Opposite
21
+ direction with -C.
22
+
23
+ Patch a subtree of <prototypes>/<the-prototype> with the same
24
+ subtree of <my-site>/content with (--content-to-prototype|-p).
25
+ (For patching nanDoc prototypes.) Opposite direction with -P.
26
+
27
+ This operates on a subset of the indicated trees, for now either
28
+ the css folders in <my-site>/output/ and <my-site>/content/ or the
29
+ layout folders between the prototype and the <my-site>. Indicate
30
+ which with -s (css|layouts) (default: css)
31
+
32
+ So, with this wierd chain, you can tweak your CSS, for example,
33
+ in the generated output and then push these changes all the way back to
34
+ the prototype with -cY and then -pY
35
+
36
+ Or you can undo your changes pulling all the way back from the prototype
37
+ with -PY and then -CY
38
+ LONG_DESC
39
+ end
40
+
41
+ def usage;
42
+ 'nandoc diff [-c|-C|-p|-P] [-s (css|layouts|js|root)] [-Y [-b]] [<path>]'
43
+ end
44
+
45
+ def option_definitions
46
+ pttp = 'pass-thru to patch. only for use with -Y'
47
+ [ { :long => 'backup', :short => 'b', :argument=>:none,
48
+ :desc => pttp
49
+ },
50
+ { :long => 'content-to-output', :short => 'c', :argument => :none,
51
+ :desc => 'show diff or patch content with output (default)'
52
+ },
53
+ { :long => 'content-to-proto', :short => 'P', :argument => :none,
54
+ :desc => 'show diff or patch content with proto (sure why not)'
55
+ },
56
+ { :long => 'dry-run', :short => 'r', :argument => :none,
57
+ :desc => pttp
58
+ },
59
+ { :long => 'output-to-content', :short => 'C', :argument => :none,
60
+ :desc => 'show diff or patch output with content (kind of weird)'
61
+ },
62
+ { :long => 'patch', :short => 'Y', :argument => :none,
63
+ :desc => 'apply the patch to the target (no undo!)'
64
+ },
65
+ { :long => 'proto-to-content', :short => 'p', :argument => :none,
66
+ :desc => ("show diff or patch prototype with content\n"<<
67
+ (' '*22)+"(this would be for patching/altering nandoc)")
68
+ },
69
+ { :long => 'subset', :short => 's', :argument => :required,
70
+ :desc => "'css' or 'layouts' or 'js' (default: css)"
71
+ }
72
+ ]
73
+ end
74
+
75
+ def run opts, args
76
+ opts = normalize_opts opts
77
+ site_path = deduce_site_path_or_fail(args)
78
+ src, dest = deduce_src_and_dest site_path, opts
79
+ subset = subsets.parse(opts)
80
+ if opts[:patch] # @todo this doesn't belong here probably
81
+ patch_opts = process_patch_opts(opts, src, dest, site_path)
82
+ go_patch src, dest, subset, site_path, patch_opts
83
+ else
84
+ process_diff_opts(opts) and fail("no more opts for this guy")
85
+ go_diff src, dest, subset, site_path
86
+ end
87
+ end
88
+
89
+ private
90
+
91
+ def deduce_root_paths src, dest, site_path
92
+ @diff_method = :diff_root_subset
93
+ path_src = deduce_subfolder_path site_path, src
94
+ path_dst = deduce_subfolder_path site_path, dest
95
+ if path_src == path_dst
96
+ task_abort("source and destination paths are the same. "<<
97
+ "did you forget -p or -P ?\nusage: #{usage}\n"<<
98
+ invite_to_more_command_help)
99
+ end
100
+ [path_src, path_dst]
101
+ end
102
+
103
+ def deduce_subfolder_path site_path, which, sub=nil
104
+ tox =
105
+ case which
106
+ when :output; [site_path, sub ? 'output' : nil, sub]
107
+ when :content; [site_path, sub ? 'content': nil, sub]
108
+ when :proto; [proto_path(site_path), sub ? 'content' : nil, sub]
109
+ else fail(
110
+ "implement me: get #{which.to_s} subpath")
111
+ end
112
+ File.join(tox.compact)
113
+ end
114
+
115
+ def deduce_subfolder_paths sub, src, dest, site_path
116
+ paths = [src, dest].map do |which|
117
+ deduce_subfolder_path site_path, which, sub
118
+ end
119
+ paths
120
+ end
121
+
122
+ def deduce_layout_paths src, dest, site_path
123
+ if [src, dest].index(:output)
124
+ task_abort "Sorry, it doesn't make sense to look at layout " <<
125
+ "in output directory because there is none.\n" <<
126
+ "Please use -P or -p to compare layout btwn proto and <my-site>.\n"<<
127
+ "usage: #{usage}\n#{invite_to_more_command_help}"
128
+ end
129
+ paths = [src, dest].map do |which|
130
+ case which
131
+ when :content; site_path + '/layouts'
132
+ when :proto; proto_path(site_path)+'/layouts'
133
+ else fail(
134
+ "implement me: get #{which.to_s} path from #{src.inspect}")
135
+ end
136
+ end
137
+ paths
138
+ end
139
+
140
+ def deduce_paths src, dest, subset, site_path
141
+ paths = case subset
142
+ when 'css'; deduce_subfolder_paths 'css', src, dest, site_path
143
+ when 'layouts'; deduce_layout_paths src, dest, site_path
144
+ when 'js'; deduce_subfolder_paths 'js', src, dest, site_path
145
+ when 'root'; deduce_root_paths src, dest, site_path
146
+ else; fail("unimplemented subset: #{subset}")
147
+ end
148
+ src_path, dest_path = paths
149
+ assert_path "source path", src_path
150
+ assert_path "destination path", dest_path
151
+ [src_path, dest_path]
152
+ end
153
+
154
+ def deduce_src_and_dest site_path, opts
155
+ flag = exclusive_opt_flags(opts) do
156
+ flags :content_to_output, :content_to_proto,
157
+ :output_to_content, :proto_to_content
158
+ default '-c', :content_to_output
159
+ end
160
+ /\A(.+)_to_(.+)\Z/ =~ flag.to_s or fail("no: #{flag}")
161
+ src, dest = $1.to_sym, $2.to_sym
162
+ [src, dest]
163
+ end
164
+
165
+ def diff_normal src_path, dest_path
166
+ NanDoc::DiffProxy.diff(src_path, dest_path)
167
+ end
168
+
169
+ def diff_root_subset src, dest
170
+ require File.expand_path('../../support/site-diff.rb', __FILE__)
171
+ thing = NanDoc::SiteDiff.new(src, dest)
172
+ thing.get_diff_object
173
+ end
174
+
175
+ def get_diff_object(*a)
176
+ @diff_method = :diff_normal
177
+ src_path, dest_path = deduce_paths(*a)
178
+ diff = case @diff_method # no send() b/c trace stacks look dumb
179
+ when :diff_normal; diff_normal(src_path, dest_path)
180
+ when :diff_root_subset; diff_root_subset(src_path, dest_path)
181
+ end
182
+ if diff.error?
183
+ task_abort diff.error
184
+ end
185
+ diff
186
+ end
187
+
188
+ def go_diff(*a)
189
+ diff = get_diff_object(*a)
190
+ $stderr.puts diff.command
191
+ if $stdout.tty? && NanDoc::Config.colorize?
192
+ diff.colorize($stdout, :styles => NanDoc::Config.diff_stylesheet)
193
+ else
194
+ $stdout.puts diff.to_s
195
+ end
196
+ end
197
+
198
+ def go_patch *a
199
+ patch_opts = a.pop
200
+ pass_thru = patch_opts[:pass_thru] or fail('suxxorz')
201
+ diff = get_diff_object(*a)
202
+
203
+ # Make a tempdir and write the diff to a file
204
+ tmpdir = empty_tmpdir('for-a-patch')
205
+ Treebis::Task.new do
206
+ notice 'command', diff.command
207
+ write 'diff', diff.to_s
208
+ end.on(tmpdir).run
209
+
210
+ # Patch this sucker and pray we didn't mess up too badly
211
+ Treebis::Task.new do
212
+ from tmpdir
213
+ apply 'diff', pass_thru
214
+ end.on('.').run
215
+ end
216
+
217
+ def subsets
218
+ cmd = self
219
+ @subsets ||= OptEnum.new do |oe|
220
+ command cmd
221
+ name :subset
222
+ values %w(css layouts js root)
223
+ default 'css'
224
+ end
225
+ end
226
+
227
+ def proto_path site_path
228
+ config = parse_config_for_site_path site_path
229
+ result = path_tardo(config, 'data_sources/[0]/site_prototype')
230
+ if result.found?
231
+ thing_in_config = result.value
232
+ full_path = "proto/#{thing_in_config}"
233
+ full_path
234
+ else
235
+ task_abort(
236
+ result.error_message + " in " + config_path_for_site_path(site_path)
237
+ )
238
+ end
239
+ end
240
+
241
+ PatchPassThru = [:backup, :dry_run, :directory]
242
+ def process_diff_opts opts
243
+ if (bad = opts.keys & PatchPassThru).any?
244
+ bads = bad.map{|x| unnormalize_opt_key(x)}.join('and')
245
+ task_abort "#{bads} cannot be used with diffing only patching.\n"<<
246
+ "usage: #{usage}\n#{invite_to_more_command_help}"
247
+ end
248
+ end
249
+
250
+ def process_patch_opts opts, src, dest, site_path
251
+ keys = opts.keys & PatchPassThru
252
+ switches = keys.map{|k| unnormalize_opt_key(k)}
253
+ switch_h = Hash[switches.zip(Array.new(switches.size, ''))]
254
+ switch_h['--posix'] = '' # always on else patches don't work
255
+ process_patch_opts_for_directory switch_h, opts, src, dest, site_path
256
+ {:pass_thru => switch_h }
257
+ end
258
+
259
+ #
260
+ # when applying a root-based patch to a prototype we will have to change
261
+ # directory to that prototype, just for when we apply the patch. and..?
262
+ #
263
+ def process_patch_opts_for_directory switch_h, opts, src, dest, site_path
264
+ return unless opts[:subset] == 'root'
265
+ if ! opts[:proto_to_content]
266
+ task_abort("we need to test this for root patching for this target")
267
+ end
268
+ # it's not explicitly an available pass-thru option
269
+ fail("huh?") if switch_h['--directory'] ||
270
+ switch_h.keys.grep(/^-p\d+$/).any?
271
+ proto = proto_path(site_path)
272
+ # for a path like './a/Rules', we cd to the root of the proto, so we
273
+ # want to disregard two levels of path context.
274
+ switch_h['-p2'] = ''
275
+ switch_h['--directory'] = proto
276
+ nil
277
+ end
278
+ end
279
+ end
@@ -0,0 +1,58 @@
1
+ module NanDoc
2
+ module Config
3
+ extend self
4
+
5
+ #
6
+ # this name etc etc
7
+ #
8
+
9
+ @orphan_surrogate_filename = Root + '/proto/misc/orphan-surrogate.md'
10
+ attr_accessor :orphan_surrogate_filename
11
+
12
+
13
+ #
14
+ # in the future make this smarter. i don't like now nanoc handles color
15
+ # (a command line argument?) There should be an autodetect, and/or set
16
+ # last setting in sticky json file; or however it is it is done. @todo
17
+ #
18
+ def colorize?
19
+ true
20
+ end
21
+
22
+ #
23
+ # everybody wants to look like git
24
+ #
25
+ def diff_stylesheet
26
+ @diff_stylesheet ||= {
27
+ :header => [:bold, :yellow],
28
+ :add => [:bold, :green],
29
+ :remove => [:bold, :red],
30
+ :range => [:bold, :magenta]
31
+ }
32
+ end
33
+
34
+ #
35
+ # some FileUtils actions are wrapped with this proxy to allow
36
+ # formatting and customizations from the typical FileUtils actions,
37
+ # to indent and colorize the notice stream sorta like nanoc does
38
+ #
39
+
40
+ def file_utils
41
+ @file_utils ||= begin
42
+ Treebis::FileUtilsProxy.new do |fu|
43
+ fu.pretty!
44
+ fu.color?{ NanDoc::Config.colorize? }
45
+ fu.prefix = ' ' * 6 # like nanoc
46
+ fu.ui = proc{ $stdout }
47
+ # it's normally $stderr, it needs to be reference-like
48
+ # so that capture3 will work!
49
+ end
50
+ end
51
+ end
52
+ module Accessors
53
+ def file_utils
54
+ NanDoc::Config.file_utils
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,13 @@
1
+ module Cri
2
+ class Base
3
+ # hack
4
+ include NanDoc::TaskCommon
5
+ def remove_command command_class
6
+ if idx = @commands.index{|x| x.kind_of?(command_class) }
7
+ @commands.delete_at(idx)
8
+ else
9
+ task_abort("command not found of class #{command_class}")
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,239 @@
1
+ module NanDoc
2
+
3
+ class DataSource < ::Nanoc3::DataSources::FilesystemUnified
4
+ #
5
+ # Make a Nanoc3 DataSource class as necessary to A) pull in
6
+ # content files that exist outside of the <my-site>/content directory and
7
+ # B) allow one of those files to act as the root index page for the
8
+ # generated site, as indicated by config.yaml. and C) rescue orphan
9
+ # files that have a parent directory but no parent file, and
10
+ # D) to see as text files without an extension (e.g. README)
11
+ #
12
+ # Some of the hacks in this file are the worst in the whole project. @todo
13
+ #
14
+
15
+
16
+ include NanDoc::TaskCommon # task_abort()
17
+
18
+ def initialize *a
19
+ super(*a)
20
+
21
+ # hack to see as text files without an extension!
22
+ unless @site.config[:text_extensions].include?(nil)
23
+ @site.config[:text_extensions].unshift(nil)
24
+ end
25
+ @hax_filename_for_last = nil
26
+ @config = a.last
27
+ @basenames = @config[:source_file_basenames] or
28
+ fail("must have source_file_basenames in config.yaml "<<
29
+ "for nandoc to work."
30
+ )
31
+ @hax_mode = false # this gets turned on when we are doing s/thing weird
32
+ @hax_root_found = false
33
+ end
34
+
35
+ #
36
+ # the content filename for ../README is ../README
37
+ #
38
+ def filename_for(base_filename, ext)
39
+ return super unless @hax_mode && ext.nil?
40
+ if @hax_filename_for_last != base_filename
41
+ @hax_filename_for_last = base_filename
42
+ return super
43
+ else
44
+ base_filename
45
+ end
46
+ end
47
+
48
+
49
+ #
50
+ # Hack the items returned by this datasource object to include also
51
+ # files outside of the <my-site>/content directory, e.g. README.md
52
+ # or README/**/*, NEWS.md, based on settings in the config.
53
+ #
54
+ # Rescue orphan nodes with no parent page somehow.
55
+ #
56
+ def items
57
+ _ = Nanoc3::Item # autoload it now for easier step debug-ging
58
+ these = super
59
+ dot_dot_names = @basenames.map{|x| "../#{x}"}
60
+ @hax_mode = true
61
+ additional = dot_dot_names.map do |basename|
62
+ if File.file?(basename) # was different
63
+ load_objects(basename, 'item', Nanoc3::Item)
64
+ else
65
+ load_objects(basename, 'item', Nanoc3::Item)
66
+ end
67
+ end.compact.flatten(1)
68
+ @hax_mode = false
69
+ error_for_no_files(dot_dot_names) if additional.empty?
70
+ res = these + additional
71
+ orphan_rescue(res)
72
+ res
73
+ end
74
+
75
+ private
76
+
77
+ #
78
+ # a hook to grab the folder name that later gets stripped out
79
+ # also a supremely ugly hack to get e.g. ../README.md
80
+ #
81
+ def all_split_files_in dir_name
82
+ return super unless @hax_mode
83
+ @hax_last_dirname = dir_name
84
+ ret = super
85
+ lone_files = ["#{dir_name}.md", dir_name]
86
+ lone_file = find_first_file(lone_files)
87
+ if lone_file # e.g. '../README.md', 'README'
88
+ @hax_last_filename = lone_file.dup
89
+ class << lone_file
90
+ #
91
+ # supremely fragile hack:
92
+ # make it so that it ignores the next + operation in the first
93
+ # line of Nanoc3::DataSources::Filesystem#all_split_files_in()
94
+ # this is shorter and easier than overriding and rewring
95
+ # the above function but it is a supreme haxie guaranteed to fail
96
+ # one day!!!
97
+ #
98
+ def + _; self end
99
+ end
100
+ other = super(lone_file)
101
+ ret.merge!(other)
102
+ end
103
+ ret
104
+ end
105
+
106
+ # removed old deal_with_index_page_children in e7bf7ee
107
+
108
+ #
109
+ # We crap out if we didn't find any weird files because after all
110
+ # this is NanDoc.
111
+ #
112
+ def error_for_no_files files
113
+ task_abort <<-HERE.gsub(/\n +/,"\n").strip
114
+ No matching content file(s) found at or under (#{files.join(', ')})
115
+ from here. (This corresponds to the 'source_file_basenames' setting in
116
+ config.yaml.) Did you generate the NanDoc site in the right directory?
117
+ HERE
118
+ end
119
+
120
+ def find_first_file names
121
+ names.detect{ |n| File.file?(n) }
122
+ end
123
+
124
+ #
125
+ # We don't seem to want any urls with uppercase characters in them
126
+ # because .. not sure. But the rsync by default downcases our files.
127
+ # It's probably a good habbit to do this. If we want to accept a variety
128
+ # of casing with our server that's ok but internally we should keep it consistent
129
+ # and simple. (This causes gotchas sometimes when moving from a case-insensitive
130
+ # filesystem like that of OSX to a case-sensitive one like that of debian.)
131
+ # The titles of items, on the other hand ...
132
+ #
133
+ def identifier_normalize identifier
134
+ if /[A-Z]/ =~ identifier
135
+ use_identifier = identifier.downcase
136
+ else
137
+ use_identifier = identifier
138
+ end
139
+ use_identifier
140
+ end
141
+
142
+ #
143
+ # more crazy hacks - normally content/foo/bar.html => "/foo/bar/" but
144
+ # for this case we don't want to have stripped the containing folder,
145
+ # *and* we try to hack it so that it's laid alongside content in
146
+ # the content folder for the final site. (or not, here)
147
+ # also we need to make sure one item is '/' somehow
148
+ # @todo unhack this whole page
149
+ #
150
+ # @todo some stuff that happens in the orphanage should happen here
151
+ # instead
152
+ #
153
+ def identifier_for_filename fn
154
+ return super unless @hax_mode
155
+ if 'md' == fn # there has to be a better way :(
156
+ no_dot_dot = dot_dot_strip_assert(@hax_last_filename)
157
+ if no_dot_dot == @config[:use_as_main_index] # 'README.md'
158
+ identifier = '/' # overwrite the index.html generated by nandoc!
159
+ else
160
+ # '../README.md' => 'README.md' => '/README/'
161
+ identifier = super(no_dot_dot)
162
+ end
163
+ else
164
+ if fn
165
+ if dot_dot_has?(fn)
166
+ fail("fix this -- should never have dot dot name here: #{hn}")
167
+ end
168
+ identifier = super(fn)
169
+ else
170
+ identifier = dot_dot_strip_assert(@hax_last_dirname)+'/'
171
+ end
172
+ end
173
+ # before we get to resuce orphans we need to make sure we have
174
+ # resolved some file as a site root. First one wins.
175
+ if ! @hax_root_found
176
+ shorter = slash_strip_assert(identifier)
177
+ if @basenames.include?(shorter)
178
+ @hax_root_found = true
179
+ identifier = '/'
180
+ end
181
+ end
182
+ use_identifier = identifier_normalize(identifier)
183
+ use_identifier
184
+ end
185
+
186
+ #
187
+ # A) Experimentally generate index pages for child nodes without them
188
+ # B) merge many filesystem roots to one docroot (hack!) per 'basenames'
189
+ # (undefined on name collision)
190
+ #
191
+ def orphan_rescue items
192
+ require File.expand_path('../support/orphanage.rb', __FILE__)
193
+ Orphanage.rescue_orphans(@config, items)
194
+ end
195
+
196
+ private
197
+
198
+ module ItemMethods
199
+ # must have @items. make public if u need it
200
+
201
+ def find_parent item_identifier
202
+ parent_path = parent_identifier(item_identifier)
203
+ parent = @items.find { |p| p.identifier == parent_path }
204
+ parent
205
+ end
206
+ def identifier_bare_rootname identifier
207
+ /\A\/([^\/]+)\// =~ identifier and $1
208
+ end
209
+ def identifier_bare_rootname_assert identifier
210
+ identifier_bare_rootname(identifier) or
211
+ fail("hack fail: couldn't find rootname for #{identifier.inspect}")
212
+ end
213
+ # exactly one leading and one trailing slash
214
+ def slash_strip identifier
215
+ /\A\/(.+)\/\Z/ =~ identifier and $1
216
+ end
217
+ def slash_strip_assert identifier
218
+ slash_strip(identifier) or fail("hack fail: #{identifier}")
219
+ end
220
+ def parent_identifier identifier
221
+ identifier.sub(/[^\/]+\/$/, '')
222
+ end
223
+ def site_root
224
+ @site_root ||= @items.find{|x| x.identifier == '/' }
225
+ end
226
+ def dot_dot_has? str
227
+ /\A\.\./ =~ str
228
+ end
229
+ def dot_dot_strip str
230
+ /\A\.\.(.*)\Z/ =~ str and $1
231
+ end
232
+ def dot_dot_strip_assert str
233
+ dot_dot_strip(str) or
234
+ fail("hack fail: no leading dot dot: #{str.inspect}")
235
+ end
236
+ end
237
+ include ItemMethods
238
+ end
239
+ end