elq-jekyll 0.4.2

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.
Files changed (42) hide show
  1. data/History.txt +105 -0
  2. data/README.textile +490 -0
  3. data/VERSION.yml +4 -0
  4. data/bin/jekyll +137 -0
  5. data/lib/jekyll.rb +67 -0
  6. data/lib/jekyll/albino.rb +119 -0
  7. data/lib/jekyll/converters/csv.rb +26 -0
  8. data/lib/jekyll/converters/mephisto.rb +79 -0
  9. data/lib/jekyll/converters/mt.rb +59 -0
  10. data/lib/jekyll/converters/textpattern.rb +50 -0
  11. data/lib/jekyll/converters/typo.rb +49 -0
  12. data/lib/jekyll/converters/wordpress.rb +56 -0
  13. data/lib/jekyll/convertible.rb +71 -0
  14. data/lib/jekyll/core_ext.rb +22 -0
  15. data/lib/jekyll/filters.rb +43 -0
  16. data/lib/jekyll/layout.rb +33 -0
  17. data/lib/jekyll/page.rb +64 -0
  18. data/lib/jekyll/post.rb +188 -0
  19. data/lib/jekyll/site.rb +170 -0
  20. data/lib/jekyll/tags/highlight.rb +56 -0
  21. data/lib/jekyll/tags/include.rb +31 -0
  22. data/test/helper.rb +14 -0
  23. data/test/source/_includes/sig.markdown +3 -0
  24. data/test/source/_layouts/default.html +27 -0
  25. data/test/source/_layouts/simple.html +1 -0
  26. data/test/source/_posts/2008-10-18-foo-bar.textile +8 -0
  27. data/test/source/_posts/2008-11-21-complex.textile +8 -0
  28. data/test/source/_posts/2008-12-03-permalinked-post.textile +9 -0
  29. data/test/source/_posts/2008-12-13-include.markdown +8 -0
  30. data/test/source/category/_posts/2008-9-23-categories.textile +6 -0
  31. data/test/source/css/screen.css +76 -0
  32. data/test/source/foo/_posts/bar/2008-12-12-topical-post.textile +8 -0
  33. data/test/source/index.html +22 -0
  34. data/test/source/z_category/_posts/2008-9-23-categories.textile +6 -0
  35. data/test/suite.rb +9 -0
  36. data/test/test_filters.rb +41 -0
  37. data/test/test_generated_site.rb +32 -0
  38. data/test/test_jekyll.rb +0 -0
  39. data/test/test_post.rb +135 -0
  40. data/test/test_site.rb +36 -0
  41. data/test/test_tags.rb +31 -0
  42. metadata +211 -0
