tsion-jekyll 0.5.5

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 (71) hide show
  1. data/.gitignore +6 -0
  2. data/History.txt +151 -0
  3. data/README.textile +42 -0
  4. data/Rakefile +92 -0
  5. data/VERSION.yml +5 -0
  6. data/bin/jekyll +150 -0
  7. data/features/create_sites.feature +46 -0
  8. data/features/embed_filters.feature +60 -0
  9. data/features/pagination.feature +40 -0
  10. data/features/permalinks.feature +65 -0
  11. data/features/post_data.feature +153 -0
  12. data/features/site_configuration.feature +63 -0
  13. data/features/site_data.feature +82 -0
  14. data/features/step_definitions/jekyll_steps.rb +136 -0
  15. data/features/support/env.rb +16 -0
  16. data/lib/jekyll.rb +85 -0
  17. data/lib/jekyll/albino.rb +122 -0
  18. data/lib/jekyll/converters/csv.rb +26 -0
  19. data/lib/jekyll/converters/mephisto.rb +79 -0
  20. data/lib/jekyll/converters/mt.rb +59 -0
  21. data/lib/jekyll/converters/textpattern.rb +50 -0
  22. data/lib/jekyll/converters/typo.rb +49 -0
  23. data/lib/jekyll/converters/wordpress.rb +54 -0
  24. data/lib/jekyll/convertible.rb +84 -0
  25. data/lib/jekyll/core_ext.rb +30 -0
  26. data/lib/jekyll/filters.rb +47 -0
  27. data/lib/jekyll/layout.rb +36 -0
  28. data/lib/jekyll/page.rb +112 -0
  29. data/lib/jekyll/pager.rb +45 -0
  30. data/lib/jekyll/post.rb +251 -0
  31. data/lib/jekyll/site.rb +265 -0
  32. data/lib/jekyll/tags/highlight.rb +56 -0
  33. data/lib/jekyll/tags/include.rb +31 -0
  34. data/test/helper.rb +27 -0
  35. data/test/source/_includes/sig.markdown +3 -0
  36. data/test/source/_layouts/default.html +27 -0
  37. data/test/source/_layouts/simple.html +1 -0
  38. data/test/source/_posts/2008-02-02-not-published.textile +8 -0
  39. data/test/source/_posts/2008-02-02-published.textile +8 -0
  40. data/test/source/_posts/2008-10-18-foo-bar.textile +8 -0
  41. data/test/source/_posts/2008-11-21-complex.textile +8 -0
  42. data/test/source/_posts/2008-12-03-permalinked-post.textile +9 -0
  43. data/test/source/_posts/2008-12-13-include.markdown +8 -0
  44. data/test/source/_posts/2009-01-27-array-categories.textile +10 -0
  45. data/test/source/_posts/2009-01-27-categories.textile +7 -0
  46. data/test/source/_posts/2009-01-27-category.textile +7 -0
  47. data/test/source/_posts/2009-03-12-hash-#1.markdown +6 -0
  48. data/test/source/_posts/2009-05-18-tag.textile +6 -0
  49. data/test/source/_posts/2009-05-18-tags.textile +9 -0
  50. data/test/source/_posts/2009-06-22-empty-yaml.textile +3 -0
  51. data/test/source/_posts/2009-06-22-no-yaml.textile +1 -0
  52. data/test/source/about.html +6 -0
  53. data/test/source/category/_posts/2008-9-23-categories.textile +6 -0
  54. data/test/source/contacts.html +5 -0
  55. data/test/source/css/screen.css +76 -0
  56. data/test/source/foo/_posts/bar/2008-12-12-topical-post.textile +8 -0
  57. data/test/source/index.html +22 -0
  58. data/test/source/sitemap.xml +23 -0
  59. data/test/source/win/_posts/2009-05-24-yaml-linebreak.markdown +7 -0
  60. data/test/source/z_category/_posts/2008-9-23-categories.textile +6 -0
  61. data/test/suite.rb +9 -0
  62. data/test/test_configuration.rb +29 -0
  63. data/test/test_filters.rb +49 -0
  64. data/test/test_generated_site.rb +40 -0
  65. data/test/test_page.rb +98 -0
  66. data/test/test_pager.rb +47 -0
  67. data/test/test_post.rb +302 -0
  68. data/test/test_site.rb +85 -0
  69. data/test/test_tags.rb +116 -0
  70. data/tsion-jekyll.gemspec +138 -0
  71. metadata +193 -0
