spinto-jekyll 0.11.2.1
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/Gemfile +2 -0
- data/History.txt +321 -0
- data/LICENSE +21 -0
- data/README.textile +41 -0
- data/Rakefile +166 -0
- data/bin/jekyll +289 -0
- data/cucumber.yml +1 -0
- data/features/create_sites.feature +112 -0
- data/features/embed_filters.feature +60 -0
- data/features/markdown.feature +30 -0
- data/features/pagination.feature +27 -0
- data/features/permalinks.feature +65 -0
- data/features/post_data.feature +153 -0
- data/features/site_configuration.feature +145 -0
- data/features/site_data.feature +82 -0
- data/features/step_definitions/jekyll_steps.rb +145 -0
- data/features/support/env.rb +19 -0
- data/lib/jekyll.rb +136 -0
- data/lib/jekyll/converter.rb +50 -0
- data/lib/jekyll/converters/identity.rb +22 -0
- data/lib/jekyll/converters/markdown.rb +125 -0
- data/lib/jekyll/converters/textile.rb +50 -0
- data/lib/jekyll/convertible.rb +112 -0
- data/lib/jekyll/core_ext.rb +52 -0
- data/lib/jekyll/errors.rb +6 -0
- data/lib/jekyll/filters.rb +118 -0
- data/lib/jekyll/generator.rb +7 -0
- data/lib/jekyll/generators/pagination.rb +113 -0
- data/lib/jekyll/layout.rb +44 -0
- data/lib/jekyll/migrators/csv.rb +26 -0
- data/lib/jekyll/migrators/drupal.rb +103 -0
- data/lib/jekyll/migrators/enki.rb +49 -0
- data/lib/jekyll/migrators/joomla.rb +53 -0
- data/lib/jekyll/migrators/marley.rb +52 -0
- data/lib/jekyll/migrators/mephisto.rb +84 -0
- data/lib/jekyll/migrators/mt.rb +86 -0
- data/lib/jekyll/migrators/posterous.rb +67 -0
- data/lib/jekyll/migrators/rss.rb +47 -0
- data/lib/jekyll/migrators/textpattern.rb +58 -0
- data/lib/jekyll/migrators/tumblr.rb +195 -0
- data/lib/jekyll/migrators/typo.rb +51 -0
- data/lib/jekyll/migrators/wordpress.rb +294 -0
- data/lib/jekyll/migrators/wordpressdotcom.rb +70 -0
- data/lib/jekyll/page.rb +155 -0
- data/lib/jekyll/plugin.rb +77 -0
- data/lib/jekyll/post.rb +257 -0
- data/lib/jekyll/site.rb +337 -0
- data/lib/jekyll/static_file.rb +72 -0
- data/lib/jekyll/tags/highlight.rb +76 -0
- data/lib/jekyll/tags/include.rb +37 -0
- data/lib/jekyll/tags/post_url.rb +38 -0
- data/lib/spinto-jekyll.rb +3 -0
- data/spinto-jekyll.gemspec +155 -0
- data/test/helper.rb +34 -0
- data/test/source/.htaccess +8 -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/_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-03-12-hash-#1.markdown +6 -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/2009-05-18-tag.textile +6 -0
- data/test/source/_posts/2009-05-18-tags.textile +9 -0
- data/test/source/_posts/2009-06-22-empty-yaml.textile +3 -0
- data/test/source/_posts/2009-06-22-no-yaml.textile +1 -0
- data/test/source/_posts/2010-01-08-triple-dash.markdown +5 -0
- data/test/source/_posts/2010-01-09-date-override.textile +7 -0
- data/test/source/_posts/2010-01-09-time-override.textile +7 -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/_posts/2011-04-12-md-extension.md +7 -0
- data/test/source/_posts/2011-04-12-text-extension.text +0 -0
- data/test/source/about.html +6 -0
- data/test/source/category/_posts/2008-9-23-categories.textile +6 -0
- data/test/source/contacts.html +5 -0
- data/test/source/css/screen.css +76 -0
- data/test/source/deal.with.dots.html +7 -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/sitemap.xml +32 -0
- data/test/source/win/_posts/2009-05-24-yaml-linebreak.markdown +7 -0
- data/test/source/z_category/_posts/2008-9-23-categories.textile +6 -0
- data/test/suite.rb +11 -0
- data/test/test_configuration.rb +29 -0
- data/test/test_core_ext.rb +66 -0
- data/test/test_filters.rb +62 -0
- data/test/test_generated_site.rb +72 -0
- data/test/test_kramdown.rb +23 -0
- data/test/test_page.rb +117 -0
- data/test/test_pager.rb +113 -0
- data/test/test_post.rb +456 -0
- data/test/test_rdiscount.rb +18 -0
- data/test/test_redcarpet.rb +21 -0
- data/test/test_redcloth.rb +86 -0
- data/test/test_site.rb +220 -0
- data/test/test_tags.rb +201 -0
- metadata +336 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'rubygems'
|
|
4
|
+
require 'hpricot'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
require 'yaml'
|
|
7
|
+
require 'time'
|
|
8
|
+
|
|
9
|
+
module Jekyll
|
|
10
|
+
# This importer takes a wordpress.xml file, which can be exported from your
|
|
11
|
+
# wordpress.com blog (/wp-admin/export.php).
|
|
12
|
+
module WordpressDotCom
|
|
13
|
+
def self.process(filename = "wordpress.xml")
|
|
14
|
+
import_count = Hash.new(0)
|
|
15
|
+
doc = Hpricot::XML(File.read(filename))
|
|
16
|
+
|
|
17
|
+
(doc/:channel/:item).each do |item|
|
|
18
|
+
title = item.at(:title).inner_text.strip
|
|
19
|
+
permalink_title = item.at('wp:post_name').inner_text
|
|
20
|
+
# Fallback to "prettified" title if post_name is empty (can happen)
|
|
21
|
+
if permalink_title == ""
|
|
22
|
+
permalink_title = title.downcase.split.join('-')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
date = Time.parse(item.at('wp:post_date').inner_text)
|
|
26
|
+
status = item.at('wp:status').inner_text
|
|
27
|
+
|
|
28
|
+
if status == "publish"
|
|
29
|
+
published = true
|
|
30
|
+
else
|
|
31
|
+
published = false
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
type = item.at('wp:post_type').inner_text
|
|
35
|
+
tags = (item/:category).map{|c| c.inner_text}.reject{|c| c == 'Uncategorized'}.uniq
|
|
36
|
+
|
|
37
|
+
metas = Hash.new
|
|
38
|
+
item.search("wp:postmeta").each do |meta|
|
|
39
|
+
key = meta.at('wp:meta_key').inner_text
|
|
40
|
+
value = meta.at('wp:meta_value').inner_text
|
|
41
|
+
metas[key] = value;
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
name = "#{date.strftime('%Y-%m-%d')}-#{permalink_title}.html"
|
|
45
|
+
header = {
|
|
46
|
+
'layout' => type,
|
|
47
|
+
'title' => title,
|
|
48
|
+
'tags' => tags,
|
|
49
|
+
'status' => status,
|
|
50
|
+
'type' => type,
|
|
51
|
+
'published' => published,
|
|
52
|
+
'meta' => metas
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
FileUtils.mkdir_p "_#{type}s"
|
|
56
|
+
File.open("_#{type}s/#{name}", "w") do |f|
|
|
57
|
+
f.puts header.to_yaml
|
|
58
|
+
f.puts '---'
|
|
59
|
+
f.puts item.at('content:encoded').inner_text
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
import_count[type] += 1
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
import_count.each do |key, value|
|
|
66
|
+
puts "Imported #{value} #{key}s"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
data/lib/jekyll/page.rb
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
module Jekyll
|
|
2
|
+
|
|
3
|
+
class Page
|
|
4
|
+
include Convertible
|
|
5
|
+
|
|
6
|
+
attr_writer :dir
|
|
7
|
+
attr_accessor :site, :pager
|
|
8
|
+
attr_accessor :name, :ext, :basename
|
|
9
|
+
attr_accessor :data, :content, :output
|
|
10
|
+
|
|
11
|
+
# Initialize a new Page.
|
|
12
|
+
#
|
|
13
|
+
# site - The Site object.
|
|
14
|
+
# base - The String path to the source.
|
|
15
|
+
# dir - The String path between the source and the file.
|
|
16
|
+
# name - The String filename of the file.
|
|
17
|
+
def initialize(site, base, dir, name)
|
|
18
|
+
@site = site
|
|
19
|
+
@base = base
|
|
20
|
+
@dir = dir
|
|
21
|
+
@name = name
|
|
22
|
+
|
|
23
|
+
self.process(name)
|
|
24
|
+
self.read_yaml(File.join(base, dir), name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The generated directory into which the page will be placed
|
|
28
|
+
# upon generation. This is derived from the permalink or, if
|
|
29
|
+
# permalink is absent, we be '/'
|
|
30
|
+
#
|
|
31
|
+
# Returns the String destination directory.
|
|
32
|
+
def dir
|
|
33
|
+
url[-1, 1] == '/' ? url : File.dirname(url)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The full path and filename of the post. Defined in the YAML of the post
|
|
37
|
+
# body.
|
|
38
|
+
#
|
|
39
|
+
# Returns the String permalink or nil if none has been set.
|
|
40
|
+
def permalink
|
|
41
|
+
self.data && self.data['permalink']
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# The template of the permalink.
|
|
45
|
+
#
|
|
46
|
+
# Returns the template String.
|
|
47
|
+
def template
|
|
48
|
+
if self.site.permalink_style == :pretty && !index? && html?
|
|
49
|
+
"/:basename/"
|
|
50
|
+
else
|
|
51
|
+
"/:basename:output_ext"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The generated relative url of this page. e.g. /about.html.
|
|
56
|
+
#
|
|
57
|
+
# Returns the String url.
|
|
58
|
+
def url
|
|
59
|
+
return @url if @url
|
|
60
|
+
|
|
61
|
+
url = if permalink
|
|
62
|
+
permalink
|
|
63
|
+
else
|
|
64
|
+
{
|
|
65
|
+
"basename" => self.basename,
|
|
66
|
+
"output_ext" => self.output_ext,
|
|
67
|
+
}.inject(template) { |result, token|
|
|
68
|
+
result.gsub(/:#{token.first}/, token.last)
|
|
69
|
+
}.gsub(/\/\//, "/")
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# sanitize url
|
|
73
|
+
@url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
|
|
74
|
+
@url += "/" if url =~ /\/$/
|
|
75
|
+
@url
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Extract information from the page filename.
|
|
79
|
+
#
|
|
80
|
+
# name - The String filename of the page file.
|
|
81
|
+
#
|
|
82
|
+
# Returns nothing.
|
|
83
|
+
def process(name)
|
|
84
|
+
self.ext = File.extname(name)
|
|
85
|
+
self.basename = name[0 .. -self.ext.length-1]
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Add any necessary layouts to this post
|
|
89
|
+
#
|
|
90
|
+
# layouts - The Hash of {"name" => "layout"}.
|
|
91
|
+
# site_payload - The site payload Hash.
|
|
92
|
+
#
|
|
93
|
+
# Returns nothing.
|
|
94
|
+
def render(layouts, site_payload)
|
|
95
|
+
payload = {
|
|
96
|
+
"page" => self.to_liquid,
|
|
97
|
+
'paginator' => pager.to_liquid
|
|
98
|
+
}.deep_merge(site_payload)
|
|
99
|
+
|
|
100
|
+
do_layout(payload, layouts)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# Convert this Page's data to a Hash suitable for use by Liquid.
|
|
104
|
+
#
|
|
105
|
+
# Returns the Hash representation of this Page.
|
|
106
|
+
def to_liquid
|
|
107
|
+
self.data.deep_merge({
|
|
108
|
+
"url" => File.join(@dir, self.url),
|
|
109
|
+
"content" => self.content })
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Obtain destination path.
|
|
113
|
+
#
|
|
114
|
+
# dest - The String path to the destination dir.
|
|
115
|
+
#
|
|
116
|
+
# Returns the destination file path String.
|
|
117
|
+
def destination(dest)
|
|
118
|
+
# The url needs to be unescaped in order to preserve the correct
|
|
119
|
+
# filename.
|
|
120
|
+
path = File.join(dest, @dir, CGI.unescape(self.url))
|
|
121
|
+
path = File.join(path, "index.html") if self.url =~ /\/$/
|
|
122
|
+
path
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Write the generated page file to the destination directory.
|
|
126
|
+
#
|
|
127
|
+
# dest - The String path to the destination dir.
|
|
128
|
+
#
|
|
129
|
+
# Returns nothing.
|
|
130
|
+
def write(dest)
|
|
131
|
+
path = destination(dest)
|
|
132
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
133
|
+
File.open(path, 'w') do |f|
|
|
134
|
+
f.write(self.output)
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
# Returns the object as a debug String.
|
|
139
|
+
def inspect
|
|
140
|
+
"#<Jekyll:Page @name=#{self.name.inspect}>"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Returns the Boolean of whether this Page is HTML or not.
|
|
144
|
+
def html?
|
|
145
|
+
output_ext == '.html'
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Returns the Boolean of whether this Page is an index file or not.
|
|
149
|
+
def index?
|
|
150
|
+
basename == 'index'
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
@priority ||= nil
|
|
38
|
+
if priority && PRIORITIES.has_key?(priority)
|
|
39
|
+
@priority = priority
|
|
40
|
+
end
|
|
41
|
+
@priority || :normal
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Get or set the safety of this plugin. When called without an argument
|
|
45
|
+
# it returns the safety. When an argument is given, it will set the
|
|
46
|
+
# safety.
|
|
47
|
+
#
|
|
48
|
+
# safe - The Boolean safety (default: nil).
|
|
49
|
+
#
|
|
50
|
+
# Returns the safety Boolean.
|
|
51
|
+
def self.safe(safe = nil)
|
|
52
|
+
if safe
|
|
53
|
+
@safe = safe
|
|
54
|
+
end
|
|
55
|
+
@safe || false
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Spaceship is priority [higher -> lower]
|
|
59
|
+
#
|
|
60
|
+
# other - The class to be compared.
|
|
61
|
+
#
|
|
62
|
+
# Returns -1, 0, 1.
|
|
63
|
+
def self.<=>(other)
|
|
64
|
+
PRIORITIES[other.priority] <=> PRIORITIES[self.priority]
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Initialize a new plugin. This should be overridden by the subclass.
|
|
68
|
+
#
|
|
69
|
+
# config - The Hash of configuration options.
|
|
70
|
+
#
|
|
71
|
+
# Returns a new instance.
|
|
72
|
+
def initialize(config = {})
|
|
73
|
+
# no-op for default
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
data/lib/jekyll/post.rb
ADDED
|
@@ -0,0 +1,257 @@
|
|
|
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+)-(.*)(\.[^.]+)$/
|
|
12
|
+
|
|
13
|
+
# Post name validator. Post filenames must be like:
|
|
14
|
+
# 2008-11-05-my-awesome-post.textile
|
|
15
|
+
#
|
|
16
|
+
# Returns <Bool>
|
|
17
|
+
def self.valid?(name)
|
|
18
|
+
name =~ MATCHER
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_accessor :site
|
|
22
|
+
attr_accessor :data, :content, :output, :ext
|
|
23
|
+
attr_accessor :date, :slug, :published, :tags, :categories
|
|
24
|
+
|
|
25
|
+
attr_reader :name
|
|
26
|
+
|
|
27
|
+
# Initialize this Post instance.
|
|
28
|
+
# +site+ is the Site
|
|
29
|
+
# +base+ is the String path to the dir containing the post file
|
|
30
|
+
# +name+ is the String filename of the post file
|
|
31
|
+
# +categories+ is an Array of Strings for the categories for this post
|
|
32
|
+
#
|
|
33
|
+
# Returns <Post>
|
|
34
|
+
def initialize(site, source, dir, name)
|
|
35
|
+
@site = site
|
|
36
|
+
@base = File.join(source, dir, '_posts')
|
|
37
|
+
@name = name
|
|
38
|
+
|
|
39
|
+
self.categories = dir.split('/').reject { |x| x.empty? }
|
|
40
|
+
self.process(name)
|
|
41
|
+
self.read_yaml(@base, name)
|
|
42
|
+
|
|
43
|
+
#If we've added a date and time to the yaml, use that instead of the filename date
|
|
44
|
+
#Means we'll sort correctly.
|
|
45
|
+
if self.data.has_key?('date')
|
|
46
|
+
# ensure Time via to_s and reparse
|
|
47
|
+
self.date = Time.parse(self.data["date"].to_s)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
if self.data.has_key?('published') && self.data['published'] == false
|
|
51
|
+
self.published = false
|
|
52
|
+
else
|
|
53
|
+
self.published = true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
self.tags = self.data.pluralized_array("tag", "tags")
|
|
57
|
+
|
|
58
|
+
if self.categories.empty?
|
|
59
|
+
self.categories = self.data.pluralized_array('category', 'categories')
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Spaceship is based on Post#date, slug
|
|
64
|
+
#
|
|
65
|
+
# Returns -1, 0, 1
|
|
66
|
+
def <=>(other)
|
|
67
|
+
cmp = self.date <=> other.date
|
|
68
|
+
if 0 == cmp
|
|
69
|
+
cmp = self.slug <=> other.slug
|
|
70
|
+
end
|
|
71
|
+
return cmp
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Extract information from the post filename
|
|
75
|
+
# +name+ is the String filename of the post file
|
|
76
|
+
#
|
|
77
|
+
# Returns nothing
|
|
78
|
+
def process(name)
|
|
79
|
+
m, cats, date, slug, ext = *name.match(MATCHER)
|
|
80
|
+
self.date = Time.parse(date)
|
|
81
|
+
self.slug = slug
|
|
82
|
+
self.ext = ext
|
|
83
|
+
rescue ArgumentError
|
|
84
|
+
raise FatalException.new("Post #{name} does not have a valid date.")
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The generated directory into which the post will be placed
|
|
88
|
+
# upon generation. This is derived from the permalink or, if
|
|
89
|
+
# permalink is absent, set to the default date
|
|
90
|
+
# e.g. "/2008/11/05/" if the permalink style is :date, otherwise nothing
|
|
91
|
+
#
|
|
92
|
+
# Returns <String>
|
|
93
|
+
def dir
|
|
94
|
+
File.dirname(url)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# The full path and filename of the post.
|
|
98
|
+
# Defined in the YAML of the post body
|
|
99
|
+
# (Optional)
|
|
100
|
+
#
|
|
101
|
+
# Returns <String>
|
|
102
|
+
def permalink
|
|
103
|
+
self.data && self.data['permalink']
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def template
|
|
107
|
+
case self.site.permalink_style
|
|
108
|
+
when :pretty
|
|
109
|
+
"/:categories/:year/:month/:day/:title/"
|
|
110
|
+
when :none
|
|
111
|
+
"/:categories/:title.html"
|
|
112
|
+
when :date
|
|
113
|
+
"/:categories/:year/:month/:day/:title.html"
|
|
114
|
+
else
|
|
115
|
+
self.site.permalink_style.to_s
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# The generated relative url of this post
|
|
120
|
+
# e.g. /2008/11/05/my-awesome-post.html
|
|
121
|
+
#
|
|
122
|
+
# Returns <String>
|
|
123
|
+
def url
|
|
124
|
+
return @url if @url
|
|
125
|
+
|
|
126
|
+
url = if permalink
|
|
127
|
+
permalink
|
|
128
|
+
else
|
|
129
|
+
{
|
|
130
|
+
"year" => date.strftime("%Y"),
|
|
131
|
+
"month" => date.strftime("%m"),
|
|
132
|
+
"day" => date.strftime("%d"),
|
|
133
|
+
"title" => CGI.escape(slug),
|
|
134
|
+
"i_day" => date.strftime("%d").to_i.to_s,
|
|
135
|
+
"i_month" => date.strftime("%m").to_i.to_s,
|
|
136
|
+
"categories" => categories.map { |c| URI.escape(c) }.join('/'),
|
|
137
|
+
"output_ext" => self.output_ext
|
|
138
|
+
}.inject(template) { |result, token|
|
|
139
|
+
result.gsub(/:#{Regexp.escape token.first}/, token.last)
|
|
140
|
+
}.gsub(/\/\//, "/")
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# sanitize url
|
|
144
|
+
@url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
|
|
145
|
+
@url += "/" if url =~ /\/$/
|
|
146
|
+
@url
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# The UID for this post (useful in feeds)
|
|
150
|
+
# e.g. /2008/11/05/my-awesome-post
|
|
151
|
+
#
|
|
152
|
+
# Returns <String>
|
|
153
|
+
def id
|
|
154
|
+
File.join(self.dir, self.slug)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Calculate related posts.
|
|
158
|
+
#
|
|
159
|
+
# Returns [<Post>]
|
|
160
|
+
def related_posts(posts)
|
|
161
|
+
return [] unless posts.size > 1
|
|
162
|
+
|
|
163
|
+
if self.site.lsi
|
|
164
|
+
self.class.lsi ||= begin
|
|
165
|
+
puts "Running the classifier... this could take a while."
|
|
166
|
+
lsi = Classifier::LSI.new
|
|
167
|
+
posts.each { |x| $stdout.print(".");$stdout.flush;lsi.add_item(x) }
|
|
168
|
+
puts ""
|
|
169
|
+
lsi
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
related = self.class.lsi.find_related(self.content, 11)
|
|
173
|
+
related - [self]
|
|
174
|
+
else
|
|
175
|
+
(posts - [self])[0..9]
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Add any necessary layouts to this post
|
|
180
|
+
# +layouts+ is a Hash of {"name" => "layout"}
|
|
181
|
+
# +site_payload+ is the site payload hash
|
|
182
|
+
#
|
|
183
|
+
# Returns nothing
|
|
184
|
+
def render(layouts, site_payload)
|
|
185
|
+
# construct payload
|
|
186
|
+
payload = {
|
|
187
|
+
"site" => { "related_posts" => related_posts(site_payload["site"]["posts"]) },
|
|
188
|
+
"page" => self.to_liquid
|
|
189
|
+
}.deep_merge(site_payload)
|
|
190
|
+
|
|
191
|
+
do_layout(payload, layouts)
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Obtain destination path.
|
|
195
|
+
# +dest+ is the String path to the destination dir
|
|
196
|
+
#
|
|
197
|
+
# Returns destination file path.
|
|
198
|
+
def destination(dest)
|
|
199
|
+
# The url needs to be unescaped in order to preserve the correct filename
|
|
200
|
+
path = File.join(dest, CGI.unescape(self.url))
|
|
201
|
+
path = File.join(path, "index.html") if template[/\.html$/].nil?
|
|
202
|
+
path
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Write the generated post file to the destination directory.
|
|
206
|
+
# +dest+ is the String path to the destination dir
|
|
207
|
+
#
|
|
208
|
+
# Returns nothing
|
|
209
|
+
def write(dest)
|
|
210
|
+
path = destination(dest)
|
|
211
|
+
FileUtils.mkdir_p(File.dirname(path))
|
|
212
|
+
File.open(path, 'w') do |f|
|
|
213
|
+
f.write(self.output)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Convert this post into a Hash for use in Liquid templates.
|
|
218
|
+
#
|
|
219
|
+
# Returns <Hash>
|
|
220
|
+
def to_liquid
|
|
221
|
+
self.data.deep_merge({
|
|
222
|
+
"title" => self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
|
|
223
|
+
"url" => self.url,
|
|
224
|
+
"date" => self.date,
|
|
225
|
+
"id" => self.id,
|
|
226
|
+
"categories" => self.categories,
|
|
227
|
+
"next" => self.next,
|
|
228
|
+
"previous" => self.previous,
|
|
229
|
+
"tags" => self.tags,
|
|
230
|
+
"content" => self.content })
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def inspect
|
|
234
|
+
"<Post: #{self.id}>"
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def next
|
|
238
|
+
pos = self.site.posts.index(self)
|
|
239
|
+
|
|
240
|
+
if pos && pos < self.site.posts.length-1
|
|
241
|
+
self.site.posts[pos+1]
|
|
242
|
+
else
|
|
243
|
+
nil
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
def previous
|
|
248
|
+
pos = self.site.posts.index(self)
|
|
249
|
+
if pos && pos > 0
|
|
250
|
+
self.site.posts[pos-1]
|
|
251
|
+
else
|
|
252
|
+
nil
|
|
253
|
+
end
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
end
|