nandoc 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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