monad 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.
- data/CONTRIBUTING.md +68 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/README.md +88 -0
- data/Rakefile +136 -0
- data/bin/monad +102 -0
- data/cucumber.yml +3 -0
- data/features/create_sites.feature +112 -0
- data/features/data_sources.feature +76 -0
- data/features/drafts.feature +25 -0
- data/features/embed_filters.feature +60 -0
- data/features/markdown.feature +30 -0
- data/features/pagination.feature +54 -0
- data/features/permalinks.feature +65 -0
- data/features/post_data.feature +214 -0
- data/features/site_configuration.feature +206 -0
- data/features/site_data.feature +101 -0
- data/features/step_definitions/monad_steps.rb +175 -0
- data/features/support/env.rb +25 -0
- data/lib/monad.rb +90 -0
- data/lib/monad/command.rb +27 -0
- data/lib/monad/commands/build.rb +64 -0
- data/lib/monad/commands/doctor.rb +29 -0
- data/lib/monad/commands/new.rb +50 -0
- data/lib/monad/commands/serve.rb +33 -0
- data/lib/monad/configuration.rb +183 -0
- data/lib/monad/converter.rb +48 -0
- data/lib/monad/converters/identity.rb +21 -0
- data/lib/monad/converters/markdown.rb +43 -0
- data/lib/monad/converters/markdown/kramdown_parser.rb +44 -0
- data/lib/monad/converters/markdown/maruku_parser.rb +47 -0
- data/lib/monad/converters/markdown/rdiscount_parser.rb +35 -0
- data/lib/monad/converters/markdown/redcarpet_parser.rb +70 -0
- data/lib/monad/converters/textile.rb +50 -0
- data/lib/monad/convertible.rb +152 -0
- data/lib/monad/core_ext.rb +68 -0
- data/lib/monad/deprecator.rb +32 -0
- data/lib/monad/draft.rb +35 -0
- data/lib/monad/drivers/json_driver.rb +39 -0
- data/lib/monad/drivers/yaml_driver.rb +23 -0
- data/lib/monad/errors.rb +4 -0
- data/lib/monad/filters.rb +154 -0
- data/lib/monad/generator.rb +4 -0
- data/lib/monad/generators/pagination.rb +143 -0
- data/lib/monad/layout.rb +42 -0
- data/lib/monad/logger.rb +54 -0
- data/lib/monad/mime.types +85 -0
- data/lib/monad/page.rb +163 -0
- data/lib/monad/plugin.rb +75 -0
- data/lib/monad/post.rb +377 -0
- data/lib/monad/site.rb +455 -0
- data/lib/monad/static_file.rb +70 -0
- data/lib/monad/tags/gist.rb +30 -0
- data/lib/monad/tags/highlight.rb +85 -0
- data/lib/monad/tags/include.rb +37 -0
- data/lib/monad/tags/post_url.rb +61 -0
- data/lib/site_template/.gitignore +1 -0
- data/lib/site_template/_config.yml +2 -0
- data/lib/site_template/_layouts/default.html +46 -0
- data/lib/site_template/_layouts/post.html +9 -0
- data/lib/site_template/_posts/0000-00-00-welcome-to-monad.markdown.erb +24 -0
- data/lib/site_template/css/main.css +165 -0
- data/lib/site_template/css/syntax.css +60 -0
- data/lib/site_template/index.html +13 -0
- data/monad.gemspec +197 -0
- data/script/bootstrap +2 -0
- data/test/fixtures/broken_front_matter1.erb +5 -0
- data/test/fixtures/broken_front_matter2.erb +4 -0
- data/test/fixtures/broken_front_matter3.erb +7 -0
- data/test/fixtures/exploit_front_matter.erb +4 -0
- data/test/fixtures/front_matter.erb +4 -0
- data/test/fixtures/members.yaml +7 -0
- data/test/helper.rb +62 -0
- data/test/source/.htaccess +8 -0
- data/test/source/_includes/sig.markdown +3 -0
- data/test/source/_layouts/default.html +27 -0
- data/test/source/_layouts/simple.html +1 -0
- data/test/source/_plugins/dummy.rb +8 -0
- data/test/source/_posts/2008-02-02-not-published.textile +8 -0
- data/test/source/_posts/2008-02-02-published.textile +8 -0
- data/test/source/_posts/2008-10-18-foo-bar.textile +8 -0
- data/test/source/_posts/2008-11-21-complex.textile +8 -0
- data/test/source/_posts/2008-12-03-permalinked-post.textile +9 -0
- data/test/source/_posts/2008-12-13-include.markdown +8 -0
- data/test/source/_posts/2009-01-27-array-categories.textile +10 -0
- data/test/source/_posts/2009-01-27-categories.textile +7 -0
- data/test/source/_posts/2009-01-27-category.textile +7 -0
- data/test/source/_posts/2009-01-27-empty-categories.textile +7 -0
- data/test/source/_posts/2009-01-27-empty-category.textile +7 -0
- data/test/source/_posts/2009-03-12-hash-#1.markdown +6 -0
- data/test/source/_posts/2009-05-18-empty-tag.textile +6 -0
- data/test/source/_posts/2009-05-18-empty-tags.textile +6 -0
- data/test/source/_posts/2009-05-18-tag.textile +6 -0
- data/test/source/_posts/2009-05-18-tags.textile +9 -0
- data/test/source/_posts/2009-06-22-empty-yaml.textile +3 -0
- data/test/source/_posts/2009-06-22-no-yaml.textile +1 -0
- data/test/source/_posts/2010-01-08-triple-dash.markdown +5 -0
- data/test/source/_posts/2010-01-09-date-override.textile +7 -0
- data/test/source/_posts/2010-01-09-time-override.textile +7 -0
- data/test/source/_posts/2010-01-09-timezone-override.textile +7 -0
- data/test/source/_posts/2010-01-16-override-data.textile +4 -0
- data/test/source/_posts/2011-04-12-md-extension.md +7 -0
- data/test/source/_posts/2011-04-12-text-extension.text +0 -0
- data/test/source/_posts/2013-01-02-post-excerpt.markdown +14 -0
- data/test/source/_posts/2013-01-12-nil-layout.textile +6 -0
- data/test/source/_posts/2013-01-12-no-layout.textile +5 -0
- data/test/source/_posts/2013-03-19-not-a-post.markdown/.gitkeep +0 -0
- data/test/source/_posts/2013-04-11-custom-excerpt.markdown +10 -0
- data/test/source/_posts/2013-05-10-number-category.textile +7 -0
- data/test/source/_posts/es/2008-11-21-nested.textile +8 -0
- data/test/source/about.html +6 -0
- data/test/source/category/_posts/2008-9-23-categories.textile +6 -0
- data/test/source/contacts.html +5 -0
- data/test/source/contacts/bar.html +5 -0
- data/test/source/contacts/index.html +5 -0
- data/test/source/css/screen.css +76 -0
- data/test/source/deal.with.dots.html +7 -0
- data/test/source/foo/_posts/bar/2008-12-12-topical-post.textile +8 -0
- data/test/source/index.html +22 -0
- data/test/source/sitemap.xml +32 -0
- data/test/source/symlink-test/symlinked-file +22 -0
- data/test/source/win/_posts/2009-05-24-yaml-linebreak.markdown +7 -0
- data/test/source/z_category/_posts/2008-9-23-categories.textile +6 -0
- data/test/suite.rb +11 -0
- data/test/test_command.rb +39 -0
- data/test/test_configuration.rb +137 -0
- data/test/test_convertible.rb +51 -0
- data/test/test_core_ext.rb +88 -0
- data/test/test_filters.rb +102 -0
- data/test/test_generated_site.rb +83 -0
- data/test/test_json_driver.rb +63 -0
- data/test/test_kramdown.rb +35 -0
- data/test/test_new_command.rb +104 -0
- data/test/test_page.rb +193 -0
- data/test/test_pager.rb +115 -0
- data/test/test_post.rb +573 -0
- data/test/test_rdiscount.rb +22 -0
- data/test/test_redcarpet.rb +61 -0
- data/test/test_redcloth.rb +86 -0
- data/test/test_site.rb +374 -0
- data/test/test_tags.rb +310 -0
- data/test/test_yaml_driver.rb +35 -0
- metadata +554 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'helper'
|
|
2
|
+
|
|
3
|
+
class TestRdiscount < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
context "rdiscount" do
|
|
6
|
+
setup do
|
|
7
|
+
config = {
|
|
8
|
+
'markdown' => 'rdiscount',
|
|
9
|
+
'rdiscount' => { 'extensions' => ['smart', 'generate_toc'], 'toc_token' => '{:toc}' }
|
|
10
|
+
}
|
|
11
|
+
@markdown = Converters::Markdown.new config
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
should "pass rdiscount extensions" do
|
|
15
|
+
assert_equal "<p>“smart”</p>", @markdown.convert('"smart"').strip
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
should "render toc" do
|
|
19
|
+
assert_equal "<h1 id=\"Header+1\">Header 1</h1>\n\n<h2 id=\"Header+2\">Header 2</h2>\n\n<p>\n <ul>\n <li><a href=\"#Header+1\">Header 1</a>\n <ul>\n <li><a href=\"#Header+2\">Header 2</a> </li>\n </ul>\n </li>\n </ul>\n\n</p>", @markdown.convert("# Header 1\n\n## Header 2\n\n{:toc}").strip
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'helper'
|
|
2
|
+
|
|
3
|
+
class TestRedcarpet < Test::Unit::TestCase
|
|
4
|
+
context "redcarpet" do
|
|
5
|
+
setup do
|
|
6
|
+
@config = {
|
|
7
|
+
'redcarpet' => { 'extensions' => ['smart', 'strikethrough', 'filter_html'] },
|
|
8
|
+
'markdown' => 'redcarpet'
|
|
9
|
+
}
|
|
10
|
+
@markdown = Converters::Markdown.new @config
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
should "pass redcarpet options" do
|
|
14
|
+
assert_equal "<h1>Some Header</h1>", @markdown.convert('# Some Header #').strip
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
should "pass redcarpet SmartyPants options" do
|
|
18
|
+
assert_equal "<p>“smart”</p>", @markdown.convert('"smart"').strip
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
should "pass redcarpet extensions" do
|
|
22
|
+
assert_equal "<p><del>deleted</del></p>", @markdown.convert('~~deleted~~').strip
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
should "pass redcarpet render options" do
|
|
26
|
+
assert_equal "<p><strong>bad code not here</strong>: i am bad</p>", @markdown.convert('**bad code not here**: <script>i am bad</script>').strip
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
context "with pygments enabled" do
|
|
30
|
+
setup do
|
|
31
|
+
@markdown = Converters::Markdown.new @config.merge({ 'pygments' => true })
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
should "render fenced code blocks with syntax highlighting" do
|
|
35
|
+
assert_equal "<div class=\"highlight\"><pre><code class=\"ruby language-ruby\" data-lang=\"ruby\"><span class=\"nb\">puts</span> <span class=\"s2\">"Hello world"</span>\n</code></pre></div>", @markdown.convert(
|
|
36
|
+
<<-EOS
|
|
37
|
+
```ruby
|
|
38
|
+
puts "Hello world"
|
|
39
|
+
```
|
|
40
|
+
EOS
|
|
41
|
+
).strip
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
context "with pygments disabled" do
|
|
46
|
+
setup do
|
|
47
|
+
@markdown = Converters::Markdown.new @config.merge({ 'pygments' => false })
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
should "render fenced code blocks without syntax highlighting" do
|
|
51
|
+
assert_equal "<div class=\"highlight\"><pre><code class=\"ruby language-ruby\" data-lang=\"ruby\">puts "Hello world"\n</code></pre></div>", @markdown.convert(
|
|
52
|
+
<<-EOS
|
|
53
|
+
```ruby
|
|
54
|
+
puts "Hello world"
|
|
55
|
+
```
|
|
56
|
+
EOS
|
|
57
|
+
).strip
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/helper'
|
|
2
|
+
|
|
3
|
+
class TestRedCloth < Test::Unit::TestCase
|
|
4
|
+
|
|
5
|
+
context "RedCloth default (no explicit config) hard_breaks enabled" do
|
|
6
|
+
setup do
|
|
7
|
+
@textile = Converters::Textile.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
should "preserve single line breaks in HTML output" do
|
|
11
|
+
assert_equal "<p>line1<br />\nline2</p>", @textile.convert("p. line1\nline2").strip
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context "Default hard_breaks enabled w/ redcloth section, no hard_breaks value" do
|
|
16
|
+
setup do
|
|
17
|
+
config = {
|
|
18
|
+
'redcloth' => {}
|
|
19
|
+
}
|
|
20
|
+
@textile = Converters::Textile.new config
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
should "preserve single line breaks in HTML output" do
|
|
24
|
+
assert_equal "<p>line1<br />\nline2</p>", @textile.convert("p. line1\nline2").strip
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
context "RedCloth with hard_breaks enabled" do
|
|
29
|
+
setup do
|
|
30
|
+
config = {
|
|
31
|
+
'redcloth' => {
|
|
32
|
+
'hard_breaks' => true # default
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
@textile = Converters::Textile.new config
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
should "preserve single line breaks in HTML output" do
|
|
39
|
+
assert_equal "<p>line1<br />\nline2</p>", @textile.convert("p. line1\nline2").strip
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
context "RedCloth with hard_breaks disabled" do
|
|
44
|
+
setup do
|
|
45
|
+
config = {
|
|
46
|
+
'redcloth' => {
|
|
47
|
+
'hard_breaks' => false
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
@textile = Converters::Textile.new config
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
should "not generate break tags in HTML output" do
|
|
54
|
+
assert_equal "<p>line1\nline2</p>", @textile.convert("p. line1\nline2").strip
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
context "RedCloth w/no_span_caps set to false" do
|
|
59
|
+
setup do
|
|
60
|
+
config = {
|
|
61
|
+
'redcloth' => {
|
|
62
|
+
'no_span_caps' => false
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
@textile = Converters::Textile.new config
|
|
66
|
+
end
|
|
67
|
+
should "generate span tags around capitalized words" do
|
|
68
|
+
assert_equal "<p><span class=\"caps\">NSC</span></p>", @textile.convert("NSC").strip
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
context "RedCloth w/no_span_caps set to true" do
|
|
73
|
+
setup do
|
|
74
|
+
config = {
|
|
75
|
+
'redcloth' => {
|
|
76
|
+
'no_span_caps' => true
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
@textile = Converters::Textile.new config
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
should "not generate span tags around capitalized words" do
|
|
83
|
+
assert_equal "<p>NSC</p>", @textile.convert("NSC").strip
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/test/test_site.rb
ADDED
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
require 'helper'
|
|
2
|
+
|
|
3
|
+
class TestSite < Test::Unit::TestCase
|
|
4
|
+
context "configuring sites" do
|
|
5
|
+
should "have an array for plugins by default" do
|
|
6
|
+
site = Site.new(Monad::Configuration::DEFAULTS)
|
|
7
|
+
assert_equal [File.join(Dir.pwd, '_plugins')], site.plugins
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
should "look for plugins under the site directory by default" do
|
|
11
|
+
site = Site.new(Monad::Configuration::DEFAULTS.merge({'source' => File.expand_path(source_dir)}))
|
|
12
|
+
assert_equal [File.join(source_dir, '_plugins')], site.plugins
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
should "have an array for plugins if passed as a string" do
|
|
16
|
+
site = Site.new(Monad::Configuration::DEFAULTS.merge({'plugins' => '/tmp/plugins'}))
|
|
17
|
+
assert_equal ['/tmp/plugins'], site.plugins
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
should "have an array for plugins if passed as an array" do
|
|
21
|
+
site = Site.new(Monad::Configuration::DEFAULTS.merge({'plugins' => ['/tmp/plugins', '/tmp/otherplugins']}))
|
|
22
|
+
assert_equal ['/tmp/plugins', '/tmp/otherplugins'], site.plugins
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
should "have an empty array for plugins if nothing is passed" do
|
|
26
|
+
site = Site.new(Monad::Configuration::DEFAULTS.merge({'plugins' => []}))
|
|
27
|
+
assert_equal [], site.plugins
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
should "have an empty array for plugins if nil is passed" do
|
|
31
|
+
site = Site.new(Monad::Configuration::DEFAULTS.merge({'plugins' => nil}))
|
|
32
|
+
assert_equal [], site.plugins
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
should "expose default baseurl" do
|
|
36
|
+
site = Site.new(Monad::Configuration::DEFAULTS)
|
|
37
|
+
assert_equal Monad::Configuration::DEFAULTS['baseurl'], site.baseurl
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
should "expose baseurl passed in from config" do
|
|
41
|
+
site = Site.new(Monad::Configuration::DEFAULTS.merge({'baseurl' => '/blog'}))
|
|
42
|
+
assert_equal '/blog', site.baseurl
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
context "creating sites" do
|
|
46
|
+
setup do
|
|
47
|
+
stub(Monad).configuration do
|
|
48
|
+
Monad::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir})
|
|
49
|
+
end
|
|
50
|
+
@site = Site.new(Monad.configuration)
|
|
51
|
+
@num_invalid_posts = 2
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
should "have an empty tag hash by default" do
|
|
55
|
+
assert_equal Hash.new, @site.tags
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
should "give site with parsed pages and posts to generators" do
|
|
59
|
+
@site.reset
|
|
60
|
+
@site.read
|
|
61
|
+
class MyGenerator < Generator
|
|
62
|
+
def generate(site)
|
|
63
|
+
site.pages.dup.each do |page|
|
|
64
|
+
raise "#{page} isn't a page" unless page.is_a?(Page)
|
|
65
|
+
raise "#{page} doesn't respond to :name" unless page.respond_to?(:name)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
@site.generate
|
|
70
|
+
assert_not_equal 0, @site.pages.size
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
should "reset data before processing" do
|
|
74
|
+
clear_dest
|
|
75
|
+
@site.process
|
|
76
|
+
before_posts = @site.posts.length
|
|
77
|
+
before_layouts = @site.layouts.length
|
|
78
|
+
before_categories = @site.categories.length
|
|
79
|
+
before_tags = @site.tags.length
|
|
80
|
+
before_pages = @site.pages.length
|
|
81
|
+
before_static_files = @site.static_files.length
|
|
82
|
+
before_time = @site.time
|
|
83
|
+
|
|
84
|
+
@site.process
|
|
85
|
+
assert_equal before_posts, @site.posts.length
|
|
86
|
+
assert_equal before_layouts, @site.layouts.length
|
|
87
|
+
assert_equal before_categories, @site.categories.length
|
|
88
|
+
assert_equal before_tags, @site.tags.length
|
|
89
|
+
assert_equal before_pages, @site.pages.length
|
|
90
|
+
assert_equal before_static_files, @site.static_files.length
|
|
91
|
+
assert before_time <= @site.time
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
should "write only modified static files" do
|
|
95
|
+
clear_dest
|
|
96
|
+
StaticFile.reset_cache
|
|
97
|
+
|
|
98
|
+
@site.process
|
|
99
|
+
some_static_file = @site.static_files[0].path
|
|
100
|
+
dest = File.expand_path(@site.static_files[0].destination(@site.dest))
|
|
101
|
+
mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file
|
|
102
|
+
|
|
103
|
+
# need to sleep because filesystem timestamps have best resolution in seconds
|
|
104
|
+
sleep 1
|
|
105
|
+
@site.process
|
|
106
|
+
mtime2 = File.stat(dest).mtime.to_i
|
|
107
|
+
assert_equal mtime1, mtime2
|
|
108
|
+
|
|
109
|
+
# simulate file modification by user
|
|
110
|
+
FileUtils.touch some_static_file
|
|
111
|
+
|
|
112
|
+
sleep 1
|
|
113
|
+
@site.process
|
|
114
|
+
mtime3 = File.stat(dest).mtime.to_i
|
|
115
|
+
assert_not_equal mtime2, mtime3 # must be regenerated!
|
|
116
|
+
|
|
117
|
+
sleep 1
|
|
118
|
+
@site.process
|
|
119
|
+
mtime4 = File.stat(dest).mtime.to_i
|
|
120
|
+
assert_equal mtime3, mtime4 # no modifications, so must be the same
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
should "write static files if not modified but missing in destination" do
|
|
124
|
+
clear_dest
|
|
125
|
+
StaticFile.reset_cache
|
|
126
|
+
|
|
127
|
+
@site.process
|
|
128
|
+
some_static_file = @site.static_files[0].path
|
|
129
|
+
dest = File.expand_path(@site.static_files[0].destination(@site.dest))
|
|
130
|
+
mtime1 = File.stat(dest).mtime.to_i # first run must generate dest file
|
|
131
|
+
|
|
132
|
+
# need to sleep because filesystem timestamps have best resolution in seconds
|
|
133
|
+
sleep 1
|
|
134
|
+
@site.process
|
|
135
|
+
mtime2 = File.stat(dest).mtime.to_i
|
|
136
|
+
assert_equal mtime1, mtime2
|
|
137
|
+
|
|
138
|
+
# simulate destination file deletion
|
|
139
|
+
File.unlink dest
|
|
140
|
+
|
|
141
|
+
sleep 1
|
|
142
|
+
@site.process
|
|
143
|
+
mtime3 = File.stat(dest).mtime.to_i
|
|
144
|
+
assert_not_equal mtime2, mtime3 # must be regenerated and differ!
|
|
145
|
+
|
|
146
|
+
sleep 1
|
|
147
|
+
@site.process
|
|
148
|
+
mtime4 = File.stat(dest).mtime.to_i
|
|
149
|
+
assert_equal mtime3, mtime4 # no modifications, so must be the same
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
should "setup plugins in priority order" do
|
|
153
|
+
assert_equal @site.converters.sort_by(&:class).map{|c|c.class.priority}, @site.converters.map{|c|c.class.priority}
|
|
154
|
+
assert_equal @site.generators.sort_by(&:class).map{|g|g.class.priority}, @site.generators.map{|g|g.class.priority}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
should "read layouts" do
|
|
158
|
+
@site.read_layouts
|
|
159
|
+
assert_equal ["default", "simple"].sort, @site.layouts.keys.sort
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
should "read posts" do
|
|
163
|
+
@site.read_posts('')
|
|
164
|
+
posts = Dir[source_dir('_posts', '**', '*')]
|
|
165
|
+
posts.delete_if { |post| File.directory?(post) && !Post.valid?(post) }
|
|
166
|
+
assert_equal posts.size - @num_invalid_posts, @site.posts.size
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
should "deploy payload" do
|
|
170
|
+
clear_dest
|
|
171
|
+
@site.process
|
|
172
|
+
|
|
173
|
+
posts = Dir[source_dir("**", "_posts", "**", "*")]
|
|
174
|
+
posts.delete_if { |post| File.directory?(post) && !Post.valid?(post) }
|
|
175
|
+
categories = %w(2013 bar baz category foo z_category publish_test win).sort
|
|
176
|
+
|
|
177
|
+
assert_equal posts.size - @num_invalid_posts, @site.posts.size
|
|
178
|
+
assert_equal categories, @site.categories.keys.sort
|
|
179
|
+
assert_equal 4, @site.categories['foo'].size
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
should "filter entries" do
|
|
183
|
+
ent1 = %w[foo.markdown bar.markdown baz.markdown #baz.markdown#
|
|
184
|
+
.baz.markdow foo.markdown~]
|
|
185
|
+
ent2 = %w[.htaccess _posts _pages bla.bla]
|
|
186
|
+
|
|
187
|
+
assert_equal %w[foo.markdown bar.markdown baz.markdown], @site.filter_entries(ent1)
|
|
188
|
+
assert_equal %w[.htaccess bla.bla], @site.filter_entries(ent2)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
should "filter entries with exclude" do
|
|
192
|
+
excludes = %w[README TODO]
|
|
193
|
+
files = %w[index.html site.css .htaccess]
|
|
194
|
+
|
|
195
|
+
@site.exclude = excludes + ["exclude*"]
|
|
196
|
+
assert_equal files, @site.filter_entries(excludes + files + ["excludeA"])
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
should "not filter entries within include" do
|
|
200
|
+
includes = %w[_index.html .htaccess include*]
|
|
201
|
+
files = %w[index.html _index.html .htaccess includeA]
|
|
202
|
+
|
|
203
|
+
@site.include = includes
|
|
204
|
+
assert_equal files, @site.filter_entries(files)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
should "filter symlink entries when safe mode enabled" do
|
|
208
|
+
stub(Monad).configuration do
|
|
209
|
+
Monad::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir, 'safe' => true})
|
|
210
|
+
end
|
|
211
|
+
site = Site.new(Monad.configuration)
|
|
212
|
+
stub(File).symlink?('symlink.js') {true}
|
|
213
|
+
files = %w[symlink.js]
|
|
214
|
+
assert_equal [], site.filter_entries(files)
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
should "not filter symlink entries when safe mode disabled" do
|
|
218
|
+
stub(File).symlink?('symlink.js') {true}
|
|
219
|
+
files = %w[symlink.js]
|
|
220
|
+
assert_equal files, @site.filter_entries(files)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
should "not include symlinks in safe mode" do
|
|
224
|
+
stub(Monad).configuration do
|
|
225
|
+
Monad::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir, 'safe' => true})
|
|
226
|
+
end
|
|
227
|
+
site = Site.new(Monad.configuration)
|
|
228
|
+
|
|
229
|
+
site.read_directories("symlink-test")
|
|
230
|
+
assert_equal [], site.pages
|
|
231
|
+
assert_equal [], site.static_files
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
should "include symlinks in unsafe mode" do
|
|
235
|
+
stub(Monad).configuration do
|
|
236
|
+
Monad::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir, 'safe' => false})
|
|
237
|
+
end
|
|
238
|
+
site = Site.new(Monad.configuration)
|
|
239
|
+
|
|
240
|
+
site.read_directories("symlink-test")
|
|
241
|
+
assert_not_equal [], site.pages
|
|
242
|
+
assert_not_equal [], site.static_files
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
context 'error handling' do
|
|
246
|
+
should "raise if destination is included in source" do
|
|
247
|
+
stub(Monad).configuration do
|
|
248
|
+
Monad::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => source_dir})
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
assert_raise Monad::FatalException do
|
|
252
|
+
site = Site.new(Monad.configuration)
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
should "raise if destination is source" do
|
|
257
|
+
stub(Monad).configuration do
|
|
258
|
+
Monad::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => File.join(source_dir, "..")})
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
assert_raise Monad::FatalException do
|
|
262
|
+
site = Site.new(Monad.configuration)
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
context 'with orphaned files in destination' do
|
|
268
|
+
setup do
|
|
269
|
+
clear_dest
|
|
270
|
+
@site.process
|
|
271
|
+
# generate some orphaned files:
|
|
272
|
+
# single file
|
|
273
|
+
File.open(dest_dir('obsolete.html'), 'w')
|
|
274
|
+
# single file in sub directory
|
|
275
|
+
FileUtils.mkdir(dest_dir('qux'))
|
|
276
|
+
File.open(dest_dir('qux/obsolete.html'), 'w')
|
|
277
|
+
# empty directory
|
|
278
|
+
FileUtils.mkdir(dest_dir('quux'))
|
|
279
|
+
FileUtils.mkdir(dest_dir('.git'))
|
|
280
|
+
FileUtils.mkdir(dest_dir('.svn'))
|
|
281
|
+
FileUtils.mkdir(dest_dir('.hg'))
|
|
282
|
+
# single file in repository
|
|
283
|
+
File.open(dest_dir('.git/HEAD'), 'w')
|
|
284
|
+
File.open(dest_dir('.svn/HEAD'), 'w')
|
|
285
|
+
File.open(dest_dir('.hg/HEAD'), 'w')
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
teardown do
|
|
289
|
+
FileUtils.rm_f(dest_dir('obsolete.html'))
|
|
290
|
+
FileUtils.rm_rf(dest_dir('qux'))
|
|
291
|
+
FileUtils.rm_f(dest_dir('quux'))
|
|
292
|
+
FileUtils.rm_rf(dest_dir('.git'))
|
|
293
|
+
FileUtils.rm_rf(dest_dir('.svn'))
|
|
294
|
+
FileUtils.rm_rf(dest_dir('.hg'))
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
should 'remove orphaned files in destination' do
|
|
298
|
+
@site.process
|
|
299
|
+
assert !File.exist?(dest_dir('obsolete.html'))
|
|
300
|
+
assert !File.exist?(dest_dir('qux'))
|
|
301
|
+
assert !File.exist?(dest_dir('quux'))
|
|
302
|
+
assert File.exist?(dest_dir('.git'))
|
|
303
|
+
assert File.exist?(dest_dir('.git/HEAD'))
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
should 'remove orphaned files in destination - keep_files .svn' do
|
|
307
|
+
config = Monad::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => dest_dir, 'keep_files' => ['.svn']})
|
|
308
|
+
@site = Site.new(config)
|
|
309
|
+
@site.process
|
|
310
|
+
assert !File.exist?(dest_dir('.htpasswd'))
|
|
311
|
+
assert !File.exist?(dest_dir('obsolete.html'))
|
|
312
|
+
assert !File.exist?(dest_dir('qux'))
|
|
313
|
+
assert !File.exist?(dest_dir('quux'))
|
|
314
|
+
assert !File.exist?(dest_dir('.git'))
|
|
315
|
+
assert !File.exist?(dest_dir('.git/HEAD'))
|
|
316
|
+
assert File.exist?(dest_dir('.svn'))
|
|
317
|
+
assert File.exist?(dest_dir('.svn/HEAD'))
|
|
318
|
+
end
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
context 'with an invalid markdown processor in the configuration' do
|
|
322
|
+
should 'not throw an error at initialization time' do
|
|
323
|
+
bad_processor = 'not a processor name'
|
|
324
|
+
assert_nothing_raised do
|
|
325
|
+
Site.new(Monad.configuration.merge({ 'markdown' => bad_processor }))
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
should 'throw FatalException at process time' do
|
|
330
|
+
bad_processor = 'not a processor name'
|
|
331
|
+
s = Site.new(Monad.configuration.merge({ 'markdown' => bad_processor }))
|
|
332
|
+
assert_raise Monad::FatalException do
|
|
333
|
+
s.process
|
|
334
|
+
end
|
|
335
|
+
end
|
|
336
|
+
end
|
|
337
|
+
|
|
338
|
+
context 'data sources' do
|
|
339
|
+
should 'not throw exception if data sources is nil' do
|
|
340
|
+
assert_nothing_raised do
|
|
341
|
+
Site.new(Monad.configuration.merge({ 'data_sources' => nil }))
|
|
342
|
+
end
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
should 'not throw exception if data sources is empty' do
|
|
346
|
+
assert_nothing_raised do
|
|
347
|
+
Site.new(Monad.configuration.merge({ 'data_sources' => [] }))
|
|
348
|
+
end
|
|
349
|
+
end
|
|
350
|
+
|
|
351
|
+
should 'load one data source' do
|
|
352
|
+
site = Site.new(Monad.configuration.merge({ 'data_sources' => [{'name' => 'jsonip', 'type' => 'json', 'url' => 'http://jsonip.com/'}] }))
|
|
353
|
+
site.process
|
|
354
|
+
|
|
355
|
+
assert_not_nil site.data_sources['jsonip']['ip']
|
|
356
|
+
assert_not_nil site.data_sources['jsonip']['about']
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
should 'load multiple data sources' do
|
|
360
|
+
jsonip = {'name' => 'jsonip', 'type' => 'json', 'url' => 'http://jsonip.com/'}
|
|
361
|
+
|
|
362
|
+
base = File.expand_path('../fixtures', __FILE__)
|
|
363
|
+
members = {'name' => 'members', 'type' => 'yaml', 'path' => File.join(base, 'members.yaml')}
|
|
364
|
+
|
|
365
|
+
site = Site.new(Monad.configuration.merge({ 'data_sources' => [jsonip, members] }))
|
|
366
|
+
site.process
|
|
367
|
+
|
|
368
|
+
assert_not_nil site.data_sources['jsonip']['ip']
|
|
369
|
+
assert_not_nil site.data_sources['jsonip']['about']
|
|
370
|
+
assert_equal site.data_sources['members'].size, 2
|
|
371
|
+
end
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|