9 layouts_dir: '_layouts',
11 output_dir: '_site' # will get wiped!
15 a.start_with?(b) && b.count('/') + 1 == a.count('/')
19 attr_reader :pages, :layouts
38 Pathname.glob(layouts_dir + Pathname('*.html')) do |path|
40 layout = Layout.new(self, path)
42 @layouts[path.basename('.html').to_s] = layout
47 pages_dir.find do |path|
48 if path.file? && path.extname == '.html'
49 page = Page.new(self, path)
57 @pages.each {|page| page.process}
61 `rsync -a --delete --exclude='*~' #{static_dir}/. #{output_dir}`
64 def find_children(url)
65 sorted_pages.select { |p| child_url?(p.url, url) }
68 def toplevel() @toplevel_memo ||= find_children('/') end
69 def sorted_pages() @sorted_pages_memo ||= @pages.sort_by{ |p| p.sort_url } end
71 def pages_dir() @pages_dir_memo ||= Pathname(CONFIG[:pages_dir]) end
72 def layouts_dir() @layouts_dir_memo ||= Pathname(CONFIG[:layouts_dir]) end
73 def static_dir() @static_dir_memo ||= Pathname(CONFIG[:static_dir]) end
74 def output_dir() @output_dir_memo ||= Pathname(CONFIG[:output_dir]) end
78 attr_reader :path, :out_path, :url, :sort_url
80 def initialize(site, path)
84 relative_path = @path.relative_path_from(@site.pages_dir);
85 a = relative_path.each_filename.map do |x|
86 x.sub(/^[0-9]*[-_]/, '')
88 a[-1].sub!(/\.html$/, '')
91 @out_path = @site.output_dir + Pathname(s) + Pathname("index.html")
93 @sort_url = @path.to_s.sub(/\.html$/, '')
97 # should we show p in the index on selfs page?
98 url.start_with?(p.url) || child_url?(url, p.url)
102 @page_context['title'] || ""
106 @page_context['menu_title'] || title
111 frontmatter, @content = split_frontmatter(content) || abort("File not well-formatted: #{@path}")
112 @page_context = YAML.load(frontmatter)
113 @template = Liquid::Template.parse(@content)
116 def split_frontmatter(txt)
117 @split_regex ||= /\A---[ \t\r]*\n(?<frontmatter>.*?)^---[ \t\r]*\n(?<content>.*)\z/m
118 match = @split_regex.match txt
119 match ? [match['frontmatter'], match['content']] : nil
123 @site.layouts[@page_context['layout'] || 'default']
127 @children ||= @site.find_children(@url)
131 registers = {page: self, site: @site}
132 context = {'page' => @page_context}
133 content = @template.render!(context, registers: registers)
134 find_layout.render(context.merge({'content' => content}), registers)
140 path.open('w') { |f| f.write(render) }
145 def render(context, registers)
146 context = context.dup
147 context['page'] = @page_context.merge(context['page'])
148 content = @template.render!(context, registers: registers)
149 if @page_context.has_key?('layout')
150 find_layout.render(context.merge({'content' => content}), registers)
157 class Tag_tree < Liquid::Tag
158 def join(children_html)
159 children_html.empty? ? "" : "<dl>\n" + children_html.join + "</dl>\n"
163 current = context.registers[:page]
164 site = context.registers[:site]
166 format_entry = lambda do |page|
167 children = page.children
169 css = (page == current) ? ' class="active"' : ""
170 children_html = current.related_to?(page) ? join(children.map(&format_entry)) : ""
174 <a href='#{page.url}'>#{page.menu_title}</a>
182 join(site.toplevel.map(&format_entry))
186 class Tag_children < Liquid::Tag
188 children = context.registers[:page].children
189 entries = children.map {|p| "<li><a href='#{p.url}'>#{p.title}</a></li>" }
191 "<div id='subtopics'>
192 <h2>This chapter covers the following topics:</h2>
201 class Tag_prevnext < Liquid::Tag
203 current = context.registers[:page]
204 pages = context.registers[:site].sorted_pages
206 index = pages.index { |page| page == current }
209 link = lambda do |p, cls, txt|
210 "<li><a title='#{p.title}' href='#{p.url}' class='#{cls}'>#{txt}</a></li>"
212 prev_link = index > 0 ? link.call(pages[index-1], "previous", " < Previous ") : ""
213 next_link = index < pages.length-1 ? link.call(pages[index+1], "next", " Next > ") : ""
215 "<ul class='pager'>#{prev_link}#{next_link}</ul>"
223 listener = Listen.to(CONFIG[:pages_dir], wait_for_delay: 1.0, only: /.html$/) do |modified, added, removed|
232 listener = options[:watch] && start_watcher
234 puts "Serving at http://localhost:8000/ ..."
235 server = WEBrick::HTTPServer.new :Port => 8000, :DocumentRoot => CONFIG[:output_dir]
240 listener.stop if listener
245 Liquid::Template.register_tag('tree', Tag_tree)
246 Liquid::Template.register_tag('children', Tag_children)
247 Liquid::Template.register_tag('prevnext', Tag_prevnext)
249 if defined? Liquid::Template.error_mode
250 Liquid::Template.error_mode = :strict
254 OptionParser.new do |opts|
255 opts.on("--watch", "Watch for changes") { options[:watch] = true }
261 Server.new.run(options)