Autoload yaml files under _data directory
The jekyll engine will autoload all yaml files(ends with .yml or .yaml) under _data. If there's a file members.yml under the directory, then user can access contents of the file through site.members.
This commit is contained in:
parent
92907c9545
commit
760cbc7f91
|
@ -0,0 +1,65 @@
|
||||||
|
Feature: Data
|
||||||
|
In order to use well-formatted data in my blog
|
||||||
|
As a blog's user
|
||||||
|
I want to use _data directory in my site
|
||||||
|
|
||||||
|
Scenario: autoload *.yaml files in _data directory
|
||||||
|
Given I have a _data directory
|
||||||
|
And I have a "_data/products.yaml" file with content:
|
||||||
|
"""
|
||||||
|
- name: sugar
|
||||||
|
price: 5.3
|
||||||
|
- name: salt
|
||||||
|
price: 2.5
|
||||||
|
"""
|
||||||
|
And I have an "index.html" page that contains "{% for product in site.data.products %}{{product.name}}{% endfor %}"
|
||||||
|
When I run jekyll
|
||||||
|
Then the "_site/index.html" file should exist
|
||||||
|
And I should see "sugar" in "_site/index.html"
|
||||||
|
And I should see "salt" in "_site/index.html"
|
||||||
|
|
||||||
|
Scenario: autoload *.yml files in _data directory
|
||||||
|
Given I have a _data directory
|
||||||
|
And I have a "_data/members.yml" file with content:
|
||||||
|
"""
|
||||||
|
- name: Jack
|
||||||
|
age: 28
|
||||||
|
- name: Leon
|
||||||
|
age: 34
|
||||||
|
"""
|
||||||
|
And I have an "index.html" page that contains "{% for member in site.data.members %}{{member.name}}{% endfor %}"
|
||||||
|
When I run jekyll
|
||||||
|
Then the "_site/index.html" file should exist
|
||||||
|
And I should see "Jack" in "_site/index.html"
|
||||||
|
And I should see "Leon" in "_site/index.html"
|
||||||
|
|
||||||
|
Scenario: autoload *.yml files in _data directory with space in file name
|
||||||
|
Given I have a _data directory
|
||||||
|
And I have a "_data/team members.yml" file with content:
|
||||||
|
"""
|
||||||
|
- name: Jack
|
||||||
|
age: 28
|
||||||
|
- name: Leon
|
||||||
|
age: 34
|
||||||
|
"""
|
||||||
|
And I have an "index.html" page that contains "{% for member in site.data.team_members %}{{member.name}}{% endfor %}"
|
||||||
|
When I run jekyll
|
||||||
|
Then the "_site/index.html" file should exist
|
||||||
|
And I should see "Jack" in "_site/index.html"
|
||||||
|
And I should see "Leon" in "_site/index.html"
|
||||||
|
|
||||||
|
Scenario: should be backward compatible with site.data in _config.yml
|
||||||
|
Given I have a "_config.yml" file with content:
|
||||||
|
"""
|
||||||
|
data:
|
||||||
|
- name: Jack
|
||||||
|
age: 28
|
||||||
|
- name: Leon
|
||||||
|
age: 34
|
||||||
|
"""
|
||||||
|
And I have an "index.html" page that contains "{% for member in site.data %}{{member.name}}{% endfor %}"
|
||||||
|
When I run jekyll
|
||||||
|
Then the "_site/index.html" file should exist
|
||||||
|
And I should see "Jack" in "_site/index.html"
|
||||||
|
And I should see "Leon" in "_site/index.html"
|
||||||
|
|
|
@ -43,6 +43,12 @@ Given /^I have an? (.*) (layout|theme) that contains "(.*)"$/ do |name, type, te
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Given /^I have an? "(.*)" file with content:$/ do |file, text|
|
||||||
|
File.open(file, 'w') do |f|
|
||||||
|
f.write(text)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
Given /^I have an? (.*) directory$/ do |dir|
|
Given /^I have an? (.*) directory$/ do |dir|
|
||||||
FileUtils.mkdir_p(dir)
|
FileUtils.mkdir_p(dir)
|
||||||
end
|
end
|
||||||
|
|
|
@ -59,6 +59,7 @@ Gem::Specification.new do |s|
|
||||||
bin/jekyll
|
bin/jekyll
|
||||||
cucumber.yml
|
cucumber.yml
|
||||||
features/create_sites.feature
|
features/create_sites.feature
|
||||||
|
features/data.feature
|
||||||
features/drafts.feature
|
features/drafts.feature
|
||||||
features/embed_filters.feature
|
features/embed_filters.feature
|
||||||
features/include_tag.feature
|
features/include_tag.feature
|
||||||
|
@ -203,6 +204,7 @@ Gem::Specification.new do |s|
|
||||||
test/helper.rb
|
test/helper.rb
|
||||||
test/source/+/foo.md
|
test/source/+/foo.md
|
||||||
test/source/.htaccess
|
test/source/.htaccess
|
||||||
|
test/source/_data/members.yaml
|
||||||
test/source/_includes/params.html
|
test/source/_includes/params.html
|
||||||
test/source/_includes/sig.markdown
|
test/source/_includes/sig.markdown
|
||||||
test/source/_layouts/default.html
|
test/source/_layouts/default.html
|
||||||
|
|
|
@ -10,6 +10,7 @@ module Jekyll
|
||||||
'destination' => File.join(Dir.pwd, '_site'),
|
'destination' => File.join(Dir.pwd, '_site'),
|
||||||
'plugins' => '_plugins',
|
'plugins' => '_plugins',
|
||||||
'layouts' => '_layouts',
|
'layouts' => '_layouts',
|
||||||
|
'data_source' => '_data',
|
||||||
'keep_files' => ['.git','.svn'],
|
'keep_files' => ['.git','.svn'],
|
||||||
|
|
||||||
'timezone' => nil, # use the local timezone
|
'timezone' => nil, # use the local timezone
|
||||||
|
|
|
@ -3,7 +3,7 @@ module Jekyll
|
||||||
attr_accessor :config, :layouts, :posts, :pages, :static_files,
|
attr_accessor :config, :layouts, :posts, :pages, :static_files,
|
||||||
:categories, :exclude, :include, :source, :dest, :lsi, :pygments,
|
:categories, :exclude, :include, :source, :dest, :lsi, :pygments,
|
||||||
:permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts,
|
:permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts,
|
||||||
:show_drafts, :keep_files, :baseurl, :file_read_opts
|
:show_drafts, :keep_files, :baseurl, :data, :file_read_opts
|
||||||
|
|
||||||
attr_accessor :converters, :generators
|
attr_accessor :converters, :generators
|
||||||
|
|
||||||
|
@ -56,6 +56,7 @@ module Jekyll
|
||||||
self.static_files = []
|
self.static_files = []
|
||||||
self.categories = Hash.new { |hash, key| hash[key] = [] }
|
self.categories = Hash.new { |hash, key| hash[key] = [] }
|
||||||
self.tags = Hash.new { |hash, key| hash[key] = [] }
|
self.tags = Hash.new { |hash, key| hash[key] = [] }
|
||||||
|
self.data = {}
|
||||||
|
|
||||||
if self.limit_posts < 0
|
if self.limit_posts < 0
|
||||||
raise ArgumentError, "limit_posts must be a non-negative number"
|
raise ArgumentError, "limit_posts must be a non-negative number"
|
||||||
|
@ -110,6 +111,7 @@ module Jekyll
|
||||||
def read
|
def read
|
||||||
self.read_layouts
|
self.read_layouts
|
||||||
self.read_directories
|
self.read_directories
|
||||||
|
self.read_data(config['data_source'])
|
||||||
end
|
end
|
||||||
|
|
||||||
# Read all the files in <source>/<layouts> and create a new Layout object
|
# Read all the files in <source>/<layouts> and create a new Layout object
|
||||||
|
@ -197,6 +199,25 @@ module Jekyll
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Read and parse all yaml files under <source>/<dir>
|
||||||
|
#
|
||||||
|
# Returns nothing
|
||||||
|
def read_data(dir)
|
||||||
|
base = File.join(self.source, dir)
|
||||||
|
return unless File.directory?(base) && (!self.safe || !File.symlink?(base))
|
||||||
|
|
||||||
|
entries = Dir.chdir(base) { Dir['*.{yaml,yml}'] }
|
||||||
|
entries.delete_if { |e| File.directory?(File.join(base, e)) }
|
||||||
|
|
||||||
|
entries.each do |entry|
|
||||||
|
path = File.join(self.source, dir, entry)
|
||||||
|
next if File.symlink?(path) && self.safe
|
||||||
|
|
||||||
|
key = sanitize_filename(File.basename(entry, '.*'))
|
||||||
|
self.data[key] = YAML.safe_load_file(path)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Run each of the Generators.
|
# Run each of the Generators.
|
||||||
#
|
#
|
||||||
# Returns nothing.
|
# Returns nothing.
|
||||||
|
@ -262,6 +283,14 @@ module Jekyll
|
||||||
hash
|
hash
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Prepare site data for site payload. The method maintains backward compatibility
|
||||||
|
# if the key 'data' is already used in _config.yml.
|
||||||
|
#
|
||||||
|
# Returns the Hash to be hooked to site.data.
|
||||||
|
def site_data
|
||||||
|
self.config['data'] || self.data
|
||||||
|
end
|
||||||
|
|
||||||
# The Hash payload containing site-wide data.
|
# The Hash payload containing site-wide data.
|
||||||
#
|
#
|
||||||
# Returns the Hash: { "site" => data } where data is a Hash with keys:
|
# Returns the Hash: { "site" => data } where data is a Hash with keys:
|
||||||
|
@ -283,7 +312,8 @@ module Jekyll
|
||||||
"pages" => self.pages,
|
"pages" => self.pages,
|
||||||
"html_pages" => self.pages.reject { |page| !page.html? },
|
"html_pages" => self.pages.reject { |page| !page.html? },
|
||||||
"categories" => post_attr_hash('categories'),
|
"categories" => post_attr_hash('categories'),
|
||||||
"tags" => post_attr_hash('tags')})}
|
"tags" => post_attr_hash('tags'),
|
||||||
|
"data" => site_data})}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Filter out any files/directories that are hidden or backup files (start
|
# Filter out any files/directories that are hidden or backup files (start
|
||||||
|
@ -393,5 +423,11 @@ module Jekyll
|
||||||
def site_cleaner
|
def site_cleaner
|
||||||
@site_cleaner ||= Cleaner.new(self)
|
@site_cleaner ||= Cleaner.new(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def sanitize_filename(name)
|
||||||
|
name = name.gsub(/[^\w\s_-]+/, '')
|
||||||
|
name = name.gsub(/(^|\b\s)\s+($|\s?\b)/, '\\1\\2')
|
||||||
|
name = name.gsub(/\s+/, '_')
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -31,6 +31,8 @@ A basic Jekyll site usually looks something like this:
|
||||||
├── _posts
|
├── _posts
|
||||||
| ├── 2007-10-29-why-every-programmer-should-play-nethack.textile
|
| ├── 2007-10-29-why-every-programmer-should-play-nethack.textile
|
||||||
| └── 2009-04-26-barcamp-boston-4-roundup.textile
|
| └── 2009-04-26-barcamp-boston-4-roundup.textile
|
||||||
|
├── _data
|
||||||
|
| └── members.yml
|
||||||
├── _site
|
├── _site
|
||||||
└── index.html
|
└── index.html
|
||||||
{% endhighlight %}
|
{% endhighlight %}
|
||||||
|
@ -121,6 +123,21 @@ An overview of what each of these does:
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<p><code>_data</code></p>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p>
|
||||||
|
|
||||||
|
Well-formatted site data should be placed here. The jekyll engine will
|
||||||
|
autoload all yaml files (ends with <code>.yml</code> or <code>.yaml</code>)
|
||||||
|
in this directory. If there's a file <code>members.yml</code> under the directory,
|
||||||
|
then you can access contents of the file through <code>site.data.members</code>.
|
||||||
|
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<p><code>_site</code></p>
|
<p><code>_site</code></p>
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
- java
|
||||||
|
- ruby
|
|
@ -0,0 +1,7 @@
|
||||||
|
- name: Jack
|
||||||
|
age: 27
|
||||||
|
blog: http://example.com/jack
|
||||||
|
|
||||||
|
- name: John
|
||||||
|
age: 32
|
||||||
|
blog: http://example.com/john
|
|
@ -0,0 +1 @@
|
||||||
|
../products.yml
|
|
@ -0,0 +1,4 @@
|
||||||
|
- name: sugar
|
||||||
|
price: 5.3
|
||||||
|
- name: salt
|
||||||
|
price: 2.5
|
|
@ -0,0 +1 @@
|
||||||
|
../_data
|
|
@ -335,5 +335,62 @@ class TestSite < Test::Unit::TestCase
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'data directory' do
|
||||||
|
should 'auto load yaml files' do
|
||||||
|
site = Site.new(Jekyll.configuration)
|
||||||
|
site.process
|
||||||
|
|
||||||
|
file_content = YAML.safe_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
|
||||||
|
end
|
||||||
|
|
||||||
|
should 'auto load yml files' do
|
||||||
|
site = Site.new(Jekyll.configuration)
|
||||||
|
site.process
|
||||||
|
|
||||||
|
file_content = YAML.safe_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
|
||||||
|
end
|
||||||
|
|
||||||
|
should "load symlink files in unsafe mode" do
|
||||||
|
site = Site.new(Jekyll.configuration.merge({'safe' => false}))
|
||||||
|
site.process
|
||||||
|
|
||||||
|
file_content = YAML.safe_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
|
||||||
|
end
|
||||||
|
|
||||||
|
should "not load symlink files in safe mode" do
|
||||||
|
site = Site.new(Jekyll.configuration.merge({'safe' => true}))
|
||||||
|
site.process
|
||||||
|
|
||||||
|
assert_nil site.data['products']
|
||||||
|
assert_nil site.site_payload['site']['data']['products']
|
||||||
|
end
|
||||||
|
|
||||||
|
should "load symlink directory in unsafe mode" do
|
||||||
|
site = Site.new(Jekyll.configuration.merge({'safe' => false, 'data_source' => File.join('symlink-test', '_data')}))
|
||||||
|
site.process
|
||||||
|
|
||||||
|
assert_not_nil site.data['products']
|
||||||
|
assert_not_nil site.data['languages']
|
||||||
|
assert_not_nil site.data['members']
|
||||||
|
end
|
||||||
|
|
||||||
|
should "not load symlink directory in safe mode" do
|
||||||
|
site = Site.new(Jekyll.configuration.merge({'safe' => true, 'data_source' => File.join('symlink-test', '_data')}))
|
||||||
|
site.process
|
||||||
|
|
||||||
|
assert_nil site.data['products']
|
||||||
|
assert_nil site.data['languages']
|
||||||
|
assert_nil site.data['members']
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue