diff --git a/features/collections.feature b/features/collections.feature
index 6ff41080..32e9161c 100644
--- a/features/collections.feature
+++ b/features/collections.feature
@@ -9,30 +9,64 @@ Feature: Collections
And I have a configuration file with "collections" set to "['methods']"
When I run jekyll build
Then the _site directory should exist
- 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"
+ And I should see "Collections:
Use Jekyll.configuration
to build a full configuration for use w/Jekyll.
\n\nWhatever: foo.bar
\nJekyll.sanitized_path
is used to make sure your path is in your source.
\nRun your generators! default
\nCreate dat site.
\nRun your generators! default
" in "_site/index.html"
+ And the "_site/methods/configuration.html" file should not exist
Scenario: Rendered collection
Given I have an "index.html" page that contains "Collections: {{ site.collections }}"
+ And I have an "collection_metadata.html" page that contains "Methods metadata: {{ site.collections.methods.foo }} {{ site.collections.methods }}"
And I have fixture collections
- And I have a configuration file with:
- | key | value |
- | collections | ['methods'] |
- | render | ['methods'] |
+ And I have a "_config.yml" file with content:
+ """
+ collections:
+ methods:
+ output: true
+ foo: bar
+ """
When I run jekyll build
Then the _site directory should exist
- And I should see "Collections: methods" in "_site/index.html"
+ And I should see "Collections: {\"methods" in "_site/index.html"
+ And I should see "Methods metadata: bar" in "_site/collection_metadata.html"
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 }}"
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'] |
+ And I have a "_config.yml" file with content:
+ """
+ collections:
+ methods:
+ output: true
+ foo: bar
+ """
When I run jekyll build
Then the _site directory should exist
- And I should see "Collections: methods" in "_site/index.html"
+ 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
+ And I should see "Tom Preston-Werner
" in "_site/methods/site/generate.html"
+
+ Scenario: Collections specified as an array
+ Given I have an "index.html" page that contains "Collections: {% for method in site.methods %}{{ method.relative_path }} {% endfor %}"
+ And I have fixture collections
+ And I have a "_config.yml" file with content:
+ """
+ collections:
+ - methods
+ """
+ When I run jekyll build
+ Then the _site directory should exist
+ And I should see "Collections: _methods/configuration.md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
+
+ Scenario: Collections specified as an hash
+ Given I have an "index.html" page that contains "Collections: {% for method in site.methods %}{{ method.relative_path }} {% endfor %}"
+ And I have fixture collections
+ And I have a "_config.yml" file with content:
+ """
+ collections:
+ methods:
+ baz: bin
+ """
+ When I run jekyll build
+ Then the _site directory should exist
+ And I should see "Collections: _methods/configuration.md _methods/sanitized_path.md _methods/site/generate.md _methods/site/initialize.md _methods/um_hi.md" in "_site/index.html"
diff --git a/lib/jekyll/collection.rb b/lib/jekyll/collection.rb
index ad0e5fa5..e5aead12 100644
--- a/lib/jekyll/collection.rb
+++ b/lib/jekyll/collection.rb
@@ -1,6 +1,6 @@
module Jekyll
class Collection
- attr_reader :site, :label
+ attr_reader :site, :label, :metadata
# Create a new Collection.
#
@@ -9,8 +9,9 @@ module Jekyll
#
# Returns nothing.
def initialize(site, label)
- @site = site
- @label = sanitize_label(label)
+ @site = site
+ @label = sanitize_label(label)
+ @metadata = extract_metadata
end
# Fetch the Documents in this collection.
@@ -114,7 +115,32 @@ module Jekyll
#
# Returns a representation of this collection for use in Liquid.
def to_liquid
- docs
+ metadata.merge({
+ "label" => label,
+ "docs" => docs,
+ "directory" => directory,
+ "written" => write?,
+ "relative_directory" => relative_directory
+ })
+ end
+
+ # Whether the collection's documents ought to be written as individual
+ # files in the output.
+ #
+ # Returns true if the 'write' metadata is true, false otherwise.
+ def write?
+ !!metadata['output']
+ end
+
+ # Extract options for this collection from the site configuration.
+ #
+ # Returns the metadata for this collection
+ def extract_metadata
+ if site.config['collections'].is_a?(Hash)
+ site.config['collections'][label] || Hash.new
+ else
+ {}
+ end
end
end
diff --git a/lib/jekyll/document.rb b/lib/jekyll/document.rb
index ed200511..0a75fe96 100644
--- a/lib/jekyll/document.rb
+++ b/lib/jekyll/document.rb
@@ -112,7 +112,7 @@ module Jekyll
#
# Returns the permalink or nil if no permalink was set in the data.
def permalink
- data && data['permalink']
+ data && data.is_a?(Hash) && data['permalink']
end
# The computed URL for the document. See `Jekyll::URL#to_s` for more details.
@@ -192,12 +192,16 @@ module Jekyll
#
# Returns a Hash representing this Document's data.
def to_liquid
- Utils.deep_merge_hashes data, {
- "content" => content,
- "path" => path,
- "relative_path" => relative_path,
- "url" => url
- }
+ if data.is_a?(Hash)
+ Utils.deep_merge_hashes data, {
+ "content" => content,
+ "path" => path,
+ "relative_path" => relative_path,
+ "url" => url
+ }
+ else
+ data
+ end
end
# The inspect string for this document.
diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb
index 51d2be5a..ccc6d2c6 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, :collections
+ :plugin_manager
attr_accessor :converters, :generators
@@ -55,6 +55,7 @@ module Jekyll
self.pages = []
self.static_files = []
self.data = {}
+ @collections = nil
if limit_posts < 0
raise ArgumentError, "limit_posts must be a non-negative number"
@@ -90,18 +91,24 @@ module Jekyll
#
# 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)] } ]
- else
- Hash.new
- end
+ @collections ||= Hash[collection_names.map { |coll| [coll, Jekyll::Collection.new(self, coll)] } ]
end
- # The list of collections to render.
+ # The list of collection names.
#
- # The array of collection labels to render.
- def to_render
- @to_render ||= (config['render'] || Array.new)
+ # Returns an array of collection names from the configuration,
+ # or an empty array if the `collections` key is not set.
+ def collection_names
+ case config['collections']
+ when Hash
+ config['collections'].keys
+ when Array
+ config['collections']
+ when nil
+ []
+ else
+ raise ArgumentError, "Your `collections` key must be a hash or an array."
+ end
end
# Read Site data from disk and load it into internal data structures.
@@ -188,17 +195,18 @@ module Jekyll
#
# Returns nothing
def read_data(dir)
- 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
+ base = File.join(source, dir)
+ return unless File.directory?(base) && (!safe || !File.symlink?(base))
- collections['data'] = Jekyll::Collection.new(self, "data")
- collections['data'].read
+ entries = Dir.chdir(base) { Dir['*.{yaml,yml}'] }
+ entries.delete_if { |e| File.directory?(File.join(base, e)) }
- collections['data'].docs.each do |doc|
- key = sanitize_filename(doc.basename(".*"))
- self.data[key] = doc.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] = SafeYAML.load_file(path)
end
end
@@ -206,7 +214,9 @@ module Jekyll
#
# Returns nothing.
def read_collections
- collections.each { |_, collection| collection.read }
+ collections.each do |_, collection|
+ collection.read unless collection.label.eql?("data")
+ end
end
# Run each of the Generators.
@@ -224,8 +234,8 @@ module Jekyll
def render
relative_permalinks_deprecation_method
- to_render.each do |label|
- collections[label].docs.each do |document|
+ collections.each do |label, collection|
+ collection.docs.each do |document|
document.output = Jekyll::Renderer.new(self, document).run
end
end
@@ -306,7 +316,7 @@ module Jekyll
def site_payload
{"jekyll" => { "version" => Jekyll::VERSION },
"site" => Utils.deep_merge_hashes(config,
- Utils.deep_merge_hashes(collections, {
+ Utils.deep_merge_hashes(Hash[collections.map{|label, coll| [label, coll.docs]}], {
"time" => time,
"posts" => posts.sort { |a, b| b <=> a },
"pages" => pages,
@@ -314,6 +324,7 @@ module Jekyll
"html_pages" => pages.reject { |page| !page.html? },
"categories" => post_attr_hash('categories'),
"tags" => post_attr_hash('tags'),
+ "collections" => collections,
"data" => site_data
}))
}
@@ -395,9 +406,9 @@ module Jekyll
end
def documents
- collections.reduce(Set.new) do |docs, (label, coll)|
- if to_render.include?(label)
- docs.merge(coll.docs)
+ collections.reduce(Set.new) do |docs, (_, collection)|
+ if collection.write?
+ docs.merge(collection.docs)
else
docs
end
diff --git a/script/test b/script/test
new file mode 100755
index 00000000..78e6866a
--- /dev/null
+++ b/script/test
@@ -0,0 +1,11 @@
+#! /bin/bash
+
+set -x
+
+if [ -z "$1" ]; then
+ TEST_FILES="test/test*.rb"
+else
+ TEST_FILES="$@"
+fi
+
+/usr/bin/env bundle exec ruby -I"lib:test" -r rake -r rake/rake_test_loader ${TEST_FILES}
diff --git a/test/test_collections.rb b/test/test_collections.rb
index 8b4c2d23..3171b4fd 100644
--- a/test/test_collections.rb
+++ b/test/test_collections.rb
@@ -49,6 +49,35 @@ class TestCollections < Test::Unit::TestCase
should "know the full path to itself on the filesystem" do
assert_equal @collection.directory, source_dir("_methods")
end
+
+ context "when turned into Liquid" do
+ should "have a label attribute" do
+ assert_equal @collection.to_liquid["label"], "methods"
+ end
+
+ should "have a docs attribute" do
+ assert_equal @collection.to_liquid["docs"], Array.new
+ end
+
+ should "have a directory attribute" do
+ assert_equal @collection.to_liquid["directory"], source_dir("_methods")
+ end
+
+ should "have a relative_directory attribute" do
+ assert_equal @collection.to_liquid["relative_directory"], "_methods"
+ end
+
+ should "have a written attribute" do
+ assert_equal @collection.to_liquid["written"], false
+ end
+ end
+
+ should "know whether it should be written or not" do
+ assert_equal @collection.write?, false
+ @collection.metadata['output'] = true
+ assert_equal @collection.write?, true
+ @collection.metadata.delete 'output'
+ end
end
context "with no collections specified" do
@@ -57,10 +86,8 @@ class TestCollections < Test::Unit::TestCase
@site.process
end
- 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
+ should "not contain any collections" do
+ assert_equal Hash.new, @site.collections
end
end
@@ -107,6 +134,25 @@ class TestCollections < Test::Unit::TestCase
end
end
+ context "with a collection with metadata" do
+ setup do
+ @site = fixture_site({
+ "collections" => {
+ "methods" => {
+ "foo" => "bar",
+ "baz" => "whoo"
+ }
+ }
+ })
+ @site.process
+ @collection = @site.collections["methods"]
+ end
+
+ should "extract the configuration collection information as metadata" do
+ assert_equal @collection.metadata, {"foo" => "bar", "baz" => "whoo"}
+ end
+ end
+
context "in safe mode" do
setup do
@site = fixture_site({