jekyll-reloaded 0.12

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. data/Gemfile +2 -0
  2. data/History.txt +321 -0
  3. data/LICENSE +21 -0
  4. data/README.textile +41 -0
  5. data/Rakefile +161 -0
  6. data/bin/jekyll +289 -0
  7. data/cucumber.yml +1 -0
  8. data/features/create_sites.feature +112 -0
  9. data/features/embed_filters.feature +60 -0
  10. data/features/markdown.feature +30 -0
  11. data/features/pagination.feature +27 -0
  12. data/features/permalinks.feature +65 -0
  13. data/features/post_data.feature +153 -0
  14. data/features/site_configuration.feature +145 -0
  15. data/features/site_data.feature +82 -0
  16. data/features/step_definitions/jekyll_steps.rb +145 -0
  17. data/features/support/env.rb +19 -0
  18. data/jekyll.gemspec +146 -0
  19. data/lib/guard/jekyll.rb +57 -0
  20. data/lib/jekyll/converter.rb +50 -0
  21. data/lib/jekyll/converters/identity.rb +22 -0
  22. data/lib/jekyll/converters/markdown.rb +125 -0
  23. data/lib/jekyll/converters/textile.rb +50 -0
  24. data/lib/jekyll/convertible.rb +116 -0
  25. data/lib/jekyll/core_ext.rb +52 -0
  26. data/lib/jekyll/errors.rb +6 -0
  27. data/lib/jekyll/filters.rb +118 -0
  28. data/lib/jekyll/generator.rb +7 -0
  29. data/lib/jekyll/generators/pagination.rb +113 -0
  30. data/lib/jekyll/layout.rb +51 -0
  31. data/lib/jekyll/live_site.rb +216 -0
  32. data/lib/jekyll/migrators/csv.rb +26 -0
  33. data/lib/jekyll/migrators/drupal.rb +103 -0
  34. data/lib/jekyll/migrators/enki.rb +49 -0
  35. data/lib/jekyll/migrators/joomla.rb +53 -0
  36. data/lib/jekyll/migrators/marley.rb +52 -0
  37. data/lib/jekyll/migrators/mephisto.rb +84 -0
  38. data/lib/jekyll/migrators/mt.rb +86 -0
  39. data/lib/jekyll/migrators/posterous.rb +67 -0
  40. data/lib/jekyll/migrators/rss.rb +47 -0
  41. data/lib/jekyll/migrators/textpattern.rb +58 -0
  42. data/lib/jekyll/migrators/tumblr.rb +195 -0
  43. data/lib/jekyll/migrators/typo.rb +51 -0
  44. data/lib/jekyll/migrators/wordpress.rb +294 -0
  45. data/lib/jekyll/migrators/wordpressdotcom.rb +70 -0
  46. data/lib/jekyll/page.rb +160 -0
  47. data/lib/jekyll/plugin.rb +77 -0
  48. data/lib/jekyll/post.rb +262 -0
  49. data/lib/jekyll/site.rb +339 -0
  50. data/lib/jekyll/static_file.rb +77 -0
  51. data/lib/jekyll/tags/highlight.rb +118 -0
  52. data/lib/jekyll/tags/include.rb +37 -0
  53. data/lib/jekyll/tags/post_url.rb +38 -0
  54. data/lib/jekyll.rb +134 -0
  55. data/test/helper.rb +34 -0
  56. data/test/source/.htaccess +8 -0
  57. data/test/source/_includes/sig.markdown +3 -0
  58. data/test/source/_layouts/default.html +27 -0
  59. data/test/source/_layouts/simple.html +1 -0
  60. data/test/source/_posts/2008-02-02-not-published.textile +8 -0
  61. data/test/source/_posts/2008-02-02-published.textile +8 -0
  62. data/test/source/_posts/2008-10-18-foo-bar.textile +8 -0
  63. data/test/source/_posts/2008-11-21-complex.textile +8 -0
  64. data/test/source/_posts/2008-12-03-permalinked-post.textile +9 -0
  65. data/test/source/_posts/2008-12-13-include.markdown +8 -0
  66. data/test/source/_posts/2009-01-27-array-categories.textile +10 -0
  67. data/test/source/_posts/2009-01-27-categories.textile +7 -0
  68. data/test/source/_posts/2009-01-27-category.textile +7 -0
  69. data/test/source/_posts/2009-01-27-empty-categories.textile +7 -0
  70. data/test/source/_posts/2009-01-27-empty-category.textile +7 -0
  71. data/test/source/_posts/2009-03-12-hash-#1.markdown +6 -0
  72. data/test/source/_posts/2009-05-18-empty-tag.textile +6 -0
  73. data/test/source/_posts/2009-05-18-empty-tags.textile +6 -0
  74. data/test/source/_posts/2009-05-18-tag.textile +6 -0
  75. data/test/source/_posts/2009-05-18-tags.textile +9 -0
  76. data/test/source/_posts/2009-06-22-empty-yaml.textile +3 -0
  77. data/test/source/_posts/2009-06-22-no-yaml.textile +1 -0
  78. data/test/source/_posts/2010-01-08-triple-dash.markdown +5 -0
  79. data/test/source/_posts/2010-01-09-date-override.textile +7 -0
  80. data/test/source/_posts/2010-01-09-time-override.textile +7 -0
  81. data/test/source/_posts/2010-01-09-timezone-override.textile +7 -0
  82. data/test/source/_posts/2010-01-16-override-data.textile +4 -0
  83. data/test/source/_posts/2011-04-12-md-extension.md +7 -0
  84. data/test/source/_posts/2011-04-12-text-extension.text +0 -0
  85. data/test/source/about.html +6 -0
  86. data/test/source/category/_posts/2008-9-23-categories.textile +6 -0
  87. data/test/source/contacts.html +5 -0
  88. data/test/source/css/screen.css +76 -0
  89. data/test/source/deal.with.dots.html +7 -0
  90. data/test/source/foo/_posts/bar/2008-12-12-topical-post.textile +8 -0
  91. data/test/source/index.html +22 -0
  92. data/test/source/sitemap.xml +32 -0
  93. data/test/source/win/_posts/2009-05-24-yaml-linebreak.markdown +7 -0
  94. data/test/source/z_category/_posts/2008-9-23-categories.textile +6 -0
  95. data/test/suite.rb +11 -0
  96. data/test/test_configuration.rb +29 -0
  97. data/test/test_core_ext.rb +66 -0
  98. data/test/test_filters.rb +62 -0
  99. data/test/test_generated_site.rb +72 -0
  100. data/test/test_kramdown.rb +23 -0
  101. data/test/test_page.rb +117 -0
  102. data/test/test_pager.rb +113 -0
  103. data/test/test_post.rb +450 -0
  104. data/test/test_rdiscount.rb +18 -0
  105. data/test/test_redcarpet.rb +21 -0
  106. data/test/test_redcloth.rb +86 -0
  107. data/test/test_site.rb +220 -0
  108. data/test/test_tags.rb +201 -0
  109. metadata +332 -0
