jberkel-jekyll 0.5.4 → 0.5.7
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +37 -0
- data/README.textile +15 -8
- data/VERSION.yml +3 -2
- data/bin/jekyll +5 -1
- data/features/create_sites.feature +49 -1
- data/features/embed_filters.feature +3 -3
- data/features/markdown.feature +30 -0
- data/features/pagination.feature +12 -25
- data/features/post_data.feature +18 -18
- data/features/step_definitions/jekyll_steps.rb +8 -8
- data/jekyll.gemspec +14 -4
- data/lib/jekyll.rb +4 -3
- data/lib/jekyll/convertible.rb +6 -2
- data/lib/jekyll/core_ext.rb +22 -0
- data/lib/jekyll/page.rb +5 -1
- data/lib/jekyll/pager.rb +1 -1
- data/lib/jekyll/post.rb +13 -25
- data/lib/jekyll/site.rb +82 -68
- data/lib/jekyll/static_file.rb +28 -0
- data/lib/jekyll/tags/highlight.rb +14 -8
- data/test/helper.rb +1 -1
- 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-05-18-empty-tag.textile +6 -0
- data/test/source/_posts/2009-05-18-empty-tags.textile +6 -0
- data/test/source/_posts/2010-01-08-triple-dash.markdown +5 -0
- data/test/source/_posts/2010-01-09-date-override.textile +5 -0
- data/test/source/_posts/2010-01-09-time-override.textile +5 -0
- data/test/test_configuration.rb +5 -5
- data/test/test_core_ext.rb +66 -0
- data/test/test_generated_site.rb +4 -0
- data/test/test_pager.rb +92 -26
- data/test/test_post.rb +46 -3
- data/test/test_site.rb +8 -2
- data/test/test_tags.rb +2 -2
- metadata +71 -30
- data/lib/jekyll/converters/marley.rb +0 -53
data/lib/jekyll.rb
CHANGED
@@ -26,6 +26,7 @@ require 'jekyll/filters'
|
|
26
26
|
require 'jekyll/tags/highlight'
|
27
27
|
require 'jekyll/tags/include'
|
28
28
|
require 'jekyll/albino'
|
29
|
+
require 'jekyll/static_file'
|
29
30
|
|
30
31
|
module Jekyll
|
31
32
|
# Default options. Overriden by values in _config.yml or command-line opts.
|
@@ -68,10 +69,10 @@ module Jekyll
|
|
68
69
|
begin
|
69
70
|
config = YAML.load_file(config_file)
|
70
71
|
raise "Invalid configuration - #{config_file}" if !config.is_a?(Hash)
|
71
|
-
|
72
|
+
$stdout.puts "Configuration from #{config_file}"
|
72
73
|
rescue => err
|
73
|
-
|
74
|
-
|
74
|
+
$stderr.puts "WARNING: Could not read configuration. Using defaults (and options)."
|
75
|
+
$stderr.puts "\t" + err.to_s
|
75
76
|
config = {}
|
76
77
|
end
|
77
78
|
|
data/lib/jekyll/convertible.rb
CHANGED
@@ -3,6 +3,10 @@
|
|
3
3
|
#
|
4
4
|
# Requires
|
5
5
|
# self.site -> Jekyll::Site
|
6
|
+
# self.content=
|
7
|
+
# self.data=
|
8
|
+
# self.ext=
|
9
|
+
# self.output=
|
6
10
|
module Jekyll
|
7
11
|
module Convertible
|
8
12
|
# Return the contents as a string
|
@@ -20,7 +24,7 @@ module Jekyll
|
|
20
24
|
|
21
25
|
self.data ||= {}
|
22
26
|
|
23
|
-
if self.content =~ /^(---\s*\n.*?\n?)(
|
27
|
+
if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
|
24
28
|
self.content = self.content[($1.size + $2.size)..-1]
|
25
29
|
self.data = self.data.deep_merge(YAML.load($1) || {})
|
26
30
|
end
|
@@ -52,7 +56,7 @@ module Jekyll
|
|
52
56
|
case self.ext[1..-1]
|
53
57
|
when /textile/i
|
54
58
|
return 'textile'
|
55
|
-
when /markdown/i, /mkdn/i, /md/i
|
59
|
+
when /markdown/i, /mkdn/i, /md/i, /mkd/i
|
56
60
|
return 'markdown'
|
57
61
|
when /haml/i
|
58
62
|
return 'haml'
|
data/lib/jekyll/core_ext.rb
CHANGED
@@ -19,6 +19,28 @@ class Hash
|
|
19
19
|
|
20
20
|
target
|
21
21
|
end
|
22
|
+
|
23
|
+
# Read array from the supplied hash favouring the singular key
|
24
|
+
# and then the plural key, and handling any nil entries.
|
25
|
+
# +hash+ the hash to read from
|
26
|
+
# +singular_key+ the singular key
|
27
|
+
# +plural_key+ the singular key
|
28
|
+
#
|
29
|
+
# Returns an array
|
30
|
+
def pluralized_array(singular_key, plural_key)
|
31
|
+
hash = self
|
32
|
+
if hash.has_key?(singular_key)
|
33
|
+
array = [hash[singular_key]] if hash[singular_key]
|
34
|
+
elsif hash.has_key?(plural_key)
|
35
|
+
case hash[plural_key]
|
36
|
+
when String
|
37
|
+
array = hash[plural_key].split
|
38
|
+
when Array
|
39
|
+
array = hash[plural_key].compact
|
40
|
+
end
|
41
|
+
end
|
42
|
+
array || []
|
43
|
+
end
|
22
44
|
end
|
23
45
|
|
24
46
|
|
data/lib/jekyll/page.rb
CHANGED
@@ -4,7 +4,7 @@ module Jekyll
|
|
4
4
|
include Convertible
|
5
5
|
|
6
6
|
attr_accessor :site
|
7
|
-
attr_accessor :name, :ext, :basename
|
7
|
+
attr_accessor :name, :ext, :basename, :dir
|
8
8
|
attr_accessor :data, :content, :output
|
9
9
|
|
10
10
|
# Initialize a new Page.
|
@@ -101,6 +101,10 @@ module Jekyll
|
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
|
+
def inspect
|
105
|
+
"#<Jekyll:Page @name=#{self.name.inspect}>"
|
106
|
+
end
|
107
|
+
|
104
108
|
private
|
105
109
|
|
106
110
|
def index?
|
data/lib/jekyll/pager.rb
CHANGED
data/lib/jekyll/post.rb
CHANGED
@@ -20,12 +20,9 @@ module Jekyll
|
|
20
20
|
name =~ MATCHER
|
21
21
|
end
|
22
22
|
|
23
|
-
attr_accessor :site
|
24
|
-
|
25
|
-
|
26
|
-
def categories
|
27
|
-
@categories ||= []
|
28
|
-
end
|
23
|
+
attr_accessor :site
|
24
|
+
attr_accessor :data, :content, :output, :ext
|
25
|
+
attr_accessor :date, :slug, :published, :tags, :categories
|
29
26
|
|
30
27
|
# Initialize this Post instance.
|
31
28
|
# +site+ is the Site
|
@@ -46,6 +43,13 @@ module Jekyll
|
|
46
43
|
self.read_yaml(@base, name)
|
47
44
|
|
48
45
|
extract_title_from_first_header_or_slug
|
46
|
+
#
|
47
|
+
#If we've added a date and time to the yaml, use that instead of the filename date
|
48
|
+
#Means we'll sort correctly.
|
49
|
+
if self.data.has_key?('date')
|
50
|
+
# ensure Time via to_s and reparse
|
51
|
+
self.date = Time.parse(self.data["date"].to_s)
|
52
|
+
end
|
49
53
|
|
50
54
|
if self.data.has_key?('published') && self.data['published'] == false
|
51
55
|
self.published = false
|
@@ -53,26 +57,10 @@ module Jekyll
|
|
53
57
|
self.published = true
|
54
58
|
end
|
55
59
|
|
56
|
-
|
57
|
-
self.tags = [self.data["tag"]]
|
58
|
-
elsif self.data.has_key?("tags")
|
59
|
-
self.tags = self.data['tags']
|
60
|
-
else
|
61
|
-
self.tags = []
|
62
|
-
end
|
60
|
+
self.tags = self.data.pluralized_array("tag", "tags")
|
63
61
|
|
64
62
|
if self.categories.empty?
|
65
|
-
|
66
|
-
self.categories << self.data['category']
|
67
|
-
elsif self.data.has_key?('categories')
|
68
|
-
# Look for categories in the YAML-header, either specified as
|
69
|
-
# an array or a string.
|
70
|
-
if self.data['categories'].kind_of? String
|
71
|
-
self.categories = self.data['categories'].split
|
72
|
-
else
|
73
|
-
self.categories = self.data['categories']
|
74
|
-
end
|
75
|
-
end
|
63
|
+
self.categories = self.data.pluralized_array('category', 'categories')
|
76
64
|
end
|
77
65
|
end
|
78
66
|
|
@@ -143,7 +131,7 @@ module Jekyll
|
|
143
131
|
"month" => date.strftime("%m"),
|
144
132
|
"day" => date.strftime("%d"),
|
145
133
|
"title" => CGI.escape(slug),
|
146
|
-
"categories" => categories.
|
134
|
+
"categories" => categories.join('/')
|
147
135
|
}.inject(template) { |result, token|
|
148
136
|
result.gsub(/:#{token.first}/, token.last)
|
149
137
|
}.gsub("//", "/")
|
data/lib/jekyll/site.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
module Jekyll
|
2
2
|
|
3
3
|
class Site
|
4
|
-
attr_accessor :config, :layouts, :posts, :categories, :exclude,
|
4
|
+
attr_accessor :config, :layouts, :posts, :pages, :static_files, :categories, :exclude,
|
5
5
|
:source, :dest, :lsi, :pygments, :permalink_style, :tags
|
6
|
-
|
6
|
+
|
7
7
|
attr_accessor :sass, :post_defaults, :collated_posts
|
8
8
|
|
9
9
|
|
@@ -14,7 +14,7 @@ module Jekyll
|
|
14
14
|
def initialize(config)
|
15
15
|
self.config = config.clone
|
16
16
|
|
17
|
-
self.source = config['source']
|
17
|
+
self.source = File.expand_path(config['source'])
|
18
18
|
self.dest = config['destination']
|
19
19
|
self.lsi = config['lsi']
|
20
20
|
self.pygments = config['pygments']
|
@@ -30,6 +30,8 @@ module Jekyll
|
|
30
30
|
self.layouts = {}
|
31
31
|
self.posts = []
|
32
32
|
self.collated_posts = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = [] } } }
|
33
|
+
self.pages = []
|
34
|
+
self.static_files = []
|
33
35
|
self.categories = Hash.new { |hash, key| hash[key] = [] }
|
34
36
|
self.tags = Hash.new { |hash, key| hash[key] = [] }
|
35
37
|
end
|
@@ -37,7 +39,7 @@ module Jekyll
|
|
37
39
|
def setup
|
38
40
|
# Check to see if LSI is enabled.
|
39
41
|
require 'classifier' if self.lsi
|
40
|
-
|
42
|
+
|
41
43
|
if self.config['sass']
|
42
44
|
begin
|
43
45
|
require 'sass'
|
@@ -47,7 +49,7 @@ module Jekyll
|
|
47
49
|
puts 'You must have the haml gem installed first'
|
48
50
|
end
|
49
51
|
end
|
50
|
-
|
52
|
+
|
51
53
|
if self.config['haml']
|
52
54
|
begin
|
53
55
|
require 'haml'
|
@@ -55,11 +57,11 @@ module Jekyll
|
|
55
57
|
helpers = File.join(source, '_helpers.rb')
|
56
58
|
require helpers if File.exist?(helpers)
|
57
59
|
puts 'Enabled Haml'
|
58
|
-
rescue LoadError
|
59
|
-
|
60
|
+
rescue LoadError
|
61
|
+
puts 'You must have the haml gem installed first'
|
60
62
|
end
|
61
63
|
end
|
62
|
-
|
64
|
+
|
63
65
|
# Set the Markdown interpreter (and Maruku self.config, if necessary)
|
64
66
|
case self.config['markdown']
|
65
67
|
when 'rdiscount'
|
@@ -114,22 +116,30 @@ module Jekyll
|
|
114
116
|
end
|
115
117
|
|
116
118
|
# Do the actual work of processing the site and generating the
|
117
|
-
# real deal.
|
119
|
+
# real deal. Now has 4 phases; reset, read, render, write. This allows
|
120
|
+
# rendering to have full site payload available.
|
118
121
|
#
|
119
122
|
# Returns nothing
|
120
123
|
def process
|
121
124
|
self.reset
|
122
|
-
self.
|
123
|
-
self.
|
125
|
+
self.read
|
126
|
+
self.render
|
127
|
+
self.write
|
124
128
|
self.transform_sass if self.sass
|
125
|
-
self.write_posts
|
126
129
|
end
|
127
130
|
|
128
|
-
|
131
|
+
def read
|
132
|
+
self.read_layouts # existing implementation did this at top level only so preserved that
|
133
|
+
self.read_directories
|
134
|
+
end
|
135
|
+
|
136
|
+
# Read all the files in <source>/<dir>/_layouts and create a new Layout
|
137
|
+
# object with each one.
|
129
138
|
#
|
130
139
|
# Returns nothing
|
131
|
-
def read_layouts
|
132
|
-
base = File.join(self.source, "_layouts")
|
140
|
+
def read_layouts(dir = '')
|
141
|
+
base = File.join(self.source, dir, "_layouts")
|
142
|
+
return unless File.exists?(base)
|
133
143
|
entries = []
|
134
144
|
Dir.chdir(base) { entries = filter_entries(Dir['*.*']) }
|
135
145
|
|
@@ -137,17 +147,16 @@ module Jekyll
|
|
137
147
|
name = f.split(".")[0..-2].join(".")
|
138
148
|
self.layouts[name] = Layout.new(self, base, f)
|
139
149
|
end
|
140
|
-
rescue Errno::ENOENT => e
|
141
|
-
# ignore missing layout dir
|
142
150
|
end
|
143
151
|
|
144
|
-
# Read all the files in <
|
152
|
+
# Read all the files in <source>/<dir>/_posts and create a new Post
|
153
|
+
# object with each one.
|
145
154
|
#
|
146
155
|
# Returns nothing
|
147
156
|
def read_posts(dir)
|
148
157
|
base = File.join(self.source, dir, '_posts')
|
149
|
-
|
150
|
-
Dir.chdir(base) {
|
158
|
+
return unless File.exists?(base)
|
159
|
+
entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
|
151
160
|
|
152
161
|
# first pass processes, but does not yet render post content
|
153
162
|
entries.each do |f|
|
@@ -163,69 +172,69 @@ module Jekyll
|
|
163
172
|
end
|
164
173
|
|
165
174
|
self.posts.sort!
|
175
|
+
end
|
166
176
|
|
167
|
-
|
177
|
+
def render
|
168
178
|
self.posts.each do |post|
|
169
179
|
post.render(self.layouts, site_payload)
|
170
180
|
self.collated_posts[post.date.year][post.date.month][post.date.day].unshift(post)
|
171
181
|
end
|
172
182
|
|
183
|
+
self.pages.dup.each do |page|
|
184
|
+
if Pager.pagination_enabled?(self.config, page.name)
|
185
|
+
paginate(page)
|
186
|
+
else
|
187
|
+
page.render(self.layouts, site_payload)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
173
191
|
self.categories.values.map { |ps| ps.sort! { |a, b| b <=> a} }
|
174
192
|
self.tags.values.map { |ps| ps.sort! { |a, b| b <=> a} }
|
175
193
|
rescue Errno::ENOENT => e
|
176
194
|
# ignore missing layout dir
|
177
195
|
end
|
178
196
|
|
179
|
-
# Write
|
197
|
+
# Write static files, pages and posts
|
180
198
|
#
|
181
199
|
# Returns nothing
|
182
|
-
def
|
200
|
+
def write
|
183
201
|
self.posts.each do |post|
|
184
202
|
post.write(self.dest)
|
185
203
|
end
|
204
|
+
self.pages.each do |page|
|
205
|
+
page.write(self.dest)
|
206
|
+
end
|
207
|
+
self.static_files.each do |sf|
|
208
|
+
sf.write(self.dest)
|
209
|
+
end
|
186
210
|
end
|
187
211
|
|
188
|
-
#
|
189
|
-
#
|
190
|
-
# with "." or "#" or end with "~") or contain site content (start with "_")
|
191
|
-
# unless they are "_posts" directories or web server files such as
|
192
|
-
# '.htaccess'
|
212
|
+
# Reads the directories and finds posts, pages and static files that will
|
213
|
+
# become part of the valid site according to the rules in +filter_entries+.
|
193
214
|
# The +dir+ String is a relative path used to call this method
|
194
215
|
# recursively as it descends through directories
|
195
216
|
#
|
196
217
|
# Returns nothing
|
197
|
-
def
|
218
|
+
def read_directories(dir = '')
|
198
219
|
base = File.join(self.source, dir)
|
199
220
|
entries = filter_entries(Dir.entries(base))
|
200
|
-
directories = entries.select { |e| File.directory?(File.join(base, e)) }
|
201
|
-
files = entries.reject { |e| File.directory?(File.join(base, e)) || File.symlink?(File.join(base, e)) }
|
202
221
|
|
203
|
-
|
204
|
-
# might not be available yet to other templates as {{ site.posts }}
|
205
|
-
if directories.include?('_posts')
|
206
|
-
directories.delete('_posts')
|
207
|
-
read_posts(dir)
|
208
|
-
end
|
222
|
+
self.read_posts(dir)
|
209
223
|
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
224
|
+
entries.each do |f|
|
225
|
+
f_abs = File.join(base, f)
|
226
|
+
f_rel = File.join(dir, f)
|
227
|
+
if File.directory?(f_abs)
|
228
|
+
next if self.dest.sub(/\/$/, '') == f_abs
|
229
|
+
read_directories(f_rel)
|
230
|
+
elsif !File.symlink?(f_abs)
|
231
|
+
first3 = File.open(f_abs) { |fd| fd.read(3) }
|
232
|
+
if first3 == "---"
|
233
|
+
# file appears to have a YAML header so process it as a page
|
234
|
+
pages << Page.new(self, self.source, dir, f)
|
217
235
|
else
|
218
|
-
|
219
|
-
|
220
|
-
# file appears to have a YAML header so process it as a page
|
221
|
-
page = Page.new(self, self.source, dir, f)
|
222
|
-
page.render(self.layouts, site_payload)
|
223
|
-
page.write(self.dest)
|
224
|
-
else
|
225
|
-
# otherwise copy the file without transforming it
|
226
|
-
FileUtils.mkdir_p(File.join(self.dest, dir))
|
227
|
-
FileUtils.cp(File.join(self.source, dir, f), File.join(self.dest, dir, f))
|
228
|
-
end
|
236
|
+
# otherwise treat it as a static file
|
237
|
+
static_files << StaticFile.new(self, self.source, dir, f)
|
229
238
|
end
|
230
239
|
end
|
231
240
|
end
|
@@ -279,19 +288,21 @@ module Jekyll
|
|
279
288
|
end
|
280
289
|
|
281
290
|
# Filter out any files/directories that are hidden or backup files (start
|
282
|
-
# with "." or "#" or end with "~") or contain site content (start with "_")
|
283
|
-
#
|
284
|
-
# '.htaccess'
|
291
|
+
# with "." or "#" or end with "~"), or contain site content (start with "_"),
|
292
|
+
# or are excluded in the site configuration, unless they are web server
|
293
|
+
# files such as '.htaccess'
|
285
294
|
def filter_entries(entries)
|
286
295
|
entries = entries.reject do |e|
|
287
|
-
unless ['
|
296
|
+
unless ['.htaccess'].include?(e)
|
288
297
|
['.', '_', '#'].include?(e[0..0]) || e[-1..-1] == '~' || self.exclude.include?(e)
|
289
298
|
end
|
290
299
|
end
|
291
300
|
end
|
292
301
|
|
293
|
-
# Paginates the blog's posts. Renders the index.html file into paginated
|
294
|
-
# and adds more
|
302
|
+
# Paginates the blog's posts. Renders the index.html file into paginated
|
303
|
+
# directories, ie: page2/index.html, page3/index.html, etc and adds more
|
304
|
+
# site-wide data.
|
305
|
+
# +page+ is the index.html Page that requires pagination
|
295
306
|
#
|
296
307
|
# {"paginator" => { "page" => <Number>,
|
297
308
|
# "per_page" => <Number>,
|
@@ -300,16 +311,19 @@ module Jekyll
|
|
300
311
|
# "total_pages" => <Number>,
|
301
312
|
# "previous_page" => <Number>,
|
302
313
|
# "next_page" => <Number> }}
|
303
|
-
def
|
304
|
-
all_posts =
|
314
|
+
def paginate(page)
|
315
|
+
all_posts = site_payload['site']['posts']
|
305
316
|
pages = Pager.calculate_pages(all_posts, self.config['paginate'].to_i)
|
306
|
-
pages += 1
|
307
317
|
(1..pages).each do |num_page|
|
308
318
|
pager = Pager.new(self.config, num_page, all_posts, pages)
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
319
|
+
if num_page > 1
|
320
|
+
newpage = Page.new(self, self.source, page.dir, page.name)
|
321
|
+
newpage.render(self.layouts, site_payload.merge({'paginator' => pager.to_hash}))
|
322
|
+
newpage.dir = File.join(page.dir, "page#{num_page}")
|
323
|
+
self.pages << newpage
|
324
|
+
else
|
325
|
+
page.render(self.layouts, site_payload.merge({'paginator' => pager.to_hash}))
|
326
|
+
end
|
313
327
|
end
|
314
328
|
end
|
315
329
|
end
|