monad 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. data/CONTRIBUTING.md +68 -0
  2. data/Gemfile +2 -0
  3. data/LICENSE +21 -0
  4. data/README.md +88 -0
  5. data/Rakefile +136 -0
  6. data/bin/monad +102 -0
  7. data/cucumber.yml +3 -0
  8. data/features/create_sites.feature +112 -0
  9. data/features/data_sources.feature +76 -0
  10. data/features/drafts.feature +25 -0
  11. data/features/embed_filters.feature +60 -0
  12. data/features/markdown.feature +30 -0
  13. data/features/pagination.feature +54 -0
  14. data/features/permalinks.feature +65 -0
  15. data/features/post_data.feature +214 -0
  16. data/features/site_configuration.feature +206 -0
  17. data/features/site_data.feature +101 -0
  18. data/features/step_definitions/monad_steps.rb +175 -0
  19. data/features/support/env.rb +25 -0
  20. data/lib/monad.rb +90 -0
  21. data/lib/monad/command.rb +27 -0
  22. data/lib/monad/commands/build.rb +64 -0
  23. data/lib/monad/commands/doctor.rb +29 -0
  24. data/lib/monad/commands/new.rb +50 -0
  25. data/lib/monad/commands/serve.rb +33 -0
  26. data/lib/monad/configuration.rb +183 -0
  27. data/lib/monad/converter.rb +48 -0
  28. data/lib/monad/converters/identity.rb +21 -0
  29. data/lib/monad/converters/markdown.rb +43 -0
  30. data/lib/monad/converters/markdown/kramdown_parser.rb +44 -0
  31. data/lib/monad/converters/markdown/maruku_parser.rb +47 -0
  32. data/lib/monad/converters/markdown/rdiscount_parser.rb +35 -0
  33. data/lib/monad/converters/markdown/redcarpet_parser.rb +70 -0
  34. data/lib/monad/converters/textile.rb +50 -0
  35. data/lib/monad/convertible.rb +152 -0
  36. data/lib/monad/core_ext.rb +68 -0
  37. data/lib/monad/deprecator.rb +32 -0
  38. data/lib/monad/draft.rb +35 -0
  39. data/lib/monad/drivers/json_driver.rb +39 -0
  40. data/lib/monad/drivers/yaml_driver.rb +23 -0
  41. data/lib/monad/errors.rb +4 -0
  42. data/lib/monad/filters.rb +154 -0
  43. data/lib/monad/generator.rb +4 -0
  44. data/lib/monad/generators/pagination.rb +143 -0
  45. data/lib/monad/layout.rb +42 -0
  46. data/lib/monad/logger.rb +54 -0
  47. data/lib/monad/mime.types +85 -0
  48. data/lib/monad/page.rb +163 -0
  49. data/lib/monad/plugin.rb +75 -0
  50. data/lib/monad/post.rb +377 -0
  51. data/lib/monad/site.rb +455 -0
  52. data/lib/monad/static_file.rb +70 -0
  53. data/lib/monad/tags/gist.rb +30 -0
  54. data/lib/monad/tags/highlight.rb +85 -0
  55. data/lib/monad/tags/include.rb +37 -0
  56. data/lib/monad/tags/post_url.rb +61 -0
  57. data/lib/site_template/.gitignore +1 -0
  58. data/lib/site_template/_config.yml +2 -0
  59. data/lib/site_template/_layouts/default.html +46 -0
  60. data/lib/site_template/_layouts/post.html +9 -0
  61. data/lib/site_template/_posts/0000-00-00-welcome-to-monad.markdown.erb +24 -0
  62. data/lib/site_template/css/main.css +165 -0
  63. data/lib/site_template/css/syntax.css +60 -0
  64. data/lib/site_template/index.html +13 -0
  65. data/monad.gemspec +197 -0
  66. data/script/bootstrap +2 -0
  67. data/test/fixtures/broken_front_matter1.erb +5 -0
  68. data/test/fixtures/broken_front_matter2.erb +4 -0
  69. data/test/fixtures/broken_front_matter3.erb +7 -0
  70. data/test/fixtures/exploit_front_matter.erb +4 -0
  71. data/test/fixtures/front_matter.erb +4 -0
  72. data/test/fixtures/members.yaml +7 -0
  73. data/test/helper.rb +62 -0
  74. data/test/source/.htaccess +8 -0
  75. data/test/source/_includes/sig.markdown +3 -0
  76. data/test/source/_layouts/default.html +27 -0
  77. data/test/source/_layouts/simple.html +1 -0
  78. data/test/source/_plugins/dummy.rb +8 -0
  79. data/test/source/_posts/2008-02-02-not-published.textile +8 -0
  80. data/test/source/_posts/2008-02-02-published.textile +8 -0
  81. data/test/source/_posts/2008-10-18-foo-bar.textile +8 -0
  82. data/test/source/_posts/2008-11-21-complex.textile +8 -0
  83. data/test/source/_posts/2008-12-03-permalinked-post.textile +9 -0
  84. data/test/source/_posts/2008-12-13-include.markdown +8 -0
  85. data/test/source/_posts/2009-01-27-array-categories.textile +10 -0
  86. data/test/source/_posts/2009-01-27-categories.textile +7 -0
  87. data/test/source/_posts/2009-01-27-category.textile +7 -0
  88. data/test/source/_posts/2009-01-27-empty-categories.textile +7 -0
  89. data/test/source/_posts/2009-01-27-empty-category.textile +7 -0
  90. data/test/source/_posts/2009-03-12-hash-#1.markdown +6 -0
  91. data/test/source/_posts/2009-05-18-empty-tag.textile +6 -0
  92. data/test/source/_posts/2009-05-18-empty-tags.textile +6 -0
  93. data/test/source/_posts/2009-05-18-tag.textile +6 -0
  94. data/test/source/_posts/2009-05-18-tags.textile +9 -0
  95. data/test/source/_posts/2009-06-22-empty-yaml.textile +3 -0
  96. data/test/source/_posts/2009-06-22-no-yaml.textile +1 -0
  97. data/test/source/_posts/2010-01-08-triple-dash.markdown +5 -0
  98. data/test/source/_posts/2010-01-09-date-override.textile +7 -0
  99. data/test/source/_posts/2010-01-09-time-override.textile +7 -0
  100. data/test/source/_posts/2010-01-09-timezone-override.textile +7 -0
  101. data/test/source/_posts/2010-01-16-override-data.textile +4 -0
  102. data/test/source/_posts/2011-04-12-md-extension.md +7 -0
  103. data/test/source/_posts/2011-04-12-text-extension.text +0 -0
  104. data/test/source/_posts/2013-01-02-post-excerpt.markdown +14 -0
  105. data/test/source/_posts/2013-01-12-nil-layout.textile +6 -0
  106. data/test/source/_posts/2013-01-12-no-layout.textile +5 -0
  107. data/test/source/_posts/2013-03-19-not-a-post.markdown/.gitkeep +0 -0
  108. data/test/source/_posts/2013-04-11-custom-excerpt.markdown +10 -0
  109. data/test/source/_posts/2013-05-10-number-category.textile +7 -0
  110. data/test/source/_posts/es/2008-11-21-nested.textile +8 -0
  111. data/test/source/about.html +6 -0
  112. data/test/source/category/_posts/2008-9-23-categories.textile +6 -0
  113. data/test/source/contacts.html +5 -0
  114. data/test/source/contacts/bar.html +5 -0
  115. data/test/source/contacts/index.html +5 -0
  116. data/test/source/css/screen.css +76 -0
  117. data/test/source/deal.with.dots.html +7 -0
  118. data/test/source/foo/_posts/bar/2008-12-12-topical-post.textile +8 -0
  119. data/test/source/index.html +22 -0
  120. data/test/source/sitemap.xml +32 -0
  121. data/test/source/symlink-test/symlinked-file +22 -0
  122. data/test/source/win/_posts/2009-05-24-yaml-linebreak.markdown +7 -0
  123. data/test/source/z_category/_posts/2008-9-23-categories.textile +6 -0
  124. data/test/suite.rb +11 -0
  125. data/test/test_command.rb +39 -0
  126. data/test/test_configuration.rb +137 -0
  127. data/test/test_convertible.rb +51 -0
  128. data/test/test_core_ext.rb +88 -0
  129. data/test/test_filters.rb +102 -0
  130. data/test/test_generated_site.rb +83 -0
  131. data/test/test_json_driver.rb +63 -0
  132. data/test/test_kramdown.rb +35 -0
  133. data/test/test_new_command.rb +104 -0
  134. data/test/test_page.rb +193 -0
  135. data/test/test_pager.rb +115 -0
  136. data/test/test_post.rb +573 -0
  137. data/test/test_rdiscount.rb +22 -0
  138. data/test/test_redcarpet.rb +61 -0
  139. data/test/test_redcloth.rb +86 -0
  140. data/test/test_site.rb +374 -0
  141. data/test/test_tags.rb +310 -0
  142. data/test/test_yaml_driver.rb +35 -0
  143. metadata +554 -0