@@ -0,0 +1,170 @@
1
+ module Jekyll
2
+
3
+ class Site
4
+ attr_accessor :source, :dest
5
+ attr_accessor :layouts, :posts, :categories
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
+ self.categories = Hash.new { |hash, key| hash[key] = Array.new }
20
+ end
21
+
22
+ # Do the actual work of processing the site and generating the
23
+ # real deal.
24
+ #
25
+ # Returns nothing
26
+ def process
27
+ self.read_layouts
28
+ self.transform_pages
29
+ self.write_posts
30
+ end
31
+
32
+ # Read all the files in <source>/_layouts except backup files
33
+ # (end with "~") into memory for later use.
34
+ #
35
+ # Returns nothing
36
+ def read_layouts
37
+ base = File.join(self.source, "_layouts")
38
+ entries = Dir.entries(base)
39
+ entries = entries.reject { |e| e[-1..-1] == '~' }
40
+ entries = entries.reject { |e| File.directory?(File.join(base, e)) }
41
+
42
+ entries.each do |f|
43
+ name = f.split(".")[0..-2].join(".")
44
+ self.layouts[name] = Layout.new(base, f)
45
+ end
46
+ rescue Errno::ENOENT => e
47
+ # ignore missing layout dir
48
+ end
49
+
50
+ # Read all the files in <base>/_posts except backup files (end with "~")
51
+ # and create a new Post object with each one.
52
+ #
53
+ # Returns nothing
54
+ def read_posts(dir)
55
+ base = File.join(self.source, dir, '_posts')
56
+
57
+ entries = []
58
+ Dir.chdir(base) { entries = Dir['**/*'] }
59
+ entries = entries.reject { |e| e[-1..-1] == '~' }
60
+ entries = entries.reject { |e| File.directory?(File.join(base, e)) }
61
+
62
+ # first pass processes, but does not yet render post content
63
+ entries.each do |f|
64
+ if Post.valid?(f)
65
+ post = Post.new(self.source, dir, f)
66
+
67
+ if post.published
68
+ self.posts << post
69
+ post.categories.each { |c| self.categories[c] << post }
70
+ end
71
+ end
72
+ end
73
+
74
+ # second pass renders each post now that full site payload is available
75
+ self.posts.each do |post|
76
+ post.render(self.layouts, site_payload)
77
+ end
78
+
79
+ self.posts.sort!
80
+ self.categories.values.map { |cats| cats.sort! { |a, b| b <=> a} }
81
+ rescue Errno::ENOENT => e
82
+ # ignore missing layout dir
83
+ end
84
+
85
+ # Write each post to <dest>/<year>/<month>/<day>/<slug>
86
+ #
87
+ # Returns nothing
88
+ def write_posts
89
+ self.posts.each do |post|
90
+ post.write(self.dest)
91
+ end
92
+ end
93
+
94
+ # Copy all regular files from <source> to <dest>/ ignoring
95
+ # any files/directories that are hidden or backup files (start
96
+ # with "." or end with "~") or contain site content (start with "_")
97
+ # unless they are "_posts" directories or web server files such as
98
+ # '.htaccess'
99
+ # The +dir+ String is a relative path used to call this method
100
+ # recursively as it descends through directories
101
+ #
102
+ # Returns nothing
103
+ def transform_pages(dir = '')
104
+ base = File.join(self.source, dir)
105
+ entries = Dir.entries(base)
106
+ entries = entries.reject { |e| e[-1..-1] == '~' }
107
+ entries = entries.reject do |e|
108
+ (e != '_posts') and ['.', '_'].include?(e[0..0]) unless ['.htaccess'].include?(e)
109
+ end
110
+ directories = entries.select { |e| File.directory?(File.join(base, e)) }
111
+ files = entries.reject { |e| File.directory?(File.join(base, e)) }
112
+
113
+ # we need to make sure to process _posts *first* otherwise they
114
+ # might not be available yet to other templates as {{ site.posts }}
115
+ if entries.include?('_posts')
116
+ entries.delete('_posts')
117
+ read_posts(dir)
118
+ end
119
+ [directories, files].each do |entries|
120
+ entries.each do |f|
121
+ if File.directory?(File.join(base, f))
122
+ next if self.dest.sub(/\/$/, '') == File.join(base, f)
123
+ transform_pages(File.join(dir, f))
124
+ else
125
+ first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
126
+
127
+ if first3 == "---"
128
+ # file appears to have a YAML header so process it as a page
129
+ page = Page.new(self.source, dir, f)
130
+ page.render(self.layouts, site_payload)
131
+ page.write(self.dest)
132
+ else
133
+ # otherwise copy the file without transforming it
134
+ FileUtils.mkdir_p(File.join(self.dest, dir))
135
+ FileUtils.cp(File.join(self.source, dir, f), File.join(self.dest, dir, f))
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ # Constructs a hash map of Posts indexed by the specified Post attribute
143
+ #
144
+ # Returns {post_attr => [<Post>]}
145
+ def post_attr_hash(post_attr)
146
+ # Build a hash map based on the specified post attribute ( post attr => array of posts )
147
+ # then sort each array in reverse order
148
+ hash = Hash.new { |hash, key| hash[key] = Array.new }
149
+ self.posts.each { |p| p.send(post_attr.to_sym).each { |t| hash[t] << p } }
150
+ hash.values.map { |sortme| sortme.sort! { |a, b| b <=> a} }
151
+ return hash
152
+ end
153
+
154
+ # The Hash payload containing site-wide data
155
+ #
156
+ # Returns {"site" => {"time" => <Time>,
157
+ # "posts" => [<Post>],
158
+ # "categories" => [<Post>],
159
+ # "topics" => [<Post>] }}
160
+ def site_payload
161
+ {"site" => {
162
+ "time" => Time.now,
163
+ "posts" => self.posts.sort { |a,b| b <=> a },
164
+ "categories" => post_attr_hash('categories'),
165
+ "topics" => post_attr_hash('topics')
166
+ }}
167
+ end
168
+ end
169
+
170
+ end
@@ -0,0 +1,56 @@
1
+ module Jekyll
2
+
3
+ class HighlightBlock < Liquid::Block
4
+ include Liquid::StandardFilters
5
+ # we need a language, but the linenos argument is optional.
6
+ SYNTAX = /(\w+)\s?(:?linenos)?\s?/
7
+
8
+ def initialize(tag_name, markup, tokens)
9
+ super
10
+ if markup =~ SYNTAX
11
+ @lang = $1
12
+ if defined? $2
13
+ # additional options to pass to Albino.
14
+ @options = { 'O' => 'linenos=inline' }
15
+ else
16
+ @options = {}
17
+ end
18
+ else
19
+ raise SyntaxError.new("Syntax Error in 'highlight' - Valid syntax: highlight <lang> [linenos]")
20
+ end
21
+ end
22
+
23
+ def render(context)
24
+ if Jekyll.pygments
25
+ render_pygments(context, super.to_s)
26
+ else
27
+ render_codehighlighter(context, super.to_s)
28
+ end
29
+ end
30
+
31
+ def render_pygments(context, code)
32
+
33
+ code.gsub!(/\+\+/, "==++==")
34
+ if Jekyll.content_type == :markdown
35
+ return "\n" + Albino.new(code, @lang).to_s(@options) + "\n"
36
+ else
37
+ return "<notextile>" + Albino.new(code, @lang).to_s(@options) + "</notextile>"
38
+
39
+ end
40
+ end
41
+
42
+ def render_codehighlighter(context, code)
43
+ #The div is required because RDiscount blows ass
44
+ <<-HTML
45
+ <div>
46
+ <pre>
47
+ <code class='#{@lang}'>#{h(code).strip}</code>
48
+ </pre>
49
+ </div>
50
+ HTML
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ 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,14 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. lib jekyll])
2
+
3
+ require 'test/unit'
4
+ require 'redgreen'
5
+
6
+ include Jekyll
7
+
8
+ def dest_dir
9
+ File.join(File.dirname(__FILE__), *%w[dest])
10
+ end
11
+
12
+ def clear_dest
13
+ FileUtils.rm_rf(dest_dir)
14
+ 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,6 @@
1
+ ---
2
+ layout: default
3
+ title: Categories
4
+ ---
5
+
6
+ Categories _should_ work
@@ -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,8 @@
1
+ ---
2
+ layout: default
3
+ title: Topical Post
4
+ ---
5
+
6
+ h1. {{ page.title }}
7
+
8
+ This post has a topic.