cduruk-jekyll 0.5.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.
- data/History.txt +115 -0
- data/README.textile +649 -0
- data/Rakefile +91 -0
- data/VERSION.yml +4 -0
- data/bin/jekyll +156 -0
- data/lib/jekyll.rb +84 -0
- data/lib/jekyll/albino.rb +122 -0
- data/lib/jekyll/converters/csv.rb +26 -0
- data/lib/jekyll/converters/mephisto.rb +79 -0
- data/lib/jekyll/converters/mt.rb +59 -0
- data/lib/jekyll/converters/textpattern.rb +50 -0
- data/lib/jekyll/converters/typo.rb +49 -0
- data/lib/jekyll/converters/wordpress.rb +54 -0
- data/lib/jekyll/convertible.rb +117 -0
- data/lib/jekyll/core_ext.rb +29 -0
- data/lib/jekyll/filters.rb +47 -0
- data/lib/jekyll/haml_helpers.rb +15 -0
- data/lib/jekyll/layout.rb +37 -0
- data/lib/jekyll/page.rb +67 -0
- data/lib/jekyll/post.rb +305 -0
- data/lib/jekyll/site.rb +300 -0
- data/lib/jekyll/tags/highlight.rb +68 -0
- data/lib/jekyll/tags/include.rb +31 -0
- data/test/helper.rb +24 -0
- data/test/source/_includes/sig.markdown +3 -0
- data/test/source/_layouts/default.html +27 -0
- data/test/source/_layouts/simple.html +1 -0
- 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/2008-10-18-foo-bar.textile +8 -0
- data/test/source/_posts/2008-11-21-complex.textile +8 -0
- data/test/source/_posts/2008-12-03-permalinked-post.textile +9 -0
- data/test/source/_posts/2008-12-13-include.markdown +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/source/category/_posts/2008-9-23-categories.textile +6 -0
- data/test/source/css/screen.css +76 -0
- data/test/source/foo/_posts/bar/2008-12-12-topical-post.textile +8 -0
- data/test/source/index.html +22 -0
- data/test/source/z_category/_posts/2008-9-23-categories.textile +6 -0
- data/test/suite.rb +9 -0
- data/test/test_filters.rb +41 -0
- data/test/test_generated_site.rb +38 -0
- data/test/test_post.rb +262 -0
- data/test/test_site.rb +57 -0
- data/test/test_tags.rb +51 -0
- metadata +165 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module Jekyll
|
2
|
+
|
3
|
+
module Filters
|
4
|
+
def textilize(input)
|
5
|
+
RedCloth.new(input).to_html
|
6
|
+
end
|
7
|
+
|
8
|
+
def date_to_string(date)
|
9
|
+
date.strftime("%d %b %Y")
|
10
|
+
end
|
11
|
+
|
12
|
+
def date_to_long_string(date)
|
13
|
+
date.strftime("%d %B %Y")
|
14
|
+
end
|
15
|
+
|
16
|
+
def date_to_xmlschema(date)
|
17
|
+
date.xmlschema
|
18
|
+
end
|
19
|
+
|
20
|
+
def date_to_utc(date)
|
21
|
+
date.utc
|
22
|
+
end
|
23
|
+
|
24
|
+
def xml_escape(input)
|
25
|
+
input.gsub("&", "&").gsub("<", "<").gsub(">", ">")
|
26
|
+
end
|
27
|
+
|
28
|
+
def number_of_words(input)
|
29
|
+
input.split.length
|
30
|
+
end
|
31
|
+
|
32
|
+
def array_to_sentence_string(array)
|
33
|
+
connector = "and"
|
34
|
+
case array.length
|
35
|
+
when 0
|
36
|
+
""
|
37
|
+
when 1
|
38
|
+
array[0].to_s
|
39
|
+
when 2
|
40
|
+
"#{array[0]} #{connector} #{array[1]}"
|
41
|
+
else
|
42
|
+
"#{array[0...-1].join(', ')}, #{connector} #{array[-1]}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Jekyll
|
2
|
+
|
3
|
+
class Layout
|
4
|
+
include Convertible
|
5
|
+
|
6
|
+
attr_accessor :site
|
7
|
+
attr_accessor :ext
|
8
|
+
attr_accessor :data, :content
|
9
|
+
|
10
|
+
# Initialize a new Layout.
|
11
|
+
# +site+ is the Site
|
12
|
+
# +base+ is the String path to the <source>
|
13
|
+
# +name+ is the String filename of the post file
|
14
|
+
#
|
15
|
+
# Returns <Page>
|
16
|
+
def initialize(site, base, name)
|
17
|
+
@site = site
|
18
|
+
@base = base
|
19
|
+
@name = name
|
20
|
+
|
21
|
+
self.data = {}
|
22
|
+
|
23
|
+
self.process(name)
|
24
|
+
self.read_yaml(base, name)
|
25
|
+
self.transform
|
26
|
+
end
|
27
|
+
|
28
|
+
# Extract information from the layout filename
|
29
|
+
# +name+ is the String filename of the layout file
|
30
|
+
#
|
31
|
+
# Returns nothing
|
32
|
+
def process(name)
|
33
|
+
self.ext = File.extname(name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/lib/jekyll/page.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module Jekyll
|
2
|
+
|
3
|
+
class Page
|
4
|
+
include Convertible
|
5
|
+
|
6
|
+
attr_accessor :site
|
7
|
+
attr_accessor :ext
|
8
|
+
attr_accessor :data, :content, :output
|
9
|
+
|
10
|
+
# Initialize a new Page.
|
11
|
+
# +site+ is the Site
|
12
|
+
# +base+ is the String path to the <source>
|
13
|
+
# +dir+ is the String path between <source> and the file
|
14
|
+
# +name+ is the String filename of the file
|
15
|
+
#
|
16
|
+
# Returns <Page>
|
17
|
+
def initialize(site, base, dir, name)
|
18
|
+
@site = site
|
19
|
+
@base = base
|
20
|
+
@dir = dir
|
21
|
+
@name = name
|
22
|
+
|
23
|
+
self.data = {}
|
24
|
+
|
25
|
+
self.process(name)
|
26
|
+
self.read_yaml(File.join(base, dir), name)
|
27
|
+
#self.transform
|
28
|
+
end
|
29
|
+
|
30
|
+
# Extract information from the page filename
|
31
|
+
# +name+ is the String filename of the page file
|
32
|
+
#
|
33
|
+
# Returns nothing
|
34
|
+
def process(name)
|
35
|
+
self.ext = File.extname(name)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Add any necessary layouts to this post
|
39
|
+
# +layouts+ is a Hash of {"name" => "layout"}
|
40
|
+
# +site_payload+ is the site payload hash
|
41
|
+
#
|
42
|
+
# Returns nothing
|
43
|
+
def render(layouts, site_payload)
|
44
|
+
payload = {"page" => self.data}.deep_merge(site_payload)
|
45
|
+
do_layout(payload, layouts)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Write the generated page file to the destination directory.
|
49
|
+
# +dest+ is the String path to the destination dir
|
50
|
+
#
|
51
|
+
# Returns nothing
|
52
|
+
def write(dest)
|
53
|
+
FileUtils.mkdir_p(File.join(dest, @dir))
|
54
|
+
|
55
|
+
name = @name
|
56
|
+
if self.ext != ""
|
57
|
+
name = @name.split(".")[0..-2].join('.') + self.ext
|
58
|
+
end
|
59
|
+
|
60
|
+
path = File.join(dest, @dir, name)
|
61
|
+
File.open(path, 'w') do |f|
|
62
|
+
f.write(self.output)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/jekyll/post.rb
ADDED
@@ -0,0 +1,305 @@
|
|
1
|
+
module Jekyll
|
2
|
+
|
3
|
+
class Post
|
4
|
+
include Comparable
|
5
|
+
include Convertible
|
6
|
+
|
7
|
+
class << self
|
8
|
+
attr_accessor :lsi
|
9
|
+
end
|
10
|
+
|
11
|
+
MATCHER = /^(.+\/)*(\d+-\d+-\d+(?:_\d+-\d+)?)-(.*)(\.[^.]+)$/
|
12
|
+
|
13
|
+
# Post name validator. Post filenames must be like:
|
14
|
+
# 2008-11-05-my-awesome-post.textile
|
15
|
+
# or:
|
16
|
+
# 2008-11-05_12-45-my-awesome-post.textile
|
17
|
+
#
|
18
|
+
# Returns <Bool>
|
19
|
+
def self.valid?(name)
|
20
|
+
name =~ MATCHER
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_accessor :site, :date, :slug, :ext, :topics, :tags, :published, :data, :content, :output
|
24
|
+
attr_writer :categories
|
25
|
+
|
26
|
+
def categories
|
27
|
+
@categories ||= []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Initialize this Post instance.
|
31
|
+
# +site+ is the Site
|
32
|
+
# +base+ is the String path to the dir containing the post file
|
33
|
+
# +name+ is the String filename of the post file
|
34
|
+
# +categories+ is an Array of Strings for the categories for this post
|
35
|
+
# +tags+ is an Array of Strings for the tags for this post
|
36
|
+
#
|
37
|
+
# Returns <Post>
|
38
|
+
def initialize(site, source, dir, name)
|
39
|
+
@site = site
|
40
|
+
@base = File.join(source, dir, '_posts')
|
41
|
+
@name = name
|
42
|
+
|
43
|
+
self.categories = dir.split('/').reject { |x| x.empty? }
|
44
|
+
|
45
|
+
parts = name.split('/')
|
46
|
+
self.topics = parts.size > 1 ? parts[0..-2] : []
|
47
|
+
|
48
|
+
self.process(name)
|
49
|
+
self.data = self.site.post_defaults.dup
|
50
|
+
self.read_yaml(@base, name)
|
51
|
+
|
52
|
+
extract_title_from_first_header_or_slug
|
53
|
+
|
54
|
+
if self.data.has_key?('published') && self.data['published'] == false
|
55
|
+
self.published = false
|
56
|
+
else
|
57
|
+
self.published = true
|
58
|
+
end
|
59
|
+
|
60
|
+
if self.categories.empty?
|
61
|
+
if self.data.has_key?('category')
|
62
|
+
self.categories << self.data['category']
|
63
|
+
elsif self.data.has_key?('categories')
|
64
|
+
# Look for categories in the YAML-header, either specified as
|
65
|
+
# an array or a string.
|
66
|
+
if self.data['categories'].kind_of? String
|
67
|
+
self.categories = self.data['categories'].split
|
68
|
+
else
|
69
|
+
self.categories = self.data['categories']
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
self.tags = self.data['tags'] || []
|
75
|
+
end
|
76
|
+
|
77
|
+
# Spaceship is based on Post#date
|
78
|
+
#
|
79
|
+
# Returns -1, 0, 1
|
80
|
+
def <=>(other)
|
81
|
+
self.date <=> other.date
|
82
|
+
end
|
83
|
+
|
84
|
+
# Extract information from the post filename
|
85
|
+
# +name+ is the String filename of the post file
|
86
|
+
#
|
87
|
+
# Returns nothing
|
88
|
+
def process(name)
|
89
|
+
m, cats, date, slug, ext = *name.match(MATCHER)
|
90
|
+
date = date.sub(/_(\d+)-(\d+)\Z/, ' \1:\2') # Make optional time part parsable.
|
91
|
+
self.date = Time.parse(date)
|
92
|
+
self.slug = slug
|
93
|
+
self.ext = ext
|
94
|
+
end
|
95
|
+
|
96
|
+
# The generated directory into which the post will be placed
|
97
|
+
# upon generation. This is derived from the permalink or, if
|
98
|
+
# permalink is absent, set to the default date
|
99
|
+
# e.g. "/2008/11/05/" if the permalink style is :date, otherwise nothing
|
100
|
+
#
|
101
|
+
# Returns <String>
|
102
|
+
def dir
|
103
|
+
File.dirname(generated_path)
|
104
|
+
end
|
105
|
+
|
106
|
+
# The full path and filename of the post.
|
107
|
+
# Defined in the YAML of the post body
|
108
|
+
# (Optional)
|
109
|
+
#
|
110
|
+
# Returns <String>
|
111
|
+
def permalink
|
112
|
+
self.data && self.data['permalink']
|
113
|
+
end
|
114
|
+
|
115
|
+
def template
|
116
|
+
case self.site.permalink_style
|
117
|
+
when :pretty
|
118
|
+
"/:categories/:year/:month/:day/:title"
|
119
|
+
when :none
|
120
|
+
"/:categories/:title.html"
|
121
|
+
when :date
|
122
|
+
"/:categories/:year/:month/:day/:title.html"
|
123
|
+
else
|
124
|
+
self.site.permalink_style.to_s
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
# The generated relative path of this post
|
129
|
+
# e.g. /2008/11/05/my-awesome-post.html
|
130
|
+
#
|
131
|
+
# Returns <String>
|
132
|
+
def generated_path
|
133
|
+
return permalink if permalink
|
134
|
+
|
135
|
+
@generated_path ||= {
|
136
|
+
"year" => date.strftime("%Y"),
|
137
|
+
"month" => date.strftime("%m"),
|
138
|
+
"day" => date.strftime("%d"),
|
139
|
+
"title" => slug,
|
140
|
+
"categories" => categories.sort.join('/')
|
141
|
+
}.inject(template) { |result, token|
|
142
|
+
result.gsub(/:#{token.first}/, token.last)
|
143
|
+
}.gsub("//", "/")
|
144
|
+
end
|
145
|
+
|
146
|
+
# The generated relative url of this post
|
147
|
+
# e.g. /2008/11/05/my-awesome-post
|
148
|
+
#
|
149
|
+
# Returns <String>
|
150
|
+
def url
|
151
|
+
site.config['multiviews'] ? generated_path.sub(/\.html$/, '') : generated_path
|
152
|
+
end
|
153
|
+
|
154
|
+
# The UID for this post (useful in feeds)
|
155
|
+
# e.g. /2008/11/05/my-awesome-post
|
156
|
+
#
|
157
|
+
# Returns <String>
|
158
|
+
def id
|
159
|
+
File.join(self.dir, self.slug)
|
160
|
+
end
|
161
|
+
|
162
|
+
# The post title
|
163
|
+
#
|
164
|
+
# Returns <String>
|
165
|
+
def title
|
166
|
+
self.data && self.data["title"]
|
167
|
+
end
|
168
|
+
|
169
|
+
# The post date and time
|
170
|
+
#
|
171
|
+
# Returns <Time>
|
172
|
+
def date
|
173
|
+
@date_with_time ||= begin
|
174
|
+
if self.data && self.data.key?("time")
|
175
|
+
time = Time.parse(self.data["time"])
|
176
|
+
Time.mktime(@date.year, @date.month, @date.day, time.hour, time.min)
|
177
|
+
else
|
178
|
+
@date
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# The path to the post file.
|
184
|
+
#
|
185
|
+
# Returns <String>
|
186
|
+
def path
|
187
|
+
File.expand_path(File.join(@base, @name))
|
188
|
+
end
|
189
|
+
|
190
|
+
# Calculate related posts.
|
191
|
+
#
|
192
|
+
# Returns [<Post>]
|
193
|
+
def related_posts(posts)
|
194
|
+
return [] unless posts.size > 1
|
195
|
+
|
196
|
+
if self.site.lsi
|
197
|
+
self.class.lsi ||= begin
|
198
|
+
puts "Running the classifier... this could take a while."
|
199
|
+
lsi = Classifier::LSI.new
|
200
|
+
posts.each { |x| $stdout.print(".");$stdout.flush;lsi.add_item(x) }
|
201
|
+
puts ""
|
202
|
+
lsi
|
203
|
+
end
|
204
|
+
|
205
|
+
related = self.class.lsi.find_related(self.content, 11)
|
206
|
+
related - [self]
|
207
|
+
else
|
208
|
+
(posts - [self])[0..9]
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# Add any necessary layouts to this post
|
213
|
+
# +layouts+ is a Hash of {"name" => "layout"}
|
214
|
+
# +site_payload+ is the site payload hash
|
215
|
+
#
|
216
|
+
# Returns nothing
|
217
|
+
def render(layouts, site_payload)
|
218
|
+
# construct payload
|
219
|
+
payload =
|
220
|
+
{
|
221
|
+
"site" => { "related_posts" => related_posts(site_payload["site"]["posts"]) },
|
222
|
+
"page" => self.to_liquid
|
223
|
+
}
|
224
|
+
payload = payload.deep_merge(site_payload)
|
225
|
+
|
226
|
+
do_layout(payload, layouts)
|
227
|
+
end
|
228
|
+
|
229
|
+
# Write the generated post file to the destination directory.
|
230
|
+
# +dest+ is the String path to the destination dir
|
231
|
+
#
|
232
|
+
# Returns nothing
|
233
|
+
def write(dest)
|
234
|
+
FileUtils.mkdir_p(File.join(dest, dir))
|
235
|
+
|
236
|
+
path = File.join(dest, self.generated_path)
|
237
|
+
|
238
|
+
if template[/\.html$/].nil?
|
239
|
+
FileUtils.mkdir_p(path)
|
240
|
+
path = File.join(path, "index.html")
|
241
|
+
end
|
242
|
+
|
243
|
+
File.open(path, 'w') do |f|
|
244
|
+
f.write(self.output)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
# Attempt to extract title from topmost header or slug.
|
249
|
+
#
|
250
|
+
# Returns <String>
|
251
|
+
def extract_title_from_first_header_or_slug
|
252
|
+
# Done before the transformation to HTML, or it won't go into <title>s.
|
253
|
+
self.data["title"] ||=
|
254
|
+
case content_type
|
255
|
+
when 'textile'
|
256
|
+
self.content[/\A\s*h\d\.\s*(.+)/, 1] # h1. Header
|
257
|
+
when 'markdown'
|
258
|
+
self.content[/\A\s*#+\s*(.+)\s*#*$/, 1] || # "# Header"
|
259
|
+
self.content[/\A\s*(\S.*)\r?\n\s*(-+|=+)\s*$/, 1] # "Header\n====="
|
260
|
+
end
|
261
|
+
self.data["title"] ||= self.slug.split('-').select {|w| w.capitalize! || w }.join(' ')
|
262
|
+
end
|
263
|
+
|
264
|
+
# Convert this post into a Hash for use in Liquid templates.
|
265
|
+
#
|
266
|
+
# Returns <Hash>
|
267
|
+
def to_liquid
|
268
|
+
{ "title" => self.title,
|
269
|
+
"url" => self.url,
|
270
|
+
"date" => self.date,
|
271
|
+
"id" => self.id,
|
272
|
+
"path" => self.path,
|
273
|
+
"topics" => self.topics,
|
274
|
+
"categories" => self.categories,
|
275
|
+
"tags" => self.tags,
|
276
|
+
"next" => self.next,
|
277
|
+
"previous" => self.previous,
|
278
|
+
"content" => self.content }.deep_merge(self.data)
|
279
|
+
end
|
280
|
+
|
281
|
+
def inspect
|
282
|
+
"<Post: #{self.id}>"
|
283
|
+
end
|
284
|
+
|
285
|
+
def next
|
286
|
+
pos = self.site.posts.index(self)
|
287
|
+
|
288
|
+
if pos && pos < self.site.posts.length-1
|
289
|
+
self.site.posts[pos+1]
|
290
|
+
else
|
291
|
+
nil
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def previous
|
296
|
+
pos = self.site.posts.index(self)
|
297
|
+
if pos && pos > 0
|
298
|
+
self.site.posts[pos-1]
|
299
|
+
else
|
300
|
+
nil
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
end
|