jekyll 0.5.7 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of jekyll might be problematic. Click here for more details.
- data/History.txt +88 -31
- data/LICENSE +21 -0
- data/README.textile +1 -9
- data/Rakefile +119 -51
- data/bin/jekyll +26 -2
- data/cucumber.yml +1 -0
- data/features/create_sites.feature +28 -10
- data/features/post_data.feature +7 -7
- data/features/site_configuration.feature +41 -1
- data/features/step_definitions/jekyll_steps.rb +13 -4
- data/jekyll.gemspec +125 -143
- data/lib/jekyll.rb +40 -20
- data/lib/jekyll/albino.rb +5 -7
- data/lib/jekyll/converter.rb +50 -0
- data/lib/jekyll/converters/identity.rb +22 -0
- data/lib/jekyll/converters/markdown.rb +77 -0
- data/lib/jekyll/converters/textile.rb +33 -0
- data/lib/jekyll/convertible.rb +18 -24
- data/lib/jekyll/errors.rb +6 -0
- data/lib/jekyll/generator.rb +7 -0
- data/lib/jekyll/generators/pagination.rb +87 -0
- data/lib/jekyll/{converters → migrators}/csv.rb +0 -0
- data/lib/jekyll/{converters → migrators}/mephisto.rb +0 -0
- data/lib/jekyll/{converters → migrators}/mt.rb +0 -0
- data/lib/jekyll/{converters → migrators}/textpattern.rb +0 -0
- data/lib/jekyll/{converters → migrators}/typo.rb +0 -0
- data/lib/jekyll/{converters → migrators}/wordpress.rb +1 -0
- data/lib/jekyll/page.rb +28 -11
- data/lib/jekyll/plugin.rb +76 -0
- data/lib/jekyll/post.rb +10 -8
- data/lib/jekyll/site.rb +40 -88
- data/lib/jekyll/static_file.rb +52 -4
- data/lib/jekyll/tags/highlight.rb +20 -9
- data/test/helper.rb +6 -0
- data/test/source/_posts/2010-01-09-date-override.textile +2 -0
- data/test/source/_posts/2010-01-09-time-override.textile +2 -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/sitemap.xml +27 -18
- data/test/test_configuration.rb +1 -1
- data/test/test_generated_site.rb +1 -1
- data/test/test_post.rb +63 -12
- data/test/test_site.rb +69 -7
- data/test/test_tags.rb +6 -14
- metadata +156 -52
- data/.gitignore +0 -6
- data/VERSION.yml +0 -5
- data/lib/jekyll/pager.rb +0 -45
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/jekyll/page.rb
CHANGED
@@ -3,7 +3,7 @@ module Jekyll
|
|
3
3
|
class Page
|
4
4
|
include Convertible
|
5
5
|
|
6
|
-
attr_accessor :site
|
6
|
+
attr_accessor :site, :pager
|
7
7
|
attr_accessor :name, :ext, :basename, :dir
|
8
8
|
attr_accessor :data, :content, :output
|
9
9
|
|
@@ -43,10 +43,10 @@ module Jekyll
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def template
|
46
|
-
if self.site.permalink_style == :pretty && !index?
|
47
|
-
"/:
|
46
|
+
if self.site.permalink_style == :pretty && !index? && html?
|
47
|
+
"/:basename/"
|
48
48
|
else
|
49
|
-
"/:
|
49
|
+
"/:basename:output_ext"
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
@@ -57,7 +57,12 @@ module Jekyll
|
|
57
57
|
def url
|
58
58
|
return permalink if permalink
|
59
59
|
|
60
|
-
@url ||=
|
60
|
+
@url ||= {
|
61
|
+
"basename" => self.basename,
|
62
|
+
"output_ext" => self.output_ext,
|
63
|
+
}.inject(template) { |result, token|
|
64
|
+
result.gsub(/:#{token.first}/, token.last)
|
65
|
+
}.gsub(/\/\//, "/")
|
61
66
|
end
|
62
67
|
|
63
68
|
# Extract information from the page filename
|
@@ -75,10 +80,20 @@ module Jekyll
|
|
75
80
|
#
|
76
81
|
# Returns nothing
|
77
82
|
def render(layouts, site_payload)
|
78
|
-
payload = {
|
83
|
+
payload = {
|
84
|
+
"page" => self.to_liquid,
|
85
|
+
'paginator' => pager.to_liquid
|
86
|
+
}.deep_merge(site_payload)
|
87
|
+
|
79
88
|
do_layout(payload, layouts)
|
80
89
|
end
|
81
90
|
|
91
|
+
def to_liquid
|
92
|
+
self.data.deep_merge({
|
93
|
+
"url" => self.url,
|
94
|
+
"content" => self.content })
|
95
|
+
end
|
96
|
+
|
82
97
|
# Write the generated page file to the destination directory.
|
83
98
|
# +dest_prefix+ is the String path to the destination dir
|
84
99
|
# +dest_suffix+ is a suffix path to the destination dir
|
@@ -91,7 +106,7 @@ module Jekyll
|
|
91
106
|
|
92
107
|
# The url needs to be unescaped in order to preserve the correct filename
|
93
108
|
path = File.join(dest, CGI.unescape(self.url))
|
94
|
-
if self.
|
109
|
+
if self.url =~ /\/$/
|
95
110
|
FileUtils.mkdir_p(path)
|
96
111
|
path = File.join(path, "index.html")
|
97
112
|
end
|
@@ -105,11 +120,13 @@ module Jekyll
|
|
105
120
|
"#<Jekyll:Page @name=#{self.name.inspect}>"
|
106
121
|
end
|
107
122
|
|
108
|
-
|
123
|
+
def html?
|
124
|
+
output_ext == '.html'
|
125
|
+
end
|
109
126
|
|
110
|
-
|
111
|
-
|
112
|
-
|
127
|
+
def index?
|
128
|
+
basename == 'index'
|
129
|
+
end
|
113
130
|
|
114
131
|
end
|
115
132
|
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Jekyll
|
2
|
+
|
3
|
+
class Plugin
|
4
|
+
PRIORITIES = { :lowest => -100,
|
5
|
+
:low => -10,
|
6
|
+
:normal => 0,
|
7
|
+
:high => 10,
|
8
|
+
:highest => 100 }
|
9
|
+
|
10
|
+
# Install a hook so that subclasses are recorded. This method is only
|
11
|
+
# ever called by Ruby itself.
|
12
|
+
#
|
13
|
+
# base - The Class subclass.
|
14
|
+
#
|
15
|
+
# Returns nothing.
|
16
|
+
def self.inherited(base)
|
17
|
+
subclasses << base
|
18
|
+
subclasses.sort!
|
19
|
+
end
|
20
|
+
|
21
|
+
# The list of Classes that have been subclassed.
|
22
|
+
#
|
23
|
+
# Returns an Array of Class objects.
|
24
|
+
def self.subclasses
|
25
|
+
@subclasses ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
# Get or set the priority of this plugin. When called without an
|
29
|
+
# argument it returns the priority. When an argument is given, it will
|
30
|
+
# set the priority.
|
31
|
+
#
|
32
|
+
# priority - The Symbol priority (default: nil). Valid options are:
|
33
|
+
# :lowest, :low, :normal, :high, :highest
|
34
|
+
#
|
35
|
+
# Returns the Symbol priority.
|
36
|
+
def self.priority(priority = nil)
|
37
|
+
if priority && PRIORITIES.has_key?(priority)
|
38
|
+
@priority = priority
|
39
|
+
end
|
40
|
+
@priority || :normal
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get or set the safety of this plugin. When called without an argument
|
44
|
+
# it returns the safety. When an argument is given, it will set the
|
45
|
+
# safety.
|
46
|
+
#
|
47
|
+
# safe - The Boolean safety (default: nil).
|
48
|
+
#
|
49
|
+
# Returns the safety Boolean.
|
50
|
+
def self.safe(safe = nil)
|
51
|
+
if safe
|
52
|
+
@safe = safe
|
53
|
+
end
|
54
|
+
@safe || false
|
55
|
+
end
|
56
|
+
|
57
|
+
# Spaceship is priority [higher -> lower]
|
58
|
+
#
|
59
|
+
# other - The class to be compared.
|
60
|
+
#
|
61
|
+
# Returns -1, 0, 1.
|
62
|
+
def self.<=>(other)
|
63
|
+
PRIORITIES[other.priority] <=> PRIORITIES[self.priority]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Initialize a new plugin. This should be overridden by the subclass.
|
67
|
+
#
|
68
|
+
# config - The Hash of configuration options.
|
69
|
+
#
|
70
|
+
# Returns a new instance.
|
71
|
+
def initialize(config = {})
|
72
|
+
# no-op for default
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
data/lib/jekyll/post.rb
CHANGED
@@ -124,9 +124,12 @@ module Jekyll
|
|
124
124
|
"month" => date.strftime("%m"),
|
125
125
|
"day" => date.strftime("%d"),
|
126
126
|
"title" => CGI.escape(slug),
|
127
|
-
"
|
127
|
+
"i_day" => date.strftime("%d").to_i.to_s,
|
128
|
+
"i_month" => date.strftime("%m").to_i.to_s,
|
129
|
+
"categories" => categories.join('/'),
|
130
|
+
"output_ext" => self.output_ext
|
128
131
|
}.inject(template) { |result, token|
|
129
|
-
result.gsub(/:#{token.first}/, token.last)
|
132
|
+
result.gsub(/:#{Regexp.escape token.first}/, token.last)
|
130
133
|
}.gsub(/\/\//, "/")
|
131
134
|
end
|
132
135
|
|
@@ -167,12 +170,10 @@ module Jekyll
|
|
167
170
|
# Returns nothing
|
168
171
|
def render(layouts, site_payload)
|
169
172
|
# construct payload
|
170
|
-
payload =
|
171
|
-
{
|
173
|
+
payload = {
|
172
174
|
"site" => { "related_posts" => related_posts(site_payload["site"]["posts"]) },
|
173
175
|
"page" => self.to_liquid
|
174
|
-
}
|
175
|
-
payload = payload.deep_merge(site_payload)
|
176
|
+
}.deep_merge(site_payload)
|
176
177
|
|
177
178
|
do_layout(payload, layouts)
|
178
179
|
end
|
@@ -201,7 +202,8 @@ module Jekyll
|
|
201
202
|
#
|
202
203
|
# Returns <Hash>
|
203
204
|
def to_liquid
|
204
|
-
|
205
|
+
self.data.deep_merge({
|
206
|
+
"title" => self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
|
205
207
|
"url" => self.url,
|
206
208
|
"date" => self.date,
|
207
209
|
"id" => self.id,
|
@@ -209,7 +211,7 @@ module Jekyll
|
|
209
211
|
"next" => self.next,
|
210
212
|
"previous" => self.previous,
|
211
213
|
"tags" => self.tags,
|
212
|
-
"content" => self.content }
|
214
|
+
"content" => self.content })
|
213
215
|
end
|
214
216
|
|
215
217
|
def inspect
|
data/lib/jekyll/site.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
module Jekyll
|
2
2
|
|
3
3
|
class Site
|
4
|
-
attr_accessor :config, :layouts, :posts, :pages, :static_files,
|
5
|
-
:
|
4
|
+
attr_accessor :config, :layouts, :posts, :pages, :static_files,
|
5
|
+
:categories, :exclude, :source, :dest, :lsi, :pygments,
|
6
|
+
:permalink_style, :tags, :time, :future, :safe, :plugins
|
7
|
+
attr_accessor :converters, :generators
|
6
8
|
|
7
9
|
# Initialize the site
|
8
10
|
# +config+ is a Hash containing site configurations details
|
@@ -11,18 +13,22 @@ module Jekyll
|
|
11
13
|
def initialize(config)
|
12
14
|
self.config = config.clone
|
13
15
|
|
16
|
+
self.safe = config['safe']
|
14
17
|
self.source = config['source']
|
15
18
|
self.dest = config['destination']
|
19
|
+
self.plugins = config['plugins']
|
16
20
|
self.lsi = config['lsi']
|
17
21
|
self.pygments = config['pygments']
|
18
22
|
self.permalink_style = config['permalink'].to_sym
|
19
23
|
self.exclude = config['exclude'] || []
|
24
|
+
self.future = config['future']
|
20
25
|
|
21
26
|
self.reset
|
22
27
|
self.setup
|
23
28
|
end
|
24
29
|
|
25
30
|
def reset
|
31
|
+
self.time = Time.parse(self.config['time'].to_s) || Time.now
|
26
32
|
self.layouts = {}
|
27
33
|
self.posts = []
|
28
34
|
self.pages = []
|
@@ -32,70 +38,38 @@ module Jekyll
|
|
32
38
|
end
|
33
39
|
|
34
40
|
def setup
|
35
|
-
# Check to see if LSI is enabled.
|
36
41
|
require 'classifier' if self.lsi
|
37
42
|
|
38
|
-
#
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
RDiscount.new(content).to_html
|
46
|
-
end
|
47
|
-
|
48
|
-
rescue LoadError
|
49
|
-
puts 'You must have the rdiscount gem installed first'
|
50
|
-
end
|
51
|
-
when 'maruku'
|
52
|
-
begin
|
53
|
-
require 'maruku'
|
54
|
-
|
55
|
-
def markdown(content)
|
56
|
-
Maruku.new(content).to_html
|
57
|
-
end
|
58
|
-
|
59
|
-
if self.config['maruku']['use_divs']
|
60
|
-
require 'maruku/ext/div'
|
61
|
-
puts 'Maruku: Using extended syntax for div elements.'
|
62
|
-
end
|
63
|
-
|
64
|
-
if self.config['maruku']['use_tex']
|
65
|
-
require 'maruku/ext/math'
|
66
|
-
puts "Maruku: Using LaTeX extension. Images in `#{self.config['maruku']['png_dir']}`."
|
67
|
-
|
68
|
-
# Switch off MathML output
|
69
|
-
MaRuKu::Globals[:html_math_output_mathml] = false
|
70
|
-
MaRuKu::Globals[:html_math_engine] = 'none'
|
43
|
+
# If safe mode is off, load in any ruby files under the plugins
|
44
|
+
# directory.
|
45
|
+
unless self.safe
|
46
|
+
Dir[File.join(self.plugins, "**/*.rb")].each do |f|
|
47
|
+
require f
|
48
|
+
end
|
49
|
+
end
|
71
50
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
MaRuKu::Globals[:html_png_dir] = self.config['maruku']['png_dir']
|
77
|
-
MaRuKu::Globals[:html_png_url] = self.config['maruku']['png_url']
|
78
|
-
end
|
79
|
-
rescue LoadError
|
80
|
-
puts "The maruku gem is required for markdown support!"
|
81
|
-
end
|
82
|
-
else
|
83
|
-
raise "Invalid Markdown processor: '#{self.config['markdown']}' -- did you mean 'maruku' or 'rdiscount'?"
|
51
|
+
self.converters = Jekyll::Converter.subclasses.select do |c|
|
52
|
+
!self.safe || c.safe
|
53
|
+
end.map do |c|
|
54
|
+
c.new(self.config)
|
84
55
|
end
|
85
|
-
end
|
86
56
|
|
87
|
-
|
88
|
-
|
57
|
+
self.generators = Jekyll::Generator.subclasses.select do |c|
|
58
|
+
!self.safe || c.safe
|
59
|
+
end.map do |c|
|
60
|
+
c.new(self.config)
|
61
|
+
end
|
89
62
|
end
|
90
63
|
|
91
64
|
# Do the actual work of processing the site and generating the
|
92
|
-
# real deal.
|
65
|
+
# real deal. 5 phases; reset, read, generate, render, write. This allows
|
93
66
|
# rendering to have full site payload available.
|
94
67
|
#
|
95
68
|
# Returns nothing
|
96
69
|
def process
|
97
70
|
self.reset
|
98
71
|
self.read
|
72
|
+
self.generate
|
99
73
|
self.render
|
100
74
|
self.write
|
101
75
|
end
|
@@ -135,7 +109,7 @@ module Jekyll
|
|
135
109
|
if Post.valid?(f)
|
136
110
|
post = Post.new(self, self.source, dir, f)
|
137
111
|
|
138
|
-
if post.published
|
112
|
+
if post.published && (self.future || post.date <= self.time)
|
139
113
|
self.posts << post
|
140
114
|
post.categories.each { |c| self.categories[c] << post }
|
141
115
|
post.tags.each { |c| self.tags[c] << post }
|
@@ -146,17 +120,19 @@ module Jekyll
|
|
146
120
|
self.posts.sort!
|
147
121
|
end
|
148
122
|
|
123
|
+
def generate
|
124
|
+
self.generators.each do |generator|
|
125
|
+
generator.generate(self)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
149
129
|
def render
|
150
130
|
self.posts.each do |post|
|
151
131
|
post.render(self.layouts, site_payload)
|
152
132
|
end
|
153
133
|
|
154
|
-
self.pages.
|
155
|
-
|
156
|
-
paginate(page)
|
157
|
-
else
|
158
|
-
page.render(self.layouts, site_payload)
|
159
|
-
end
|
134
|
+
self.pages.each do |page|
|
135
|
+
page.render(self.layouts, site_payload)
|
160
136
|
end
|
161
137
|
|
162
138
|
self.categories.values.map { |ps| ps.sort! { |a, b| b <=> a} }
|
@@ -180,7 +156,7 @@ module Jekyll
|
|
180
156
|
end
|
181
157
|
end
|
182
158
|
|
183
|
-
# Reads the directories and finds posts, pages and static files that will
|
159
|
+
# Reads the directories and finds posts, pages and static files that will
|
184
160
|
# become part of the valid site according to the rules in +filter_entries+.
|
185
161
|
# The +dir+ String is a relative path used to call this method
|
186
162
|
# recursively as it descends through directories
|
@@ -227,11 +203,14 @@ module Jekyll
|
|
227
203
|
#
|
228
204
|
# Returns {"site" => {"time" => <Time>,
|
229
205
|
# "posts" => [<Post>],
|
206
|
+
# "pages" => [<Page>],
|
230
207
|
# "categories" => [<Post>]}
|
231
208
|
def site_payload
|
232
209
|
{"site" => self.config.merge({
|
233
|
-
"time" =>
|
210
|
+
"time" => self.time,
|
234
211
|
"posts" => self.posts.sort { |a,b| b <=> a },
|
212
|
+
"pages" => self.pages,
|
213
|
+
"html_pages" => self.pages.reject { |page| !page.html? },
|
235
214
|
"categories" => post_attr_hash('categories'),
|
236
215
|
"tags" => post_attr_hash('tags')})}
|
237
216
|
end
|
@@ -248,32 +227,5 @@ module Jekyll
|
|
248
227
|
end
|
249
228
|
end
|
250
229
|
|
251
|
-
# Paginates the blog's posts. Renders the index.html file into paginated
|
252
|
-
# directories, ie: page2/index.html, page3/index.html, etc and adds more
|
253
|
-
# site-wide data.
|
254
|
-
# +page+ is the index.html Page that requires pagination
|
255
|
-
#
|
256
|
-
# {"paginator" => { "page" => <Number>,
|
257
|
-
# "per_page" => <Number>,
|
258
|
-
# "posts" => [<Post>],
|
259
|
-
# "total_posts" => <Number>,
|
260
|
-
# "total_pages" => <Number>,
|
261
|
-
# "previous_page" => <Number>,
|
262
|
-
# "next_page" => <Number> }}
|
263
|
-
def paginate(page)
|
264
|
-
all_posts = site_payload['site']['posts']
|
265
|
-
pages = Pager.calculate_pages(all_posts, self.config['paginate'].to_i)
|
266
|
-
(1..pages).each do |num_page|
|
267
|
-
pager = Pager.new(self.config, num_page, all_posts, pages)
|
268
|
-
if num_page > 1
|
269
|
-
newpage = Page.new(self, self.source, page.dir, page.name)
|
270
|
-
newpage.render(self.layouts, site_payload.merge({'paginator' => pager.to_hash}))
|
271
|
-
newpage.dir = File.join(page.dir, "page#{num_page}")
|
272
|
-
self.pages << newpage
|
273
|
-
else
|
274
|
-
page.render(self.layouts, site_payload.merge({'paginator' => pager.to_hash}))
|
275
|
-
end
|
276
|
-
end
|
277
|
-
end
|
278
230
|
end
|
279
231
|
end
|
data/lib/jekyll/static_file.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Jekyll
|
2
2
|
|
3
3
|
class StaticFile
|
4
|
+
@@mtimes = Hash.new # the cache of last modification times [path] -> mtime
|
5
|
+
|
4
6
|
# Initialize a new StaticFile.
|
5
7
|
# +site+ is the Site
|
6
8
|
# +base+ is the String path to the <source>
|
@@ -15,13 +17,59 @@ module Jekyll
|
|
15
17
|
@name = name
|
16
18
|
end
|
17
19
|
|
18
|
-
#
|
20
|
+
# Obtains source file path.
|
21
|
+
#
|
22
|
+
# Returns source file path.
|
23
|
+
def path
|
24
|
+
File.join(@base, @dir, @name)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Obtain destination path.
|
19
28
|
# +dest+ is the String path to the destination dir
|
20
29
|
#
|
21
|
-
# Returns
|
30
|
+
# Returns destination file path.
|
31
|
+
def destination(dest)
|
32
|
+
File.join(dest, @dir, @name)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Obtain mtime of the source path.
|
36
|
+
#
|
37
|
+
# Returns last modifiaction time for this file.
|
38
|
+
def mtime
|
39
|
+
File.stat(path).mtime.to_i
|
40
|
+
end
|
41
|
+
|
42
|
+
# Is source path modified?
|
43
|
+
#
|
44
|
+
# Returns true if modified since last write.
|
45
|
+
def modified?
|
46
|
+
@@mtimes[path] != mtime
|
47
|
+
end
|
48
|
+
|
49
|
+
# Write the static file to the destination directory (if modified).
|
50
|
+
# +dest+ is the String path to the destination dir
|
51
|
+
#
|
52
|
+
# Returns false if the file was not modified since last time (no-op).
|
22
53
|
def write(dest)
|
23
|
-
|
24
|
-
|
54
|
+
dest_path = destination(dest)
|
55
|
+
dest_dir = File.join(dest, @dir)
|
56
|
+
|
57
|
+
return false if File.exist? dest_path and !modified?
|
58
|
+
@@mtimes[path] = mtime
|
59
|
+
|
60
|
+
FileUtils.mkdir_p(dest_dir)
|
61
|
+
FileUtils.cp(path, dest_path)
|
62
|
+
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
# Reset the mtimes cache (for testing purposes).
|
67
|
+
#
|
68
|
+
# Returns nothing.
|
69
|
+
def self.reset_cache
|
70
|
+
@@mtimes = Hash.new
|
71
|
+
|
72
|
+
nil
|
25
73
|
end
|
26
74
|
end
|
27
75
|
|