jekyll 0.8.0 → 0.9.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.

@@ -1,3 +1,11 @@
1
+ == 0.9.0 / 2010-12-15
2
+ * Minor Enhancements
3
+ * Use OptionParser's [no-] functionality for better boolean parsing.
4
+ * Add Drupal migrator (#245)
5
+ * Complain about YAML and Liquid errors (#249)
6
+ * Remove orphaned files during regeneration (#247)
7
+ * Add Marley migrator (#28)
8
+
1
9
  == 0.8.0 / 2010-11-22
2
10
  * Minor Enhancements
3
11
  * Add wordpress.com importer (#207)
data/bin/jekyll CHANGED
@@ -23,16 +23,12 @@ options = {}
23
23
  opts = OptionParser.new do |opts|
24
24
  opts.banner = help
25
25
 
26
- opts.on("--safe", "Safe mode (default unsafe)") do
27
- options['safe'] = true
26
+ opts.on("--[no-]safe", "Safe mode (default unsafe)") do |safe|
27
+ options['safe'] = safe
28
28
  end
29
29
 
30
- opts.on("--auto", "Auto-regenerate") do
31
- options['auto'] = true
32
- end
33
-
34
- opts.on("--no-auto", "No auto-regenerate") do
35
- options['auto'] = false
30
+ opts.on("--[no-]auto", "Auto-regenerate") do |auto|
31
+ options['auto'] = auto
36
32
  end
37
33
 
38
34
  opts.on("--server [PORT]", "Start web server (default port 4000)") do |port|
@@ -44,12 +40,12 @@ opts = OptionParser.new do |opts|
44
40
  options['baseurl'] = baseurl
45
41
  end
46
42
 
47
- opts.on("--lsi", "Use LSI for better related posts") do
48
- options['lsi'] = true
43
+ opts.on("--[no-]lsi", "Use LSI for better related posts") do |lsi|
44
+ options['lsi'] = lsi
49
45
  end
50
46
 
51
- opts.on("--pygments", "Use pygments to highlight code") do
52
- options['pygments'] = true
47
+ opts.on("--[no-]pygments", "Use pygments to highlight code") do |pygments|
48
+ options['pygments'] = pygments
53
49
  end
54
50
 
55
51
  opts.on("--rdiscount", "Use rdiscount gem for Markdown") do
@@ -64,12 +60,8 @@ opts = OptionParser.new do |opts|
64
60
  options['time'] = Time.parse(time)
65
61
  end
66
62
 
67
- opts.on("--future", "Render future dated posts") do
68
- options['future'] = true
69
- end
70
-
71
- opts.on("--no-future", "Do not render future dated posts") do
72
- options['future'] = false
63
+ opts.on("--[no-]future", "Render future dated posts") do |future|
64
+ options['future'] = future
73
65
  end
74
66
 
75
67
  opts.on("--permalink [TYPE]", "Use 'date' (default) for YYYY/MM/DD") do |style|
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
4
4
  s.rubygems_version = '1.3.5'
5
5
 
6
6
  s.name = 'jekyll'
7
- s.version = '0.8.0'
8
- s.date = '2010-11-22'
7
+ s.version = '0.9.0'
8
+ s.date = '2010-12-15'
9
9
  s.rubyforge_project = 'jekyll'
10
10
 
11
11
  s.summary = "A simple, blog aware, static site generator."
@@ -68,6 +68,8 @@ Gem::Specification.new do |s|
68
68
  lib/jekyll/generators/pagination.rb
69
69
  lib/jekyll/layout.rb
70
70
  lib/jekyll/migrators/csv.rb
71
+ lib/jekyll/migrators/drupal.rb
72
+ lib/jekyll/migrators/marley.rb
71
73
  lib/jekyll/migrators/mephisto.rb
72
74
  lib/jekyll/migrators/mt.rb
73
75
  lib/jekyll/migrators/textpattern.rb
@@ -45,7 +45,7 @@ require_all 'jekyll/generators'
45
45
  require_all 'jekyll/tags'
46
46
 
47
47
  module Jekyll
48
- VERSION = '0.8.0'
48
+ VERSION = '0.9.0'
49
49
 
50
50
  # Default options. Overriden by values in _config.yml or command-line opts.
51
51
  # (Strings rather symbols used for compatability with YAML).
@@ -26,7 +26,11 @@ module Jekyll
26
26
  if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
27
27
  self.content = self.content[($1.size + $2.size)..-1]
28
28
 
29
- self.data = YAML.load($1)
29
+ begin
30
+ self.data = YAML.load($1)
31
+ rescue => e
32
+ puts "YAML Exception: #{e.message}"
33
+ end
30
34
  end
31
35
 
32
36
  self.data ||= {}
@@ -63,7 +67,13 @@ module Jekyll
63
67
  # render and transform content (this becomes the final content of the object)
64
68
  payload["pygments_prefix"] = converter.pygments_prefix
65
69
  payload["pygments_suffix"] = converter.pygments_suffix
66
- self.content = Liquid::Template.parse(self.content).render(payload, info)
70
+
71
+ begin
72
+ self.content = Liquid::Template.parse(self.content).render(payload, info)
73
+ rescue => e
74
+ puts "Liquid Exception: #{e.message} in #{self.data["layout"]}"
75
+ end
76
+
67
77
  self.transform
68
78
 
69
79
  # output keeps track of what will finally be written
@@ -73,7 +83,12 @@ module Jekyll
73
83
  layout = layouts[self.data["layout"]]
74
84
  while layout
75
85
  payload = payload.deep_merge({"content" => self.output, "page" => layout.data})
76
- self.output = Liquid::Template.parse(layout.content).render(payload, info)
86
+
87
+ begin
88
+ self.output = Liquid::Template.parse(layout.content).render(payload, info)
89
+ rescue => e
90
+ puts "Liquid Exception: #{e.message} in #{self.data["layout"]}"
91
+ end
77
92
 
78
93
  layout = layouts[layout.data["layout"]]
79
94
  end
@@ -0,0 +1,86 @@
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 Drupal
14
+
15
+ # Reads a MySQL database via Sequel and creates a post file for each
16
+ # post in wp_posts that has post_status = 'publish'.
17
+ # This restriction is made because 'draft' posts are not guaranteed to
18
+ # have valid dates.
19
+ QUERY = "SELECT node.nid, node.title, node_revisions.body, node.created, node.status FROM node, node_revisions WHERE (node.type = 'blog' OR node.type = 'story') AND node.vid = node_revisions.vid"
20
+
21
+ def self.process(dbname, user, pass, host = 'localhost')
22
+ db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host, :encoding => 'utf8')
23
+
24
+ FileUtils.mkdir_p "_posts"
25
+ FileUtils.mkdir_p "_drafts"
26
+
27
+ # Create the refresh layout
28
+ # Change the refresh url if you customized your permalink config
29
+ File.open("_layouts/refresh.html", "w") do |f|
30
+ f.puts <<EOF
31
+ <!DOCTYPE html>
32
+ <html>
33
+ <head>
34
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
35
+ <meta http-equiv="refresh" content="0;url={{ page.refresh_to_post_id }}.html" />
36
+ </head>
37
+ </html>
38
+ EOF
39
+ end
40
+
41
+ db[QUERY].each do |post|
42
+ # Get required fields and construct Jekyll compatible name
43
+ node_id = post[:nid]
44
+ title = post[:title]
45
+ content = post[:body]
46
+ created = post[:created]
47
+ time = Time.at(created)
48
+ is_published = post[:status] == 1
49
+ dir = is_published ? "_posts" : "_drafts"
50
+ slug = title.strip.downcase.gsub(/(&|&amp;)/, ' and ').gsub(/[\s\.\/\\]/, '-').gsub(/[^\w-]/, '').gsub(/[-_]{2,}/, '-').gsub(/^[-_]/, '').gsub(/[-_]$/, '')
51
+ name = time.strftime("%Y-%m-%d-") + slug + '.md'
52
+
53
+ # Get the relevant fields as a hash, delete empty fields and convert
54
+ # to YAML for the header
55
+ data = {
56
+ 'layout' => 'post',
57
+ 'title' => title.to_s,
58
+ 'created' => created,
59
+ }.delete_if { |k,v| v.nil? || v == ''}.to_yaml
60
+
61
+ # Write out the data and content to file
62
+ File.open("#{dir}/#{name}", "w") do |f|
63
+ f.puts data
64
+ f.puts "---"
65
+ f.puts content
66
+ end
67
+
68
+ # Make a file to redirect from the old Drupal URL
69
+ if is_published
70
+ FileUtils.mkdir_p "node/#{node_id}"
71
+ File.open("node/#{node_id}/index.md", "w") do |f|
72
+ f.puts "---"
73
+ f.puts "layout: refresh"
74
+ f.puts "refresh_to_post_id: /#{time.strftime("%Y/%m/%d/") + slug}"
75
+ f.puts "---"
76
+ end
77
+ end
78
+ end
79
+
80
+ # TODO: Make dirs & files for nodes of type 'page'
81
+ # Make refresh pages for these as well
82
+
83
+ # TODO: Make refresh dirs & files according to entries in url_alias table
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,53 @@
1
+ require 'yaml'
2
+ require 'fileutils'
3
+
4
+ module Jekyll
5
+ module Marley
6
+
7
+ def self.regexp
8
+ { :id => /^\d{0,4}-{0,1}(.*)$/,
9
+ :title => /^#\s*(.*)\s+$/,
10
+ :title_with_date => /^#\s*(.*)\s+\(([0-9\/]+)\)$/,
11
+ :published_on => /.*\s+\(([0-9\/]+)\)$/,
12
+ :perex => /^([^\#\n]+\n)$/,
13
+ :meta => /^\{\{\n(.*)\}\}\n$/mi # Multiline Regexp
14
+ }
15
+ end
16
+
17
+ def self.process(marley_data_dir)
18
+ raise ArgumentError, "marley dir #{marley_data_dir} not found" unless File.directory?(marley_data_dir)
19
+
20
+ FileUtils.mkdir_p "_posts"
21
+
22
+ posts = 0
23
+ Dir["#{marley_data_dir}/**/*.txt"].each do |f|
24
+ next unless File.exists?(f)
25
+
26
+ #copied over from marley's app/lib/post.rb
27
+ file_content = File.read(f)
28
+ meta_content = file_content.slice!( self.regexp[:meta] )
29
+ body = file_content.sub( self.regexp[:title], '').sub( self.regexp[:perex], '').strip
30
+
31
+ title = file_content.scan( self.regexp[:title] ).first.to_s.strip
32
+ prerex = file_content.scan( self.regexp[:perex] ).first.to_s.strip
33
+ published_on = DateTime.parse( post[:published_on] ) rescue File.mtime( File.dirname(f) )
34
+ meta = ( meta_content ) ? YAML::load( meta_content.scan( self.regexp[:meta]).to_s ) : {}
35
+ meta['title'] = title
36
+ meta['layout'] = 'post'
37
+
38
+ formatted_date = published_on.strftime('%Y-%m-%d')
39
+ post_name = File.dirname(f).split(%r{/}).last.gsub(/\A\d+-/, '')
40
+
41
+ name = "#{formatted_date}-#{post_name}"
42
+ File.open("_posts/#{name}.markdown", "w") do |f|
43
+ f.puts meta.to_yaml
44
+ f.puts "---\n"
45
+ f.puts "\n#{prerex}\n\n" if prerex
46
+ f.puts body
47
+ end
48
+ posts += 1
49
+ end
50
+ "Created #{posts} posts!"
51
+ end
52
+ end
53
+ end
@@ -93,24 +93,25 @@ module Jekyll
93
93
  "url" => File.join(@dir, self.url),
94
94
  "content" => self.content })
95
95
  end
96
+
97
+ # Obtain destination path.
98
+ # +dest+ is the String path to the destination dir
99
+ #
100
+ # Returns destination file path.
101
+ def destination(dest)
102
+ # The url needs to be unescaped in order to preserve the correct filename
103
+ path = File.join(dest, @dir, CGI.unescape(self.url))
104
+ path = File.join(path, "index.html") if self.url =~ /\/$/
105
+ path
106
+ end
96
107
 
97
108
  # Write the generated page file to the destination directory.
98
- # +dest_prefix+ is the String path to the destination dir
99
- # +dest_suffix+ is a suffix path to the destination dir
109
+ # +dest+ is the String path to the destination dir
100
110
  #
101
111
  # Returns nothing
102
- def write(dest_prefix, dest_suffix = nil)
103
- dest = File.join(dest_prefix, @dir)
104
- dest = File.join(dest, dest_suffix) if dest_suffix
105
- FileUtils.mkdir_p(dest)
106
-
107
- # The url needs to be unescaped in order to preserve the correct filename
108
- path = File.join(dest, CGI.unescape(self.url))
109
- if self.url =~ /\/$/
110
- FileUtils.mkdir_p(path)
111
- path = File.join(path, "index.html")
112
- end
113
-
112
+ def write(dest)
113
+ path = destination(dest)
114
+ FileUtils.mkdir_p(File.dirname(path))
114
115
  File.open(path, 'w') do |f|
115
116
  f.write(self.output)
116
117
  end
@@ -177,22 +177,25 @@ module Jekyll
177
177
 
178
178
  do_layout(payload, layouts)
179
179
  end
180
+
181
+ # Obtain destination path.
182
+ # +dest+ is the String path to the destination dir
183
+ #
184
+ # Returns destination file path.
185
+ def destination(dest)
186
+ # The url needs to be unescaped in order to preserve the correct filename
187
+ path = File.join(dest, CGI.unescape(self.url))
188
+ path = File.join(path, "index.html") if template[/\.html$/].nil?
189
+ path
190
+ end
180
191
 
181
192
  # Write the generated post file to the destination directory.
182
193
  # +dest+ is the String path to the destination dir
183
194
  #
184
195
  # Returns nothing
185
196
  def write(dest)
186
- FileUtils.mkdir_p(File.join(dest, dir))
187
-
188
- # The url needs to be unescaped in order to preserve the correct filename
189
- path = File.join(dest, CGI.unescape(self.url))
190
-
191
- if template[/\.html$/].nil?
192
- FileUtils.mkdir_p(path)
193
- path = File.join(path, "index.html")
194
- end
195
-
197
+ path = destination(dest)
198
+ FileUtils.mkdir_p(File.dirname(path))
196
199
  File.open(path, 'w') do |f|
197
200
  f.write(self.output)
198
201
  end
@@ -79,6 +79,7 @@ module Jekyll
79
79
  self.read
80
80
  self.generate
81
81
  self.render
82
+ self.cleanup
82
83
  self.write
83
84
  end
84
85
 
@@ -151,6 +152,36 @@ module Jekyll
151
152
  rescue Errno::ENOENT => e
152
153
  # ignore missing layout dir
153
154
  end
155
+
156
+ # Remove orphaned files and empty directories in destination
157
+ #
158
+ # Returns nothing
159
+ def cleanup
160
+ # all files and directories in destination, including hidden ones
161
+ dest_files = []
162
+ Dir.glob(File.join(self.dest, "**", "*"), File::FNM_DOTMATCH) do |file|
163
+ dest_files << file unless file =~ /\/\.{1,2}$/
164
+ end
165
+
166
+ # files to be written
167
+ files = []
168
+ self.posts.each do |post|
169
+ files << post.destination(self.dest)
170
+ end
171
+ self.pages.each do |page|
172
+ files << page.destination(self.dest)
173
+ end
174
+ self.static_files.each do |sf|
175
+ files << sf.destination(self.dest)
176
+ end
177
+
178
+ # adding files' parent directories
179
+ files.each { |file| files << File.dirname(file) unless files.include? File.dirname(file) }
180
+
181
+ obsolete_files = dest_files - files
182
+
183
+ FileUtils.rm_rf(obsolete_files)
184
+ end
154
185
 
155
186
  # Write static files, pages and posts
156
187
  #
@@ -52,12 +52,11 @@ module Jekyll
52
52
  # Returns false if the file was not modified since last time (no-op).
53
53
  def write(dest)
54
54
  dest_path = destination(dest)
55
- dest_dir = File.join(dest, @dir)
56
55
 
57
56
  return false if File.exist? dest_path and !modified?
58
57
  @@mtimes[path] = mtime
59
58
 
60
- FileUtils.mkdir_p(dest_dir)
59
+ FileUtils.mkdir_p(File.dirname(dest_path))
61
60
  FileUtils.cp(path, dest_path)
62
61
 
63
62
  true
@@ -132,6 +132,39 @@ class TestSite < Test::Unit::TestCase
132
132
  assert_equal includes, @site.filter_entries(excludes + includes)
133
133
  end
134
134
 
135
+ context 'with orphaned files in destination' do
136
+ setup do
137
+ clear_dest
138
+ @site.process
139
+ # generate some orphaned files:
140
+ # hidden file
141
+ File.open(dest_dir('.htpasswd'), 'w')
142
+ # single file
143
+ File.open(dest_dir('obsolete.html'), 'w')
144
+ # single file in sub directory
145
+ FileUtils.mkdir(dest_dir('qux'))
146
+ File.open(dest_dir('qux/obsolete.html'), 'w')
147
+ # empty directory
148
+ FileUtils.mkdir(dest_dir('quux'))
149
+ end
150
+
151
+ teardown do
152
+ FileUtils.rm_f(dest_dir('.htpasswd'))
153
+ FileUtils.rm_f(dest_dir('obsolete.html'))
154
+ FileUtils.rm_rf(dest_dir('qux'))
155
+ FileUtils.rm_f(dest_dir('quux'))
156
+ end
157
+
158
+ should 'remove orphaned files in destination' do
159
+ @site.process
160
+ assert !File.exist?(dest_dir('.htpasswd'))
161
+ assert !File.exist?(dest_dir('obsolete.html'))
162
+ assert !File.exist?(dest_dir('qux'))
163
+ assert !File.exist?(dest_dir('quux'))
164
+ end
165
+
166
+ end
167
+
135
168
  context 'with an invalid markdown processor in the configuration' do
136
169
  should 'not throw an error at initialization time' do
137
170
  bad_processor = 'not a processor name'
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll
3
3
  version: !ruby/object:Gem::Version
4
- hash: 63
4
+ hash: 59
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 8
8
+ - 9
9
9
  - 0
10
- version: 0.8.0
10
+ version: 0.9.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tom Preston-Werner
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-11-22 00:00:00 -08:00
18
+ date: 2010-12-15 00:00:00 -08:00
19
19
  default_executable: jekyll
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -219,6 +219,8 @@ files:
219
219
  - lib/jekyll/generators/pagination.rb
220
220
  - lib/jekyll/layout.rb
221
221
  - lib/jekyll/migrators/csv.rb
222
+ - lib/jekyll/migrators/drupal.rb
223
+ - lib/jekyll/migrators/marley.rb
222
224
  - lib/jekyll/migrators/mephisto.rb
223
225
  - lib/jekyll/migrators/mt.rb
224
226
  - lib/jekyll/migrators/textpattern.rb