jekyll 0.1.2

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.

@@ -0,0 +1,20 @@
1
+ == 0.1.2 / 2008-11-22
2
+ * Major Features
3
+ * Add a real "related posts" implementation using Classifier
4
+ * Command Line Changes
5
+ * Allow cli to be called with 0, 1, or 2 args intuiting dir paths
6
+ if they are omitted
7
+
8
+ == 0.1.1 / 2008-11-22
9
+ * Minor Additions
10
+ * Posts now support introspectional data e.g. {{ page.url }}
11
+
12
+ == 0.1.0 / 2008-11-05
13
+ * First release
14
+ * Converts posts written in Textile
15
+ * Converts regular site pages
16
+ * Simple copy of binary files
17
+
18
+ == 0.0.0 / 2008-10-19
19
+ * Birthday!
20
+
@@ -0,0 +1,28 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.textile
4
+ Rakefile
5
+ bin/jekyll
6
+ jekyll.gemspec
7
+ lib/jekyll.rb
8
+ lib/jekyll/convertible.rb
9
+ lib/jekyll/filters.rb
10
+ lib/jekyll/layout.rb
11
+ lib/jekyll/page.rb
12
+ lib/jekyll/post.rb
13
+ lib/jekyll/site.rb
14
+ test/dest/2008/10/18/foo-bar.html
15
+ test/dest/2008/11/21/complex.html
16
+ test/dest/css/screen.css
17
+ test/dest/index.html
18
+ test/helper.rb
19
+ test/source/_layouts/default.html
20
+ test/source/_layouts/simple.html
21
+ test/source/_posts/2008-10-18-foo-bar.textile
22
+ test/source/_posts/2008-11-21-complex.textile
23
+ test/source/css/screen.css
24
+ test/source/index.html
25
+ test/suite.rb
26
+ test/test_jekyll.rb
27
+ test/test_post.rb
28
+ test/test_site.rb
@@ -0,0 +1,66 @@
1
+ h1. Jekyll
2
+
3
+ Jekyll is a simple, blog aware, static site generator. It takes a template directory (representing the raw form of a website), runs it through Textile and Liquid converters, and spits out a complete, static website suitable for serving with Apache or your favorite web server. Visit "http://tom.preston-werner.com":http://tom.preston-werner.com to see an example of a Jekyll generated blog.
4
+
5
+ To understand how this all works, open up my "TPW":http://github.com/mojombo/tpw repo in a new browser window. I'll be referencing the code there.
6
+
7
+ Take a look at "index.html":http://github.com/mojombo/tpw/tree/master/index.html. This file represents the homepage of the site. At the top of the file is a chunk of YAML that contains metadata about the file. This data tells Jekyll what layout to give the file, what the page's title should be, etc. In this case, I specify that the "default" template should be used. You can find the layout files in the "_layouts":http://github.com/mojombo/tpw/tree/master/_layouts directory. If you open "default.html":http://github.com/mojombo/tpw/tree/master/_layouts/default.html you can see that the homepage is constructed by wrapping index.html with this layout.
8
+
9
+ You'll also notice Liquid templating code in these files. "Liquid":http://www.liquidmarkup.org/ is a simple, extensible templating language that makes it easy to embed data in your templates. For my homepage I wanted to have a list of all my blog posts. Jekyll hands me a Hash containing various data about my site. A reverse chronological list of all my blog posts can be found in <code>site.posts</code>. Each post, in turn, contains various fields such as <code>title</code> and <code>date</code>.
10
+
11
+ Jekyll gets the list of blog posts by parsing the files in the "_posts":http://github.com/mojombo/tpw/tree/master/_posts directory. Each post's filename contains the publishing date and slug (what shows up in the URL) that the final HTML file should have. Open up the file corresponding to a blog post: "2008-11-17-blogging-like-a-hacker.textile":http://github.com/mojombo/tpw/tree/master/_posts/2008-11-17-blogging-like-a-hacker.textile. GitHub renders textile files by default, so to better understand the file, click on the "raw":http://github.com/mojombo/tpw/tree/master/_posts/2008-11-17-blogging-like-a-hacker.textile?raw=true view to see the original file. Here I've specified the <code>post</code> layout. If you look at that file you'll see an example of a nested layout. Layouts can contain other layouts allowing you a great deal of flexibility in how pages are assembled. In my case I use a nested layout in order to show related posts for each blog entry. The YAML also specifies the post's title which is then embedded in the post's body via Liquid.
12
+
13
+ Posts are handled in a special way by Jekyll. The date you specify in the filename is used to construct the URL in the generated site. The example post, for instance, ends up at <code>http://tom.preston-werner.com/2008/11/17/blogging-like-a-hacker.html</code>.
14
+
15
+ Files that do not reside in directories prefixed with an underscore are mirrored into a corresponding directory structure in the generated site. If a file does not have a YAML preface, it is not run through the Liquid interpreter. Binary files are copied over unmodified.
16
+
17
+ In order to convert your raw site into the finished version, you simply run:
18
+
19
+ <pre class="terminal"><code>$ jekyll /path/to/raw/site /path/to/place/generated/site</code></pre>
20
+
21
+ Jekyll is still a very young project. I've only developed the exact functionality that I've needed. As time goes on I'd like to see the project mature and support additional features. If you end up using Jekyll for your own blog, drop me a line and let me know what you'd like to see in future versions. Better yet, fork the project over at GitHub and hack in the features yourself!
22
+
23
+ h2. Example Proto-Site
24
+
25
+ My own personal site/blog is generated with Jekyll.
26
+
27
+ The proto-site repo ("http://github.com/mojombo/tpw":http://github.com/mojombo/tpw)
28
+ is converted into the actual site ("http://tom.preston-werner.com/":http://tom.preston-werner.com)
29
+
30
+ h2. Install and Run
31
+
32
+ This is beta software. You will need to download the source
33
+ and run the software from there.
34
+
35
+ $ sudo gem install RedCloth
36
+ $ sudo gem install liquid
37
+ $ sudo gem install classifier
38
+
39
+ $ git clone git://github.com/mojombo/jekyll
40
+ $ cd jekyll
41
+ $ bin/jekyll /path/to/proto/site /path/to/place/generated/site
42
+
43
+ h2. License
44
+
45
+ (The MIT License)
46
+
47
+ Copyright (c) 2008 Tom Preston-Werner
48
+
49
+ Permission is hereby granted, free of charge, to any person obtaining
50
+ a copy of this software and associated documentation files (the
51
+ 'Software'), to deal in the Software without restriction, including
52
+ without limitation the rights to use, copy, modify, merge, publish,
53
+ distribute, sublicense, and/or sell copies of the Software, and to
54
+ permit persons to whom the Software is furnished to do so, subject to
55
+ the following conditions:
56
+
57
+ The above copyright notice and this permission notice shall be
58
+ included in all copies or substantial portions of the Software.
59
+
60
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
61
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
62
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
63
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
64
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
65
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
66
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,15 @@
1
+ require 'rubygems'
2
+ require 'hoe'
3
+ require 'lib/jekyll'
4
+
5
+ Hoe.new('jekyll', Jekyll::VERSION) do |p|
6
+ # p.rubyforge_name = 'jekyllx' # if different than lowercase project name
7
+ p.developer('Tom Preston-Werner', 'tom@mojombo.com')
8
+ p.summary = "Jekyll is a simple, blog aware, static site generator."
9
+ p.extra_deps = ['RedCloth', 'liquid', 'classifier']
10
+ end
11
+
12
+ desc "Open an irb session preloaded with this library"
13
+ task :console do
14
+ sh "irb -rubygems -r ./lib/jekyll.rb"
15
+ end
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'jekyll'
6
+
7
+ case ARGV.size
8
+ when 0
9
+ dest = File.join('.', '_site')
10
+ FileUtils.rm_rf(dest)
11
+ FileUtils.mkdir_p(dest)
12
+ Jekyll.process('.', dest)
13
+ when 1
14
+ Jekyll.process('.', ARGV[0])
15
+ when 2
16
+ Jekyll.process(ARGV[0], ARGV[1])
17
+ else
18
+ puts DATA.read
19
+ end
20
+
21
+ __END__
22
+ Jekyll is a blog-aware, static site generator.
23
+
24
+ Basic Command Line Usage:
25
+ jekyll # . -> ./_site
26
+ jekyll <path to write generated site> # . -> <path>
27
+ jekyll <path to source> <path to write generated site> # <path> -> <path>
@@ -0,0 +1,42 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{jekyll}
3
+ s.version = "0.1.2"
4
+
5
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
6
+ s.authors = ["Tom Preston-Werner"]
7
+ s.date = %q{2008-11-22}
8
+ s.default_executable = %q{jekyll}
9
+ s.email = ["tom@mojombo.com"]
10
+ s.executables = ["jekyll"]
11
+ s.extra_rdoc_files = ["History.txt", "Manifest.txt"]
12
+ s.files = ["History.txt", "Manifest.txt", "README.textile", "Rakefile", "bin/jekyll", "jekyll.gemspec", "lib/jekyll.rb", "lib/jekyll/convertible.rb", "lib/jekyll/filters.rb", "lib/jekyll/layout.rb", "lib/jekyll/page.rb", "lib/jekyll/post.rb", "lib/jekyll/site.rb", "test/dest/2008/10/18/foo-bar.html", "test/dest/2008/11/21/complex.html", "test/dest/css/screen.css", "test/dest/index.html", "test/helper.rb", "test/source/_layouts/default.html", "test/source/_layouts/simple.html", "test/source/_posts/2008-10-18-foo-bar.textile", "test/source/_posts/2008-11-21-complex.textile", "test/source/css/screen.css", "test/source/index.html", "test/suite.rb", "test/test_jekyll.rb", "test/test_post.rb", "test/test_site.rb"]
13
+ s.has_rdoc = true
14
+ s.rdoc_options = ["--main", "README.txt"]
15
+ s.require_paths = ["lib"]
16
+ s.rubyforge_project = %q{jekyll}
17
+ s.rubygems_version = %q{1.3.0}
18
+ s.summary = %q{Jekyll is a simple, blog aware, static site generator.}
19
+ s.test_files = ["test/test_jekyll.rb", "test/test_post.rb", "test/test_site.rb"]
20
+
21
+ if s.respond_to? :specification_version then
22
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
23
+ s.specification_version = 2
24
+
25
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
26
+ s.add_runtime_dependency(%q<RedCloth>, [">= 0"])
27
+ s.add_runtime_dependency(%q<liquid>, [">= 0"])
28
+ s.add_runtime_dependency(%q<classifier>, [">= 0"])
29
+ s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
30
+ else
31
+ s.add_dependency(%q<RedCloth>, [">= 0"])
32
+ s.add_dependency(%q<liquid>, [">= 0"])
33
+ s.add_dependency(%q<classifier>, [">= 0"])
34
+ s.add_dependency(%q<hoe>, [">= 1.8.0"])
35
+ end
36
+ else
37
+ s.add_dependency(%q<RedCloth>, [">= 0"])
38
+ s.add_dependency(%q<liquid>, [">= 0"])
39
+ s.add_dependency(%q<classifier>, [">= 0"])
40
+ s.add_dependency(%q<hoe>, [">= 1.8.0"])
41
+ end
42
+ end
@@ -0,0 +1,31 @@
1
+ $:.unshift File.dirname(__FILE__) # For use/testing when no gem is installed
2
+
3
+ # rubygems
4
+ require 'rubygems'
5
+
6
+ # core
7
+ require 'fileutils'
8
+ require 'time'
9
+
10
+ # stdlib
11
+
12
+ # 3rd party
13
+ require 'liquid'
14
+ require 'redcloth'
15
+ require 'classifier'
16
+
17
+ # internal requires
18
+ require 'jekyll/site'
19
+ require 'jekyll/convertible'
20
+ require 'jekyll/layout'
21
+ require 'jekyll/page'
22
+ require 'jekyll/post'
23
+ require 'jekyll/filters'
24
+
25
+ module Jekyll
26
+ VERSION = '0.1.2'
27
+
28
+ def self.process(source, dest)
29
+ Jekyll::Site.new(source, dest).process
30
+ end
31
+ end
@@ -0,0 +1,58 @@
1
+ module Jekyll
2
+ module Convertible
3
+ # Return the contents as a string
4
+ def to_s
5
+ self.content || ''
6
+ end
7
+
8
+ # Read the YAML frontmatter
9
+ # +base+ is the String path to the dir containing the file
10
+ # +name+ is the String filename of the file
11
+ #
12
+ # Returns nothing
13
+ def read_yaml(base, name)
14
+ self.content = File.read(File.join(base, name))
15
+
16
+ if self.content =~ /^(---\n.*?)\n---\n/m
17
+ self.content = self.content[($1.size + 5)..-1]
18
+
19
+ self.data = YAML.load($1)
20
+ end
21
+ end
22
+
23
+ # Transform the contents based on the file extension.
24
+ #
25
+ # Returns nothing
26
+ def transform
27
+ if self.ext == ".textile"
28
+ self.ext = ".html"
29
+ self.content = RedCloth.new(self.content).to_html
30
+ end
31
+ end
32
+
33
+ # Add any necessary layouts to this post
34
+ # +layouts+ is a Hash of {"name" => "layout"}
35
+ # +site_payload+ is the site payload hash
36
+ #
37
+ # Returns nothing
38
+ def do_layout(payload, layouts, site_payload)
39
+ # construct payload
40
+ payload = payload.merge(site_payload)
41
+
42
+ # render content
43
+ self.content = Liquid::Template.parse(self.content).render(payload, [Jekyll::Filters])
44
+
45
+ # output keeps track of what will finally be written
46
+ self.output = self.content
47
+
48
+ # recursively render layouts
49
+ layout = layouts[self.data["layout"]]
50
+ while layout
51
+ payload = payload.merge({"content" => self.output, "page" => self.data})
52
+ self.output = Liquid::Template.parse(layout.content).render(payload, [Jekyll::Filters])
53
+
54
+ layout = layouts[layout.data["layout"]]
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ module Jekyll
2
+
3
+ module Filters
4
+ def date_to_string(date)
5
+ date.strftime("%d %b %Y")
6
+ end
7
+
8
+ def date_to_xmlschema(date)
9
+ date.xmlschema
10
+ end
11
+
12
+ def xml_escape(input)
13
+ input.gsub("<", "&lt;").gsub(">", "&gt;")
14
+ end
15
+ end
16
+
17
+ end
@@ -0,0 +1,48 @@
1
+ module Jekyll
2
+
3
+ class Layout
4
+ include Convertible
5
+
6
+ attr_accessor :ext
7
+ attr_accessor :data, :content
8
+
9
+ # Initialize a new Layout.
10
+ # +base+ is the String path to the <source>
11
+ # +name+ is the String filename of the post file
12
+ #
13
+ # Returns <Page>
14
+ def initialize(base, name)
15
+ @base = base
16
+ @name = name
17
+
18
+ self.data = {}
19
+
20
+ self.process(name)
21
+ self.read_yaml(base, name)
22
+ end
23
+
24
+ # Extract information from the layout filename
25
+ # +name+ is the String filename of the layout file
26
+ #
27
+ # Returns nothing
28
+ def process(name)
29
+ self.ext = File.extname(name)
30
+ end
31
+
32
+ # Add any necessary layouts to this post
33
+ # +layouts+ is a Hash of {"name" => "layout"}
34
+ # +site_payload+ is the site payload hash
35
+ #
36
+ # Returns nothing
37
+ def add_layout(layouts, site_payload)
38
+ payload = {"page" => self.data}.merge(site_payload)
39
+ self.content = Liquid::Template.parse(self.content).render(payload, [Jekyll::Filters])
40
+
41
+ layout = layouts[self.data["layout"]] || self.content
42
+ payload = {"content" => self.content, "page" => self.data}
43
+
44
+ self.content = Liquid::Template.parse(layout).render(payload, [Jekyll::Filters])
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,64 @@
1
+ module Jekyll
2
+
3
+ class Page
4
+ include Convertible
5
+
6
+ attr_accessor :ext
7
+ attr_accessor :data, :content, :output
8
+
9
+ # Initialize a new Page.
10
+ # +base+ is the String path to the <source>
11
+ # +dir+ is the String path between <source> and the file
12
+ # +name+ is the String filename of the post file
13
+ #
14
+ # Returns <Page>
15
+ def initialize(base, dir, name)
16
+ @base = base
17
+ @dir = dir
18
+ @name = name
19
+
20
+ self.data = {}
21
+
22
+ self.process(name)
23
+ self.read_yaml(File.join(base, dir), name)
24
+ self.transform
25
+ end
26
+
27
+ # Extract information from the post filename
28
+ # +name+ is the String filename of the post file
29
+ #
30
+ # Returns nothing
31
+ def process(name)
32
+ self.ext = File.extname(name)
33
+ end
34
+
35
+ # Add any necessary layouts to this post
36
+ # +layouts+ is a Hash of {"name" => "layout"}
37
+ # +site_payload+ is the site payload hash
38
+ #
39
+ # Returns nothing
40
+ def add_layout(layouts, site_payload)
41
+ payload = {"page" => self.data}
42
+ do_layout(payload, layouts, site_payload)
43
+ end
44
+
45
+ # Write the generated page file to the destination directory.
46
+ # +dest+ is the String path to the destination dir
47
+ #
48
+ # Returns nothing
49
+ def write(dest)
50
+ FileUtils.mkdir_p(File.join(dest, @dir))
51
+
52
+ name = @name
53
+ if self.ext != ""
54
+ name = @name.split(".")[0..-2].join('.') + self.ext
55
+ end
56
+
57
+ path = File.join(dest, @dir, name)
58
+ File.open(path, 'w') do |f|
59
+ f.write(self.output)
60
+ end
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,131 @@
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 :date, :slug, :ext
22
+ attr_accessor :data, :content, :output
23
+
24
+ # Initialize this Post instance.
25
+ # +base+ is the String path to the dir containing the post file
26
+ # +name+ is the String filename of the post file
27
+ #
28
+ # Returns <Post>
29
+ def initialize(base, name)
30
+ @base = base
31
+ @name = name
32
+
33
+ self.process(name)
34
+ self.read_yaml(base, name)
35
+ self.transform
36
+ end
37
+
38
+ # Spaceship is based on Post#date
39
+ #
40
+ # Returns -1, 0, 1
41
+ def <=>(other)
42
+ self.date <=> other.date
43
+ end
44
+
45
+ # Extract information from the post filename
46
+ # +name+ is the String filename of the post file
47
+ #
48
+ # Returns nothing
49
+ def process(name)
50
+ m, date, slug, ext = *name.match(MATCHER)
51
+ self.date = Time.parse(date)
52
+ self.slug = slug
53
+ self.ext = ext
54
+ end
55
+
56
+ # The generated directory into which the post will be placed
57
+ # upon generation. e.g. "/2008/11/05/"
58
+ #
59
+ # Returns <String>
60
+ def dir
61
+ self.date.strftime("/%Y/%m/%d/")
62
+ end
63
+
64
+ # The generated relative url of this post
65
+ # e.g. /2008/11/05/my-awesome-post.html
66
+ #
67
+ # Returns <String>
68
+ def url
69
+ self.dir + self.slug + ".html"
70
+ end
71
+
72
+ # The UID for this post (useful in feeds)
73
+ # e.g. /2008/11/05/my-awesome-post
74
+ #
75
+ # Returns <String>
76
+ def id
77
+ self.dir + self.slug
78
+ end
79
+
80
+ # Calculate related posts.
81
+ #
82
+ # Returns [<Post>]
83
+ def related_posts(posts)
84
+ self.class.lsi ||= begin
85
+ lsi = Classifier::LSI.new
86
+ posts.each { |x| lsi.add_item(x) }
87
+ lsi
88
+ end
89
+
90
+ related = self.class.lsi.find_related(self.content, 11)
91
+ related - [self]
92
+ end
93
+
94
+ # Add any necessary layouts to this post
95
+ # +layouts+ is a Hash of {"name" => "layout"}
96
+ # +site_payload+ is the site payload hash
97
+ #
98
+ # Returns nothing
99
+ def add_layout(layouts, site_payload)
100
+ # construct post payload
101
+ related = related_posts(site_payload["site"]["posts"])
102
+ payload = {"page" => self.to_liquid.merge(self.data), "related_posts" => related}
103
+ do_layout(payload, layouts, site_payload)
104
+ end
105
+
106
+ # Write the generated post file to the destination directory.
107
+ # +dest+ is the String path to the destination dir
108
+ #
109
+ # Returns nothing
110
+ def write(dest)
111
+ FileUtils.mkdir_p(File.join(dest, self.dir))
112
+
113
+ path = File.join(dest, self.url)
114
+ File.open(path, 'w') do |f|
115
+ f.write(self.output)
116
+ end
117
+ end
118
+
119
+ # Convert this post into a Hash for use in Liquid templates.
120
+ #
121
+ # Returns <Hash>
122
+ def to_liquid
123
+ { "title" => self.data["title"] || "",
124
+ "url" => self.url,
125
+ "date" => self.date,
126
+ "id" => self.id,
127
+ "content" => self.content }
128
+ end
129
+ end
130
+
131
+ end
@@ -0,0 +1,113 @@
1
+ module Jekyll
2
+
3
+ class Site
4
+ attr_accessor :source, :dest
5
+ attr_accessor :layouts, :posts
6
+
7
+ # Initialize the site
8
+ # +source+ is String path to the source directory containing
9
+ # the proto-site
10
+ # +dest+ is the String path to the directory where the generated
11
+ # site should be written
12
+ #
13
+ # Returns <Site>
14
+ def initialize(source, dest)
15
+ self.source = source
16
+ self.dest = dest
17
+ self.layouts = {}
18
+ self.posts = []
19
+ end
20
+
21
+ # Do the actual work of processing the site and generating the
22
+ # real deal.
23
+ #
24
+ # Returns nothing
25
+ def process
26
+ self.read_layouts
27
+ self.read_posts
28
+ self.write_posts
29
+ self.transform_pages
30
+ end
31
+
32
+ # Read all the files in <source>/_layouts into memory for
33
+ # later use.
34
+ #
35
+ # Returns nothing
36
+ def read_layouts
37
+ base = File.join(self.source, "_layouts")
38
+ entries = Dir.entries(base)
39
+ entries = entries.reject { |e| File.directory?(e) }
40
+
41
+ entries.each do |f|
42
+ name = f.split(".")[0..-2].join(".")
43
+ self.layouts[name] = Layout.new(base, f)
44
+ end
45
+ rescue Errno::ENOENT => e
46
+ # ignore missing layout dir
47
+ end
48
+
49
+ # Read all the files in <source>/posts and create a new Post
50
+ # object with each one.
51
+ #
52
+ # Returns nothing
53
+ def read_posts
54
+ base = File.join(self.source, "_posts")
55
+ entries = Dir.entries(base)
56
+ entries = entries.reject { |e| File.directory?(e) }
57
+
58
+ entries.each do |f|
59
+ self.posts << Post.new(base, f) if Post.valid?(f)
60
+ end
61
+
62
+ self.posts.sort!
63
+ rescue Errno::ENOENT => e
64
+ # ignore missing layout dir
65
+ end
66
+
67
+ # Write each post to <dest>/<year>/<month>/<day>/<slug>
68
+ #
69
+ # Returns nothing
70
+ def write_posts
71
+ self.posts.each do |post|
72
+ post.add_layout(self.layouts, site_payload)
73
+ post.write(self.dest)
74
+ end
75
+ end
76
+
77
+ # Recursively transform and write all non-post pages to <dest>/
78
+ # +dir+ is the String path part representing the path from
79
+ # <source> to the currently processing dir (default '')
80
+ #
81
+ # Returns nothing
82
+ def transform_pages(dir = '')
83
+ base = File.join(self.source, dir)
84
+ entries = Dir.entries(base)
85
+ entries = entries.reject { |e| ['.', '_'].include?(e[0..0]) }
86
+
87
+ entries.each do |f|
88
+ if File.directory?(File.join(base, f))
89
+ transform_pages(File.join(dir, f))
90
+ else
91
+ first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
92
+
93
+ if first3 == "---"
94
+ page = Page.new(self.source, dir, f)
95
+ page.add_layout(self.layouts, site_payload)
96
+ page.write(self.dest)
97
+ else
98
+ FileUtils.mkdir_p(File.join(self.dest, dir))
99
+ FileUtils.cp(File.join(self.source, dir, f), File.join(self.dest, dir, f))
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ # The Hash payload containing site-wide data
106
+ #
107
+ # Returns {"site" => {"time" => <Time>, "posts" => [<Post>]}}
108
+ def site_payload
109
+ {"site" => {"time" => Time.now, "posts" => self.posts.sort.reverse}}
110
+ end
111
+ end
112
+
113
+ end
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
5
+ <head>
6
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
7
+ <title>Foo Bar</title>
8
+ <meta name="author" content="<%= @page.author %>" />
9
+
10
+ <!-- CodeRay syntax highlighting CSS -->
11
+ <link rel="stylesheet" href="/css/coderay.css" type="text/css" />
12
+
13
+ <!-- Homepage CSS -->
14
+ <link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen, projection" />
15
+ </head>
16
+ <body>
17
+
18
+ <div class="site">
19
+ <div class="title">
20
+ Tom Preston-Werner
21
+ </div>
22
+
23
+ <h1>Foo Bar</h1>
24
+ <p>Best <strong>post</strong> ever</p>
25
+ </div>
26
+
27
+ </body>
28
+ </html>
@@ -0,0 +1,29 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
5
+ <head>
6
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
7
+ <title>Complex</title>
8
+ <meta name="author" content="<%= @page.author %>" />
9
+
10
+ <!-- CodeRay syntax highlighting CSS -->
11
+ <link rel="stylesheet" href="/css/coderay.css" type="text/css" />
12
+
13
+ <!-- Homepage CSS -->
14
+ <link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen, projection" />
15
+ </head>
16
+ <body>
17
+
18
+ <div class="site">
19
+ <div class="title">
20
+ Tom Preston-Werner
21
+ </div>
22
+
23
+ <p>url: /2008/11/21/complex.html<br />
24
+ date: Fri Nov 21 00:00:00 -0800 2008<br />
25
+ id: /2008/11/21/complex</p>
26
+ </div>
27
+
28
+ </body>
29
+ </html>
@@ -0,0 +1,76 @@
1
+ /*****************************************************************************/
2
+ /*
3
+ /* Common
4
+ /*
5
+ /*****************************************************************************/
6
+
7
+ /* Global Reset */
8
+
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ html, body {
15
+ height: 100%;
16
+ }
17
+
18
+ body {
19
+ background-color: white;
20
+ font: 13.34px helvetica, arial, clean, sans-serif;
21
+ *font-size: small;
22
+ text-align: center;
23
+ }
24
+
25
+ h1, h2, h3, h4, h5, h6 {
26
+ font-size: 100%;
27
+ }
28
+
29
+ h1 {
30
+ margin-bottom: 1em;
31
+ }
32
+
33
+ p {
34
+ margin: 1em 0;
35
+ }
36
+
37
+ a {
38
+ color: #00a;
39
+ }
40
+
41
+ a:hover {
42
+ color: black;
43
+ }
44
+
45
+ a:visited {
46
+ color: #a0a;
47
+ }
48
+
49
+ table {
50
+ font-size: inherit;
51
+ font: 100%;
52
+ }
53
+
54
+ /*****************************************************************************/
55
+ /*
56
+ /* Site
57
+ /*
58
+ /*****************************************************************************/
59
+
60
+ .site {
61
+ font-size: 110%;
62
+ text-align: justify;
63
+ width: 40em;
64
+ margin: 3em auto 2em auto;
65
+ line-height: 1.5em;
66
+ }
67
+
68
+ .title {
69
+ color: #a00;
70
+ font-weight: bold;
71
+ margin-bottom: 2em;
72
+ }
73
+
74
+ .site .meta {
75
+ color: #aaa;
76
+ }
@@ -0,0 +1,36 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
5
+ <head>
6
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
7
+ <title>Tom Preston-Werner</title>
8
+ <meta name="author" content="<%= @page.author %>" />
9
+
10
+ <!-- CodeRay syntax highlighting CSS -->
11
+ <link rel="stylesheet" href="/css/coderay.css" type="text/css" />
12
+
13
+ <!-- Homepage CSS -->
14
+ <link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen, projection" />
15
+ </head>
16
+ <body>
17
+
18
+ <div class="site">
19
+ <div class="title">
20
+ Tom Preston-Werner
21
+ </div>
22
+
23
+
24
+ h1. Welcome to my site
25
+
26
+ <ul>
27
+
28
+ <li>Fri Nov 21 00:00:00 -0800 2008 <a href="/2008/11/21/complex.html">Complex</a></li>
29
+
30
+ <li>Sat Oct 18 00:00:00 -0700 2008 <a href="/2008/10/18/foo-bar.html">Foo Bar</a></li>
31
+
32
+ </ul>
33
+ </div>
34
+
35
+ </body>
36
+ </html>
@@ -0,0 +1,13 @@
1
+ require File.join(File.dirname(__FILE__), *%w[.. lib jekyll])
2
+
3
+ require 'test/unit'
4
+
5
+ include Jekyll
6
+
7
+ def dest_dir
8
+ File.join(File.dirname(__FILE__), *%w[dest])
9
+ end
10
+
11
+ def clear_dest
12
+ FileUtils.rm_rf(dest_dir)
13
+ end
@@ -0,0 +1,27 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
5
+ <head>
6
+ <meta http-equiv="content-type" content="text/html; charset=utf-8" />
7
+ <title>{{ page.title }}</title>
8
+ <meta name="author" content="<%= @page.author %>" />
9
+
10
+ <!-- CodeRay syntax highlighting CSS -->
11
+ <link rel="stylesheet" href="/css/coderay.css" type="text/css" />
12
+
13
+ <!-- Homepage CSS -->
14
+ <link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen, projection" />
15
+ </head>
16
+ <body>
17
+
18
+ <div class="site">
19
+ <div class="title">
20
+ Tom Preston-Werner
21
+ </div>
22
+
23
+ {{ content }}
24
+ </div>
25
+
26
+ </body>
27
+ </html>
@@ -0,0 +1 @@
1
+ <<< {{ content }} >>>
@@ -0,0 +1,8 @@
1
+ ---
2
+ layout: default
3
+ title: Foo Bar
4
+ ---
5
+
6
+ h1. {{ page.title }}
7
+
8
+ Best *post* ever
@@ -0,0 +1,8 @@
1
+ ---
2
+ layout: default
3
+ title: Complex
4
+ ---
5
+
6
+ url: {{ page.url }}
7
+ date: {{ page.date }}
8
+ id: {{ page.id }}
@@ -0,0 +1,76 @@
1
+ /*****************************************************************************/
2
+ /*
3
+ /* Common
4
+ /*
5
+ /*****************************************************************************/
6
+
7
+ /* Global Reset */
8
+
9
+ * {
10
+ margin: 0;
11
+ padding: 0;
12
+ }
13
+
14
+ html, body {
15
+ height: 100%;
16
+ }
17
+
18
+ body {
19
+ background-color: white;
20
+ font: 13.34px helvetica, arial, clean, sans-serif;
21
+ *font-size: small;
22
+ text-align: center;
23
+ }
24
+
25
+ h1, h2, h3, h4, h5, h6 {
26
+ font-size: 100%;
27
+ }
28
+
29
+ h1 {
30
+ margin-bottom: 1em;
31
+ }
32
+
33
+ p {
34
+ margin: 1em 0;
35
+ }
36
+
37
+ a {
38
+ color: #00a;
39
+ }
40
+
41
+ a:hover {
42
+ color: black;
43
+ }
44
+
45
+ a:visited {
46
+ color: #a0a;
47
+ }
48
+
49
+ table {
50
+ font-size: inherit;
51
+ font: 100%;
52
+ }
53
+
54
+ /*****************************************************************************/
55
+ /*
56
+ /* Site
57
+ /*
58
+ /*****************************************************************************/
59
+
60
+ .site {
61
+ font-size: 110%;
62
+ text-align: justify;
63
+ width: 40em;
64
+ margin: 3em auto 2em auto;
65
+ line-height: 1.5em;
66
+ }
67
+
68
+ .title {
69
+ color: #a00;
70
+ font-weight: bold;
71
+ margin-bottom: 2em;
72
+ }
73
+
74
+ .site .meta {
75
+ color: #aaa;
76
+ }
@@ -0,0 +1,12 @@
1
+ ---
2
+ layout: default
3
+ title: Tom Preston-Werner
4
+ ---
5
+
6
+ h1. Welcome to my site
7
+
8
+ <ul>
9
+ {% for post in site.posts %}
10
+ <li>{{ post.date }} <a href="{{ post.url }}">{{ post.title }}</a></li>
11
+ {% endfor %}
12
+ </ul>
@@ -0,0 +1,6 @@
1
+ require 'test/unit'
2
+
3
+ tests = Dir["#{File.dirname(__FILE__)}/test_*.rb"]
4
+ tests.each do |file|
5
+ require file
6
+ end
File without changes
@@ -0,0 +1,70 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestPost < Test::Unit::TestCase
4
+ def setup
5
+
6
+ end
7
+
8
+ def test_valid
9
+ assert Post.valid?("2008-10-19-foo-bar.textile")
10
+ assert !Post.valid?("blah")
11
+ end
12
+
13
+ def test_process
14
+ p = Post.allocate
15
+ p.process("2008-10-19-foo-bar.textile")
16
+
17
+ assert_equal Time.parse("2008-10-19"), p.date
18
+ assert_equal "foo-bar", p.slug
19
+ assert_equal ".textile", p.ext
20
+ end
21
+
22
+ def test_url
23
+ p = Post.allocate
24
+ p.process("2008-10-19-foo-bar.textile")
25
+
26
+ assert_equal "/2008/10/19/foo-bar.html", p.url
27
+ end
28
+
29
+ def test_read_yaml
30
+ p = Post.allocate
31
+ p.read_yaml(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-10-18-foo-bar.textile")
32
+
33
+ assert_equal({"title" => "Foo Bar", "layout" => "default"}, p.data)
34
+ assert_equal "\nh1. {{ page.title }}\n\nBest *post* ever", p.content
35
+ end
36
+
37
+ def test_transform
38
+ p = Post.allocate
39
+ p.process("2008-10-18-foo-bar.textile")
40
+ p.read_yaml(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-10-18-foo-bar.textile")
41
+ p.transform
42
+
43
+ assert_equal "<h1>{{ page.title }}</h1>\n\n\n\t<p>Best <strong>post</strong> ever</p>", p.content
44
+ end
45
+
46
+ def test_add_layout
47
+ p = Post.new(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-10-18-foo-bar.textile")
48
+ layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
49
+ p.add_layout(layouts, {"site" => {"posts" => []}})
50
+
51
+ assert_equal "<<< <h1>Foo Bar</h1>\n\n\n\t<p>Best <strong>post</strong> ever</p> >>>", p.output
52
+ end
53
+
54
+ def test_write
55
+ clear_dest
56
+
57
+ p = Post.new(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-10-18-foo-bar.textile")
58
+ layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
59
+ p.add_layout(layouts, {"site" => {"posts" => []}})
60
+ p.write(dest_dir)
61
+ end
62
+
63
+ def test_data
64
+ p = Post.new(File.join(File.dirname(__FILE__), *%w[source _posts]), "2008-11-21-complex.textile")
65
+ layouts = {"default" => Layout.new(File.join(File.dirname(__FILE__), *%w[source _layouts]), "simple.html")}
66
+ p.add_layout(layouts, {"site" => {"posts" => []}})
67
+
68
+ assert_equal "<<< <p>url: /2008/11/21/complex.html\ndate: Fri Nov 21 00:00:00 -0800 2008\nid: /2008/11/21/complex</p> >>>", p.output
69
+ end
70
+ end
@@ -0,0 +1,30 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestSite < Test::Unit::TestCase
4
+ def setup
5
+ source = File.join(File.dirname(__FILE__), *%w[source])
6
+ @s = Site.new(source, dest_dir)
7
+ end
8
+
9
+ def test_site_init
10
+
11
+ end
12
+
13
+ def test_read_layouts
14
+ @s.read_layouts
15
+
16
+ assert_equal ["default", "simple"].sort, @s.layouts.keys.sort
17
+ end
18
+
19
+ def test_read_posts
20
+ @s.read_posts
21
+
22
+ assert_equal 2, @s.posts.size
23
+ end
24
+
25
+ def test_write_posts
26
+ clear_dest
27
+
28
+ @s.process
29
+ end
30
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jekyll
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Tom Preston-Werner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-22 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: RedCloth
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: liquid
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: classifier
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: hoe
47
+ type: :development
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.8.0
54
+ version:
55
+ description:
56
+ email:
57
+ - tom@mojombo.com
58
+ executables:
59
+ - jekyll
60
+ extensions: []
61
+
62
+ extra_rdoc_files:
63
+ - History.txt
64
+ - Manifest.txt
65
+ files:
66
+ - History.txt
67
+ - Manifest.txt
68
+ - README.textile
69
+ - Rakefile
70
+ - bin/jekyll
71
+ - jekyll.gemspec
72
+ - lib/jekyll.rb
73
+ - lib/jekyll/convertible.rb
74
+ - lib/jekyll/filters.rb
75
+ - lib/jekyll/layout.rb
76
+ - lib/jekyll/page.rb
77
+ - lib/jekyll/post.rb
78
+ - lib/jekyll/site.rb
79
+ - test/dest/2008/10/18/foo-bar.html
80
+ - test/dest/2008/11/21/complex.html
81
+ - test/dest/css/screen.css
82
+ - test/dest/index.html
83
+ - test/helper.rb
84
+ - test/source/_layouts/default.html
85
+ - test/source/_layouts/simple.html
86
+ - test/source/_posts/2008-10-18-foo-bar.textile
87
+ - test/source/_posts/2008-11-21-complex.textile
88
+ - test/source/css/screen.css
89
+ - test/source/index.html
90
+ - test/suite.rb
91
+ - test/test_jekyll.rb
92
+ - test/test_post.rb
93
+ - test/test_site.rb
94
+ has_rdoc: true
95
+ homepage:
96
+ post_install_message:
97
+ rdoc_options:
98
+ - --main
99
+ - README.txt
100
+ require_paths:
101
+ - lib
102
+ required_ruby_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: "0"
107
+ version:
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: "0"
113
+ version:
114
+ requirements: []
115
+
116
+ rubyforge_project: jekyll
117
+ rubygems_version: 1.3.0
118
+ signing_key:
119
+ specification_version: 2
120
+ summary: Jekyll is a simple, blog aware, static site generator.
121
+ test_files:
122
+ - test/test_jekyll.rb
123
+ - test/test_post.rb
124
+ - test/test_site.rb