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