Merge pull request #1204 from maul-esel/include-params2

Support parameters for liquid include tags.
This commit is contained in:
Parker Moore 2013-07-09 14:14:40 -07:00
commit 08f6f3c2ed
6 changed files with 194 additions and 2 deletions

View File

@ -0,0 +1,35 @@
Feature: Include tags
In order to share their content across several pages
As a hacker who likes to blog
I want to be able to include files in my blog posts
Scenario: Include a file with parameters
Given I have an _includes directory
And I have an "_includes/header.html" file that contains "<header>My awesome blog header: {{include.param}}</header>"
And I have an "_includes/params.html" file that contains "Parameters:<ul>{% for param in include %}<li>{{param[0]}} = {{param[1]}}</li>{% endfor %}</ul>"
And I have an "_includes/ignore.html" file that contains "<footer>My blog footer</footer>"
And I have a _posts directory
And I have the following post:
| title | date | layout | content |
| Include Files | 2013-03-21 | default | {% include header.html param="myparam" %} |
| Ignore params if unused | 2013-03-21 | default | {% include ignore.html date="today" %} |
| List multiple parameters | 2013-03-21 | default | {% include params.html date="today" start="tomorrow" %} |
| Dont keep parameters | 2013-03-21 | default | {% include ignore.html param="test" %}\n{% include header.html %} |
| Allow params with spaces and quotes | 2013-04-07 | default | {% include params.html cool="param with spaces" super="\"quoted\"" single='has "quotes"' escaped='\'single\' quotes' %} |
| Parameter syntax | 2013-04-12 | default | {% include params.html param1_or_2="value" %} |
| Pass a variable | 2013-06-22 | default | {% assign var = 'some text' %}{% include params.html local=var layout=page.layout %} |
When I run jekyll
Then the _site directory should exist
And I should see "<header>My awesome blog header: myparam</header>" in "_site/2013/03/21/include-files.html"
And I should not see "myparam" in "_site/2013/03/21/ignore-params-if-unused.html"
And I should see "<li>date = today</li>" in "_site/2013/03/21/list-multiple-parameters.html"
And I should see "<li>start = tomorrow</li>" in "_site/2013/03/21/list-multiple-parameters.html"
And I should not see "<header>My awesome blog header: myparam</header>" in "_site/2013/03/21/dont-keep-parameters.html"
But I should see "<header>My awesome blog header: </header>" in "_site/2013/03/21/dont-keep-parameters.html"
And I should see "<li>cool = param with spaces</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
And I should see "<li>super = &#8220;quoted&#8221;</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
And I should see "<li>single = has &#8220;quotes&#8221;</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
And I should see "<li>escaped = &#8216;single&#8217; quotes</li>" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
And I should see "<li>param1_or_2 = value</li>" in "_site/2013/04/12/parameter-syntax.html"
And I should see "<li>local = some text</li>" in "_site/2013/06/22/pass-a-variable.html"
And I should see "<li>layout = default</li>" in "_site/2013/06/22/pass-a-variable.html"

View File

@ -143,6 +143,10 @@ Then /^I should see "(.*)" in "(.*)"$/ do |text, file|
assert Regexp.new(text).match(File.open(file).readlines.join)
end
Then /^I should not see "(.*)" in "(.*)"$/ do |text, file|
assert_no_match Regexp.new(text), File.read(file)
end
Then /^I should see escaped "(.*)" in "(.*)"$/ do |text, file|
assert Regexp.new(Regexp.escape(text)).match(File.open(file).readlines.join)
end

View File

@ -1,9 +1,51 @@
module Jekyll
module Tags
class IncludeTag < Liquid::Tag
def initialize(tag_name, file, tokens)
MATCHER = /([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))/
def initialize(tag_name, markup, tokens)
super
@file = file.strip
@file, @params = markup.strip.split(' ', 2);
end
def parse_params(context)
validate_syntax
params = {}
markup = @params
while match = MATCHER.match(markup) do
markup = markup[match.end(0)..-1]
value = if match[2]
match[2].gsub(/\\"/, '"')
elsif match[3]
match[3].gsub(/\\'/, "'")
elsif match[4]
context[match[4]]
end
params[match[1]] = value
end
params
end
# ensure the entire markup string from start to end is valid syntax, and params are separated by spaces
def validate_syntax
full_matcher = Regexp.compile('\A\s*(?:' + MATCHER.to_s + '(?=\s|\z)\s*)*\z')
unless @params =~ full_matcher
raise SyntaxError.new <<-eos
Invalid syntax for include tag:
#{@params}
Valid syntax:
{% include file.ext param='value' param2="value" %}
eos
end
end
def render(context)
@ -22,7 +64,9 @@ module Jekyll
if choices.include?(@file)
source = File.read(@file)
partial = Liquid::Template.parse(source)
context.stack do
context['include'] = parse_params(context) if @params
partial.render(context)
end
else

View File

@ -192,6 +192,18 @@ Jekyll expects all include files to be placed in an `_includes` directory at the
root of your source directory. This will embed the contents of
`<source>/_includes/sig.md` into the calling file.
You can also pass parameters to an include:
{% highlight ruby %}
{% raw %}{% include sig.textile param="value" %}{% endraw %}
{% endhighlight %}
These parameters are available via Liquid in the include:
{% highlight ruby %}
{% raw %}{{ include.param }}{% endraw %}
{% endhighlight %}
### Code snippet highlighting
Jekyll has built in support for syntax highlighting of [over 100

View File

@ -0,0 +1,7 @@
<span id='include-param'>{{include.param}}</span>
<ul id='param-list'>
{% for param in include %}
<li>{{param[0]}} = {{param[1]}}</li>
{% endfor %}
</ul>

View File

@ -347,4 +347,94 @@ CONTENT
end
end
end
context "include tag with parameters" do
context "with one parameter" do
setup do
content = <<CONTENT
---
title: Include tag parameters
---
{% include sig.markdown myparam="test" %}
{% include params.html param="value" %}
CONTENT
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
end
should "correctly output include variable" do
assert_match "<span id='include-param'>value</span>", @result.strip
end
should "ignore parameters if unused" do
assert_match "<hr />\n<p>Tom Preston-Werner github.com/mojombo</p>\n", @result
end
end
context "with invalid parameter syntax" do
should "throw a SyntaxError" do
content = <<CONTENT
---
title: Invalid parameter syntax
---
{% include params.html param s="value" %}
CONTENT
assert_raise SyntaxError, 'Did not raise exception on invalid "include" syntax' do
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
end
content = <<CONTENT
---
title: Invalid parameter syntax
---
{% include params.html params="value %}
CONTENT
assert_raise SyntaxError, 'Did not raise exception on invalid "include" syntax' do
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
end
end
end
context "with several parameters" do
setup do
content = <<CONTENT
---
title: multiple include parameters
---
{% include params.html param1="new_value" param2="another" %}
CONTENT
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
end
should "list all parameters" do
assert_match '<li>param1 = new_value</li>', @result
assert_match '<li>param2 = another</li>', @result
end
should "not include previously used parameters" do
assert_match "<span id='include-param' />", @result
end
end
context "without parameters" do
setup do
content = <<CONTENT
---
title: without parameters
---
{% include params.html %}
CONTENT
create_post(content, {'permalink' => 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
end
should "include file with empty parameters" do
assert_match "<span id='include-param' />", @result
end
end
end
end