@@ -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,84 @@
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?)(---.*?\n)/m
22
+ self.content = self.content[($1.size + $2.size)..-1]
23
+
24
+ self.data = YAML.load($1)
25
+ end
26
+
27
+ self.data ||= {}
28
+ end
29
+
30
+ # Transform the contents based on the file extension.
31
+ #
32
+ # Returns nothing
33
+ def transform
34
+ case self.content_type
35
+ when 'textile'
36
+ self.ext = ".html"
37
+ self.content = self.site.textile(self.content)
38
+ when 'markdown'
39
+ self.ext = ".html"
40
+ self.content = self.site.markdown(self.content)
41
+ end
42
+ end
43
+
44
+ # Determine which formatting engine to use based on this convertible's
45
+ # extension
46
+ #
47
+ # Returns one of :textile, :markdown or :unknown
48
+ def content_type
49
+ case self.ext[1..-1]
50
+ when /textile/i
51
+ return 'textile'
52
+ when /markdown/i, /mkdn/i, /md/i
53
+ return 'markdown'
54
+ end
55
+ return 'unknown'
56
+ end
57
+
58
+ # Add any necessary layouts to this convertible document
59
+ # +layouts+ is a Hash of {"name" => "layout"}
60
+ # +site_payload+ is the site payload hash
61
+ #
62
+ # Returns nothing
63
+ def do_layout(payload, layouts)
64
+ info = { :filters => [Jekyll::Filters], :registers => { :site => self.site } }
65
+
66
+ # render and transform content (this becomes the final content of the object)
67
+ payload["content_type"] = self.content_type
68
+ self.content = Liquid::Template.parse(self.content).render(payload, info)
69
+ self.transform
70
+
71
+ # output keeps track of what will finally be written
72
+ self.output = self.content
73
+
74
+ # recursively render layouts
75
+ layout = layouts[self.data["layout"]]
76
+ while layout
77
+ payload = payload.deep_merge({"content" => self.output, "page" => layout.data})
78
+ self.output = Liquid::Template.parse(layout.content).render(payload, info)
79
+
80
+ layout = layouts[layout.data["layout"]]
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,30 @@
1
+ class Hash
2
+ # Merges self with another hash, recursively.
3
+ #
4
+ # This code was lovingly stolen from some random gem:
5
+ # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
6
+ #
7
+ # Thanks to whoever made it.
8
+ def deep_merge(hash)
9
+ target = dup
10
+
11
+ hash.keys.each do |key|
12
+ if hash[key].is_a? Hash and self[key].is_a? Hash
13
+ target[key] = target[key].deep_merge(hash[key])
14
+ next
15
+ end
16
+
17
+ target[key] = hash[key]
18
+ end
19
+
20
+ target
21
+ end
22
+ end
23
+
24
+ # Thanks, ActiveSupport!
25
+ class Date
26
+ # Converts datetime to an appropriate format for use in XML
27
+ def xmlschema
28
+ strftime("%Y-%m-%dT%H:%M:%S%Z")
29
+ end if RUBY_VERSION < '1.9'
30
+ end
@@ -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 xml_escape(input)
21
+ CGI.escapeHTML(input)
22
+ end
23
+
24
+ def cgi_escape(input)
25
+ CGI::escape(input)
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,36 @@
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
+ end
26
+
27
+ # Extract information from the layout filename
28
+ # +name+ is the String filename of the layout file
29
+ #
30
+ # Returns nothing
31
+ def process(name)
32
+ self.ext = File.extname(name)
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,112 @@
1
+ module Jekyll
2
+
3
+ class Page
4
+ include Convertible
5
+
6
+ attr_accessor :site
7
+ attr_accessor :name, :ext, :basename
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.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, set to '/'
30
+ #
31
+ # Returns <String>
32
+ def dir
33
+ url[-1, 1] == '/' ? url : File.dirname(url)
34
+ end
35
+
36
+ # The full path and filename of the post.
37
+ # Defined in the YAML of the post body
38
+ # (Optional)
39
+ #
40
+ # Returns <String>
41
+ def permalink
42
+ self.data && self.data['permalink']
43
+ end
44
+
45
+ def template
46
+ if self.site.permalink_style == :pretty && !index?
47
+ "/:name/"
48
+ else
49
+ "/:name.html"
50
+ end
51
+ end
52
+
53
+ # The generated relative url of this page
54
+ # e.g. /about.html
55
+ #
56
+ # Returns <String>
57
+ def url
58
+ return permalink if permalink
59
+
60
+ @url ||= (ext == '.html') ? template.gsub(':name', basename) : "/#{name}"
61
+ end
62
+
63
+ # Extract information from the page filename
64
+ # +name+ is the String filename of the page file
65
+ #
66
+ # Returns nothing
67
+ def process(name)
68
+ self.ext = File.extname(name)
69
+ self.basename = name.split('.')[0..-2].first
70
+ end
71
+
72
+ # Add any necessary layouts to this post
73
+ # +layouts+ is a Hash of {"name" => "layout"}
74
+ # +site_payload+ is the site payload hash
75
+ #
76
+ # Returns nothing
77
+ def render(layouts, site_payload)
78
+ payload = {"page" => self.data}.deep_merge(site_payload)
79
+ do_layout(payload, layouts)
80
+ end
81
+
82
+ # Write the generated page file to the destination directory.
83
+ # +dest_prefix+ is the String path to the destination dir
84
+ # +dest_suffix+ is a suffix path to the destination dir
85
+ #
86
+ # Returns nothing
87
+ def write(dest_prefix, dest_suffix = nil)
88
+ dest = File.join(dest_prefix, @dir)
89
+ dest = File.join(dest, dest_suffix) if dest_suffix
90
+ FileUtils.mkdir_p(dest)
91
+
92
+ # The url needs to be unescaped in order to preserve the correct filename
93
+ path = File.join(dest, CGI.unescape(self.url))
94
+ if self.ext == '.html' && self.url[/\.html$/].nil?
95
+ FileUtils.mkdir_p(path)
96
+ path = File.join(path, "index.html")
97
+ end
98
+
99
+ File.open(path, 'w') do |f|
100
+ f.write(self.output)
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def index?
107
+ basename == 'index'
108
+ end
109
+
110
+ end
111
+
112
+ end