@@ -0,0 +1,48 @@
1
+ module Monad
2
+ class Converter < Plugin
3
+ # Public: Get or set the pygments prefix. When an argument is specified,
4
+ # the prefix will be set. If no argument is specified, the current prefix
5
+ # will be returned.
6
+ #
7
+ # pygments_prefix - The String prefix (default: nil).
8
+ #
9
+ # Returns the String prefix.
10
+ def self.pygments_prefix(pygments_prefix = nil)
11
+ @pygments_prefix = pygments_prefix if pygments_prefix
12
+ @pygments_prefix
13
+ end
14
+
15
+ # Public: Get or set the pygments suffix. When an argument is specified,
16
+ # the suffix will be set. If no argument is specified, the current suffix
17
+ # will be returned.
18
+ #
19
+ # pygments_suffix - The String suffix (default: nil).
20
+ #
21
+ # Returns the String suffix.
22
+ def self.pygments_suffix(pygments_suffix = nil)
23
+ @pygments_suffix = pygments_suffix if pygments_suffix
24
+ @pygments_suffix
25
+ end
26
+
27
+ # Initialize the converter.
28
+ #
29
+ # Returns an initialized Converter.
30
+ def initialize(config = {})
31
+ @config = config
32
+ end
33
+
34
+ # Get the pygments prefix.
35
+ #
36
+ # Returns the String prefix.
37
+ def pygments_prefix
38
+ self.class.pygments_prefix
39
+ end
40
+
41
+ # Get the pygments suffix.
42
+ #
43
+ # Returns the String suffix.
44
+ def pygments_suffix
45
+ self.class.pygments_suffix
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,21 @@
1
+ module Monad
2
+ module Converters
3
+ class Identity < Converter
4
+ safe true
5
+
6
+ priority :lowest
7
+
8
+ def matches(ext)
9
+ true
10
+ end
11
+
12
+ def output_ext(ext)
13
+ ext
14
+ end
15
+
16
+ def convert(content)
17
+ content
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ module Monad
2
+ module Converters
3
+ class Markdown < Converter
4
+ safe true
5
+
6
+ pygments_prefix "\n"
7
+ pygments_suffix "\n"
8
+
9
+ def setup
10
+ return if @setup
11
+ @parser = case @config['markdown']
12
+ when 'redcarpet'
13
+ RedcarpetParser.new @config
14
+ when 'kramdown'
15
+ KramdownParser.new @config
16
+ when 'rdiscount'
17
+ RDiscountParser.new @config
18
+ when 'maruku'
19
+ MarukuParser.new @config
20
+ else
21
+ STDERR.puts "Invalid Markdown processor: #{@config['markdown']}"
22
+ STDERR.puts " Valid options are [ maruku | rdiscount | kramdown | redcarpet ]"
23
+ raise FatalException.new("Invalid Markdown process: #{@config['markdown']}")
24
+ end
25
+ @setup = true
26
+ end
27
+
28
+ def matches(ext)
29
+ rgx = '(' + @config['markdown_ext'].gsub(',','|') +')'
30
+ ext =~ Regexp.new(rgx, Regexp::IGNORECASE)
31
+ end
32
+
33
+ def output_ext(ext)
34
+ ".html"
35
+ end
36
+
37
+ def convert(content)
38
+ setup
39
+ @parser.convert(content)
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,44 @@
1
+ module Monad
2
+ module Converters
3
+ class Markdown
4
+ class KramdownParser
5
+ def initialize(config)
6
+ require 'kramdown'
7
+ @config = config
8
+ rescue LoadError
9
+ STDERR.puts 'You are missing a library required for Markdown. Please run:'
10
+ STDERR.puts ' $ [sudo] gem install kramdown'
11
+ raise FatalException.new("Missing dependency: kramdown")
12
+ end
13
+
14
+ def convert(content)
15
+ # Check for use of coderay
16
+ kramdown_configs = if @config['kramdown']['use_coderay']
17
+ base_kramdown_configs.merge({
18
+ :coderay_wrap => @config['kramdown']['coderay']['coderay_wrap'],
19
+ :coderay_line_numbers => @config['kramdown']['coderay']['coderay_line_numbers'],
20
+ :coderay_line_number_start => @config['kramdown']['coderay']['coderay_line_number_start'],
21
+ :coderay_tab_width => @config['kramdown']['coderay']['coderay_tab_width'],
22
+ :coderay_bold_every => @config['kramdown']['coderay']['coderay_bold_every'],
23
+ :coderay_css => @config['kramdown']['coderay']['coderay_css']
24
+ })
25
+ else
26
+ # not using coderay
27
+ base_kramdown_configs
28
+ end
29
+ Kramdown::Document.new(content, kramdown_configs).to_html
30
+ end
31
+
32
+ def base_kramdown_configs
33
+ {
34
+ :auto_ids => @config['kramdown']['auto_ids'],
35
+ :footnote_nr => @config['kramdown']['footnote_nr'],
36
+ :entity_output => @config['kramdown']['entity_output'],
37
+ :toc_levels => @config['kramdown']['toc_levels'],
38
+ :smart_quotes => @config['kramdown']['smart_quotes']
39
+ }
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,47 @@
1
+ module Monad
2
+ module Converters
3
+ class Markdown
4
+ class MarukuParser
5
+ def initialize(config)
6
+ require 'maruku'
7
+ @config = config
8
+ if @config['maruku']['use_divs']
9
+ load_divs_library
10
+ end
11
+ if @config['maruku']['use_tex']
12
+ load_blahtext_library
13
+ end
14
+ rescue LoadError
15
+ STDERR.puts 'You are missing a library required for Markdown. Please run:'
16
+ STDERR.puts ' $ [sudo] gem install maruku'
17
+ raise FatalException.new("Missing dependency: maruku")
18
+ end
19
+
20
+ def load_divs_library
21
+ require 'maruku/ext/div'
22
+ STDERR.puts 'Maruku: Using extended syntax for div elements.'
23
+ end
24
+
25
+ def load_blahtext_library
26
+ require 'maruku/ext/math'
27
+ STDERR.puts "Maruku: Using LaTeX extension. Images in `#{@config['maruku']['png_dir']}`."
28
+
29
+ # Switch off MathML output
30
+ MaRuKu::Globals[:html_math_output_mathml] = false
31
+ MaRuKu::Globals[:html_math_engine] = 'none'
32
+
33
+ # Turn on math to PNG support with blahtex
34
+ # Resulting PNGs stored in `images/latex`
35
+ MaRuKu::Globals[:html_math_output_png] = true
36
+ MaRuKu::Globals[:html_png_engine] = @config['maruku']['png_engine']
37
+ MaRuKu::Globals[:html_png_dir] = @config['maruku']['png_dir']
38
+ MaRuKu::Globals[:html_png_url] = @config['maruku']['png_url']
39
+ end
40
+
41
+ def convert(content)
42
+ Maruku.new(content).to_html
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,35 @@
1
+ module Monad
2
+ module Converters
3
+ class Markdown
4
+ class RDiscountParser
5
+ def initialize(config)
6
+ require 'rdiscount'
7
+ @config = config
8
+ @rdiscount_extensions = @config['rdiscount']['extensions'].map { |e| e.to_sym }
9
+ rescue LoadError
10
+ STDERR.puts 'You are missing a library required for Markdown. Please run:'
11
+ STDERR.puts ' $ [sudo] gem install rdiscount'
12
+ raise FatalException.new("Missing dependency: rdiscount")
13
+ end
14
+
15
+ def convert(content)
16
+ rd = RDiscount.new(content, *@rdiscount_extensions)
17
+ html = rd.to_html
18
+ if @config['rdiscount']['toc_token']
19
+ html = replace_generated_toc(rd, html, @config['rdiscount']['toc_token'])
20
+ end
21
+ html
22
+ end
23
+
24
+ private
25
+ def replace_generated_toc(rd, html, toc_token)
26
+ if rd.generate_toc && html.include?(toc_token)
27
+ html.gsub(toc_token, rd.toc_content.force_encoding('utf-8'))
28
+ else
29
+ html
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,70 @@
1
+ module Monad
2
+ module Converters
3
+ class Markdown
4
+ class RedcarpetParser
5
+
6
+ module CommonMethods
7
+ def add_code_tags(code, lang)
8
+ code = code.sub(/<pre>/, "<pre><code class=\"#{lang} language-#{lang}\" data-lang=\"#{lang}\">")
9
+ code = code.sub(/<\/pre>/,"</code></pre>")
10
+ end
11
+ end
12
+
13
+ module WithPygments
14
+ include CommonMethods
15
+ def block_code(code, lang)
16
+ require 'pygments'
17
+ lang = lang && lang.split.first || "text"
18
+ output = add_code_tags(
19
+ Pygments.highlight(code, :lexer => lang, :options => { :encoding => 'utf-8' }),
20
+ lang
21
+ )
22
+ end
23
+ end
24
+
25
+ module WithoutPygments
26
+ require 'cgi'
27
+
28
+ include CommonMethods
29
+
30
+ def code_wrap(code)
31
+ "<div class=\"highlight\"><pre>#{CGI::escapeHTML(code)}</pre></div>"
32
+ end
33
+
34
+ def block_code(code, lang)
35
+ lang = lang && lang.split.first || "text"
36
+ output = add_code_tags(code_wrap(code), lang)
37
+ end
38
+ end
39
+
40
+ def initialize(config)
41
+ require 'redcarpet'
42
+ @config = config
43
+ @redcarpet_extensions = {}
44
+ @config['redcarpet']['extensions'].each { |e| @redcarpet_extensions[e.to_sym] = true }
45
+
46
+ @renderer ||= if @config['pygments']
47
+ Class.new(Redcarpet::Render::HTML) do
48
+ include WithPygments
49
+ end
50
+ else
51
+ Class.new(Redcarpet::Render::HTML) do
52
+ include WithoutPygments
53
+ end
54
+ end
55
+ rescue LoadError
56
+ STDERR.puts 'You are missing a library required for Markdown. Please run:'
57
+ STDERR.puts ' $ [sudo] gem install redcarpet'
58
+ raise FatalException.new("Missing dependency: redcarpet")
59
+ end
60
+
61
+ def convert(content)
62
+ @redcarpet_extensions[:fenced_code_blocks] = !@redcarpet_extensions[:no_fenced_code_blocks]
63
+ @renderer.send :include, Redcarpet::Render::SmartyPants if @redcarpet_extensions[:smart]
64
+ markdown = Redcarpet::Markdown.new(@renderer.new(@redcarpet_extensions), @redcarpet_extensions)
65
+ markdown.render(content)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,50 @@
1
+ module Monad
2
+ module Converters
3
+ class Textile < Converter
4
+ safe true
5
+
6
+ pygments_prefix '<notextile>'
7
+ pygments_suffix '</notextile>'
8
+
9
+ def setup
10
+ return if @setup
11
+ require 'redcloth'
12
+ @setup = true
13
+ rescue LoadError
14
+ STDERR.puts 'You are missing a library required for Textile. Please run:'
15
+ STDERR.puts ' $ [sudo] gem install RedCloth'
16
+ raise FatalException.new("Missing dependency: RedCloth")
17
+ end
18
+
19
+ def matches(ext)
20
+ rgx = '(' + @config['textile_ext'].gsub(',','|') +')'
21
+ ext =~ Regexp.new(rgx, Regexp::IGNORECASE)
22
+ end
23
+
24
+ def output_ext(ext)
25
+ ".html"
26
+ end
27
+
28
+ def convert(content)
29
+ setup
30
+
31
+ # Shortcut if config doesn't contain RedCloth section
32
+ return RedCloth.new(content).to_html if @config['redcloth'].nil?
33
+
34
+ # List of attributes defined on RedCloth
35
+ # (from http://redcloth.rubyforge.org/classes/RedCloth/TextileDoc.html)
36
+ attrs = ['filter_classes', 'filter_html', 'filter_ids', 'filter_styles',
37
+ 'hard_breaks', 'lite_mode', 'no_span_caps', 'sanitize_html']
38
+
39
+ r = RedCloth.new(content)
40
+
41
+ # Set attributes in r if they are NOT nil in the config
42
+ attrs.each do |attr|
43
+ r.instance_variable_set("@#{attr}".to_sym, @config['redcloth'][attr]) unless @config['redcloth'][attr].nil?
44
+ end
45
+
46
+ r.to_html
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,152 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'set'
4
+
5
+ # Convertible provides methods for converting a pagelike item
6
+ # from a certain type of markup into actual content
7
+ #
8
+ # Requires
9
+ # self.site -> Monad::Site
10
+ # self.content
11
+ # self.content=
12
+ # self.data=
13
+ # self.ext=
14
+ # self.output=
15
+ # self.name
16
+ module Monad
17
+ module Convertible
18
+ # Returns the contents as a String.
19
+ def to_s
20
+ self.content || ''
21
+ end
22
+
23
+ # Read the YAML frontmatter.
24
+ #
25
+ # base - The String path to the dir containing the file.
26
+ # name - The String filename of the file.
27
+ #
28
+ # Returns nothing.
29
+ def read_yaml(base, name)
30
+ begin
31
+ self.content = File.read(File.join(base, name))
32
+
33
+ if self.content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
34
+ self.content = $POSTMATCH
35
+ self.data = YAML.safe_load($1)
36
+ end
37
+ rescue SyntaxError => e
38
+ puts "YAML Exception reading #{File.join(base, name)}: #{e.message}"
39
+ rescue Exception => e
40
+ puts "Error reading file #{File.join(base, name)}: #{e.message}"
41
+ end
42
+
43
+ self.data ||= {}
44
+ end
45
+
46
+ # Transform the contents based on the content type.
47
+ #
48
+ # Returns nothing.
49
+ def transform
50
+ self.content = converter.convert(self.content)
51
+ end
52
+
53
+ # Determine the extension depending on content_type.
54
+ #
55
+ # Returns the String extension for the output file.
56
+ # e.g. ".html" for an HTML output file.
57
+ def output_ext
58
+ converter.output_ext(self.ext)
59
+ end
60
+
61
+ # Determine which converter to use based on this convertible's
62
+ # extension.
63
+ #
64
+ # Returns the Converter instance.
65
+ def converter
66
+ @converter ||= self.site.converters.find { |c| c.matches(self.ext) }
67
+ end
68
+
69
+ # Render Liquid in the content
70
+ #
71
+ # content - the raw Liquid content to render
72
+ # payload - the payload for Liquid
73
+ # info - the info for Liquid
74
+ #
75
+ # Returns the converted content
76
+ def render_liquid(content, payload, info)
77
+ Liquid::Template.parse(content).render!(payload, info)
78
+ rescue Exception => e
79
+ Monad::Logger.error "Liquid Exception:", "#{e.message} in #{payload[:file]}"
80
+ e.backtrace.each do |backtrace|
81
+ puts backtrace
82
+ end
83
+ abort("Build Failed")
84
+ end
85
+
86
+ # Recursively render layouts
87
+ #
88
+ # layouts - a list of the layouts
89
+ # payload - the payload for Liquid
90
+ # info - the info for Liquid
91
+ #
92
+ # Returns nothing
93
+ def render_all_layouts(layouts, payload, info)
94
+ # recursively render layouts
95
+ layout = layouts[self.data["layout"]]
96
+ used = Set.new([layout])
97
+
98
+ while layout
99
+ payload = payload.deep_merge({"content" => self.output, "page" => layout.data})
100
+
101
+ self.output = self.render_liquid(layout.content,
102
+ payload.merge({:file => self.data["layout"]}),
103
+ info)
104
+
105
+ if layout = layouts[layout.data["layout"]]
106
+ if used.include?(layout)
107
+ layout = nil # avoid recursive chain
108
+ else
109
+ used << layout
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ # Add any necessary layouts to this convertible document.
116
+ #
117
+ # payload - The site payload Hash.
118
+ # layouts - A Hash of {"name" => "layout"}.
119
+ #
120
+ # Returns nothing.
121
+ def do_layout(payload, layouts)
122
+ info = { :filters => [Monad::Filters], :registers => { :site => self.site, :page => payload['page'] } }
123
+
124
+ # render and transform content (this becomes the final content of the object)
125
+ payload["pygments_prefix"] = converter.pygments_prefix
126
+ payload["pygments_suffix"] = converter.pygments_suffix
127
+
128
+ self.content = self.render_liquid(self.content,
129
+ payload.merge({:file => self.name}),
130
+ info)
131
+ self.transform
132
+
133
+ # output keeps track of what will finally be written
134
+ self.output = self.content
135
+
136
+ self.render_all_layouts(layouts, payload, info)
137
+ end
138
+
139
+ # Write the generated page file to the destination directory.
140
+ #
141
+ # dest - The String path to the destination dir.
142
+ #
143
+ # Returns nothing.
144
+ def write(dest)
145
+ path = destination(dest)
146
+ FileUtils.mkdir_p(File.dirname(path))
147
+ File.open(path, 'w') do |f|
148
+ f.write(self.output)
149
+ end
150
+ end
151
+ end
152
+ end