From a77c92aebe89a127ef4d9c39b39df892798c7e6a Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Tue, 1 Apr 2014 19:06:42 -0400
Subject: [PATCH 01/30] Replace load-in of YAML data with Jekyll::Document
logic.
COLLECTIONS IS COMING
---
lib/jekyll.rb | 1 +
lib/jekyll/document.rb | 83 ++++++++++++++++++++++++++++++++++++++++++
lib/jekyll/site.rb | 2 +-
test/test_site.rb | 12 +++---
4 files changed, 91 insertions(+), 7 deletions(-)
create mode 100644 lib/jekyll/document.rb
diff --git a/lib/jekyll.rb b/lib/jekyll.rb
index e2dd369c..d133f0b4 100644
--- a/lib/jekyll.rb
+++ b/lib/jekyll.rb
@@ -34,6 +34,7 @@ require 'jekyll/utils'
require 'jekyll/stevenson'
require 'jekyll/deprecator'
require 'jekyll/configuration'
+require 'jekyll/document'
require 'jekyll/plugin_manager'
require 'jekyll/site'
require 'jekyll/convertible'
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
new file mode 100644
index 00000000..50fbb056
--- /dev/null
+++ b/lib/jekyll/document.rb
@@ -0,0 +1,83 @@
+module Jekyll
+ class Document
+
+ attr_reader :path
+ attr_accessor :content
+
+ # Create a new Document.
+ #
+ # site - the Jekyll::Site instance to which this Document belongs
+ # path - the path to the file
+ #
+ # Returns nothing.
+ def initialize(site, path)
+ @site = site
+ @path = path
+ end
+
+ # Fetch the Document's data.
+ #
+ # Returns a Hash containing the data. An empty hash is returned if
+ # no data was read.
+ def data
+ @data ||= Hash.new
+ end
+
+ def extname
+ File.extname(path)
+ end
+
+ def yaml_file?
+ %w[.yaml .yml].include?(extname)
+ end
+
+ # Returns merged option hash for File.read of self.site (if exists)
+ # and a given param
+ #
+ # opts - override options
+ #
+ # Return
+ def merged_file_read_opts(opts)
+ (site ? site.file_read_opts : {}).merge(opts)
+ end
+
+ # Whether the file is published or not, as indicated in YAML front-matter
+ #
+ # Returns true if the 'published' key is specified in the YAML front-matter and not `false`.
+ def published?
+ !(data.has_key?('published') && data['published'] == false)
+ end
+
+ # Read in the file and assign the content and data based on the file contents.
+ #
+ # Returns nothing.
+ def read
+ if yaml_file?
+ @data = SafeYAML.load_file(path)
+ else
+ begin
+ @content = File.read(path, merged_file_read_opts(opts))
+ if content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
+ @content = $POSTMATCH
+ @data = SafeYAML.load($1)
+ end
+ rescue SyntaxError => e
+ puts "YAML Exception reading #{path}: #{e.message}"
+ rescue Exception => e
+ puts "Error reading file #{path}: #{e.message}"
+ end
+ end
+ end
+
+ # Create a Liquid-understandable version of this Document.
+ #
+ # Returns a Hash representing this Document's data.
+ def to_liquid
+ data.merge({
+ "content" => content,
+ "path" => path
+ })
+ end
+
+ end
+end
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 14224898..4bb8f838 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -177,7 +177,7 @@ module Jekyll
next if File.symlink?(path) && safe
key = sanitize_filename(File.basename(entry, '.*'))
- self.data[key] = SafeYAML.load_file(path)
+ (self.data[key] = Jekyll::Document.new(self, path)).read
end
end
diff --git a/test/test_site.rb b/test/test_site.rb
index c5d78594..8b805860 100644
--- a/test/test_site.rb
+++ b/test/test_site.rb
@@ -364,8 +364,8 @@ class TestSite < Test::Unit::TestCase
file_content = SafeYAML.load_file(File.join(source_dir, '_data', 'members.yaml'))
- assert_equal site.data['members'], file_content
- assert_equal site.site_payload['site']['data']['members'], file_content
+ assert_equal site.data['members'].data, file_content
+ assert_equal site.site_payload['site']['data']['members'].data, file_content
end
should 'auto load yml files' do
@@ -374,8 +374,8 @@ class TestSite < Test::Unit::TestCase
file_content = SafeYAML.load_file(File.join(source_dir, '_data', 'languages.yml'))
- assert_equal site.data['languages'], file_content
- assert_equal site.site_payload['site']['data']['languages'], file_content
+ assert_equal site.data['languages'].data, file_content
+ assert_equal site.site_payload['site']['data']['languages'].data, file_content
end
should "load symlink files in unsafe mode" do
@@ -384,8 +384,8 @@ class TestSite < Test::Unit::TestCase
file_content = SafeYAML.load_file(File.join(source_dir, '_data', 'products.yml'))
- assert_equal site.data['products'], file_content
- assert_equal site.site_payload['site']['data']['products'], file_content
+ assert_equal site.data['products'].data, file_content
+ assert_equal site.site_payload['site']['data']['products'].data, file_content
end
should "not load symlink files in safe mode" do
From 50b46d7beeda175aef77b638ac9c66ae86c83ba8 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Tue, 1 Apr 2014 21:19:10 -0400
Subject: [PATCH 02/30] OMG it's happening ~*Collections*~
---
lib/jekyll.rb | 1 +
lib/jekyll/collection.rb | 33 ++++++++++++
lib/jekyll/configuration.rb | 1 +
lib/jekyll/document.rb | 15 ++++--
lib/jekyll/site.rb | 20 +++++--
test/source/_methods/configuration.md | 8 +++
test/source/_methods/sanitized_path.md | 5 ++
test/source/_methods/site/generate.md | 5 ++
test/source/_methods/site/initialize.md | 5 ++
test/source/_methods/um_hi.md | 1 +
test/test_collections.rb | 71 +++++++++++++++++++++++++
11 files changed, 157 insertions(+), 8 deletions(-)
create mode 100644 lib/jekyll/collection.rb
create mode 100644 test/source/_methods/configuration.md
create mode 100644 test/source/_methods/sanitized_path.md
create mode 100644 test/source/_methods/site/generate.md
create mode 100644 test/source/_methods/site/initialize.md
create mode 120000 test/source/_methods/um_hi.md
create mode 100644 test/test_collections.rb
diff --git a/lib/jekyll.rb b/lib/jekyll.rb
index d133f0b4..7fa69a7a 100644
--- a/lib/jekyll.rb
+++ b/lib/jekyll.rb
@@ -35,6 +35,7 @@ require 'jekyll/stevenson'
require 'jekyll/deprecator'
require 'jekyll/configuration'
require 'jekyll/document'
+require 'jekyll/collection'
require 'jekyll/plugin_manager'
require 'jekyll/site'
require 'jekyll/convertible'
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
new file mode 100644
index 00000000..8e0b379c
--- /dev/null
+++ b/lib/jekyll/collection.rb
@@ -0,0 +1,33 @@
+module Jekyll
+ class Collection
+ attr_reader :site, :label
+
+ def initialize(site, label)
+ @site = site
+ @label = label
+ end
+
+ def docs
+ @docs ||= []
+ end
+
+ def read
+ Dir.glob(File.join(directory, "**", "*.*")).each do |file_path|
+ if allowed_document?(file_path)
+ doc = Jekyll::Document.new(file_path, { site: site, collection: self })
+ docs << doc
+ end
+ end
+ docs
+ end
+
+ def directory
+ Jekyll.sanitized_path(site.source, "_#{label}")
+ end
+
+ def allowed_document?(path)
+ !(site.safe && File.symlink?(path))
+ end
+
+ end
+end
diff --git a/lib/jekyll/configuration.rb b/lib/jekyll/configuration.rb
index 93dc5193..ffdb0a22 100644
--- a/lib/jekyll/configuration.rb
+++ b/lib/jekyll/configuration.rb
@@ -13,6 +13,7 @@ module Jekyll
'data_source' => '_data',
'keep_files' => ['.git','.svn'],
'gems' => [],
+ 'collections' => nil,
'timezone' => nil, # use the local timezone
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index 50fbb056..198b77fb 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -1,18 +1,19 @@
module Jekyll
class Document
- attr_reader :path
- attr_accessor :content
+ attr_reader :path, :site
+ attr_accessor :content, :collection
# Create a new Document.
#
- # site - the Jekyll::Site instance to which this Document belongs
+ # shit - the Jekyll::Site instance to which this Document belongs
# path - the path to the file
#
# Returns nothing.
- def initialize(site, path)
- @site = site
+ def initialize(path, relations)
+ @site = relations[:site]
@path = path
+ @collection = relations[:collection]
end
# Fetch the Document's data.
@@ -23,6 +24,10 @@ module Jekyll
@data ||= Hash.new
end
+ def relative_path
+ Pathname.new(path).relative_path_from(Pathname.new(site.source)).to_s
+ end
+
def extname
File.extname(path)
end
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 4bb8f838..7e3cdc5d 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -4,7 +4,7 @@ module Jekyll
:exclude, :include, :source, :dest, :lsi, :highlighter,
:permalink_style, :time, :future, :unpublished, :safe, :plugins, :limit_posts,
:show_drafts, :keep_files, :baseurl, :data, :file_read_opts, :gems,
- :plugin_manager
+ :plugin_manager, :collections
attr_accessor :converters, :generators
@@ -14,7 +14,9 @@ module Jekyll
def initialize(config)
self.config = config.clone
- %w[safe lsi highlighter baseurl exclude include future unpublished show_drafts limit_posts keep_files gems].each do |opt|
+ %w[
+ safe lsi highlighter baseurl exclude include future unpublished
+ show_drafts limit_posts keep_files gems collections].each do |opt|
self.send("#{opt}=", config[opt])
end
@@ -90,6 +92,7 @@ module Jekyll
self.layouts = LayoutReader.new(self).read
read_directories
read_data(config['data_source'])
+ read_collections
end
# Recursively traverse directories to find posts, pages and static files
@@ -171,13 +174,24 @@ module Jekyll
entries = Dir.chdir(base) { Dir['*.{yaml,yml}'] }
entries.delete_if { |e| File.directory?(File.join(base, e)) }
+ data_collection = Jekyll::Collection.new(self, "data")
entries.each do |entry|
path = File.join(source, dir, entry)
next if File.symlink?(path) && safe
key = sanitize_filename(File.basename(entry, '.*'))
- (self.data[key] = Jekyll::Document.new(self, path)).read
+ (self.data[key] = Jekyll::Document.new(path, { site: self, collection: data_collection })).read
+ end
+ end
+
+ # Read in all collections specified in the configuration
+ #
+ # Returns nothing.
+ def read_collections
+ if collections
+ self.collections = Hash[collections.map { |coll| [coll, Jekyll::Collection.new(self, coll)] } ]
+ collections.each { |_, collection| collection.read }
end
end
diff --git a/test/source/_methods/configuration.md b/test/source/_methods/configuration.md
new file mode 100644
index 00000000..fd17980b
--- /dev/null
+++ b/test/source/_methods/configuration.md
@@ -0,0 +1,8 @@
+---
+title: "Jekyll.configuration"
+whatever: foo.bar
+---
+
+Use `{{ page.title }}` to build a full configuration for use w/Jekyll.
+
+Whatever: {{ page.whatever }}
diff --git a/test/source/_methods/sanitized_path.md b/test/source/_methods/sanitized_path.md
new file mode 100644
index 00000000..8b4d767a
--- /dev/null
+++ b/test/source/_methods/sanitized_path.md
@@ -0,0 +1,5 @@
+---
+title: "Jekyll.sanitized_path"
+---
+
+`{{ page.title }}` is used to make sure your path is in your source.
diff --git a/test/source/_methods/site/generate.md b/test/source/_methods/site/generate.md
new file mode 100644
index 00000000..b7eaaf62
--- /dev/null
+++ b/test/source/_methods/site/generate.md
@@ -0,0 +1,5 @@
+---
+title: "Site#generate"
+---
+
+Run your generators!
diff --git a/test/source/_methods/site/initialize.md b/test/source/_methods/site/initialize.md
new file mode 100644
index 00000000..9c23b967
--- /dev/null
+++ b/test/source/_methods/site/initialize.md
@@ -0,0 +1,5 @@
+---
+title: "Site#initialize"
+---
+
+Create dat site.
diff --git a/test/source/_methods/um_hi.md b/test/source/_methods/um_hi.md
new file mode 120000
index 00000000..c549c8b4
--- /dev/null
+++ b/test/source/_methods/um_hi.md
@@ -0,0 +1 @@
+test/source/_methods/sanitized_path.md
\ No newline at end of file
diff --git a/test/test_collections.rb b/test/test_collections.rb
new file mode 100644
index 00000000..afe8bd06
--- /dev/null
+++ b/test/test_collections.rb
@@ -0,0 +1,71 @@
+require 'helper'
+
+class TestCollections < Test::Unit::TestCase
+
+ context "with no collections specified" do
+ setup do
+ @site = Site.new(Jekyll.configuration({
+ "source" => source_dir,
+ "destination" => dest_dir
+ }))
+ @site.process
+ end
+
+ should "not contain any collections" do
+ assert_nil @site.collections
+ end
+ end
+
+ context "with a collection" do
+ setup do
+ @site = Site.new(Jekyll.configuration({
+ "collections" => ["methods"],
+ "source" => source_dir,
+ "destination" => dest_dir
+ }))
+ @site.process
+ end
+
+ should "create a Hash on Site with the label mapped to the instance of the Collection" do
+ assert @site.collections.is_a?(Hash)
+ assert_not_nil @site.collections["methods"]
+ assert @site.collections["methods"].is_a? Jekyll::Collection
+ end
+
+ should "collects docs in an array on the Collection object" do
+ assert @site.collections["methods"].docs.is_a? Array
+ @site.collections["methods"].docs.each do |doc|
+ assert doc.is_a? Jekyll::Document
+ assert_include %w[
+ _methods/configuration.md
+ _methods/sanitized_path.md
+ _methods/site/generate.md
+ _methods/site/initialize.md
+ _methods/um_hi.md
+ ], doc.relative_path
+ end
+ end
+ end
+
+ context "in safe mode" do
+ setup do
+ @site = Site.new(Jekyll.configuration({
+ "collections" => ["methods"],
+ "safe" => true,
+ "source" => source_dir,
+ "destination" => dest_dir
+ }))
+ @site.process
+ @collection = @site.collections["methods"]
+ end
+
+ should "not allow symlinks" do
+ assert !@collection.allowed_document?(File.join(@collection.directory, "um_hi.md"))
+ end
+
+ should "not include the symlinked file in the list of docs" do
+ assert_not_include %w[_methods/um_hi.md], @collection.docs.map(&:relative_path)
+ end
+ end
+
+end
From 08162dbb50dd9973e483e079de4ca816ac660853 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Tue, 1 Apr 2014 21:38:30 -0400
Subject: [PATCH 03/30] Hey girl, i heard you like YAML.
---
lib/jekyll/site.rb | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 7e3cdc5d..de3d027e 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -181,7 +181,11 @@ module Jekyll
next if File.symlink?(path) && safe
key = sanitize_filename(File.basename(entry, '.*'))
- (self.data[key] = Jekyll::Document.new(path, { site: self, collection: data_collection })).read
+
+ doc = Jekyll::Document.new(path, { site: self, collection: data_collection })
+ doc.read
+
+ self.data[key] = doc.data
end
end
From 90807ac6e78cd9977a9cc80832840326e621cd29 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Tue, 1 Apr 2014 23:10:17 -0400
Subject: [PATCH 04/30] DEM TESTS
---
lib/jekyll/collection.rb | 1 +
lib/jekyll/document.rb | 6 ++++-
lib/jekyll/site.rb | 13 +++-------
test/source/_methods/um_hi.md | 2 +-
test/test_document.rb | 45 +++++++++++++++++++++++++++++++++++
5 files changed, 55 insertions(+), 12 deletions(-)
create mode 100644 test/test_document.rb
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index 8e0b379c..95e4c8a1 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -15,6 +15,7 @@ module Jekyll
Dir.glob(File.join(directory, "**", "*.*")).each do |file_path|
if allowed_document?(file_path)
doc = Jekyll::Document.new(file_path, { site: site, collection: self })
+ doc.read
docs << doc
end
end
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index 198b77fb..e637fbd1 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -28,6 +28,10 @@ module Jekyll
Pathname.new(path).relative_path_from(Pathname.new(site.source)).to_s
end
+ def basename(suffix = "")
+ File.basename(path, suffix)
+ end
+
def extname
File.extname(path)
end
@@ -56,7 +60,7 @@ module Jekyll
# Read in the file and assign the content and data based on the file contents.
#
# Returns nothing.
- def read
+ def read(opts = {})
if yaml_file?
@data = SafeYAML.load_file(path)
else
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index de3d027e..8fe10776 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -175,16 +175,9 @@ module Jekyll
entries = Dir.chdir(base) { Dir['*.{yaml,yml}'] }
entries.delete_if { |e| File.directory?(File.join(base, e)) }
data_collection = Jekyll::Collection.new(self, "data")
-
- entries.each do |entry|
- path = File.join(source, dir, entry)
- next if File.symlink?(path) && safe
-
- key = sanitize_filename(File.basename(entry, '.*'))
-
- doc = Jekyll::Document.new(path, { site: self, collection: data_collection })
- doc.read
-
+ data_collection.read
+ data_collection.docs.each do |doc|
+ key = sanitize_filename(doc.basename(".*"))
self.data[key] = doc.data
end
end
diff --git a/test/source/_methods/um_hi.md b/test/source/_methods/um_hi.md
index c549c8b4..9ebb5325 120000
--- a/test/source/_methods/um_hi.md
+++ b/test/source/_methods/um_hi.md
@@ -1 +1 @@
-test/source/_methods/sanitized_path.md
\ No newline at end of file
+./site/generate.md
\ No newline at end of file
diff --git a/test/test_document.rb b/test/test_document.rb
new file mode 100644
index 00000000..75bf2a84
--- /dev/null
+++ b/test/test_document.rb
@@ -0,0 +1,45 @@
+require 'helper'
+
+class TestDocument < Test::Unit::TestCase
+
+ context "" do
+ setup do
+ @site = Site.new(Jekyll.configuration({
+ "collections" => ["methods"],
+ "source" => source_dir,
+ "destination" => dest_dir
+ }))
+ @site.process
+ @document = @site.collections["methods"].docs.first
+ end
+
+ should "know its relative path" do
+ assert_equal "_methods/configuration.md", @document.relative_path
+ end
+
+ should "knows its extname" do
+ assert_equal ".md", @document.extname
+ end
+
+ should "know its basename" do
+ assert_equal "configuration.md", @document.basename
+ end
+
+ should "allow the suffix to be specified for the basename" do
+ assert_equal "configuration", @document.basename(".*")
+ end
+
+ should "know whether its a yaml file" do
+ assert_equal false, @document.yaml_file?
+ end
+
+ should "know its data" do
+ assert_equal({
+ "title" => "Jekyll.configuration",
+ "whatever" => "foo.bar"
+ }, @document.data)
+ end
+
+ end
+
+end
From cefe99bed22456553284053f946f3fca997da799 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Tue, 1 Apr 2014 23:18:18 -0400
Subject: [PATCH 05/30] Sort the docs based on path
---
lib/jekyll/collection.rb | 2 +-
lib/jekyll/document.rb | 5 +++++
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index 95e4c8a1..21351374 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -19,7 +19,7 @@ module Jekyll
docs << doc
end
end
- docs
+ docs.sort!
end
def directory
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index e637fbd1..23ee437e 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -1,5 +1,6 @@
module Jekyll
class Document
+ include Comparable
attr_reader :path, :site
attr_accessor :content, :collection
@@ -88,5 +89,9 @@ module Jekyll
})
end
+ def <=>(anotherDocument)
+ path <=> anotherDocument.path
+ end
+
end
end
From c1c5cc78a529a4b70b28daadb20fb19eba010cd1 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Tue, 1 Apr 2014 23:51:48 -0400
Subject: [PATCH 06/30] Expect site.data stuff to be hashes
---
test/test_site.rb | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/test/test_site.rb b/test/test_site.rb
index 8b805860..c5d78594 100644
--- a/test/test_site.rb
+++ b/test/test_site.rb
@@ -364,8 +364,8 @@ class TestSite < Test::Unit::TestCase
file_content = SafeYAML.load_file(File.join(source_dir, '_data', 'members.yaml'))
- assert_equal site.data['members'].data, file_content
- assert_equal site.site_payload['site']['data']['members'].data, file_content
+ assert_equal site.data['members'], file_content
+ assert_equal site.site_payload['site']['data']['members'], file_content
end
should 'auto load yml files' do
@@ -374,8 +374,8 @@ class TestSite < Test::Unit::TestCase
file_content = SafeYAML.load_file(File.join(source_dir, '_data', 'languages.yml'))
- assert_equal site.data['languages'].data, file_content
- assert_equal site.site_payload['site']['data']['languages'].data, file_content
+ assert_equal site.data['languages'], file_content
+ assert_equal site.site_payload['site']['data']['languages'], file_content
end
should "load symlink files in unsafe mode" do
@@ -384,8 +384,8 @@ class TestSite < Test::Unit::TestCase
file_content = SafeYAML.load_file(File.join(source_dir, '_data', 'products.yml'))
- assert_equal site.data['products'].data, file_content
- assert_equal site.site_payload['site']['data']['products'].data, file_content
+ assert_equal site.data['products'], file_content
+ assert_equal site.site_payload['site']['data']['products'], file_content
end
should "not load symlink files in safe mode" do
From f082eca791f1f9369e8d13bf45555031d0880c20 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Wed, 2 Apr 2014 00:53:43 -0400
Subject: [PATCH 07/30] GUYS failing test for rendering
---
features/collections.feature | 24 +++++++++++++++++++++++
features/step_definitions/jekyll_steps.rb | 15 +++++++++++---
features/support/env.rb | 5 +++++
lib/jekyll/collection.rb | 11 +++++++++++
lib/jekyll/document.rb | 4 ++++
lib/jekyll/site.rb | 3 ++-
6 files changed, 58 insertions(+), 4 deletions(-)
create mode 100644 features/collections.feature
diff --git a/features/collections.feature b/features/collections.feature
new file mode 100644
index 00000000..209232e3
--- /dev/null
+++ b/features/collections.feature
@@ -0,0 +1,24 @@
+Feature: Collections
+ As a hacker who likes to structure content
+ I want to be able to create collections of similar information
+ And render them
+
+ Scenario: Unrendered collection
+ Given I have an "index.html" page that contains "Collections: {{ site.collections }}"
+ And I have fixture collections
+ And I have a configuration file with "collections" set to "['methods']"
+ When I debug jekyll
+ Then the _site directory should exist
+ And I should see "Collections: {\"methods\"=>#, #, #, #, #\]>}" in "_site/index.html"
+
+ Scenario: Rendered collection
+ Given I have an "index.html" page that contains "Collections: {{ site.collections.methods.label }}"
+ And I have fixture collections
+ And I have a configuration file with:
+ | key | value |
+ | collections | ['methods'] |
+ | render | \n methods: /methods/:collection_name/:subdir/:title:extname |
+ When I run jekyll
+ Then the _site directory should exist
+ And I should see "Collections: methods" in "_site/index.html"
+ And I should see "Whatever: foo.bar" in "_site/methods/configuration.html"
\ No newline at end of file
diff --git a/features/step_definitions/jekyll_steps.rb b/features/step_definitions/jekyll_steps.rb
index 93de5c10..45210630 100644
--- a/features/step_definitions/jekyll_steps.rb
+++ b/features/step_definitions/jekyll_steps.rb
@@ -16,15 +16,14 @@ def file_content_from_hash(input_hash)
EOF
end
-
Before do
FileUtils.mkdir_p(TEST_DIR) unless File.exist?(TEST_DIR)
Dir.chdir(TEST_DIR)
end
After do
- FileUtils.rm_rf(TEST_DIR) if File.exist?(TEST_DIR)
- FileUtils.rm(JEKYLL_COMMAND_OUTPUT_FILE)
+ FileUtils.rm_rf(TEST_DIR) if File.exists?(TEST_DIR)
+ FileUtils.rm(JEKYLL_COMMAND_OUTPUT_FILE) if File.exists?(JEKYLL_COMMAND_OUTPUT_FILE)
end
World(Test::Unit::Assertions)
@@ -130,6 +129,16 @@ Given /^I have a configuration file with "([^\"]*)" set to:$/ do |key, table|
end
end
+Given /^I have fixture collections$/ do
+ FileUtils.cp_r File.join(JEKYLL_SOURCE_DIR, "test", "source", "_methods"), source_dir
+end
+
+##################
+#
+# Changing stuff
+#
+##################
+
When /^I run jekyll(?: with "(.+)")?$/ do |opt|
run_jekyll_build(opt)
end
diff --git a/features/support/env.rb b/features/support/env.rb
index ad3bb38a..593e1304 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -6,10 +6,15 @@ require 'rr'
require 'test/unit'
require 'time'
+JEKYLL_SOURCE_DIR = File.dirname(File.dirname(File.dirname(__FILE__)))
TEST_DIR = File.expand_path(File.join('..', '..', 'tmp', 'jekyll'), File.dirname(__FILE__))
JEKYLL_PATH = File.join(File.dirname(__FILE__), '..', '..', 'bin', 'jekyll')
JEKYLL_COMMAND_OUTPUT_FILE = File.join(File.dirname(TEST_DIR), 'jekyll_output.txt')
+def source_dir(*files)
+ File.join(TEST_DIR, *files)
+end
+
def jekyll_output_file
JEKYLL_COMMAND_OUTPUT_FILE
end
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index 21351374..6a12fd65 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -30,5 +30,16 @@ module Jekyll
!(site.safe && File.symlink?(path))
end
+ def inspect
+ "#"
+ end
+
+ def to_liquid
+ {
+ "label" => label,
+ "docs" => docs
+ }
+ end
+
end
end
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index 23ee437e..f137e988 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -89,6 +89,10 @@ module Jekyll
})
end
+ def inspect
+ "#"
+ end
+
def <=>(anotherDocument)
path <=> anotherDocument.path
end
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 8fe10776..204d0bbd 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -290,7 +290,8 @@ module Jekyll
"html_pages" => pages.reject { |page| !page.html? },
"categories" => post_attr_hash('categories'),
"tags" => post_attr_hash('tags'),
- "data" => site_data})}
+ "data" => site_data,
+ "collections" => collections})}
end
# Filter out any files/directories that are hidden or backup files (start
From 7fef0302a72e873fa467e73115dffd51d426818e Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Wed, 2 Apr 2014 00:56:17 -0400
Subject: [PATCH 08/30] Strike duplicate methods & :collection_name filler
---
features/collections.feature | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/features/collections.feature b/features/collections.feature
index 209232e3..8c760915 100644
--- a/features/collections.feature
+++ b/features/collections.feature
@@ -17,7 +17,7 @@ Feature: Collections
And I have a configuration file with:
| key | value |
| collections | ['methods'] |
- | render | \n methods: /methods/:collection_name/:subdir/:title:extname |
+ | render | \n methods: /methods/:subdir/:title:extname |
When I run jekyll
Then the _site directory should exist
And I should see "Collections: methods" in "_site/index.html"
From 75f49a751e5e33d1b660cacb6783548d7d43ded7 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Wed, 2 Apr 2014 16:31:02 -0400
Subject: [PATCH 09/30] OMG COLLECTIONS ARE RENDERING CALL THE POLICE
---
features/collections.feature | 2 +-
lib/jekyll.rb | 1 +
lib/jekyll/cleaner.rb | 8 ++-
lib/jekyll/collection.rb | 6 +-
lib/jekyll/document.rb | 64 ++++++++++++++++-
lib/jekyll/renderer.rb | 133 +++++++++++++++++++++++++++++++++++
lib/jekyll/site.rb | 40 +++++++++--
lib/jekyll/url.rb | 4 +-
script/console | 38 ++++++++++
9 files changed, 279 insertions(+), 17 deletions(-)
create mode 100644 lib/jekyll/renderer.rb
create mode 100755 script/console
diff --git a/features/collections.feature b/features/collections.feature
index 8c760915..0abf15e8 100644
--- a/features/collections.feature
+++ b/features/collections.feature
@@ -17,7 +17,7 @@ Feature: Collections
And I have a configuration file with:
| key | value |
| collections | ['methods'] |
- | render | \n methods: /methods/:subdir/:title:extname |
+ | render | ['methods'] |
When I run jekyll
Then the _site directory should exist
And I should see "Collections: methods" in "_site/index.html"
diff --git a/lib/jekyll.rb b/lib/jekyll.rb
index 7fa69a7a..d7a2c2ea 100644
--- a/lib/jekyll.rb
+++ b/lib/jekyll.rb
@@ -53,6 +53,7 @@ require 'jekyll/cleaner'
require 'jekyll/entry_filter'
require 'jekyll/layout_reader'
require 'jekyll/publisher'
+require 'jekyll/renderer'
# extensions
require 'jekyll/plugin'
diff --git a/lib/jekyll/cleaner.rb b/lib/jekyll/cleaner.rb
index e3a89b4b..583fc844 100644
--- a/lib/jekyll/cleaner.rb
+++ b/lib/jekyll/cleaner.rb
@@ -4,6 +4,8 @@ module Jekyll
class Site
# Handles the cleanup of a site's destination before it is built.
class Cleaner
+ attr_reader :site
+
def initialize(site)
@site = site
end
@@ -27,7 +29,7 @@ module Jekyll
# Returns a Set with the file paths
def existing_files
files = Set.new
- Dir.glob(File.join(@site.dest, "**", "*"), File::FNM_DOTMATCH) do |file|
+ Dir.glob(File.join(site.dest, "**", "*"), File::FNM_DOTMATCH) do |file|
files << file unless file =~ /\/\.{1,2}$/ || file =~ keep_file_regex
end
files
@@ -38,7 +40,7 @@ module Jekyll
# Returns a Set with the file paths
def new_files
files = Set.new
- @site.each_site_file { |item| files << item.destination(@site.dest) }
+ site.each_site_file { |item| files << item.destination(site.dest) }
files
end
@@ -64,7 +66,7 @@ module Jekyll
#
# Returns the regular expression
def keep_file_regex
- or_list = @site.keep_files.join("|")
+ or_list = site.keep_files.join("|")
pattern = "\/(#{or_list.gsub(".", "\.")})"
Regexp.new pattern
end
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index 6a12fd65..e7878962 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -22,8 +22,12 @@ module Jekyll
docs.sort!
end
+ def relative_directory
+ "_#{label}"
+ end
+
def directory
- Jekyll.sanitized_path(site.source, "_#{label}")
+ Jekyll.sanitized_path(site.source, relative_directory)
end
def allowed_document?(path)
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index f137e988..a82408ce 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -3,7 +3,7 @@ module Jekyll
include Comparable
attr_reader :path, :site
- attr_accessor :content, :collection
+ attr_accessor :content, :collection, :output
# Create a new Document.
#
@@ -37,10 +37,66 @@ module Jekyll
File.extname(path)
end
+ def cleaned_relative_path
+ relative_path[0 .. -extname.length - 1].sub(collection.relative_directory, "")
+ end
+
def yaml_file?
%w[.yaml .yml].include?(extname)
end
+ def sass_file?
+ %w[.sass .scss].include?(extname)
+ end
+
+ def render_with_liquid?
+ !(sass_file? || yaml_file?)
+ end
+
+ def url_template
+ "/:collection/:path:output_ext"
+ end
+
+ def url_placeholders
+ {
+ collection: collection.label,
+ path: cleaned_relative_path,
+ output_ext: Jekyll::Renderer.new(site, self).output_ext
+ }
+ end
+
+ def permalink
+ return nil if data.nil? || data['permalink'].nil?
+ data['permalink']
+ end
+
+ def url
+ @url ||= URL.new({
+ :template => url_template,
+ :placeholders => url_placeholders,
+ :permalink => permalink
+ }).to_s
+ end
+
+ def destination(base_directory)
+ path = Jekyll.sanitized_path(base_directory, url)
+ path = File.join(path, "index.html") if url =~ /\/$/
+ path
+ end
+
+ # Write the generated Document file to the destination directory.
+ #
+ # dest - The String path to the destination dir.
+ #
+ # Returns nothing.
+ def write(dest)
+ path = destination(dest)
+ FileUtils.mkdir_p(File.dirname(path))
+ File.open(path, 'wb') do |f|
+ f.write(output)
+ end
+ end
+
# Returns merged option hash for File.read of self.site (if exists)
# and a given param
#
@@ -84,8 +140,10 @@ module Jekyll
# Returns a Hash representing this Document's data.
def to_liquid
data.merge({
- "content" => content,
- "path" => path
+ "content" => content,
+ "path" => path,
+ "relative_path" => relative_path,
+ "url" => url
})
end
diff --git a/lib/jekyll/renderer.rb b/lib/jekyll/renderer.rb
new file mode 100644
index 00000000..568619d7
--- /dev/null
+++ b/lib/jekyll/renderer.rb
@@ -0,0 +1,133 @@
+module Jekyll
+ class Renderer
+
+ attr_reader :document, :site
+
+ def initialize(site, document)
+ @site = site
+ @document = document
+ end
+
+ # Determine which converters to use based on this document's
+ # extension.
+ #
+ # Returns an array of Converter instances.
+ def converters
+ @converters ||= site.converters.select { |c| c.matches(document.extname) }
+ end
+
+ # Determine the extname the outputted file should have
+ #
+ # Returns the output extname including the leading period.
+ def output_ext
+ converters.first.output_ext(document.extname)
+ end
+
+ ######################
+ ## DAT RENDER THO
+ ######################
+
+ def run
+ payload = Utils.deep_merge_hashes({
+ "page" => document.to_liquid
+ }, site.site_payload)
+ info = {
+ filters: [Jekyll::Filters],
+ registers: { :site => site, :page => payload['page'] }
+ }
+
+ # render and transform content (this becomes the final content of the object)
+ payload["highlighter_prefix"] = converters.first.highlighter_prefix
+ payload["highlighter_suffix"] = converters.first.highlighter_suffix
+
+ output = document.content
+
+ if document.render_with_liquid?
+ output = render_liquid(output, payload, info)
+ end
+
+ place_in_layouts(
+ convert(output),
+ payload,
+ info
+ )
+ end
+
+ # Convert the given content using the converters which match this renderer's document.
+ #
+ # content - the raw, unconverted content
+ #
+ # Returns the converted content.
+ def convert(content)
+ output = content.dup
+ converters.each do |converter|
+ begin
+ output = converter.convert(output)
+ rescue => e
+ Jekyll.logger.error "Conversion error:", "#{converter.class} encountered an error converting '#{document.relative_path}'."
+ raise e
+ end
+ end
+ output
+ end
+
+ # Render the given content with the payload and info
+ #
+ # content -
+ # payload -
+ # info -
+ # path - (optional) the path to the file, for use in ex
+ #
+ # Returns the content, rendered by Liquid.
+ def render_liquid(content, payload, info, path = nil)
+ Liquid::Template.parse(content).render!(payload, info)
+ rescue Tags::IncludeTagError => e
+ Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{e.path}, included in #{path || document.relative_path}"
+ raise e
+ rescue Exception => e
+ Jekyll.logger.error "Liquid Exception:", "#{e.message} in #{path || document.relative_path}"
+ raise e
+ end
+
+ # Render layouts and place given content inside.
+ #
+ # content - the content to be placed in the layout
+ #
+ #
+ # Returns the content placed in the Liquid-rendered layouts
+ def place_in_layouts(content, payload, info)
+ output = content.dup
+ layout = site.layouts[document.data["layout"]]
+ used = Set.new([layout])
+
+ while layout
+ payload = Utils.deep_merge_hashes(
+ payload,
+ {
+ "content" => output,
+ "page" => document.to_liquid,
+ "layout" => layout.data
+ }
+ )
+
+ output = render_liquid(
+ layout.content,
+ payload,
+ info,
+ File.join(site.config['layouts'], layout.name)
+ )
+
+ if layout = layouts[layout.data["layout"]]
+ if used.include?(layout)
+ layout = nil # avoid recursive chain
+ else
+ used << layout
+ end
+ end
+ end
+
+ output
+ end
+
+ end
+end
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 204d0bbd..3172dd16 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -14,14 +14,13 @@ module Jekyll
def initialize(config)
self.config = config.clone
- %w[
- safe lsi highlighter baseurl exclude include future unpublished
- show_drafts limit_posts keep_files gems collections].each do |opt|
+ %w[safe lsi highlighter baseurl exclude include future unpublished
+ show_drafts limit_posts keep_files gems].each do |opt|
self.send("#{opt}=", config[opt])
end
- self.source = File.expand_path(config['source'])
- self.dest = File.expand_path(config['destination'])
+ self.source = File.expand_path(config['source'])
+ self.dest = File.expand_path(config['destination'])
self.permalink_style = config['permalink'].to_sym
self.plugin_manager = Jekyll::PluginManager.new(self)
@@ -85,6 +84,14 @@ module Jekyll
end
end
+ def collections
+ @collections ||= if config['collections']
+ Hash[config['collections'].map { |coll| [coll, Jekyll::Collection.new(self, coll)] } ]
+ else
+ Hash.new
+ end
+ end
+
# Read Site data from disk and load it into internal data structures.
#
# Returns nothing.
@@ -187,7 +194,6 @@ module Jekyll
# Returns nothing.
def read_collections
if collections
- self.collections = Hash[collections.map { |coll| [coll, Jekyll::Collection.new(self, coll)] } ]
collections.each { |_, collection| collection.read }
end
end
@@ -207,6 +213,14 @@ module Jekyll
def render
relative_permalinks_deprecation_method
+ if collections
+ collections.each do |label, collection|
+ collection.docs.each do |document|
+ document.output = Jekyll::Renderer.new(self, document).run
+ end
+ end
+ end
+
payload = site_payload
[posts, pages].flatten.each do |page_or_post|
page_or_post.render(layouts, payload)
@@ -369,8 +383,20 @@ module Jekyll
end
end
+ def documents
+ docs = Set.new
+ if collections
+ collections.each do |label, coll|
+ if config['render'].include?(label)
+ docs = docs.merge(coll.docs)
+ end
+ end
+ end
+ docs
+ end
+
def each_site_file
- %w(posts pages static_files).each do |type|
+ %w(posts pages static_files documents).each do |type|
send(type).each do |item|
yield item
end
diff --git a/lib/jekyll/url.rb b/lib/jekyll/url.rb
index 66b4412d..8cd47242 100644
--- a/lib/jekyll/url.rb
+++ b/lib/jekyll/url.rb
@@ -24,9 +24,9 @@ module Jekyll
# template. Instead, the given permalink will be
# used as URL.
def initialize(options)
- @template = options[:template]
+ @template = options[:template]
@placeholders = options[:placeholders] || {}
- @permalink = options[:permalink]
+ @permalink = options[:permalink]
if (@template || @permalink).nil?
raise ArgumentError, "One of :template or :permalink must be supplied."
diff --git a/script/console b/script/console
new file mode 100755
index 00000000..34ad6e8a
--- /dev/null
+++ b/script/console
@@ -0,0 +1,38 @@
+#!/usr/bin/env ruby
+
+require 'pry'
+$LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })
+require 'jekyll'
+
+TEST_DIR = File.expand_path(File.join(File.dirname(__FILE__), *%w{ .. test }))
+
+def fixture_site(overrides = {})
+ Jekyll::Site.new(site_configuration(overrides))
+end
+
+def build_configs(overrides, base_hash = Jekyll::Configuration::DEFAULTS)
+ Jekyll::Utils.deep_merge_hashes(base_hash, overrides)
+end
+
+def site_configuration(overrides = {})
+ build_configs({
+ "source" => source_dir,
+ "destination" => dest_dir
+ }, build_configs(overrides))
+end
+
+def dest_dir(*subdirs)
+ test_dir('dest', *subdirs)
+end
+
+def source_dir(*subdirs)
+ test_dir('source', *subdirs)
+end
+
+def test_dir(*subdirs)
+ File.join(TEST_DIR, *subdirs)
+end
+
+module Jekyll
+ binding.pry
+end
From a15a58413622698ca4971531720d73155c6f84c5 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Wed, 2 Apr 2014 16:47:31 -0400
Subject: [PATCH 10/30] Don't let that render get you down.
---
lib/jekyll/site.rb | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 3172dd16..1c036939 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -92,6 +92,10 @@ module Jekyll
end
end
+ def render
+ config['render'] || Array.new
+ end
+
# Read Site data from disk and load it into internal data structures.
#
# Returns nothing.
@@ -387,7 +391,7 @@ module Jekyll
docs = Set.new
if collections
collections.each do |label, coll|
- if config['render'].include?(label)
+ if render.include?(label)
docs = docs.merge(coll.docs)
end
end
From 37a7236e20b5b671e1e609501afe0ddec64a83f3 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Wed, 2 Apr 2014 17:17:55 -0400
Subject: [PATCH 11/30] Homagah it all renders I think.
---
features/collections.feature | 18 ++++++++++++++++--
lib/jekyll/renderer.rb | 3 ++-
lib/jekyll/site.rb | 22 ++++++++--------------
test/source/_methods/site/generate.md | 3 ++-
test/test_collections.rb | 2 +-
test/test_document.rb | 5 ++++-
6 files changed, 33 insertions(+), 20 deletions(-)
diff --git a/features/collections.feature b/features/collections.feature
index 0abf15e8..4eb526d3 100644
--- a/features/collections.feature
+++ b/features/collections.feature
@@ -7,7 +7,7 @@ Feature: Collections
Given I have an "index.html" page that contains "Collections: {{ site.collections }}"
And I have fixture collections
And I have a configuration file with "collections" set to "['methods']"
- When I debug jekyll
+ When I run jekyll
Then the _site directory should exist
And I should see "Collections: {\"methods\"=>#, #, #, #, #\]>}" in "_site/index.html"
@@ -21,4 +21,18 @@ Feature: Collections
When I run jekyll
Then the _site directory should exist
And I should see "Collections: methods" in "_site/index.html"
- And I should see "Whatever: foo.bar" in "_site/methods/configuration.html"
\ No newline at end of file
+ And I should see "
Whatever: foo.bar
" in "_site/methods/configuration.html"
+
+ Scenario: Rendered document in a layout
+ Given I have an "index.html" page that contains "Collections: {{ site.collections.methods.label }}"
+ And I have a default layout that contains "
Tom Preston-Werner
{{content}}"
+ And I have fixture collections
+ And I have a configuration file with:
+ | key | value |
+ | collections | ['methods'] |
+ | render | ['methods'] |
+ When I run jekyll
+ Then the _site directory should exist
+ And I should see "Collections: methods" in "_site/index.html"
+ And I should see "
Run your generators! default
" in "_site/methods/site/generate.html"
+ And I should see "
Tom Preston-Werner
" in "_site/methods/site/generate.html"
\ No newline at end of file
diff --git a/lib/jekyll/renderer.rb b/lib/jekyll/renderer.rb
index 568619d7..0fb7b7f1 100644
--- a/lib/jekyll/renderer.rb
+++ b/lib/jekyll/renderer.rb
@@ -31,6 +31,7 @@ module Jekyll
payload = Utils.deep_merge_hashes({
"page" => document.to_liquid
}, site.site_payload)
+
info = {
filters: [Jekyll::Filters],
registers: { :site => site, :page => payload['page'] }
@@ -117,7 +118,7 @@ module Jekyll
File.join(site.config['layouts'], layout.name)
)
- if layout = layouts[layout.data["layout"]]
+ if layout = site.layouts[layout.data["layout"]]
if used.include?(layout)
layout = nil # avoid recursive chain
else
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 1c036939..2bdf1898 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -92,7 +92,7 @@ module Jekyll
end
end
- def render
+ def to_render
config['render'] || Array.new
end
@@ -197,9 +197,7 @@ module Jekyll
#
# Returns nothing.
def read_collections
- if collections
- collections.each { |_, collection| collection.read }
- end
+ collections.each { |_, collection| collection.read }
end
# Run each of the Generators.
@@ -217,11 +215,9 @@ module Jekyll
def render
relative_permalinks_deprecation_method
- if collections
- collections.each do |label, collection|
- collection.docs.each do |document|
- document.output = Jekyll::Renderer.new(self, document).run
- end
+ collections.each do |label, collection|
+ collection.docs.each do |document|
+ document.output = Jekyll::Renderer.new(self, document).run
end
end
@@ -389,11 +385,9 @@ module Jekyll
def documents
docs = Set.new
- if collections
- collections.each do |label, coll|
- if render.include?(label)
- docs = docs.merge(coll.docs)
- end
+ collections.each do |label, coll|
+ if to_render.include?(label)
+ docs = docs.merge(coll.docs)
end
end
docs
diff --git a/test/source/_methods/site/generate.md b/test/source/_methods/site/generate.md
index b7eaaf62..1cab376e 100644
--- a/test/source/_methods/site/generate.md
+++ b/test/source/_methods/site/generate.md
@@ -1,5 +1,6 @@
---
title: "Site#generate"
+layout: default
---
-Run your generators!
+Run your generators! {{ page.layout }}
diff --git a/test/test_collections.rb b/test/test_collections.rb
index afe8bd06..06120790 100644
--- a/test/test_collections.rb
+++ b/test/test_collections.rb
@@ -12,7 +12,7 @@ class TestCollections < Test::Unit::TestCase
end
should "not contain any collections" do
- assert_nil @site.collections
+ assert_equal @site.collections, Hash.new
end
end
diff --git a/test/test_document.rb b/test/test_document.rb
index 75bf2a84..794c2117 100644
--- a/test/test_document.rb
+++ b/test/test_document.rb
@@ -2,7 +2,7 @@ require 'helper'
class TestDocument < Test::Unit::TestCase
- context "" do
+ context "a document in a collection" do
setup do
@site = Site.new(Jekyll.configuration({
"collections" => ["methods"],
@@ -42,4 +42,7 @@ class TestDocument < Test::Unit::TestCase
end
+ context " a document part of a rendered collection" do
+ end
+
end
From 77bb678a3db0dc324c228b320bf84efbe5f0da1f Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Wed, 2 Apr 2014 17:54:17 -0400
Subject: [PATCH 12/30] Add some documentation
---
site/_data/docs.yml | 1 +
site/docs/collections.md | 48 ++++++++++++++++++++++++++++++++++++++++
site/docs/datafiles.md | 2 +-
site/docs/variables.md | 2 +-
4 files changed, 51 insertions(+), 2 deletions(-)
create mode 100644 site/docs/collections.md
diff --git a/site/_data/docs.yml b/site/_data/docs.yml
index 1c790636..7d0a83f3 100644
--- a/site/_data/docs.yml
+++ b/site/_data/docs.yml
@@ -14,6 +14,7 @@
- drafts
- pages
- variables
+ - collections
- datafiles
- assets
- migrations
diff --git a/site/docs/collections.md b/site/docs/collections.md
new file mode 100644
index 00000000..0016cf4c
--- /dev/null
+++ b/site/docs/collections.md
@@ -0,0 +1,48 @@
+---
+layout: docs
+title: Collections
+prev_section: variables
+next_section: datafiles
+permalink: /docs/collections/
+---
+
+
+
+Put some things in a folder and add the folder to your config. It's simple...
+
+Why did we write this feature? What is it useful for?
+
+## Using Collections
+
+### Step 1: Tell Jekyll to read in your collection
+
+{% highlight yaml %}
+collections:
+- my_collection
+{% endhighlight %}
+
+### Step 2: Add your content
+
+Create a corresponding folder (e.g. `/_my_collection`) and add documents.
+YAML front-matter is read in as data if it exists, if not, then everything is just
+stuck in the Document's `content` attribute.
+
+### Step 3: Optionally render your collection's documents into independent files
+
+If you'd like your files rendered, add it to your config:
+
+{% highlight yaml %}
+render:
+- my_collection
+{% endhighlight %}
+
+This will produce a file for each document in the collection.
+For example, if you have `_my_collection/some_subdir/some_doc.md`,
+it will be rendered using Liquid and the Markdown converter of your
+choice and written out to `/my_collection/some_subdir/some_doc.html`.
diff --git a/site/docs/datafiles.md b/site/docs/datafiles.md
index 55af6ec4..1dbe4097 100644
--- a/site/docs/datafiles.md
+++ b/site/docs/datafiles.md
@@ -1,7 +1,7 @@
---
layout: docs
title: Data Files
-prev_section: variables
+prev_section: collections
next_section: assets
permalink: /docs/datafiles/
---
diff --git a/site/docs/variables.md b/site/docs/variables.md
index 2c6d3849..d6c7390b 100644
--- a/site/docs/variables.md
+++ b/site/docs/variables.md
@@ -2,7 +2,7 @@
layout: docs
title: Variables
prev_section: pages
-next_section: datafiles
+next_section: collections
permalink: /docs/variables/
---
From aa2fb685d95959ce32ae63e1ea7587fcffdcec51 Mon Sep 17 00:00:00 2001
From: Ben Balter
Date: Thu, 3 Apr 2014 13:57:40 -0400
Subject: [PATCH 13/30] I am the King of Copy
---
site/docs/collections.md | 18 ++++++++++++++----
1 file changed, 14 insertions(+), 4 deletions(-)
diff --git a/site/docs/collections.md b/site/docs/collections.md
index 0016cf4c..f4c2239e 100644
--- a/site/docs/collections.md
+++ b/site/docs/collections.md
@@ -14,14 +14,23 @@ permalink: /docs/collections/
+
+
Collections support is unstable and may change
+
+ This is an experimental feature and that the API may likely change until the feature stabilizes.
+
+
+
Put some things in a folder and add the folder to your config. It's simple...
-Why did we write this feature? What is it useful for?
+Not everything is a post or a page. Maybe you want to document the various methods in your open source project, members of a team, or talks at a conference. Collections allow you to define a new type of document that behave like Pages or Posts do normally, but also have their own unique properties and namespace.
## Using Collections
### Step 1: Tell Jekyll to read in your collection
+Add the following to your site's `_config.yml` file, replacing `my_collection` with the name of your collection:
+
{% highlight yaml %}
collections:
- my_collection
@@ -30,12 +39,13 @@ collections:
### Step 2: Add your content
Create a corresponding folder (e.g. `/_my_collection`) and add documents.
-YAML front-matter is read in as data if it exists, if not, then everything is just
-stuck in the Document's `content` attribute.
+YAML front-matter is read in as data if it exists, if not, then everything is just stuck in the Document's `content` attribute.
+
+Note: the folder must be named identical to the collection you defined in you config.yml file, with the addition of the preceding `_` character.
### Step 3: Optionally render your collection's documents into independent files
-If you'd like your files rendered, add it to your config:
+If you'd like Jekyll to create a public-facing, rendered version of each document in your collection, add your collection name to the `render` config key in your `_config.yml`:
{% highlight yaml %}
render:
From d84cde1f7a277288afed026581cb10b04378d1cd Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Thu, 3 Apr 2014 14:06:00 -0400
Subject: [PATCH 14/30] Add docs for how the collections stuff is exposed via
Liquid.
---
site/docs/collections.md | 101 +++++++++++++++++++++++++++++++++++++++
1 file changed, 101 insertions(+)
diff --git a/site/docs/collections.md b/site/docs/collections.md
index f4c2239e..3bf9d989 100644
--- a/site/docs/collections.md
+++ b/site/docs/collections.md
@@ -56,3 +56,104 @@ This will produce a file for each document in the collection.
For example, if you have `_my_collection/some_subdir/some_doc.md`,
it will be rendered using Liquid and the Markdown converter of your
choice and written out to `/my_collection/some_subdir/some_doc.html`.
+
+## Liquid Attributes
+
+### Collections
+
+Each collection is part of the `site.collections` array in Liquid. Each collection has the following attributes:
+
+
+
+
+
+
Variable
+
Description
+
+
+
+
+
+
label
+
+
+
+ The name of the collection.
+
+
+
+
+
+
docs
+
+
+
+ An array of Documents contained in this collection.
+
+
+
+
+
+
+
+### Documents
+
+In addition to any YAML front-matter provided in the document's corresponding file, each document has the following attributes:
+
+
+
+
+
+
Variable
+
Description
+
+
+
+
+
+
content
+
+
+
+ The content of the document. If no YAML front-matter is provided,
+ this is the entirety of the file contents. If YAML front-matter
+ is used, then this is all the contents of the file after the terminating
+ `---` of the front-matter.
+
+
+
+
+
+
path
+
+
+
+ The full path to the document's source file.
+
+
+
+
+
+
relative_path
+
+
+
+ The path to the document's source file relative to the site source.
+
+
+
+
+
+
url
+
+
+
+ The URL of the rendered collection. The file is only written to the
+ destination when the name of the collection to which it belongs is
+ included in the render key in the site's configuration file.
+
+
+
+
+
+
From be769dcf00874cb03da668a937a8967e1a49379d Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Thu, 3 Apr 2014 14:13:35 -0400
Subject: [PATCH 15/30] SANITIZE THE collection name plz.
---
lib/jekyll/collection.rb | 6 +++++-
test/test_collections.rb | 40 ++++++++++++++++++++++++++--------------
2 files changed, 31 insertions(+), 15 deletions(-)
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index e7878962..b74af552 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -4,7 +4,7 @@ module Jekyll
def initialize(site, label)
@site = site
- @label = label
+ @label = sanitize_label(label)
end
def docs
@@ -38,6 +38,10 @@ module Jekyll
"#"
end
+ def sanitize_label(label)
+ label.gsub(/[^a-z0-9_\-]/i, '')
+ end
+
def to_liquid
{
"label" => label,
diff --git a/test/test_collections.rb b/test/test_collections.rb
index 06120790..d550536a 100644
--- a/test/test_collections.rb
+++ b/test/test_collections.rb
@@ -2,12 +2,28 @@ require 'helper'
class TestCollections < Test::Unit::TestCase
- context "with no collections specified" do
- setup do
- @site = Site.new(Jekyll.configuration({
+ def fixture_site(overrides = {})
+ Jekyll::Site.new(Jekyll.configuration(
+ overrides.merge({
"source" => source_dir,
"destination" => dest_dir
- }))
+ })
+ ))
+ end
+
+ context "a simple collection" do
+ setup do
+ @collection = Jekyll::Collection.new(fixture_site, "../../etc/password")
+ end
+
+ should "sanitize the label name" do
+ assert_equal @collection.label, "etcpassword"
+ end
+ end
+
+ context "with no collections specified" do
+ setup do
+ @site = fixture_site
@site.process
end
@@ -18,11 +34,9 @@ class TestCollections < Test::Unit::TestCase
context "with a collection" do
setup do
- @site = Site.new(Jekyll.configuration({
- "collections" => ["methods"],
- "source" => source_dir,
- "destination" => dest_dir
- }))
+ @site = fixture_site({
+ "collections" => ["methods"]
+ })
@site.process
end
@@ -49,12 +63,10 @@ class TestCollections < Test::Unit::TestCase
context "in safe mode" do
setup do
- @site = Site.new(Jekyll.configuration({
+ @site = fixture_site({
"collections" => ["methods"],
- "safe" => true,
- "source" => source_dir,
- "destination" => dest_dir
- }))
+ "safe" => true
+ })
@site.process
@collection = @site.collections["methods"]
end
From 45120ad3eb2517991c911e45cbf06bdd8482a086 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Fri, 4 Apr 2014 13:13:38 -0400
Subject: [PATCH 16/30] Moar tests for collections.
---
test/test_collections.rb | 32 +++++++++++++++++++++++++++++++-
1 file changed, 31 insertions(+), 1 deletion(-)
diff --git a/test/test_collections.rb b/test/test_collections.rb
index d550536a..82915d19 100644
--- a/test/test_collections.rb
+++ b/test/test_collections.rb
@@ -11,7 +11,7 @@ class TestCollections < Test::Unit::TestCase
))
end
- context "a simple collection" do
+ context "an evil collection" do
setup do
@collection = Jekyll::Collection.new(fixture_site, "../../etc/password")
end
@@ -19,6 +19,36 @@ class TestCollections < Test::Unit::TestCase
should "sanitize the label name" do
assert_equal @collection.label, "etcpassword"
end
+
+ should "have a sanitized relative path name" do
+ assert_equal @collection.relative_directory, "_etcpassword"
+ end
+
+ should "have a sanitized full path" do
+ assert_equal @collection.directory, source_dir("_etcpassword")
+ end
+ end
+
+ context "a simple collection" do
+ setup do
+ @collection = Jekyll::Collection.new(fixture_site, "methods")
+ end
+
+ should "sanitize the label name" do
+ assert_equal @collection.label, "methods"
+ end
+
+ should "contain no docs when initialized" do
+ assert_empty @collection.docs
+ end
+
+ should "know its relative directory" do
+ assert_equal @collection.relative_directory, "_methods"
+ end
+
+ should "know the full path to itself on the filesystem" do
+ assert_equal @collection.directory, source_dir("_methods")
+ end
end
context "with no collections specified" do
From 5ae1c34857c06999279bb0d110d33144abbabab9 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Fri, 4 Apr 2014 13:13:44 -0400
Subject: [PATCH 17/30] Add comments for Collection
---
lib/jekyll/collection.rb | 43 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index b74af552..1aa563e7 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -2,15 +2,28 @@ module Jekyll
class Collection
attr_reader :site, :label
+ # Create a new Collection.
+ #
+ # site - the site to which this collection belongs.
+ # label - the name of the collection
+ #
+ # Returns nothing.
def initialize(site, label)
@site = site
@label = sanitize_label(label)
end
+ # Fetch the Documents in this collection.
+ # Defaults to an empty array if no documents have been read in.
+ #
+ # Returns an array of Jekyll::Document objects.
def docs
@docs ||= []
end
+ # Read the allowed documents into the collection's array of docs.
+ #
+ # Returns the sorted array of docs.
def read
Dir.glob(File.join(directory, "**", "*.*")).each do |file_path|
if allowed_document?(file_path)
@@ -22,26 +35,56 @@ module Jekyll
docs.sort!
end
+ # The directory for this Collection, relative to the site source.
+ #
+ # Returns a String containing the directory name where the collection
+ # is stored on the filesystem.
def relative_directory
"_#{label}"
end
+ # The full path to the directory containing the
+ #
+ # Returns a String containing th directory name where the collection
+ # is stored on the filesystem.
def directory
Jekyll.sanitized_path(site.source, relative_directory)
end
+ # Determine whether the document at a given path is an allowed document.
+ #
+ # path - the path to the document within this collection
+ #
+ # Returns false if the site is in safe mode and the document is a symlink,
+ # true otherwise.
def allowed_document?(path)
!(site.safe && File.symlink?(path))
end
+ # An inspect string.
+ #
+ # Returns the inspecr string
def inspect
"#"
end
+ # Produce a sanitized label name
+ # Label names may not contain anything but alphanumeric characters,
+ # underscores, and hyphens.
+ #
+ # label - the possibly-unsafe label
+ #
+ # Returns a sanitized version of the label.
def sanitize_label(label)
label.gsub(/[^a-z0-9_\-]/i, '')
end
+ # Produce a representation of this Collection for use in Liquid.
+ # Exposes two attributes:
+ # - label
+ # - docs
+ #
+ # Returns a representation of this collection for use in Liquid.
def to_liquid
{
"label" => label,
From a307aff858642d46100af5c97ff26ba400477f8b Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Fri, 4 Apr 2014 13:22:22 -0400
Subject: [PATCH 18/30] Do not render any asset files with Liquid.
---
lib/jekyll/document.rb | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index a82408ce..69da984a 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -45,12 +45,12 @@ module Jekyll
%w[.yaml .yml].include?(extname)
end
- def sass_file?
- %w[.sass .scss].include?(extname)
+ def asset_file?
+ %w[.sass .scss .coffee].include?(extname)
end
def render_with_liquid?
- !(sass_file? || yaml_file?)
+ !(asset_file? || yaml_file?)
end
def url_template
From 00ca09a2ea9ce38bb68d9e896dd53b29964149f1 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Fri, 4 Apr 2014 14:38:19 -0400
Subject: [PATCH 19/30] Add comments for Document
---
lib/jekyll/document.rb | 65 +++++++++++++++++++++++++++++++++++++++++-
1 file changed, 64 insertions(+), 1 deletion(-)
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index 69da984a..97a2360a 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -25,38 +25,80 @@ module Jekyll
@data ||= Hash.new
end
+ # The path to the document, relative to the site source.
+ #
+ # Returns a String path which represents the relative path
+ # from the site source to this document
def relative_path
Pathname.new(path).relative_path_from(Pathname.new(site.source)).to_s
end
+ # The base filename of the document.
+ #
+ # suffix - (optional) the suffix to be removed from the end of the filename
+ #
+ # Returns the base filename of the document.
def basename(suffix = "")
File.basename(path, suffix)
end
+ # The extension name of the document.
+ #
+ # Returns the extension name of the document.
def extname
File.extname(path)
end
+ # Produces a "cleaned" relative path.
+ # The "cleaned" relative path is the relative path without the extname
+ # and with the collection's directory removed as well.
+ # This method is useful when building the URL of the document.
+ #
+ # Examples:
+ # When relative_path is "_methods/site/generate.md":
+ # cleaned_relative_path
+ # # => "/site/generate"
+ #
+ # Returns the cleaned relative path of the document.
def cleaned_relative_path
relative_path[0 .. -extname.length - 1].sub(collection.relative_directory, "")
end
+ # Determine whether the document is a YAML file.
+ #
+ # Returns true if the extname is either .yml or .yaml, false otherwise.
def yaml_file?
%w[.yaml .yml].include?(extname)
end
+ # Determine whether the document is an asset file.
+ # Asset files include CoffeeScript files and Sass/SCSS files.
+ #
+ # Returns true if the extname belongs to the set of extensions
+ # that asset files use.
def asset_file?
%w[.sass .scss .coffee].include?(extname)
end
+ # Determine whether the file should be rendered with Liquid.
+ #
+ # Returns false if the document is either an asset file or a yaml file,
+ # true otherwise.
def render_with_liquid?
!(asset_file? || yaml_file?)
end
+ # The URL template where the document would be accessible.
+ #
+ # Returns the URL template for the document.
def url_template
"/:collection/:path:output_ext"
end
+ # Construct a Hash of key-value pairs which contain a mapping between
+ # a key in the URL template and the corresponding value for this document.
+ #
+ # Returns the Hash of key-value pairs for replacement in the URL.
def url_placeholders
{
collection: collection.label,
@@ -65,11 +107,18 @@ module Jekyll
}
end
+ # The permalink for this Document.
+ # Permalink is set via the data Hash.
+ #
+ # Returns the permalink or nil if no permalink was set in the data.
def permalink
return nil if data.nil? || data['permalink'].nil?
data['permalink']
end
+ # The computed URL for the document. See `Jekyll::URL#to_s` for more details.
+ #
+ # Returns the computed URL for the document.
def url
@url ||= URL.new({
:template => url_template,
@@ -78,6 +127,11 @@ module Jekyll
}).to_s
end
+ # The full path to the output file.
+ #
+ # base_directory - the base path of the output directory
+ #
+ # Returns the full path to the output file of this document.
def destination(base_directory)
path = Jekyll.sanitized_path(base_directory, url)
path = File.join(path, "index.html") if url =~ /\/$/
@@ -102,7 +156,7 @@ module Jekyll
#
# opts - override options
#
- # Return
+ # Return the file read options hash.
def merged_file_read_opts(opts)
(site ? site.file_read_opts : {}).merge(opts)
end
@@ -147,10 +201,19 @@ module Jekyll
})
end
+ # The inspect string for this document.
+ # Includes the relative path and the collection label.
+ #
+ # Returns the inspect string for this document.
def inspect
"#"
end
+ # Compare this document against another document.
+ # Comparison is a comparison between the 2 paths of the documents.
+ #
+ # Returns -1, 0, +1 or nil depending on whether this doc's path is less than,
+ # equal or greater than the other doc's path. See String#<=> for more details.
def <=>(anotherDocument)
path <=> anotherDocument.path
end
From f0e68d7d86ee7d3336bfb35cf995e938a2c848b1 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Sat, 5 Apr 2014 17:28:04 -0400
Subject: [PATCH 20/30] Expose collections as site. in Liquid
as array of docs.
---
features/collections.feature | 8 ++++----
lib/jekyll/collection.rb | 5 +----
lib/jekyll/document.rb | 7 +++++++
lib/jekyll/site.rb | 12 +++++++-----
4 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/features/collections.feature b/features/collections.feature
index 4eb526d3..451c84d8 100644
--- a/features/collections.feature
+++ b/features/collections.feature
@@ -4,15 +4,15 @@ Feature: Collections
And render them
Scenario: Unrendered collection
- Given I have an "index.html" page that contains "Collections: {{ site.collections }}"
+ Given I have an "index.html" page that contains "Collections: {{ site.methods }}"
And I have fixture collections
And I have a configuration file with "collections" set to "['methods']"
When I run jekyll
Then the _site directory should exist
- And I should see "Collections: {\"methods\"=>#, #, #, #, #\]>}" in "_site/index.html"
+ And I should see "Collections: Use `{{ page.title }}` to build a full configuration for use w/Jekyll.\n\nWhatever: {{ page.whatever }}\n`{{ page.title }}` is used to make sure your path is in your source.\nRun your generators! {{ page.layout }}\nCreate dat site.\nRun your generators! {{ page.layout }}" in "_site/index.html"
Scenario: Rendered collection
- Given I have an "index.html" page that contains "Collections: {{ site.collections.methods.label }}"
+ Given I have an "index.html" page that contains "Collections: {{ site.collections }}"
And I have fixture collections
And I have a configuration file with:
| key | value |
@@ -24,7 +24,7 @@ Feature: Collections
And I should see "
Whatever: foo.bar
" in "_site/methods/configuration.html"
Scenario: Rendered document in a layout
- Given I have an "index.html" page that contains "Collections: {{ site.collections.methods.label }}"
+ Given I have an "index.html" page that contains "Collections: {{ site.collections }}"
And I have a default layout that contains "
Tom Preston-Werner
{{content}}"
And I have fixture collections
And I have a configuration file with:
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index 1aa563e7..6f9be70e 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -86,10 +86,7 @@ module Jekyll
#
# Returns a representation of this collection for use in Liquid.
def to_liquid
- {
- "label" => label,
- "docs" => docs
- }
+ docs
end
end
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index 97a2360a..c35b9101 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -209,6 +209,13 @@ module Jekyll
"#"
end
+ # The string representation for this document.
+ #
+ # Returns the content of the document
+ def to_s
+ output || content
+ end
+
# Compare this document against another document.
# Comparison is a comparison between the 2 paths of the documents.
#
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 2bdf1898..7b84b8b9 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -215,8 +215,8 @@ module Jekyll
def render
relative_permalinks_deprecation_method
- collections.each do |label, collection|
- collection.docs.each do |document|
+ to_render.each do |label|
+ collections[label].docs.each do |document|
document.output = Jekyll::Renderer.new(self, document).run
end
end
@@ -296,7 +296,8 @@ module Jekyll
# See Site#post_attr_hash for type info.
def site_payload
{"jekyll" => { "version" => Jekyll::VERSION },
- "site" => config.merge({
+ "site" => Utils.deep_merge_hashes(config,
+ Utils.deep_merge_hashes(collections, {
"time" => time,
"posts" => posts.sort { |a, b| b <=> a },
"pages" => pages,
@@ -304,8 +305,9 @@ module Jekyll
"html_pages" => pages.reject { |page| !page.html? },
"categories" => post_attr_hash('categories'),
"tags" => post_attr_hash('tags'),
- "data" => site_data,
- "collections" => collections})}
+ "data" => site_data
+ }))
+ }
end
# Filter out any files/directories that are hidden or backup files (start
From 323ea0ef73584eb26fd23d420d535f6e3da0917d Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Sun, 6 Apr 2014 12:58:56 -0400
Subject: [PATCH 21/30] EntryFilter#special? should also check the base name of
the entry
---
lib/jekyll/entry_filter.rb | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/lib/jekyll/entry_filter.rb b/lib/jekyll/entry_filter.rb
index 11db9e11..0b00218b 100644
--- a/lib/jekyll/entry_filter.rb
+++ b/lib/jekyll/entry_filter.rb
@@ -1,5 +1,7 @@
module Jekyll
class EntryFilter
+ SPECIAL_LEADING_CHARACTERS = ['.', '_', '#'].freeze
+
attr_reader :site
def initialize(site, base_directory = nil)
@@ -35,7 +37,8 @@ module Jekyll
end
def special?(entry)
- ['.', '_', '#'].include?(entry[0..0])
+ SPECIAL_LEADING_CHARACTERS.include?(entry[0..0]) ||
+ SPECIAL_LEADING_CHARACTERS.include?(File.basename(entry)[0..0])
end
def backup?(entry)
From aa502348e5271c99695833e5f141e4d9f52f4063 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Sun, 6 Apr 2014 12:59:31 -0400
Subject: [PATCH 22/30] Filter entries in the collection per EntryFilter#filter
---
lib/jekyll/collection.rb | 42 +++++++++++++------
lib/jekyll/document.rb | 4 +-
test/source/_methods/_do_not_read_me.md | 5 +++
.../_methods/site/_dont_include_me_either.md | 5 +++
test/test_collections.rb | 18 +++++++-
5 files changed, 57 insertions(+), 17 deletions(-)
create mode 100644 test/source/_methods/_do_not_read_me.md
create mode 100644 test/source/_methods/site/_dont_include_me_either.md
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index 6f9be70e..13455e2a 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -25,16 +25,34 @@ module Jekyll
#
# Returns the sorted array of docs.
def read
- Dir.glob(File.join(directory, "**", "*.*")).each do |file_path|
- if allowed_document?(file_path)
- doc = Jekyll::Document.new(file_path, { site: site, collection: self })
- doc.read
- docs << doc
- end
+ filtered_entries.each do |file_path|
+ doc = Jekyll::Document.new(Jekyll.sanitized_path(directory, file_path), { site: site, collection: self })
+ doc.read
+ docs << doc
end
docs.sort!
end
+ # All the entries in this collection.
+ #
+ # Returns an Array of file paths to the documents in this collection
+ # relative to the collection's directory
+ def entries
+ Dir.glob(File.join(directory, "**", "*.*")).map do |entry|
+ entry[File.join(directory, "")] = ''; entry
+ end
+ end
+
+ # Filtered version of the entries in this collection.
+ # See `Jekyll::EntryFilter#filter` for more information.
+ #
+ # Returns a list of filtered entry paths.
+ def filtered_entries
+ Dir.chdir(directory) do
+ entry_filter.filter(entries)
+ end
+ end
+
# The directory for this Collection, relative to the site source.
#
# Returns a String containing the directory name where the collection
@@ -51,14 +69,12 @@ module Jekyll
Jekyll.sanitized_path(site.source, relative_directory)
end
- # Determine whether the document at a given path is an allowed document.
+ # The entry filter for this collection.
+ # Creates an instance of Jekyll::EntryFilter.
#
- # path - the path to the document within this collection
- #
- # Returns false if the site is in safe mode and the document is a symlink,
- # true otherwise.
- def allowed_document?(path)
- !(site.safe && File.symlink?(path))
+ # Returns the instance of Jekyll::EntryFilter for this collection.
+ def entry_filter
+ @entry_filter ||= Jekyll::EntryFilter.new(site, relative_directory)
end
# An inspect string.
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index c35b9101..ce4d6bff 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -193,12 +193,12 @@ module Jekyll
#
# Returns a Hash representing this Document's data.
def to_liquid
- data.merge({
+ Utils.deep_merge_hashes data, {
"content" => content,
"path" => path,
"relative_path" => relative_path,
"url" => url
- })
+ }
end
# The inspect string for this document.
diff --git a/test/source/_methods/_do_not_read_me.md b/test/source/_methods/_do_not_read_me.md
new file mode 100644
index 00000000..1b5ad07d
--- /dev/null
+++ b/test/source/_methods/_do_not_read_me.md
@@ -0,0 +1,5 @@
+---
+title: The unreadable wonder
+---
+
+Don't read me, you fool! FILTER ME
diff --git a/test/source/_methods/site/_dont_include_me_either.md b/test/source/_methods/site/_dont_include_me_either.md
new file mode 100644
index 00000000..66079613
--- /dev/null
+++ b/test/source/_methods/site/_dont_include_me_either.md
@@ -0,0 +1,5 @@
+---
+title: Don't Include Me Either
+---
+
+Don't include me either. FILTER ME PLZ
diff --git a/test/test_collections.rb b/test/test_collections.rb
index 82915d19..18ab22d6 100644
--- a/test/test_collections.rb
+++ b/test/test_collections.rb
@@ -68,6 +68,7 @@ class TestCollections < Test::Unit::TestCase
"collections" => ["methods"]
})
@site.process
+ @collection = @site.collections["methods"]
end
should "create a Hash on Site with the label mapped to the instance of the Collection" do
@@ -89,6 +90,19 @@ class TestCollections < Test::Unit::TestCase
], doc.relative_path
end
end
+
+ should "not include files which start with an underscore in the base collection directory" do
+ assert_not_include @collection.filtered_entries, "_do_not_read_me.md"
+ end
+
+ should "not include files which start with an underscore in a subdirectory" do
+ assert_not_include @collection.filtered_entries, "site/_dont_include_me_either.md"
+ end
+
+ should "not include the underscored files in the list of docs" do
+ assert_not_include @collection.docs.map(&:relative_path), "_methods/_do_not_read_me.md"
+ assert_not_include @collection.docs.map(&:relative_path), "_methods/site/_dont_include_me_either.md"
+ end
end
context "in safe mode" do
@@ -102,11 +116,11 @@ class TestCollections < Test::Unit::TestCase
end
should "not allow symlinks" do
- assert !@collection.allowed_document?(File.join(@collection.directory, "um_hi.md"))
+ assert_not_include @collection.filtered_entries, "um_hi.md"
end
should "not include the symlinked file in the list of docs" do
- assert_not_include %w[_methods/um_hi.md], @collection.docs.map(&:relative_path)
+ assert_not_include @collection.docs.map(&:relative_path), "_methods/um_hi.md"
end
end
From 62551b5ff962690dde8587c5b919a94e541f098c Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Sun, 6 Apr 2014 13:25:05 -0400
Subject: [PATCH 23/30] Include data in the array of collections
---
lib/jekyll/site.rb | 25 +++++++++++++++++--------
1 file changed, 17 insertions(+), 8 deletions(-)
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 7b84b8b9..a2cf2b72 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -84,6 +84,11 @@ module Jekyll
end
end
+ # The list of collections and their corresponding Jekyll::Collection instances.
+ # If config['collections'] is set, a new instance is created for each item in the collection.
+ # If config['collections'] is not set, a new hash is returned.
+ #
+ # Returns a Hash containing collection name-to-instance pairs.
def collections
@collections ||= if config['collections']
Hash[config['collections'].map { |coll| [coll, Jekyll::Collection.new(self, coll)] } ]
@@ -92,8 +97,11 @@ module Jekyll
end
end
+ # The list of collections to render.
+ #
+ # The array of collection labels to render.
def to_render
- config['render'] || Array.new
+ @to_render ||= (config['render'] || Array.new)
end
# Read Site data from disk and load it into internal data structures.
@@ -180,14 +188,15 @@ module Jekyll
#
# Returns nothing
def read_data(dir)
- base = File.join(source, dir)
- return unless File.directory?(base) && (!safe || !File.symlink?(base))
+ unless dir.eql?("_data")
+ Jekyll.logger.warn "Error:", "Data source directories other than '_data' have been removed.\n" +
+ "Please move your YAML files to `_data` and remove the `data_source` key from your `_config.yml`."
+ end
- entries = Dir.chdir(base) { Dir['*.{yaml,yml}'] }
- entries.delete_if { |e| File.directory?(File.join(base, e)) }
- data_collection = Jekyll::Collection.new(self, "data")
- data_collection.read
- data_collection.docs.each do |doc|
+ collections['data'] = Jekyll::Collection.new(self, "data")
+ collections['data'].read
+
+ collections['data'].docs.each do |doc|
key = sanitize_filename(doc.basename(".*"))
self.data[key] = doc.data
end
From 2f99e1d5c10bfdbf3129e67f49256596b727b441 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Sun, 6 Apr 2014 13:33:59 -0400
Subject: [PATCH 24/30] Fix test for non-collectionized sites still containing
data coll
---
test/test_collections.rb | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/test/test_collections.rb b/test/test_collections.rb
index 18ab22d6..8b4c2d23 100644
--- a/test/test_collections.rb
+++ b/test/test_collections.rb
@@ -57,8 +57,10 @@ class TestCollections < Test::Unit::TestCase
@site.process
end
- should "not contain any collections" do
- assert_equal @site.collections, Hash.new
+ should "not contain any collections other than the default ones" do
+ collections = @site.collections.dup
+ assert collections.delete("data").is_a?(Jekyll::Collection)
+ assert_equal Hash.new, collections
end
end
From af61451f877daaa3cb073367094d7b05dc6ca78e Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Sun, 6 Apr 2014 13:34:41 -0400
Subject: [PATCH 25/30] Use #error instead of #warn when telling the user not
to use a custom data source
---
lib/jekyll/site.rb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index a2cf2b72..1f127304 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -188,8 +188,8 @@ module Jekyll
#
# Returns nothing
def read_data(dir)
- unless dir.eql?("_data")
- Jekyll.logger.warn "Error:", "Data source directories other than '_data' have been removed.\n" +
+ unless dir.to_s.eql?("_data")
+ Jekyll.logger.error "Error:", "Data source directories other than '_data' have been removed.\n" +
"Please move your YAML files to `_data` and remove the `data_source` key from your `_config.yml`."
end
From ad7efb23e6a00f3a353d8db3b1e4ad56524a54aa Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Sun, 6 Apr 2014 13:43:05 -0400
Subject: [PATCH 26/30] Code/docs cleanup, props @baweaver
---
lib/jekyll/collection.rb | 2 +-
lib/jekyll/document.rb | 13 ++++++-------
lib/jekyll/renderer.rb | 2 +-
3 files changed, 8 insertions(+), 9 deletions(-)
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index 13455e2a..4bee732a 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -79,7 +79,7 @@ module Jekyll
# An inspect string.
#
- # Returns the inspecr string
+ # Returns the inspect string
def inspect
"#"
end
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index ce4d6bff..ed200511 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -7,7 +7,7 @@ module Jekyll
# Create a new Document.
#
- # shit - the Jekyll::Site instance to which this Document belongs
+ # site - the Jekyll::Site instance to which this Document belongs
# path - the path to the file
#
# Returns nothing.
@@ -112,8 +112,7 @@ module Jekyll
#
# Returns the permalink or nil if no permalink was set in the data.
def permalink
- return nil if data.nil? || data['permalink'].nil?
- data['permalink']
+ data && data['permalink']
end
# The computed URL for the document. See `Jekyll::URL#to_s` for more details.
@@ -121,9 +120,9 @@ module Jekyll
# Returns the computed URL for the document.
def url
@url ||= URL.new({
- :template => url_template,
- :placeholders => url_placeholders,
- :permalink => permalink
+ template: url_template,
+ placeholders: url_placeholders,
+ permalink: permalink
}).to_s
end
@@ -158,7 +157,7 @@ module Jekyll
#
# Return the file read options hash.
def merged_file_read_opts(opts)
- (site ? site.file_read_opts : {}).merge(opts)
+ site ? site.file_read_opts.merge(opts) : opts
end
# Whether the file is published or not, as indicated in YAML front-matter
diff --git a/lib/jekyll/renderer.rb b/lib/jekyll/renderer.rb
index 0fb7b7f1..f9df731e 100644
--- a/lib/jekyll/renderer.rb
+++ b/lib/jekyll/renderer.rb
@@ -32,7 +32,7 @@ module Jekyll
"page" => document.to_liquid
}, site.site_payload)
- info = {
+ info = {
filters: [Jekyll::Filters],
registers: { :site => site, :page => payload['page'] }
}
From a1af95c34e0e62ffb07bba1e35ce3d56be357c70 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Mon, 14 Apr 2014 17:14:12 -0400
Subject: [PATCH 27/30] Clean up some code per @baweaver's suggestions.
---
lib/jekyll/renderer.rb | 6 ++----
lib/jekyll/site.rb | 8 ++++----
2 files changed, 6 insertions(+), 8 deletions(-)
diff --git a/lib/jekyll/renderer.rb b/lib/jekyll/renderer.rb
index f9df731e..e3d233df 100644
--- a/lib/jekyll/renderer.rb
+++ b/lib/jekyll/renderer.rb
@@ -60,16 +60,14 @@ module Jekyll
#
# Returns the converted content.
def convert(content)
- output = content.dup
- converters.each do |converter|
+ converters.reduce(content) do |output, converter|
begin
- output = converter.convert(output)
+ converter.convert output
rescue => e
Jekyll.logger.error "Conversion error:", "#{converter.class} encountered an error converting '#{document.relative_path}'."
raise e
end
end
- output
end
# Render the given content with the payload and info
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 1f127304..92e5e359 100644
--- a/lib/jekyll/site.rb
+++ b/lib/jekyll/site.rb
@@ -395,13 +395,13 @@ module Jekyll
end
def documents
- docs = Set.new
- collections.each do |label, coll|
+ collections.reduce(Set.new) do |docs, (label, coll)|
if to_render.include?(label)
- docs = docs.merge(coll.docs)
+ docs.merge(coll.docs)
+ else
+ docs
end
end
- docs
end
def each_site_file
From ee29bf39393990a1ffd8ec06d1b16b8587a0ce30 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Mon, 14 Apr 2014 17:18:59 -0400
Subject: [PATCH 28/30] Update docs around collections.
---
site/docs/collections.md | 35 +----------------------------------
1 file changed, 1 insertion(+), 34 deletions(-)
diff --git a/site/docs/collections.md b/site/docs/collections.md
index 3bf9d989..23ac190a 100644
--- a/site/docs/collections.md
+++ b/site/docs/collections.md
@@ -61,40 +61,7 @@ choice and written out to `/my_collection/some_subdir/some_doc.html`.
### Collections
-Each collection is part of the `site.collections` array in Liquid. Each collection has the following attributes:
-
-
-
-
-
-
Variable
-
Description
-
-
-
-
-
-
label
-
-
-
- The name of the collection.
-
-
-
-
-
-
docs
-
-
-
- An array of Documents contained in this collection.
-
-
-
-
-
-
+Each collection is accessible via the `site` Liquid variable. For example, if you want to access the `albums` collection found in `_albums`, you'd use `site.albums`. Each collection is itself an array of documents (e.g. `site.albums` is an array of documents, much like `site.pages` and `site.posts`). See below for how to access attributes of those documents.
### Documents
From 696aea211ac344c81382258c7dedcc4a5b15014b Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Mon, 14 Apr 2014 22:56:23 -0400
Subject: [PATCH 29/30] Don't gather any entries if the collection directory
doesn't exist
---
lib/jekyll/collection.rb | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index 4bee732a..d2d1ae93 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -38,6 +38,7 @@ module Jekyll
# Returns an Array of file paths to the documents in this collection
# relative to the collection's directory
def entries
+ return Array.new unless exists?
Dir.glob(File.join(directory, "**", "*.*")).map do |entry|
entry[File.join(directory, "")] = ''; entry
end
@@ -69,6 +70,16 @@ module Jekyll
Jekyll.sanitized_path(site.source, relative_directory)
end
+ # Checks whether the directory "exists" for this collection.
+ # The directory must exist on the filesystem and must not be a symlink
+ # if in safe mode.
+ #
+ # Returns false if the directory doesn't exist or if it's a symlink
+ # and we're in safe mode.
+ def exists?
+ File.directory?(directory) && !(File.symlink?(directory) && site.safe)
+ end
+
# The entry filter for this collection.
# Creates an instance of Jekyll::EntryFilter.
#
From 5a6f1d42a966dce1cb428c7619148264125874d8 Mon Sep 17 00:00:00 2001
From: Parker Moore
Date: Mon, 14 Apr 2014 23:03:19 -0400
Subject: [PATCH 30/30] Fix #filtered_entries so it returns a new Array if the
directory doesn't exist
---
features/support/env.rb | 2 +-
lib/jekyll/collection.rb | 1 +
2 files changed, 2 insertions(+), 1 deletion(-)
diff --git a/features/support/env.rb b/features/support/env.rb
index 593e1304..5f6752aa 100644
--- a/features/support/env.rb
+++ b/features/support/env.rb
@@ -24,7 +24,7 @@ def jekyll_run_output
end
def run_jekyll(args, output_file)
- command = "#{JEKYLL_PATH} #{args} > #{jekyll_output_file} 2>&1"
+ command = "#{JEKYLL_PATH} #{args} --trace > #{jekyll_output_file} 2>&1"
system command
end
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index d2d1ae93..ad0e5fa5 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -49,6 +49,7 @@ module Jekyll
#
# Returns a list of filtered entry paths.
def filtered_entries
+ return Array.new unless exists?
Dir.chdir(directory) do
entry_filter.filter(entries)
end