mojombo-jekyll 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,13 @@
1
+ == 0.1.1 /
2
+ * Minor Additions
3
+ * Posts now support introspectional data e.g. {{ page.url }}
4
+
5
+ == 0.1.0 / 2008-11-05
6
+ * First release
7
+ * Converts posts written in Textile
8
+ * Converts regular site pages
9
+ * Simple copy of binary files
10
+
11
+ == 0.0.0 / 2008-10-19
12
+ * Birthday!
13
+
data/Manifest.txt ADDED
@@ -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
data/README.textile ADDED
@@ -0,0 +1,65 @@
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
+
38
+ $ git clone git://github.com/mojombo/jekyll
39
+ $ cd jekyll
40
+ $ bin/jekyll /path/to/proto/site /path/to/place/generated/site
41
+
42
+ h2. License
43
+
44
+ (The MIT License)
45
+
46
+ Copyright (c) 2008 Tom Preston-Werner
47
+
48
+ Permission is hereby granted, free of charge, to any person obtaining
49
+ a copy of this software and associated documentation files (the
50
+ 'Software'), to deal in the Software without restriction, including
51
+ without limitation the rights to use, copy, modify, merge, publish,
52
+ distribute, sublicense, and/or sell copies of the Software, and to
53
+ permit persons to whom the Software is furnished to do so, subject to
54
+ the following conditions:
55
+
56
+ The above copyright notice and this permission notice shall be
57
+ included in all copies or substantial portions of the Software.
58
+
59
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
60
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
61
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
62
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
63
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
64
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
65
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -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']
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
data/bin/jekyll ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
+
5
+ require 'jekyll'
6
+
7
+ if ARGV[0] && ARGV[1]
8
+ Jekyll.process(ARGV[0], ARGV[1])
9
+ else
10
+ puts "USAGE: jekyll /path/to/raw/site /path/to/generated/site"
11
+ end
data/jekyll.gemspec ADDED
@@ -0,0 +1,39 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = %q{jekyll}
3
+ s.version = "0.1.1"
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_development_dependency(%q<hoe>, [">= 1.8.0"])
29
+ else
30
+ s.add_dependency(%q<RedCloth>, [">= 0"])
31
+ s.add_dependency(%q<liquid>, [">= 0"])
32
+ s.add_dependency(%q<hoe>, [">= 1.8.0"])
33
+ end
34
+ else
35
+ s.add_dependency(%q<RedCloth>, [">= 0"])
36
+ s.add_dependency(%q<liquid>, [">= 0"])
37
+ s.add_dependency(%q<hoe>, [">= 1.8.0"])
38
+ end
39
+ end
data/lib/jekyll.rb ADDED
@@ -0,0 +1,30 @@
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
+
16
+ # internal requires
17
+ require 'jekyll/site'
18
+ require 'jekyll/convertible'
19
+ require 'jekyll/layout'
20
+ require 'jekyll/page'
21
+ require 'jekyll/post'
22
+ require 'jekyll/filters'
23
+
24
+ module Jekyll
25
+ VERSION = '0.1.1'
26
+
27
+ def self.process(source, dest)
28
+ Jekyll::Site.new(source, dest).process
29
+ end
30
+ end
@@ -0,0 +1,53 @@
1
+ module Jekyll
2
+ module Convertible
3
+ # Read the YAML frontmatter
4
+ # +base+ is the String path to the dir containing the file
5
+ # +name+ is the String filename of the file
6
+ #
7
+ # Returns nothing
8
+ def read_yaml(base, name)
9
+ self.content = File.read(File.join(base, name))
10
+
11
+ if self.content =~ /^(---\n.*?)\n---\n/m
12
+ self.content = self.content[($1.size + 5)..-1]
13
+
14
+ self.data = YAML.load($1)
15
+ end
16
+ end
17
+
18
+ # Transform the contents based on the file extension.
19
+ #
20
+ # Returns nothing
21
+ def transform
22
+ if self.ext == ".textile"
23
+ self.ext = ".html"
24
+ self.content = RedCloth.new(self.content).to_html
25
+ end
26
+ end
27
+
28
+ # Add any necessary layouts to this post
29
+ # +layouts+ is a Hash of {"name" => "layout"}
30
+ # +site_payload+ is the site payload hash
31
+ #
32
+ # Returns nothing
33
+ def do_layout(payload, layouts, site_payload)
34
+ # construct payload
35
+ payload = payload.merge(site_payload)
36
+
37
+ # render content
38
+ self.content = Liquid::Template.parse(self.content).render(payload, [Jekyll::Filters])
39
+
40
+ # output keeps track of what will finally be written
41
+ self.output = self.content
42
+
43
+ # recursively render layouts
44
+ layout = layouts[self.data["layout"]]
45
+ while layout
46
+ payload = payload.merge({"content" => self.output, "page" => self.data})
47
+ self.output = Liquid::Template.parse(layout.content).render(payload, [Jekyll::Filters])
48
+
49
+ layout = layouts[layout.data["layout"]]
50
+ end
51
+ end
52
+ end
53
+ 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,120 @@
1
+ module Jekyll
2
+
3
+ class Post
4
+ include Comparable
5
+ include Convertible
6
+
7
+ MATCHER = /^(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
8
+
9
+ # Post name validator. Post filenames must be like:
10
+ # 2008-11-05-my-awesome-post.textile
11
+ #
12
+ # Returns <Bool>
13
+ def self.valid?(name)
14
+ name =~ MATCHER
15
+ end
16
+
17
+ attr_accessor :date, :slug, :ext
18
+ attr_accessor :data, :content, :output
19
+
20
+ # Initialize this Post instance.
21
+ # +base+ is the String path to the dir containing the post file
22
+ # +name+ is the String filename of the post file
23
+ #
24
+ # Returns <Post>
25
+ def initialize(base, name)
26
+ @base = base
27
+ @name = name
28
+
29
+ self.process(name)
30
+ self.read_yaml(base, name)
31
+ self.transform
32
+ end
33
+
34
+ # Spaceship is based on Post#date
35
+ #
36
+ # Returns -1, 0, 1
37
+ def <=>(other)
38
+ self.date <=> other.date
39
+ end
40
+
41
+ # Extract information from the post filename
42
+ # +name+ is the String filename of the post file
43
+ #
44
+ # Returns nothing
45
+ def process(name)
46
+ m, date, slug, ext = *name.match(MATCHER)
47
+ self.date = Time.parse(date)
48
+ self.slug = slug
49
+ self.ext = ext
50
+ end
51
+
52
+ # The generated directory into which the post will be placed
53
+ # upon generation. e.g. "/2008/11/05/"
54
+ #
55
+ # Returns <String>
56
+ def dir
57
+ self.date.strftime("/%Y/%m/%d/")
58
+ end
59
+
60
+ # The generated relative url of this post
61
+ # e.g. /2008/11/05/my-awesome-post.html
62
+ #
63
+ # Returns <String>
64
+ def url
65
+ self.dir + self.slug + ".html"
66
+ end
67
+
68
+ # The UID for this post (useful in feeds)
69
+ # e.g. /2008/11/05/my-awesome-post
70
+ #
71
+ # Returns <String>
72
+ def id
73
+ self.dir + self.slug
74
+ end
75
+
76
+ # Calculate related posts.
77
+ #
78
+ # Returns [<Post>]
79
+ def related_posts(posts)
80
+ related = posts - [self]
81
+ end
82
+
83
+ # Add any necessary layouts to this post
84
+ # +layouts+ is a Hash of {"name" => "layout"}
85
+ # +site_payload+ is the site payload hash
86
+ #
87
+ # Returns nothing
88
+ def add_layout(layouts, site_payload)
89
+ # construct post payload
90
+ related = related_posts(site_payload["site"]["posts"])
91
+ payload = {"page" => self.to_liquid.merge(self.data), "related_posts" => related}
92
+ do_layout(payload, layouts, site_payload)
93
+ end
94
+
95
+ # Write the generated post file to the destination directory.
96
+ # +dest+ is the String path to the destination dir
97
+ #
98
+ # Returns nothing
99
+ def write(dest)
100
+ FileUtils.mkdir_p(File.join(dest, self.dir))
101
+
102
+ path = File.join(dest, self.url)
103
+ File.open(path, 'w') do |f|
104
+ f.write(self.output)
105
+ end
106
+ end
107
+
108
+ # Convert this post into a Hash for use in Liquid templates.
109
+ #
110
+ # Returns <Hash>
111
+ def to_liquid
112
+ { "title" => self.data["title"] || "",
113
+ "url" => self.url,
114
+ "date" => self.date,
115
+ "id" => self.id,
116
+ "content" => self.content }
117
+ end
118
+ end
119
+
120
+ 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
data/test/helper.rb ADDED
@@ -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>
data/test/suite.rb ADDED
@@ -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
data/test/test_post.rb ADDED
@@ -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
data/test/test_site.rb ADDED
@@ -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,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mojombo-jekyll
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
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: jekyll
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: RedCloth
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: liquid
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: "0"
32
+ version:
33
+ - !ruby/object:Gem::Dependency
34
+ name: hoe
35
+ version_requirement:
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 1.8.0
41
+ version:
42
+ description:
43
+ email:
44
+ - tom@mojombo.com
45
+ executables:
46
+ - jekyll
47
+ extensions: []
48
+
49
+ extra_rdoc_files:
50
+ - History.txt
51
+ - Manifest.txt
52
+ files:
53
+ - History.txt
54
+ - Manifest.txt
55
+ - README.textile
56
+ - Rakefile
57
+ - bin/jekyll
58
+ - jekyll.gemspec
59
+ - lib/jekyll.rb
60
+ - lib/jekyll/convertible.rb
61
+ - lib/jekyll/filters.rb
62
+ - lib/jekyll/layout.rb
63
+ - lib/jekyll/page.rb
64
+ - lib/jekyll/post.rb
65
+ - lib/jekyll/site.rb
66
+ - test/dest/2008/10/18/foo-bar.html
67
+ - test/dest/2008/11/21/complex.html
68
+ - test/dest/css/screen.css
69
+ - test/dest/index.html
70
+ - test/helper.rb
71
+ - test/source/_layouts/default.html
72
+ - test/source/_layouts/simple.html
73
+ - test/source/_posts/2008-10-18-foo-bar.textile
74
+ - test/source/_posts/2008-11-21-complex.textile
75
+ - test/source/css/screen.css
76
+ - test/source/index.html
77
+ - test/suite.rb
78
+ - test/test_jekyll.rb
79
+ - test/test_post.rb
80
+ - test/test_site.rb
81
+ has_rdoc: true
82
+ homepage:
83
+ post_install_message:
84
+ rdoc_options:
85
+ - --main
86
+ - README.txt
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: "0"
94
+ version:
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ">="
98
+ - !ruby/object:Gem::Version
99
+ version: "0"
100
+ version:
101
+ requirements: []
102
+
103
+ rubyforge_project: jekyll
104
+ rubygems_version: 1.2.0
105
+ signing_key:
106
+ specification_version: 2
107
+ summary: Jekyll is a simple, blog aware, static site generator.
108
+ test_files:
109
+ - test/test_jekyll.rb
110
+ - test/test_post.rb
111
+ - test/test_site.rb