PerfectlyNormal-jekyll 0.5.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.
Files changed (48) hide show
  1. data/History.txt +127 -0
  2. data/README.textile +41 -0
  3. data/Rakefile +91 -0
  4. data/VERSION.yml +4 -0
  5. data/bin/jekyll +150 -0
  6. data/lib/jekyll.rb +84 -0
  7. data/lib/jekyll/albino.rb +122 -0
  8. data/lib/jekyll/converters/csv.rb +26 -0
  9. data/lib/jekyll/converters/mephisto.rb +79 -0
  10. data/lib/jekyll/converters/mt.rb +59 -0
  11. data/lib/jekyll/converters/textpattern.rb +50 -0
  12. data/lib/jekyll/converters/typo.rb +49 -0
  13. data/lib/jekyll/converters/wordpress.rb +54 -0
  14. data/lib/jekyll/convertible.rb +82 -0
  15. data/lib/jekyll/core_ext.rb +22 -0
  16. data/lib/jekyll/filters.rb +47 -0
  17. data/lib/jekyll/layout.rb +36 -0
  18. data/lib/jekyll/page.rb +70 -0
  19. data/lib/jekyll/post.rb +243 -0
  20. data/lib/jekyll/site.rb +265 -0
  21. data/lib/jekyll/tags/highlight.rb +56 -0
  22. data/lib/jekyll/tags/include.rb +31 -0
  23. data/test/helper.rb +27 -0
  24. data/test/source/_includes/sig.markdown +3 -0
  25. data/test/source/_layouts/default.html +27 -0
  26. data/test/source/_layouts/simple.html +1 -0
  27. data/test/source/_posts/2008-02-02-not-published.textile +8 -0
  28. data/test/source/_posts/2008-02-02-published.textile +8 -0
  29. data/test/source/_posts/2008-10-18-foo-bar.textile +8 -0
  30. data/test/source/_posts/2008-11-21-complex.textile +8 -0
  31. data/test/source/_posts/2008-12-03-permalinked-post.textile +9 -0
  32. data/test/source/_posts/2008-12-13-include.markdown +8 -0
  33. data/test/source/_posts/2009-01-27-array-categories.textile +10 -0
  34. data/test/source/_posts/2009-01-27-categories.textile +7 -0
  35. data/test/source/_posts/2009-01-27-category.textile +7 -0
  36. data/test/source/_posts/2009-03-12-hash-#1.markdown +6 -0
  37. data/test/source/category/_posts/2008-9-23-categories.textile +6 -0
  38. data/test/source/css/screen.css +76 -0
  39. data/test/source/foo/_posts/bar/2008-12-12-topical-post.textile +8 -0
  40. data/test/source/index.html +22 -0
  41. data/test/source/z_category/_posts/2008-9-23-categories.textile +6 -0
  42. data/test/suite.rb +9 -0
  43. data/test/test_filters.rb +49 -0
  44. data/test/test_generated_site.rb +38 -0
  45. data/test/test_post.rb +269 -0
  46. data/test/test_site.rb +65 -0
  47. data/test/test_tags.rb +116 -0
  48. metadata +164 -0
