qrush-jekyll 0.4.1 → 0.5.0
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/History.txt +19 -1
- data/README.textile +65 -4
- data/Rakefile +91 -0
- data/VERSION.yml +2 -2
- data/bin/jekyll +60 -56
- data/lib/jekyll/albino.rb +9 -5
- data/lib/jekyll/converters/mephisto.rb +8 -8
- data/lib/jekyll/converters/mt.rb +8 -8
- data/lib/jekyll/converters/textpattern.rb +4 -4
- data/lib/jekyll/converters/typo.rb +8 -8
- data/lib/jekyll/converters/wordpress.rb +1 -2
- data/lib/jekyll/convertible.rb +33 -22
- data/lib/jekyll/core_ext.rb +5 -5
- data/lib/jekyll/filters.rb +10 -6
- data/lib/jekyll/layout.rb +9 -6
- data/lib/jekyll/page.rb +13 -10
- data/lib/jekyll/post.rb +71 -33
- data/lib/jekyll/site.rb +116 -48
- data/lib/jekyll/tags/highlight.rb +12 -9
- data/lib/jekyll/tags/include.rb +5 -5
- data/lib/jekyll.rb +48 -32
- data/test/helper.rb +15 -5
- 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/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/test_filters.rb +31 -27
- data/test/test_generated_site.rb +31 -25
- data/test/test_post.rb +155 -119
- data/test/test_site.rb +52 -31
- data/test/test_tags.rb +17 -13
- metadata +19 -67
- data/test/test_jekyll.rb +0 -0
data/lib/jekyll/post.rb
CHANGED
@@ -3,13 +3,13 @@ module Jekyll
|
|
3
3
|
class Post
|
4
4
|
include Comparable
|
5
5
|
include Convertible
|
6
|
-
|
6
|
+
|
7
7
|
class << self
|
8
8
|
attr_accessor :lsi
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
|
12
|
-
|
12
|
+
|
13
13
|
# Post name validator. Post filenames must be like:
|
14
14
|
# 2008-11-05-my-awesome-post.textile
|
15
15
|
#
|
@@ -17,50 +17,59 @@ module Jekyll
|
|
17
17
|
def self.valid?(name)
|
18
18
|
name =~ MATCHER
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
|
+
attr_accessor :site
|
21
22
|
attr_accessor :date, :slug, :ext, :categories, :topics, :published
|
22
23
|
attr_accessor :data, :content, :output
|
23
|
-
|
24
|
+
|
24
25
|
# Initialize this Post instance.
|
26
|
+
# +site+ is the Site
|
25
27
|
# +base+ is the String path to the dir containing the post file
|
26
28
|
# +name+ is the String filename of the post file
|
27
29
|
# +categories+ is an Array of Strings for the categories for this post
|
28
30
|
#
|
29
31
|
# Returns <Post>
|
30
|
-
def initialize(source, dir, name)
|
32
|
+
def initialize(site, source, dir, name)
|
33
|
+
@site = site
|
31
34
|
@base = File.join(source, dir, '_posts')
|
32
35
|
@name = name
|
33
|
-
|
36
|
+
|
34
37
|
self.categories = dir.split('/').reject { |x| x.empty? }
|
35
|
-
|
38
|
+
|
36
39
|
parts = name.split('/')
|
37
40
|
self.topics = parts.size > 1 ? parts[0..-2] : []
|
38
|
-
|
41
|
+
|
39
42
|
self.process(name)
|
40
43
|
self.read_yaml(@base, name)
|
41
44
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
45
|
+
if self.data.has_key?('published') && self.data['published'] == false
|
46
|
+
self.published = false
|
47
|
+
else
|
48
|
+
self.published = true
|
49
|
+
end
|
50
|
+
|
48
51
|
if self.categories.empty?
|
49
52
|
if self.data.has_key?('category')
|
50
53
|
self.categories << self.data['category']
|
51
54
|
elsif self.data.has_key?('categories')
|
52
|
-
|
55
|
+
# Look for categories in the YAML-header, either specified as
|
56
|
+
# an array or a string.
|
57
|
+
if self.data['categories'].kind_of? String
|
58
|
+
self.categories = self.data['categories'].split
|
59
|
+
else
|
60
|
+
self.categories = self.data['categories']
|
61
|
+
end
|
53
62
|
end
|
54
63
|
end
|
55
64
|
end
|
56
|
-
|
65
|
+
|
57
66
|
# Spaceship is based on Post#date
|
58
67
|
#
|
59
68
|
# Returns -1, 0, 1
|
60
69
|
def <=>(other)
|
61
70
|
self.date <=> other.date
|
62
71
|
end
|
63
|
-
|
72
|
+
|
64
73
|
# Extract information from the post filename
|
65
74
|
# +name+ is the String filename of the post file
|
66
75
|
#
|
@@ -71,7 +80,7 @@ module Jekyll
|
|
71
80
|
self.slug = slug
|
72
81
|
self.ext = ext
|
73
82
|
end
|
74
|
-
|
83
|
+
|
75
84
|
# The generated directory into which the post will be placed
|
76
85
|
# upon generation. This is derived from the permalink or, if
|
77
86
|
# permalink is absent, set to the default date
|
@@ -83,14 +92,14 @@ module Jekyll
|
|
83
92
|
permalink.to_s.split("/")[0..-2].join("/") + '/'
|
84
93
|
else
|
85
94
|
prefix = self.categories.empty? ? '' : '/' + self.categories.join('/')
|
86
|
-
if
|
95
|
+
if [:date, :pretty].include?(self.site.permalink_style)
|
87
96
|
prefix + date.strftime("/%Y/%m/%d/")
|
88
97
|
else
|
89
98
|
prefix + '/'
|
90
99
|
end
|
91
100
|
end
|
92
101
|
end
|
93
|
-
|
102
|
+
|
94
103
|
# The full path and filename of the post.
|
95
104
|
# Defined in the YAML of the post body
|
96
105
|
# (Optional)
|
@@ -99,15 +108,16 @@ module Jekyll
|
|
99
108
|
def permalink
|
100
109
|
self.data && self.data['permalink']
|
101
110
|
end
|
102
|
-
|
111
|
+
|
103
112
|
# The generated relative url of this post
|
104
113
|
# e.g. /2008/11/05/my-awesome-post.html
|
105
114
|
#
|
106
115
|
# Returns <String>
|
107
116
|
def url
|
108
|
-
|
117
|
+
ext = self.site.permalink_style == :pretty ? '' : '.html'
|
118
|
+
permalink || self.id + ext
|
109
119
|
end
|
110
|
-
|
120
|
+
|
111
121
|
# The UID for this post (useful in feeds)
|
112
122
|
# e.g. /2008/11/05/my-awesome-post
|
113
123
|
#
|
@@ -115,14 +125,14 @@ module Jekyll
|
|
115
125
|
def id
|
116
126
|
self.dir + self.slug
|
117
127
|
end
|
118
|
-
|
128
|
+
|
119
129
|
# Calculate related posts.
|
120
130
|
#
|
121
131
|
# Returns [<Post>]
|
122
132
|
def related_posts(posts)
|
123
133
|
return [] unless posts.size > 1
|
124
|
-
|
125
|
-
if
|
134
|
+
|
135
|
+
if self.site.lsi
|
126
136
|
self.class.lsi ||= begin
|
127
137
|
puts "Running the classifier... this could take a while."
|
128
138
|
lsi = Classifier::LSI.new
|
@@ -137,7 +147,7 @@ module Jekyll
|
|
137
147
|
(posts - [self])[0..9]
|
138
148
|
end
|
139
149
|
end
|
140
|
-
|
150
|
+
|
141
151
|
# Add any necessary layouts to this post
|
142
152
|
# +layouts+ is a Hash of {"name" => "layout"}
|
143
153
|
# +site_payload+ is the site payload hash
|
@@ -151,23 +161,29 @@ module Jekyll
|
|
151
161
|
"page" => self.to_liquid
|
152
162
|
}
|
153
163
|
payload = payload.deep_merge(site_payload)
|
154
|
-
|
164
|
+
|
155
165
|
do_layout(payload, layouts)
|
156
166
|
end
|
157
|
-
|
167
|
+
|
158
168
|
# Write the generated post file to the destination directory.
|
159
169
|
# +dest+ is the String path to the destination dir
|
160
170
|
#
|
161
171
|
# Returns nothing
|
162
172
|
def write(dest)
|
163
173
|
FileUtils.mkdir_p(File.join(dest, dir))
|
164
|
-
|
174
|
+
|
165
175
|
path = File.join(dest, self.url)
|
176
|
+
|
177
|
+
if self.site.permalink_style == :pretty
|
178
|
+
FileUtils.mkdir_p(path)
|
179
|
+
path = File.join(path, "index.html")
|
180
|
+
end
|
181
|
+
|
166
182
|
File.open(path, 'w') do |f|
|
167
183
|
f.write(self.output)
|
168
184
|
end
|
169
185
|
end
|
170
|
-
|
186
|
+
|
171
187
|
# Convert this post into a Hash for use in Liquid templates.
|
172
188
|
#
|
173
189
|
# Returns <Hash>
|
@@ -177,12 +193,34 @@ module Jekyll
|
|
177
193
|
"date" => self.date,
|
178
194
|
"id" => self.id,
|
179
195
|
"topics" => self.topics,
|
196
|
+
"categories" => self.categories,
|
197
|
+
"next" => self.next,
|
198
|
+
"previous" => self.previous,
|
180
199
|
"content" => self.content }.deep_merge(self.data)
|
181
200
|
end
|
182
|
-
|
201
|
+
|
183
202
|
def inspect
|
184
203
|
"<Post: #{self.id}>"
|
185
204
|
end
|
205
|
+
|
206
|
+
def next
|
207
|
+
pos = self.site.posts.index(self)
|
208
|
+
|
209
|
+
if pos && pos < self.site.posts.length-1
|
210
|
+
self.site.posts[pos+1]
|
211
|
+
else
|
212
|
+
nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def previous
|
217
|
+
pos = self.site.posts.index(self)
|
218
|
+
if pos && pos > 0
|
219
|
+
self.site.posts[pos-1]
|
220
|
+
else
|
221
|
+
nil
|
222
|
+
end
|
223
|
+
end
|
186
224
|
end
|
187
225
|
|
188
226
|
end
|
data/lib/jekyll/site.rb
CHANGED
@@ -1,68 +1,127 @@
|
|
1
1
|
module Jekyll
|
2
|
-
|
2
|
+
|
3
3
|
class Site
|
4
|
-
attr_accessor :
|
5
|
-
attr_accessor :
|
6
|
-
|
4
|
+
attr_accessor :config, :layouts, :posts, :categories
|
5
|
+
attr_accessor :source, :dest, :lsi, :pygments, :permalink_style
|
6
|
+
|
7
7
|
# Initialize the site
|
8
|
-
# +
|
9
|
-
# the proto-site
|
10
|
-
# +dest+ is the String path to the directory where the generated
|
11
|
-
# site should be written
|
8
|
+
# +config+ is a Hash containing site configurations details
|
12
9
|
#
|
13
10
|
# Returns <Site>
|
14
|
-
def initialize(
|
15
|
-
self.
|
16
|
-
|
17
|
-
self.
|
18
|
-
self.
|
19
|
-
self.
|
11
|
+
def initialize(config)
|
12
|
+
self.config = config.clone
|
13
|
+
|
14
|
+
self.source = config['source']
|
15
|
+
self.dest = config['destination']
|
16
|
+
self.lsi = config['lsi']
|
17
|
+
self.pygments = config['pygments']
|
18
|
+
self.permalink_style = config['permalink'].to_sym
|
19
|
+
|
20
|
+
self.reset
|
21
|
+
self.setup
|
22
|
+
end
|
23
|
+
|
24
|
+
def reset
|
25
|
+
self.layouts = {}
|
26
|
+
self.posts = []
|
27
|
+
self.categories = Hash.new { |hash, key| hash[key] = Array.new }
|
28
|
+
end
|
29
|
+
|
30
|
+
def setup
|
31
|
+
# Check to see if LSI is enabled.
|
32
|
+
require 'classifier' if self.lsi
|
33
|
+
|
34
|
+
# Set the Markdown interpreter (and Maruku self.config, if necessary)
|
35
|
+
case self.config['markdown']
|
36
|
+
when 'rdiscount'
|
37
|
+
begin
|
38
|
+
require 'rdiscount'
|
39
|
+
|
40
|
+
def markdown(content)
|
41
|
+
RDiscount.new(content).to_html
|
42
|
+
end
|
43
|
+
|
44
|
+
puts 'Using rdiscount for Markdown'
|
45
|
+
rescue LoadError
|
46
|
+
puts 'You must have the rdiscount gem installed first'
|
47
|
+
end
|
48
|
+
when 'maruku'
|
49
|
+
begin
|
50
|
+
require 'maruku'
|
51
|
+
|
52
|
+
def markdown(content)
|
53
|
+
Maruku.new(content).to_html
|
54
|
+
end
|
55
|
+
|
56
|
+
if self.config['maruku']['use_divs']
|
57
|
+
require 'maruku/ext/div'
|
58
|
+
puts 'Maruku: Using extended syntax for div elements.'
|
59
|
+
end
|
60
|
+
|
61
|
+
if self.config['maruku']['use_tex']
|
62
|
+
require 'maruku/ext/math'
|
63
|
+
puts "Maruku: Using LaTeX extension. Images in `#{self.config['maruku']['png_dir']}`."
|
64
|
+
|
65
|
+
# Switch off MathML output
|
66
|
+
MaRuKu::Globals[:html_math_output_mathml] = false
|
67
|
+
MaRuKu::Globals[:html_math_engine] = 'none'
|
68
|
+
|
69
|
+
# Turn on math to PNG support with blahtex
|
70
|
+
# Resulting PNGs stored in `images/latex`
|
71
|
+
MaRuKu::Globals[:html_math_output_png] = true
|
72
|
+
MaRuKu::Globals[:html_png_engine] = self.config['maruku']['png_engine']
|
73
|
+
MaRuKu::Globals[:html_png_dir] = self.config['maruku']['png_dir']
|
74
|
+
MaRuKu::Globals[:html_png_url] = self.config['maruku']['png_url']
|
75
|
+
end
|
76
|
+
rescue LoadError
|
77
|
+
puts "The maruku gem is required for markdown support!"
|
78
|
+
end
|
79
|
+
end
|
20
80
|
end
|
21
|
-
|
81
|
+
|
82
|
+
def textile(content)
|
83
|
+
RedCloth.new(content).to_html
|
84
|
+
end
|
85
|
+
|
22
86
|
# Do the actual work of processing the site and generating the
|
23
87
|
# real deal.
|
24
88
|
#
|
25
89
|
# Returns nothing
|
26
90
|
def process
|
91
|
+
self.reset
|
27
92
|
self.read_layouts
|
28
93
|
self.transform_pages
|
29
94
|
self.write_posts
|
30
95
|
end
|
31
|
-
|
32
|
-
# Read all the files in <source>/_layouts
|
33
|
-
# (end with "~") into memory for later use.
|
96
|
+
|
97
|
+
# Read all the files in <source>/_layouts into memory for later use.
|
34
98
|
#
|
35
99
|
# Returns nothing
|
36
100
|
def read_layouts
|
37
101
|
base = File.join(self.source, "_layouts")
|
38
|
-
entries =
|
39
|
-
|
40
|
-
|
41
|
-
|
102
|
+
entries = []
|
103
|
+
Dir.chdir(base) { entries = filter_entries(Dir['*.*']) }
|
104
|
+
|
42
105
|
entries.each do |f|
|
43
106
|
name = f.split(".")[0..-2].join(".")
|
44
|
-
self.layouts[name] = Layout.new(base, f)
|
107
|
+
self.layouts[name] = Layout.new(self, base, f)
|
45
108
|
end
|
46
109
|
rescue Errno::ENOENT => e
|
47
110
|
# ignore missing layout dir
|
48
111
|
end
|
49
|
-
|
50
|
-
# Read all the files in <base>/_posts
|
51
|
-
# and create a new Post object with each one.
|
112
|
+
|
113
|
+
# Read all the files in <base>/_posts and create a new Post object with each one.
|
52
114
|
#
|
53
115
|
# Returns nothing
|
54
116
|
def read_posts(dir)
|
55
117
|
base = File.join(self.source, dir, '_posts')
|
56
|
-
|
57
118
|
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)) }
|
119
|
+
Dir.chdir(base) { entries = filter_entries(Dir['**/*']) }
|
61
120
|
|
62
121
|
# first pass processes, but does not yet render post content
|
63
122
|
entries.each do |f|
|
64
123
|
if Post.valid?(f)
|
65
|
-
post = Post.new(self.source, dir, f)
|
124
|
+
post = Post.new(self, self.source, dir, f)
|
66
125
|
|
67
126
|
if post.published
|
68
127
|
self.posts << post
|
@@ -70,18 +129,18 @@ module Jekyll
|
|
70
129
|
end
|
71
130
|
end
|
72
131
|
end
|
73
|
-
|
132
|
+
|
74
133
|
# second pass renders each post now that full site payload is available
|
75
134
|
self.posts.each do |post|
|
76
135
|
post.render(self.layouts, site_payload)
|
77
136
|
end
|
78
|
-
|
137
|
+
|
79
138
|
self.posts.sort!
|
80
139
|
self.categories.values.map { |cats| cats.sort! { |a, b| b <=> a} }
|
81
140
|
rescue Errno::ENOENT => e
|
82
141
|
# ignore missing layout dir
|
83
142
|
end
|
84
|
-
|
143
|
+
|
85
144
|
# Write each post to <dest>/<year>/<month>/<day>/<slug>
|
86
145
|
#
|
87
146
|
# Returns nothing
|
@@ -90,10 +149,10 @@ module Jekyll
|
|
90
149
|
post.write(self.dest)
|
91
150
|
end
|
92
151
|
end
|
93
|
-
|
152
|
+
|
94
153
|
# Copy all regular files from <source> to <dest>/ ignoring
|
95
154
|
# any files/directories that are hidden or backup files (start
|
96
|
-
# with "." or end with "~") or contain site content (start with "_")
|
155
|
+
# with "." or "#" or end with "~") or contain site content (start with "_")
|
97
156
|
# unless they are "_posts" directories or web server files such as
|
98
157
|
# '.htaccess'
|
99
158
|
# The +dir+ String is a relative path used to call this method
|
@@ -102,18 +161,14 @@ module Jekyll
|
|
102
161
|
# Returns nothing
|
103
162
|
def transform_pages(dir = '')
|
104
163
|
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
|
164
|
+
entries = filter_entries(Dir.entries(base))
|
110
165
|
directories = entries.select { |e| File.directory?(File.join(base, e)) }
|
111
166
|
files = entries.reject { |e| File.directory?(File.join(base, e)) }
|
112
167
|
|
113
|
-
# we need to make sure to process _posts *first* otherwise they
|
168
|
+
# we need to make sure to process _posts *first* otherwise they
|
114
169
|
# might not be available yet to other templates as {{ site.posts }}
|
115
|
-
if
|
116
|
-
|
170
|
+
if directories.include?('_posts')
|
171
|
+
directories.delete('_posts')
|
117
172
|
read_posts(dir)
|
118
173
|
end
|
119
174
|
[directories, files].each do |entries|
|
@@ -123,10 +178,10 @@ module Jekyll
|
|
123
178
|
transform_pages(File.join(dir, f))
|
124
179
|
else
|
125
180
|
first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
|
126
|
-
|
181
|
+
|
127
182
|
if first3 == "---"
|
128
183
|
# file appears to have a YAML header so process it as a page
|
129
|
-
page = Page.new(self.source, dir, f)
|
184
|
+
page = Page.new(self, self.source, dir, f)
|
130
185
|
page.render(self.layouts, site_payload)
|
131
186
|
page.write(self.dest)
|
132
187
|
else
|
@@ -159,12 +214,25 @@ module Jekyll
|
|
159
214
|
# "topics" => [<Post>] }}
|
160
215
|
def site_payload
|
161
216
|
{"site" => {
|
162
|
-
"time" => Time.now,
|
217
|
+
"time" => Time.now,
|
163
218
|
"posts" => self.posts.sort { |a,b| b <=> a },
|
164
219
|
"categories" => post_attr_hash('categories'),
|
165
220
|
"topics" => post_attr_hash('topics')
|
166
221
|
}}
|
167
222
|
end
|
168
|
-
end
|
169
223
|
|
224
|
+
# Filter out any files/directories that are hidden or backup files (start
|
225
|
+
# with "." or "#" or end with "~") or contain site content (start with "_")
|
226
|
+
# unless they are "_posts" directories or web server files such as
|
227
|
+
# '.htaccess'
|
228
|
+
def filter_entries(entries)
|
229
|
+
entries = entries.reject do |e|
|
230
|
+
unless ['_posts', '.htaccess'].include?(e)
|
231
|
+
# Reject backup/hidden
|
232
|
+
['.', '_', '#'].include?(e[0..0]) or e[-1..-1] == '~'
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
170
238
|
end
|