qrush-jekyll 0.3.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.
@@ -0,0 +1,160 @@
1
+ module Jekyll
2
+
3
+ class Site
4
+ attr_accessor :source, :dest
5
+ attr_accessor :layouts, :posts
6
+
7
+ # Initialize the site
8
+ # +source+ is String path to the source directory containing
9
+ # the proto-site
10
+ # +dest+ is the String path to the directory where the generated
11
+ # site should be written
12
+ #
13
+ # Returns <Site>
14
+ def initialize(source, dest)
15
+ self.source = source
16
+ self.dest = dest
17
+ self.layouts = {}
18
+ self.posts = []
19
+ end
20
+
21
+ # Do the actual work of processing the site and generating the
22
+ # real deal.
23
+ #
24
+ # Returns nothing
25
+ def process
26
+ self.read_layouts
27
+ self.transform_pages
28
+ self.write_posts
29
+ end
30
+
31
+ # Read all the files in <source>/_layouts except backup files
32
+ # (end with "~") into memory for later use.
33
+ #
34
+ # Returns nothing
35
+ def read_layouts
36
+ base = File.join(self.source, "_layouts")
37
+ entries = Dir.entries(base)
38
+ entries = entries.reject { |e| e[-1..-1] == '~' }
39
+ entries = entries.reject { |e| File.directory?(File.join(base, e)) }
40
+
41
+ entries.each do |f|
42
+ name = f.split(".")[0..-2].join(".")
43
+ self.layouts[name] = Layout.new(base, f)
44
+ end
45
+ rescue Errno::ENOENT => e
46
+ # ignore missing layout dir
47
+ end
48
+
49
+ # Read all the files in <base>/_posts except backup files (end with "~")
50
+ # and create a new Post object with each one.
51
+ #
52
+ # Returns nothing
53
+ def read_posts(dir)
54
+ base = File.join(self.source, dir, '_posts')
55
+
56
+ entries = []
57
+ Dir.chdir(base) { entries = Dir['**/*'] }
58
+ entries = entries.reject { |e| e[-1..-1] == '~' }
59
+ entries = entries.reject { |e| File.directory?(File.join(base, e)) }
60
+
61
+ # first pass processes, but does not yet render post content
62
+ entries.each do |f|
63
+ if Post.valid?(f)
64
+ post = Post.new(self.source, dir, f)
65
+ self.posts << post
66
+ end
67
+ end
68
+
69
+ # second pass renders each post now that full site payload is available
70
+ self.posts.each do |post|
71
+ post.render(self.layouts, site_payload)
72
+ end
73
+
74
+ self.posts.sort!
75
+ rescue Errno::ENOENT => e
76
+ # ignore missing layout dir
77
+ end
78
+
79
+ # Write each post to <dest>/<year>/<month>/<day>/<slug>
80
+ #
81
+ # Returns nothing
82
+ def write_posts
83
+ self.posts.each do |post|
84
+ post.write(self.dest)
85
+ end
86
+ end
87
+
88
+ # Copy all regular files from <source> to <dest>/ ignoring
89
+ # any files/directories that are hidden or backup files (start
90
+ # with "." or end with "~") or contain site content (start with "_")
91
+ # unless they are "_posts" directories
92
+ # The +dir+ String is a relative path used to call this method
93
+ # recursively as it descends through directories
94
+ #
95
+ # Returns nothing
96
+ def transform_pages(dir = '')
97
+ base = File.join(self.source, dir)
98
+ entries = Dir.entries(base)
99
+ entries = entries.reject { |e| e[-1..-1] == '~' }
100
+ entries = entries.reject do |e|
101
+ (e != '_posts') and ['.', '_'].include?(e[0..0])
102
+ end
103
+
104
+ # we need to make sure to process _posts *first* otherwise they
105
+ # might not be available yet to other templates as {{ site.posts }}
106
+ if entries.include?('_posts')
107
+ entries.delete('_posts')
108
+ read_posts(dir)
109
+ end
110
+
111
+ entries.each do |f|
112
+ if File.directory?(File.join(base, f))
113
+ next if self.dest.sub(/\/$/, '') == File.join(base, f)
114
+ transform_pages(File.join(dir, f))
115
+ else
116
+ first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
117
+
118
+ if first3 == "---"
119
+ # file appears to have a YAML header so process it as a page
120
+ page = Page.new(self.source, dir, f)
121
+ page.render(self.layouts, site_payload)
122
+ page.write(self.dest)
123
+ else
124
+ # otherwise copy the file without transforming it
125
+ FileUtils.mkdir_p(File.join(self.dest, dir))
126
+ FileUtils.cp(File.join(self.source, dir, f), File.join(self.dest, dir, f))
127
+ end
128
+ end
129
+ end
130
+ end
131
+
132
+ # Constructs a hash map of Posts indexed by the specified Post attribute
133
+ #
134
+ # Returns {post_attr => [<Post>]}
135
+ def post_attr_hash(post_attr)
136
+ # Build a hash map based on the specified post attribute ( post attr => array of posts )
137
+ # then sort each array in reverse order
138
+ hash = Hash.new { |hash, key| hash[key] = Array.new }
139
+ self.posts.each { |p| p.send(post_attr.to_sym).each { |t| hash[t] << p } }
140
+ hash.values.map { |sortme| sortme.sort! { |a, b| b <=> a} }
141
+ return hash
142
+ end
143
+
144
+ # The Hash payload containing site-wide data
145
+ #
146
+ # Returns {"site" => {"time" => <Time>,
147
+ # "posts" => [<Post>],
148
+ # "categories" => [<Post>],
149
+ # "topics" => [<Post>] }}
150
+ def site_payload
151
+ {"site" => {
152
+ "time" => Time.now,
153
+ "posts" => self.posts.sort { |a,b| b <=> a },
154
+ "categories" => post_attr_hash('categories'),
155
+ "topics" => post_attr_hash('topics')
156
+ }}
157
+ end
158
+ end
159
+
160
+ end
@@ -0,0 +1,37 @@
1
+ module Jekyll
2
+
3
+ class HighlightBlock < Liquid::Block
4
+ include Liquid::StandardFilters
5
+
6
+ def initialize(tag_name, lang, tokens)
7
+ super
8
+ @lang = lang.strip
9
+ end
10
+
11
+ def render(context)
12
+ if Jekyll.pygments
13
+ render_pygments(context, super.to_s)
14
+ else
15
+ render_codehighlighter(context, super.to_s)
16
+ end
17
+ end
18
+
19
+ def render_pygments(context, code)
20
+ "<notextile>" + Albino.new(code, @lang).to_s + "</notextile>"
21
+ end
22
+
23
+ def render_codehighlighter(context, code)
24
+ #The div is required because RDiscount blows ass
25
+ <<-HTML
26
+ <div>
27
+ <pre>
28
+ <code class='#{@lang}'>#{h(code).strip}</code>
29
+ </pre>
30
+ </div>
31
+ HTML
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ Liquid::Template.register_tag('highlight', Jekyll::HighlightBlock)
@@ -0,0 +1,31 @@
1
+ module Jekyll
2
+
3
+ class IncludeTag < Liquid::Tag
4
+ def initialize(tag_name, file, tokens)
5
+ super
6
+ @file = file.strip
7
+ end
8
+
9
+ def render(context)
10
+ if @file !~ /^[a-zA-Z0-9_\/\.-]+$/ || @file =~ /\.\// || @file =~ /\/\./
11
+ return "Include file '#{@file}' contains invalid characters or sequences"
12
+ end
13
+
14
+ Dir.chdir(File.join(Jekyll.source, '_includes')) do
15
+ choices = Dir['**/*'].reject { |x| File.symlink?(x) }
16
+ if choices.include?(@file)
17
+ source = File.read(@file)
18
+ partial = Liquid::Template.parse(source)
19
+ context.stack do
20
+ partial.render(context)
21
+ end
22
+ else
23
+ "Included file '#{@file}' not found in _includes directory"
24
+ end
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ Liquid::Template.register_tag('include', Jekyll::IncludeTag)
data/test/helper.rb ADDED
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. lib jekyll])
2
+
3
+ require 'test/unit'
4
+
5
+ include Jekyll
6
+
7
+ def dest_dir
8
+ File.join(File.dirname(__FILE__), *%w[dest])
9
+ end
10
+
11
+ def clear_dest
12
+ FileUtils.rm_rf(dest_dir)
13
+ end
@@ -0,0 +1,3 @@
1
+ --
2
+ Tom Preston-Werner
3
+ github.com/mojombo
@@ -0,0 +1,27 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
5
+ <head>
6
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
7
+ <title>{{ page.title }}</title>
8
+ <meta name="author" content="<%= @page.author %>" />
9
+
10
+ <!-- CodeRay syntax highlighting CSS -->
11
+ <link rel="stylesheet" href="/css/coderay.css" type="text/css" />
12
+
13
+ <!-- Homepage CSS -->
14
+ <link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen, projection" />
15
+ </head>
16
+ <body>
17
+
18
+ <div class="site">
19
+ <div class="title">
20
+ Tom Preston-Werner
21
+ </div>
22
+
23
+ {{ content }}
24
+ </div>
25
+
26
+ </body>
27
+ </html>
@@ -0,0 +1 @@
1
+ <<< {{ content }} >>>
@@ -0,0 +1,8 @@
1
+ ---
2
+ layout: default
3
+ title: Foo Bar
4
+ ---
5
+
6
+ h1. {{ page.title }}
7
+
8
+ Best *post* ever
@@ -0,0 +1,8 @@
1
+ ---
2
+ layout: default
3
+ title: Complex
4
+ ---
5
+
6
+ url: {{ page.url }}
7
+ date: {{ page.date }}
8
+ id: {{ page.id }}
@@ -0,0 +1,9 @@
1
+ ---
2
+ title: Post with Permalink
3
+ permalink: my_category/permalinked-post
4
+ ---
5
+
6
+ h1. {{ page.title }}
7
+
8
+
9
+ <p>Best <strong>post</strong> ever</p>
@@ -0,0 +1,8 @@
1
+ ---
2
+ layout: default
3
+ title: Include
4
+ ---
5
+
6
+ {% include sig.markdown %}
7
+
8
+ This _is_ cool
@@ -0,0 +1,76 @@
1
+ /*****************************************************************************/
2
+ /*
3
+ /* Common
4
+ /*
5
+ /*****************************************************************************/
6
+
7
+ /* Global Reset */
8
+
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ html, body {
15
+ height: 100%;
16
+ }
17
+
18
+ body {
19
+ background-color: white;
20
+ font: 13.34px helvetica, arial, clean, sans-serif;
21
+ *font-size: small;
22
+ text-align: center;
23
+ }
24
+
25
+ h1, h2, h3, h4, h5, h6 {
26
+ font-size: 100%;
27
+ }
28
+
29
+ h1 {
30
+ margin-bottom: 1em;
31
+ }
32
+
33
+ p {
34
+ margin: 1em 0;
35
+ }
36
+
37
+ a {
38
+ color: #00a;
39
+ }
40
+
41
+ a:hover {
42
+ color: black;
43
+ }
44
+
45
+ a:visited {
46
+ color: #a0a;
47
+ }
48
+
49
+ table {
50
+ font-size: inherit;
51
+ font: 100%;
52
+ }
53
+
54
+ /*****************************************************************************/
55
+ /*
56
+ /* Site
57
+ /*
58
+ /*****************************************************************************/
59
+
60
+ .site {
61
+ font-size: 110%;
62
+ text-align: justify;
63
+ width: 40em;
64
+ margin: 3em auto 2em auto;
65
+ line-height: 1.5em;
66
+ }
67
+
68
+ .title {
69
+ color: #a00;
70
+ font-weight: bold;
71
+ margin-bottom: 2em;
72
+ }
73
+
74
+ .site .meta {
75
+ color: #aaa;
76
+ }
@@ -0,0 +1,22 @@
1
+ ---
2
+ layout: default
3
+ title: Tom Preston-Werner
4
+ ---
5
+
6
+ h1. Welcome to my site
7
+
8
+ h2. Please read our {{ site.posts | size }} Posts
9
+
10
+ <ul>
11
+ {% for post in site.posts %}
12
+ <li>{{ post.date }} <a href="{{ post.url }}">{{ post.title }}</a></li>
13
+ {% endfor %}
14
+ </ul>
15
+
16
+ {% assign first_post = site.posts.first %}
17
+ <div id="first_post">
18
+ <h1>{{ first_post.title }}</h1>
19
+ <div>
20
+ {{ first_post.content }}
21
+ </div>
22
+ </div>
data/test/suite.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'test/unit'
2
+
3
+ # for some reason these tests fail when run via TextMate
4
+ # but succeed when run on the command line.
5
+
6
+ tests = Dir["#{File.dirname(__FILE__)}/test_*.rb"]
7
+ tests.each do |file|
8
+ require file
9
+ end
@@ -0,0 +1,21 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestGeneratedSite < Test::Unit::TestCase
4
+ def setup
5
+ clear_dest
6
+ source = File.join(File.dirname(__FILE__), *%w[source])
7
+ @s = Site.new(source, dest_dir)
8
+ @s.process
9
+ @index = File.read(File.join(dest_dir, 'index.html'))
10
+ end
11
+
12
+ def test_site_posts_in_index
13
+ # confirm that {{ site.posts }} is working
14
+ assert @index.include?("#{@s.posts.size} Posts")
15
+ end
16
+
17
+ def test_post_content_in_index
18
+ # confirm that the {{ post.content }} is rendered OK
19
+ assert @index.include?('<p>This <em>is</em> cool</p>')
20
+ end
21
+ end