@@ -0,0 +1,51 @@
1
+ # Author: Toby DiPasquale <toby@cbcg.net>
2
+ require 'fileutils'
3
+ require 'rubygems'
4
+ require 'sequel'
5
+ require 'yaml'
6
+
7
+ module Jekyll
8
+ module Typo
9
+ # This SQL *should* work for both MySQL and PostgreSQL, but I haven't
10
+ # tested PostgreSQL yet (as of 2008-12-16).
11
+ SQL = <<-EOS
12
+ SELECT c.id id,
13
+ c.title title,
14
+ c.permalink slug,
15
+ c.body body,
16
+ c.published_at date,
17
+ c.state state,
18
+ COALESCE(tf.name, 'html') filter
19
+ FROM contents c
20
+ LEFT OUTER JOIN text_filters tf
21
+ ON c.text_filter_id = tf.id
22
+ EOS
23
+
24
+ def self.process dbname, user, pass, host='localhost'
25
+ FileUtils.mkdir_p '_posts'
26
+ db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
27
+ db[SQL].each do |post|
28
+ next unless post[:state] =~ /published/
29
+
30
+ name = [ sprintf("%.04d", post[:date].year),
31
+ sprintf("%.02d", post[:date].month),
32
+ sprintf("%.02d", post[:date].day),
33
+ post[:slug].strip ].join('-')
34
+
35
+ # Can have more than one text filter in this field, but we just want
36
+ # the first one for this.
37
+ name += '.' + post[:filter].split(' ')[0]
38
+
39
+ File.open("_posts/#{name}", 'w') do |f|
40
+ f.puts({ 'layout' => 'post',
41
+ 'title' => post[:title].to_s,
42
+ 'typo_id' => post[:id]
43
+ }.delete_if { |k, v| v.nil? || v == '' }.to_yaml)
44
+ f.puts '---'
45
+ f.puts post[:body].delete("\r")
46
+ end
47
+ end
48
+ end
49
+
50
+ end
51
+ end
@@ -0,0 +1,294 @@
1
+ require 'rubygems'
2
+ require 'sequel'
3
+ require 'fileutils'
4
+ require 'yaml'
5
+
6
+ # NOTE: This converter requires Sequel and the MySQL gems.
7
+ # The MySQL gem can be difficult to install on OS X. Once you have MySQL
8
+ # installed, running the following commands should work:
9
+ # $ sudo gem install sequel
10
+ # $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
11
+
12
+ module Jekyll
13
+ module WordPress
14
+
15
+ # Main migrator function. Call this to perform the migration.
16
+ #
17
+ # dbname:: The name of the database
18
+ # user:: The database user name
19
+ # pass:: The database user's password
20
+ # host:: The address of the MySQL database host. Default: 'localhost'
21
+ # options:: A hash table of configuration options.
22
+ #
23
+ # Supported options are:
24
+ #
25
+ # :table_prefix:: Prefix of database tables used by WordPress.
26
+ # Default: 'wp_'
27
+ # :clean_entities:: If true, convert non-ASCII characters to HTML
28
+ # entities in the posts, comments, titles, and
29
+ # names. Requires the 'htmlentities' gem to
30
+ # work. Default: true.
31
+ # :comments:: If true, migrate post comments too. Comments
32
+ # are saved in the post's YAML front matter.
33
+ # Default: true.
34
+ # :categories:: If true, save the post's categories in its
35
+ # YAML front matter.
36
+ # :tags:: If true, save the post's tags in its
37
+ # YAML front matter.
38
+ # :more_excerpt:: If true, when a post has no excerpt but
39
+ # does have a <!-- more --> tag, use the
40
+ # preceding post content as the excerpt.
41
+ # Default: true.
42
+ # :more_anchor:: If true, convert a <!-- more --> tag into
43
+ # two HTML anchors with ids "more" and
44
+ # "more-NNN" (where NNN is the post number).
45
+ # Default: true.
46
+ # :status:: Array of allowed post statuses. Only
47
+ # posts with matching status will be migrated.
48
+ # Known statuses are :publish, :draft, :private,
49
+ # and :revision. If this is nil or an empty
50
+ # array, all posts are migrated regardless of
51
+ # status. Default: [:publish].
52
+ #
53
+ def self.process(dbname, user, pass, host='localhost', options={})
54
+ options = {
55
+ :table_prefix => 'wp_',
56
+ :clean_entities => true,
57
+ :comments => true,
58
+ :categories => true,
59
+ :tags => true,
60
+ :more_excerpt => true,
61
+ :more_anchor => true,
62
+ :status => [:publish] # :draft, :private, :revision
63
+ }.merge(options)
64
+
65
+ if options[:clean_entities]
66
+ begin
67
+ require 'htmlentities'
68
+ rescue LoadError
69
+ STDERR.puts "Could not require 'htmlentities', so the " +
70
+ ":clean_entities option is now disabled."
71
+ options[:clean_entities] = false
72
+ end
73
+ end
74
+
75
+ FileUtils.mkdir_p("_posts")
76
+
77
+ db = Sequel.mysql(dbname, :user => user, :password => pass,
78
+ :host => host, :encoding => 'utf8')
79
+
80
+ px = options[:table_prefix]
81
+
82
+ posts_query = "
83
+ SELECT
84
+ posts.ID AS `id`,
85
+ posts.guid AS `guid`,
86
+ posts.post_type AS `type`,
87
+ posts.post_status AS `status`,
88
+ posts.post_title AS `title`,
89
+ posts.post_name AS `slug`,
90
+ posts.post_date AS `date`,
91
+ posts.post_content AS `content`,
92
+ posts.post_excerpt AS `excerpt`,
93
+ posts.comment_count AS `comment_count`,
94
+ users.display_name AS `author`,
95
+ users.user_login AS `author_login`,
96
+ users.user_email AS `author_email`,
97
+ users.user_url AS `author_url`
98
+ FROM #{px}posts AS `posts`
99
+ LEFT JOIN #{px}users AS `users`
100
+ ON posts.post_author = users.ID"
101
+
102
+ if options[:status] and not options[:status].empty?
103
+ status = options[:status][0]
104
+ posts_query << "
105
+ WHERE posts.post_status = '#{status.to_s}'"
106
+ options[:status][1..-1].each do |status|
107
+ posts_query << " OR
108
+ posts.post_status = '#{status.to_s}'"
109
+ end
110
+ end
111
+
112
+ db[posts_query].each do |post|
113
+ process_post(post, db, options)
114
+ end
115
+ end
116
+
117
+
118
+ def self.process_post(post, db, options)
119
+ px = options[:table_prefix]
120
+
121
+ title = post[:title]
122
+ if options[:clean_entities]
123
+ title = clean_entities(title)
124
+ end
125
+
126
+ slug = post[:slug]
127
+ if !slug or slug.empty?
128
+ slug = sluggify(title)
129
+ end
130
+
131
+ date = post[:date] || Time.now
132
+ name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month,
133
+ date.day, slug]
134
+ content = post[:content].to_s
135
+ if options[:clean_entities]
136
+ content = clean_entities(content)
137
+ end
138
+
139
+ excerpt = post[:excerpt].to_s
140
+
141
+ more_index = content.index(/<!-- *more *-->/)
142
+ more_anchor = nil
143
+ if more_index
144
+ if options[:more_excerpt] and
145
+ (post[:excerpt].nil? or post[:excerpt].empty?)
146
+ excerpt = content[0...more_index]
147
+ end
148
+ if options[:more_anchor]
149
+ more_link = "more"
150
+ content.sub!(/<!-- *more *-->/,
151
+ "<a id=\"more\"></a>" +
152
+ "<a id=\"more-#{post[:id]}\"></a>")
153
+ end
154
+ end
155
+
156
+ categories = []
157
+ tags = []
158
+
159
+ if options[:categories] or options[:tags]
160
+
161
+ cquery =
162
+ "SELECT
163
+ terms.name AS `name`,
164
+ ttax.taxonomy AS `type`
165
+ FROM
166
+ #{px}terms AS `terms`,
167
+ #{px}term_relationships AS `trels`,
168
+ #{px}term_taxonomy AS `ttax`
169
+ WHERE
170
+ trels.object_id = '#{post[:id]}' AND
171
+ trels.term_taxonomy_id = ttax.term_taxonomy_id AND
172
+ terms.term_id = ttax.term_id"
173
+
174
+ db[cquery].each do |term|
175
+ if options[:categories] and term[:type] == "category"
176
+ if options[:clean_entities]
177
+ categories << clean_entities(term[:name])
178
+ else
179
+ categories << term[:name]
180
+ end
181
+ elsif options[:tags] and term[:type] == "post_tag"
182
+ if options[:clean_entities]
183
+ tags << clean_entities(term[:name])
184
+ else
185
+ tags << term[:name]
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ comments = []
192
+
193
+ if options[:comments] and post[:comment_count].to_i > 0
194
+ cquery =
195
+ "SELECT
196
+ comment_ID AS `id`,
197
+ comment_author AS `author`,
198
+ comment_author_email AS `author_email`,
199
+ comment_author_url AS `author_url`,
200
+ comment_date AS `date`,
201
+ comment_date_gmt AS `date_gmt`,
202
+ comment_content AS `content`
203
+ FROM #{px}comments
204
+ WHERE
205
+ comment_post_ID = '#{post[:id]}' AND
206
+ comment_approved != 'spam'"
207
+
208
+
209
+ db[cquery].each do |comment|
210
+
211
+ comcontent = comment[:content].to_s
212
+ if comcontent.respond_to?(:force_encoding)
213
+ comcontent.force_encoding("UTF-8")
214
+ end
215
+ if options[:clean_entities]
216
+ comcontent = clean_entities(comcontent)
217
+ end
218
+ comauthor = comment[:author].to_s
219
+ if options[:clean_entities]
220
+ comauthor = clean_entities(comauthor)
221
+ end
222
+
223
+ comments << {
224
+ 'id' => comment[:id].to_i,
225
+ 'author' => comauthor,
226
+ 'author_email' => comment[:author_email].to_s,
227
+ 'author_url' => comment[:author_url].to_s,
228
+ 'date' => comment[:date].to_s,
229
+ 'date_gmt' => comment[:date_gmt].to_s,
230
+ 'content' => comcontent,
231
+ }
232
+ end
233
+
234
+ comments.sort!{ |a,b| a['id'] <=> b['id'] }
235
+ end
236
+
237
+ # Get the relevant fields as a hash, delete empty fields and
238
+ # convert to YAML for the header.
239
+ data = {
240
+ 'layout' => post[:type].to_s,
241
+ 'status' => post[:status].to_s,
242
+ 'published' => (post[:status].to_s == "publish"),
243
+ 'title' => title.to_s,
244
+ 'author' => post[:author].to_s,
245
+ 'author_login' => post[:author_login].to_s,
246
+ 'author_email' => post[:author_email].to_s,
247
+ 'author_url' => post[:author_url].to_s,
248
+ 'excerpt' => excerpt,
249
+ 'more_anchor' => more_anchor,
250
+ 'wordpress_id' => post[:id],
251
+ 'wordpress_url' => post[:guid].to_s,
252
+ 'date' => date,
253
+ 'categories' => options[:categories] ? categories : nil,
254
+ 'tags' => options[:tags] ? tags : nil,
255
+ 'comments' => options[:comments] ? comments : nil,
256
+ }.delete_if { |k,v| v.nil? || v == '' }.to_yaml
257
+
258
+ # Write out the data and content to file
259
+ File.open("_posts/#{name}", "w") do |f|
260
+ f.puts data
261
+ f.puts "---"
262
+ f.puts content
263
+ end
264
+ end
265
+
266
+
267
+ def self.clean_entities( text )
268
+ if text.respond_to?(:force_encoding)
269
+ text.force_encoding("UTF-8")
270
+ end
271
+ text = HTMLEntities.new.encode(text, :named)
272
+ # We don't want to convert these, it would break all
273
+ # HTML tags in the post and comments.
274
+ text.gsub!("&amp;", "&")
275
+ text.gsub!("&lt;", "<")
276
+ text.gsub!("&gt;", ">")
277
+ text.gsub!("&quot;", '"')
278
+ text.gsub!("&apos;", "'")
279
+ text
280
+ end
281
+
282
+
283
+ def self.sluggify( title )
284
+ begin
285
+ require 'unidecode'
286
+ title = title.to_ascii
287
+ rescue LoadError
288
+ STDERR.puts "Could not require 'unidecode'. If your post titles have non-ASCII characters, you could get nicer permalinks by installing unidecode."
289
+ end
290
+ title.downcase.gsub(/[^0-9A-Za-z]+/, " ").strip.gsub(" ", "-")
291
+ end
292
+
293
+ end
294
+ end
@@ -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
@@ -0,0 +1,160 @@
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
+ # source - 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, source, dir, name)
18
+ @site = site
19
+ @dir = dir
20
+ @base = File.join(source, @dir)
21
+ @name = name
22
+
23
+ self.process(name)
24
+ self.read_yaml(@base, name)
25
+ end
26
+
27
+ # The source filename for this page.
28
+ def filename
29
+ File.join(@base, name)
30
+ end
31
+
32
+ # The generated directory into which the page will be placed
33
+ # upon generation. This is derived from the permalink or, if
34
+ # permalink is absent, we be '/'
35
+ #
36
+ # Returns the String destination directory.
37
+ def dir
38
+ url[-1, 1] == '/' ? url : File.dirname(url)
39
+ end
40
+
41
+ # The full path and filename of the post. Defined in the YAML of the post
42
+ # body.
43
+ #
44
+ # Returns the String permalink or nil if none has been set.
45
+ def permalink
46
+ self.data && self.data['permalink']
47
+ end
48
+
49
+ # The template of the permalink.
50
+ #
51
+ # Returns the template String.
52
+ def template
53
+ if self.site.permalink_style == :pretty && !index? && html?
54
+ "/:basename/"
55
+ else
56
+ "/:basename:output_ext"
57
+ end
58
+ end
59
+
60
+ # The generated relative url of this page. e.g. /about.html.
61
+ #
62
+ # Returns the String url.
63
+ def url
64
+ return @url if @url
65
+
66
+ url = if permalink
67
+ permalink
68
+ else
69
+ {
70
+ "basename" => self.basename,
71
+ "output_ext" => self.output_ext,
72
+ }.inject(template) { |result, token|
73
+ result.gsub(/:#{token.first}/, token.last)
74
+ }.gsub(/\/\//, "/")
75
+ end
76
+
77
+ # sanitize url
78
+ @url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
79
+ @url += "/" if url =~ /\/$/
80
+ @url
81
+ end
82
+
83
+ # Extract information from the page filename.
84
+ #
85
+ # name - The String filename of the page file.
86
+ #
87
+ # Returns nothing.
88
+ def process(name)
89
+ self.ext = File.extname(name)
90
+ self.basename = name[0 .. -self.ext.length-1]
91
+ end
92
+
93
+ # Add any necessary layouts to this post
94
+ #
95
+ # layouts - The Hash of {"name" => "layout"}.
96
+ # site_payload - The site payload Hash.
97
+ #
98
+ # Returns nothing.
99
+ def render(layouts, site_payload)
100
+ payload = {
101
+ "page" => self.to_liquid,
102
+ 'paginator' => pager.to_liquid
103
+ }.deep_merge(site_payload)
104
+
105
+ do_layout(payload, layouts)
106
+ end
107
+
108
+ # Convert this Page's data to a Hash suitable for use by Liquid.
109
+ #
110
+ # Returns the Hash representation of this Page.
111
+ def to_liquid
112
+ self.data.deep_merge({
113
+ "url" => File.join(@dir, self.url),
114
+ "content" => self.content })
115
+ end
116
+
117
+ # Obtain destination path.
118
+ #
119
+ # dest - The String path to the destination dir.
120
+ #
121
+ # Returns the destination file path String.
122
+ def destination(dest)
123
+ # The url needs to be unescaped in order to preserve the correct
124
+ # filename.
125
+ path = File.join(dest, @dir, CGI.unescape(self.url))
126
+ path = File.join(path, "index.html") if self.url =~ /\/$/
127
+ path
128
+ end
129
+
130
+ # Write the generated page file to the destination directory.
131
+ #
132
+ # dest - The String path to the destination dir.
133
+ #
134
+ # Returns nothing.
135
+ def write(dest)
136
+ path = destination(dest)
137
+ FileUtils.mkdir_p(File.dirname(path))
138
+ File.open(path, 'w') do |f|
139
+ f.write(self.output)
140
+ end
141
+ end
142
+
143
+ # Returns the object as a debug String.
144
+ def inspect
145
+ "#<Jekyll:Page @name=#{self.name.inspect}>"
146
+ end
147
+
148
+ # Returns the Boolean of whether this Page is HTML or not.
149
+ def html?
150
+ output_ext == '.html'
151
+ end
152
+
153
+ # Returns the Boolean of whether this Page is an index file or not.
154
+ def index?
155
+ basename == 'index'
156
+ end
157
+
158
+ end
159
+
160
+ 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