@@ -0,0 +1,122 @@
1
+ ##
2
+ # Wrapper for the Pygments command line tool, pygmentize.
3
+ #
4
+ # Pygments: http://pygments.org/
5
+ #
6
+ # Assumes pygmentize is in the path. If not, set its location
7
+ # with Albino.bin = '/path/to/pygmentize'
8
+ #
9
+ # Use like so:
10
+ #
11
+ # @syntaxer = Albino.new('/some/file.rb', :ruby)
12
+ # puts @syntaxer.colorize
13
+ #
14
+ # This'll print out an HTMLized, Ruby-highlighted version
15
+ # of '/some/file.rb'.
16
+ #
17
+ # To use another formatter, pass it as the third argument:
18
+ #
19
+ # @syntaxer = Albino.new('/some/file.rb', :ruby, :bbcode)
20
+ # puts @syntaxer.colorize
21
+ #
22
+ # You can also use the #colorize class method:
23
+ #
24
+ # puts Albino.colorize('/some/file.rb', :ruby)
25
+ #
26
+ # Another also: you get a #to_s, for somewhat nicer use in Rails views.
27
+ #
28
+ # ... helper file ...
29
+ # def highlight(text)
30
+ # Albino.new(text, :ruby)
31
+ # end
32
+ #
33
+ # ... view file ...
34
+ # <%= highlight text %>
35
+ #
36
+ # The default lexer is 'text'. You need to specify a lexer yourself;
37
+ # because we are using STDIN there is no auto-detect.
38
+ #
39
+ # To see all lexers and formatters available, run `pygmentize -L`.
40
+ #
41
+ # Chris Wanstrath // chris@ozmm.org
42
+ # GitHub // http://github.com
43
+ #
44
+ require 'open4'
45
+
46
+ class Albino
47
+ @@bin = Rails.development? ? 'pygmentize' : '/usr/bin/pygmentize' rescue 'pygmentize'
48
+
49
+ def self.bin=(path)
50
+ @@bin = path
51
+ end
52
+
53
+ def self.colorize(*args)
54
+ new(*args).colorize
55
+ end
56
+
57
+ def initialize(target, lexer = :text, format = :html)
58
+ @target = File.exists?(target) ? File.read(target) : target rescue target
59
+ @options = { :l => lexer, :f => format, :O => 'encoding=utf-8' }
60
+ end
61
+
62
+ def execute(command)
63
+ output = ''
64
+ Open4.popen4(command) do |pid, stdin, stdout, stderr|
65
+ stdin.puts @target
66
+ stdin.close
67
+ output = stdout.read.strip
68
+ [stdout, stderr].each { |io| io.close }
69
+ end
70
+ output
71
+ end
72
+
73
+ def colorize(options = {})
74
+ html = execute(@@bin + convert_options(options))
75
+ # Work around an RDiscount bug: http://gist.github.com/97682
76
+ html.to_s.sub(%r{</pre></div>\Z}, "</pre>\n</div>")
77
+ end
78
+ alias_method :to_s, :colorize
79
+
80
+ def convert_options(options = {})
81
+ @options.merge(options).inject('') do |string, (flag, value)|
82
+ string + " -#{flag} #{value}"
83
+ end
84
+ end
85
+ end
86
+
87
+ if $0 == __FILE__
88
+ require 'rubygems'
89
+ require 'test/spec'
90
+ require 'mocha'
91
+ begin require 'redgreen'; rescue LoadError; end
92
+
93
+ context "Albino" do
94
+ setup do
95
+ @syntaxer = Albino.new(__FILE__, :ruby)
96
+ end
97
+
98
+ specify "defaults to text" do
99
+ syntaxer = Albino.new(__FILE__)
100
+ syntaxer.expects(:execute).with('pygmentize -f html -l text').returns(true)
101
+ syntaxer.colorize
102
+ end
103
+
104
+ specify "accepts options" do
105
+ @syntaxer.expects(:execute).with('pygmentize -f html -l ruby').returns(true)
106
+ @syntaxer.colorize
107
+ end
108
+
109
+ specify "works with strings" do
110
+ syntaxer = Albino.new('class New; end', :ruby)
111
+ assert_match %r(highlight), syntaxer.colorize
112
+ end
113
+
114
+ specify "aliases to_s" do
115
+ assert_equal @syntaxer.colorize, @syntaxer.to_s
116
+ end
117
+
118
+ specify "class method colorize" do
119
+ assert_equal @syntaxer.colorize, Albino.colorize(__FILE__, :ruby)
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,26 @@
1
+ module Jekyll
2
+ module CSV
3
+ #Reads a csv with title, permalink, body, published_at, and filter.
4
+ #It creates a post file for each row in the csv
5
+ def self.process(file = "posts.csv")
6
+ FileUtils.mkdir_p "_posts"
7
+ posts = 0
8
+ FasterCSV.foreach(file) do |row|
9
+ next if row[0] == "title"
10
+ posts += 1
11
+ name = row[3].split(" ")[0]+"-"+row[1]+(row[4] =~ /markdown/ ? ".markdown" : ".textile")
12
+ File.open("_posts/#{name}", "w") do |f|
13
+ f.puts <<-HEADER
14
+ ---
15
+ layout: post
16
+ title: #{row[0]}
17
+ ---
18
+
19
+ HEADER
20
+ f.puts row[2]
21
+ end
22
+ end
23
+ "Created #{posts} posts!"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,79 @@
1
+ # Quickly hacked together my Michael Ivey
2
+ # Based on mt.rb by Nick Gerakines, open source and publically
3
+ # available under the MIT license. Use this module at your own risk.
4
+
5
+ require 'rubygems'
6
+ require 'sequel'
7
+ require 'fastercsv'
8
+ require 'fileutils'
9
+ require File.join(File.dirname(__FILE__),"csv.rb")
10
+
11
+ # NOTE: This converter requires Sequel and the MySQL gems.
12
+ # The MySQL gem can be difficult to install on OS X. Once you have MySQL
13
+ # installed, running the following commands should work:
14
+ # $ sudo gem install sequel
15
+ # $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
16
+
17
+ module Jekyll
18
+ module Mephisto
19
+ #Accepts a hash with database config variables, exports mephisto posts into a csv
20
+ #export PGPASSWORD if you must
21
+ def self.postgres(c)
22
+ sql = <<-SQL
23
+ BEGIN;
24
+ CREATE TEMP TABLE jekyll AS
25
+ SELECT title, permalink, body, published_at, filter FROM contents
26
+ WHERE user_id = 1 AND type = 'Article' ORDER BY published_at;
27
+ COPY jekyll TO STDOUT WITH CSV HEADER;
28
+ ROLLBACK;
29
+ SQL
30
+ command = %Q(psql -h #{c[:host] || "localhost"} -c "#{sql.strip}" #{c[:database]} #{c[:username]} -o #{c[:filename] || "posts.csv"})
31
+ puts command
32
+ `#{command}`
33
+ CSV.process
34
+ end
35
+
36
+ # This query will pull blog posts from all entries across all blogs. If
37
+ # you've got unpublished, deleted or otherwise hidden posts please sift
38
+ # through the created posts to make sure nothing is accidently published.
39
+
40
+ QUERY = "SELECT id, permalink, body, published_at, title FROM contents WHERE user_id = 1 AND type = 'Article' AND published_at IS NOT NULL ORDER BY published_at"
41
+
42
+ def self.process(dbname, user, pass, host = 'localhost')
43
+ db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host)
44
+
45
+ FileUtils.mkdir_p "_posts"
46
+
47
+ db[QUERY].each do |post|
48
+ title = post[:title]
49
+ slug = post[:permalink]
50
+ date = post[:published_at]
51
+ content = post[:body]
52
+ # more_content = ''
53
+
54
+ # Be sure to include the body and extended body.
55
+ # if more_content != nil
56
+ # content = content + " \n" + more_content
57
+ # end
58
+
59
+ # Ideally, this script would determine the post format (markdown, html
60
+ # , etc) and create files with proper extensions. At this point it
61
+ # just assumes that markdown will be acceptable.
62
+ name = [date.year, date.month, date.day, slug].join('-') + ".markdown"
63
+
64
+ data = {
65
+ 'layout' => 'post',
66
+ 'title' => title.to_s,
67
+ 'mt_id' => post[:entry_id],
68
+ }.delete_if { |k,v| v.nil? || v == ''}.to_yaml
69
+
70
+ File.open("_posts/#{name}", "w") do |f|
71
+ f.puts data
72
+ f.puts "---"
73
+ f.puts content
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,59 @@
1
+ # Created by Nick Gerakines, open source and publically available under the
2
+ # MIT license. Use this module at your own risk.
3
+ # I'm an Erlang/Perl/C++ guy so please forgive my dirty ruby.
4
+
5
+ require 'rubygems'
6
+ require 'sequel'
7
+ require 'fileutils'
8
+
9
+ # NOTE: This converter requires Sequel and the MySQL gems.
10
+ # The MySQL gem can be difficult to install on OS X. Once you have MySQL
11
+ # installed, running the following commands should work:
12
+ # $ sudo gem install sequel
13
+ # $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
14
+
15
+ module Jekyll
16
+ module MT
17
+ # This query will pull blog posts from all entries across all blogs. If
18
+ # you've got unpublished, deleted or otherwise hidden posts please sift
19
+ # through the created posts to make sure nothing is accidently published.
20
+ QUERY = "SELECT entry_id, entry_basename, entry_text, entry_text_more, entry_created_on, entry_title FROM mt_entry"
21
+
22
+ def self.process(dbname, user, pass, host = 'localhost')
23
+ db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host)
24
+
25
+ FileUtils.mkdir_p "_posts"
26
+
27
+ db[QUERY].each do |post|
28
+ title = post[:entry_title]
29
+ slug = post[:entry_basename]
30
+ date = post[:entry_created_on]
31
+ content = post[:entry_text]
32
+ more_content = post[:entry_text_more]
33
+
34
+ # Be sure to include the body and extended body.
35
+ if more_content != nil
36
+ content = content + " \n" + more_content
37
+ end
38
+
39
+ # Ideally, this script would determine the post format (markdown, html
40
+ # , etc) and create files with proper extensions. At this point it
41
+ # just assumes that markdown will be acceptable.
42
+ name = [date.year, date.month, date.day, slug].join('-') + ".markdown"
43
+
44
+ data = {
45
+ 'layout' => 'post',
46
+ 'title' => title.to_s,
47
+ 'mt_id' => post[:entry_id],
48
+ }.delete_if { |k,v| v.nil? || v == ''}.to_yaml
49
+
50
+ File.open("_posts/#{name}", "w") do |f|
51
+ f.puts data
52
+ f.puts "---"
53
+ f.puts content
54
+ end
55
+ end
56
+
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,50 @@
1
+ require 'rubygems'
2
+ require 'sequel'
3
+ require 'fileutils'
4
+
5
+ # NOTE: This converter requires Sequel and the MySQL gems.
6
+ # The MySQL gem can be difficult to install on OS X. Once you have MySQL
7
+ # installed, running the following commands should work:
8
+ # $ sudo gem install sequel
9
+ # $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
10
+
11
+ module Jekyll
12
+ module TextPattern
13
+ # Reads a MySQL database via Sequel and creates a post file for each post.
14
+ # The only posts selected are those with a status of 4 or 5, which means "live"
15
+ # and "sticky" respectively.
16
+ # Other statuses is 1 => draft, 2 => hidden and 3 => pending
17
+ QUERY = "select Title, url_title, Posted, Body, Keywords from textpattern where Status = '4' or Status = '5'"
18
+
19
+ def self.process(dbname, user, pass, host = 'localhost')
20
+ db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host)
21
+
22
+ FileUtils.mkdir_p "_posts"
23
+
24
+ db[QUERY].each do |post|
25
+ # Get required fields and construct Jekyll compatible name
26
+ title = post[:Title]
27
+ slug = post[:url_title]
28
+ date = post[:Posted]
29
+ content = post[:Body]
30
+
31
+ name = [date.strftime("%Y-%m-%d"), slug].join('-') + ".textile"
32
+
33
+ # Get the relevant fields as a hash, delete empty fields and convert
34
+ # to YAML for the header
35
+ data = {
36
+ 'layout' => 'post',
37
+ 'title' => title.to_s,
38
+ 'tags' => post[:Keywords].split(',')
39
+ }.delete_if { |k,v| v.nil? || v == ''}.to_yaml
40
+
41
+ # Write out the data and content to file
42
+ File.open("_posts/#{name}", "w") do |f|
43
+ f.puts data
44
+ f.puts "---"
45
+ f.puts content
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,49 @@
1
+ # Author: Toby DiPasquale <toby@cbcg.net>
2
+ require 'fileutils'
3
+ require 'rubygems'
4
+ require 'sequel'
5
+
6
+ module Jekyll
7
+ module Typo
8
+ # this SQL *should* work for both MySQL and PostgreSQL, but I haven't
9
+ # tested PostgreSQL yet (as of 2008-12-16)
10
+ SQL = <<-EOS
11
+ SELECT c.id id,
12
+ c.title title,
13
+ c.permalink slug,
14
+ c.body body,
15
+ c.published_at date,
16
+ c.state state,
17
+ COALESCE(tf.name, 'html') filter
18
+ FROM contents c
19
+ LEFT OUTER JOIN text_filters tf
20
+ ON c.text_filter_id = tf.id
21
+ EOS
22
+
23
+ def self.process dbname, user, pass, host='localhost'
24
+ FileUtils.mkdir_p '_posts'
25
+ db = Sequel.mysql dbname, :user => user, :password => pass, :host => host
26
+ db[SQL].each do |post|
27
+ next unless post[:state] =~ /Published/
28
+
29
+ name = [ sprintf("%.04d", post[:date].year),
30
+ sprintf("%.02d", post[:date].month),
31
+ sprintf("%.02d", post[:date].day),
32
+ post[:slug].strip ].join('-')
33
+ # Can have more than one text filter in this field, but we just want
34
+ # the first one for this
35
+ name += '.' + post[:filter].split(' ')[0]
36
+
37
+ File.open("_posts/#{name}", 'w') do |f|
38
+ f.puts({ 'layout' => 'post',
39
+ 'title' => post[:title].to_s,
40
+ 'typo_id' => post[:id]
41
+ }.delete_if { |k, v| v.nil? || v == '' }.to_yaml)
42
+ f.puts '---'
43
+ f.puts post[:body].delete("\r")
44
+ end
45
+ end
46
+ end
47
+
48
+ end # module Typo
49
+ end # module Jekyll
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ require 'sequel'
3
+ require 'fileutils'
4
+
5
+ # NOTE: This converter requires Sequel and the MySQL gems.
6
+ # The MySQL gem can be difficult to install on OS X. Once you have MySQL
7
+ # installed, running the following commands should work:
8
+ # $ sudo gem install sequel
9
+ # $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
10
+
11
+ module Jekyll
12
+ module WordPress
13
+
14
+ # Reads a MySQL database via Sequel and creates a post file for each
15
+ # post in wp_posts that has post_status = 'publish'.
16
+ # This restriction is made because 'draft' posts are not guaranteed to
17
+ # have valid dates.
18
+ QUERY = "select post_title, post_name, post_date, post_content, post_excerpt, ID, guid from wp_posts where post_status = 'publish' and post_type = 'post'"
19
+
20
+ def self.process(dbname, user, pass, host = 'localhost')
21
+ db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host)
22
+
23
+ FileUtils.mkdir_p "_posts"
24
+
25
+ db[QUERY].each do |post|
26
+ # Get required fields and construct Jekyll compatible name
27
+ title = post[:post_title]
28
+ slug = post[:post_name]
29
+ date = post[:post_date]
30
+ content = post[:post_content]
31
+ name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month, date.day,
32
+ slug]
33
+
34
+ # Get the relevant fields as a hash, delete empty fields and convert
35
+ # to YAML for the header
36
+ data = {
37
+ 'layout' => 'post',
38
+ 'title' => title.to_s,
39
+ 'excerpt' => post[:post_excerpt].to_s,
40
+ 'wordpress_id' => post[:ID],
41
+ 'wordpress_url' => post[:guid]
42
+ }.delete_if { |k,v| v.nil? || v == ''}.to_yaml
43
+
44
+ # Write out the data and content to file
45
+ File.open("_posts/#{name}", "w") do |f|
46
+ f.puts data
47
+ f.puts "---"
48
+ f.puts content
49
+ end
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,82 @@
1
+ # Convertible provides methods for converting a pagelike item
2
+ # from a certain type of markup into actual content
3
+ #
4
+ # Requires
5
+ # self.site -> Jekyll::Site
6
+ module Jekyll
7
+ module Convertible
8
+ # Return the contents as a string
9
+ def to_s
10
+ self.content || ''
11
+ end
12
+
13
+ # Read the YAML frontmatter
14
+ # +base+ is the String path to the dir containing the file
15
+ # +name+ is the String filename of the file
16
+ #
17
+ # Returns nothing
18
+ def read_yaml(base, name)
19
+ self.content = File.read(File.join(base, name))
20
+
21
+ if self.content =~ /^(---\s*\n.*?)\n---\s*\n/m
22
+ self.content = self.content[($1.size + 5)..-1]
23
+
24
+ self.data = YAML.load($1)
25
+ end
26
+ end
27
+
28
+ # Transform the contents based on the file extension.
29
+ #
30
+ # Returns nothing
31
+ def transform
32
+ case self.content_type
33
+ when 'textile'
34
+ self.ext = ".html"
35
+ self.content = self.site.textile(self.content)
36
+ when 'markdown'
37
+ self.ext = ".html"
38
+ self.content = self.site.markdown(self.content)
39
+ end
40
+ end
41
+
42
+ # Determine which formatting engine to use based on this convertible's
43
+ # extension
44
+ #
45
+ # Returns one of :textile, :markdown or :unknown
46
+ def content_type
47
+ case self.ext[1..-1]
48
+ when /textile/i
49
+ return 'textile'
50
+ when /markdown/i, /mkdn/i, /md/i
51
+ return 'markdown'
52
+ end
53
+ return 'unknown'
54
+ end
55
+
56
+ # Add any necessary layouts to this convertible document
57
+ # +layouts+ is a Hash of {"name" => "layout"}
58
+ # +site_payload+ is the site payload hash
59
+ #
60
+ # Returns nothing
61
+ def do_layout(payload, layouts)
62
+ info = { :filters => [Jekyll::Filters], :registers => { :site => self.site } }
63
+
64
+ # render and transform content (this becomes the final content of the object)
65
+ payload["content_type"] = self.content_type
66
+ self.content = Liquid::Template.parse(self.content).render(payload, info)
67
+ self.transform
68
+
69
+ # output keeps track of what will finally be written
70
+ self.output = self.content
71
+
72
+ # recursively render layouts
73
+ layout = layouts[self.data["layout"]]
74
+ while layout
75
+ payload = payload.deep_merge({"content" => self.output, "page" => layout.data})
76
+ self.output = Liquid::Template.parse(layout.content).render(payload, info)
77
+
78
+ layout = layouts[layout.data["layout"]]
79
+ end
80
+ end
81
+ end
82
+ end