9 puts "Please install the 'liquid' Ruby gem (available in Debian/Ubuntu as 'ruby-liquid')"
15 layouts_dir: '_layouts',
21 a.start_with?(b) && b.count('/') + 1 == a.count('/')
25 attr_reader :pages, :layouts
44 Pathname.glob(layouts_dir + Pathname('*.html')) do |path|
46 layout = Layout.new(self, path)
48 @layouts[path.basename('.html').to_s] = layout
53 pages_dir.find do |path|
54 if path.file? && path.extname == '.html'
55 page = Page.new(self, path)
63 @pages.each {|page| page.process}
67 unless system("rsync -a --delete --exclude='*~' #{static_dir}/. #{output_dir}")
68 puts "Couldn't copy static files, is rsync installed?"
72 def find_children(url)
73 sorted_pages.select { |p| child_url?(p.url, url) }
76 def toplevel() @toplevel_memo ||= find_children('/') end
77 def sorted_pages() @sorted_pages_memo ||= @pages.sort_by{ |p| p.sort_url } end
79 def pages_dir() @pages_dir_memo ||= Pathname(CONFIG[:pages_dir]) end
80 def layouts_dir() @layouts_dir_memo ||= Pathname(CONFIG[:layouts_dir]) end
81 def static_dir() @static_dir_memo ||= Pathname(CONFIG[:static_dir]) end
82 def output_dir() @output_dir_memo ||= Pathname(CONFIG[:output_dir]) end
86 attr_reader :path, :out_path, :url, :sort_url
88 def initialize(site, path)
92 relative_path = @path.relative_path_from(@site.pages_dir);
93 a = relative_path.each_filename.map do |x|
94 x.sub(/^[0-9]*[-_]/, '')
96 a[-1].sub!(/\.html$/, '')
99 @out_path = @site.output_dir + Pathname(s) + Pathname("index.html")
101 @sort_url = @path.to_s.sub(/\.html$/, '')
105 # should we show p in the index on selfs page?
106 url.start_with?(p.url) || child_url?(url, p.url)
110 @page_context['title'] || ""
114 @page_context['menu_title'] || title
119 frontmatter, @content = split_frontmatter(content) || abort("File not well-formatted: #{@path}")
120 @page_context = YAML.load(frontmatter)
121 @template = Liquid::Template.parse(@content)
124 def split_frontmatter(txt)
125 @split_regex ||= /\A---[ \t\r]*\n(?<frontmatter>.*?)^---[ \t\r]*\n(?<content>.*)\z/m
126 match = @split_regex.match txt
127 match ? [match['frontmatter'], match['content']] : nil
131 @site.layouts[@page_context['layout'] || 'default']
135 @children ||= @site.find_children(@url)
139 registers = {page: self, site: @site}
140 context = {'page' => @page_context}
141 content = @template.render!(context, registers: registers)
142 find_layout.render(context.merge({'content' => content}), registers)
148 path.open('w') { |f| f.write(render) }
153 def render(context, registers)
154 context = context.dup
155 context['page'] = @page_context.merge(context['page'])
156 content = @template.render!(context, registers: registers)
157 if @page_context.has_key?('layout')
158 find_layout.render(context.merge({'content' => content}), registers)
165 class Tag_tree < Liquid::Tag
166 def join(children_html)
167 children_html.empty? ? "" : "<dl>\n" + children_html.join + "</dl>\n"
171 current = context.registers[:page]
172 site = context.registers[:site]
174 format_entry = lambda do |page|
175 children = page.children
177 css = (page == current) ? ' class="active"' : ""
178 children_html = current.related_to?(page) ? join(children.map(&format_entry)) : ""
182 <a href='#{page.url}'>#{page.menu_title}</a>
190 join(site.toplevel.map(&format_entry))
194 class Tag_children < Liquid::Tag
196 children = context.registers[:page].children
197 entries = children.map {|p| "<li><a href='#{p.url}'>#{p.title}</a></li>" }
199 "<div id='subtopics'>
200 <h2>This chapter covers the following topics:</h2>
209 class Tag_prevnext < Liquid::Tag
211 current = context.registers[:page]
212 pages = context.registers[:site].sorted_pages
214 index = pages.index { |page| page == current }
217 link = lambda do |p, cls, txt|
218 "<li><a title='#{p.title}' href='#{p.url}' class='#{cls}'>#{txt}</a></li>"
220 prev_link = index > 0 ? link.call(pages[index-1], "previous", " < Previous ") : ""
221 next_link = index < pages.length-1 ? link.call(pages[index+1], "next", " Next > ") : ""
223 "<ul class='pager'>#{prev_link}#{next_link}</ul>"
229 begin require 'listen'
231 puts "To use the --watch function, please install the 'listen' Ruby gem"
232 puts "(available in Debian/Ubuntu as 'ruby-listen')"
236 listener = Listen.to(CONFIG[:pages_dir], wait_for_delay: 1.0, only: /.html$/) do |modified, added, removed|
245 listener = options[:watch] && start_watcher
246 port = options[:port] || 8000
248 puts "Serving at http://localhost:#{port}/ ..."
249 server = WEBrick::HTTPServer.new :Port => port, :DocumentRoot => CONFIG[:output_dir]
254 listener.stop if listener
259 Liquid::Template.register_tag('tree', Tag_tree)
260 Liquid::Template.register_tag('children', Tag_children)
261 Liquid::Template.register_tag('prevnext', Tag_prevnext)
263 if defined? Liquid::Template.error_mode
264 Liquid::Template.error_mode = :strict
268 OptionParser.new do |opts|
269 opts.banner = %{Usage: build.rb <command> [options]
271 Use 'build.rb' to build the manual. Use 'build.rb serve' to also
272 start a web server; setting any web server options implies "serve".
274 opts.on("-w", "--watch", "Watch for changes") { options[:watch] = true }
275 opts.on("-p", "--port N", Integer, "Specify port for web server") { |p| options[:port] = p }
280 if options[:watch] || options[:port] || (ARGV.length > 0 && "serve".start_with?(ARGV[0]))
281 Server.new.run(options)