diff --git a/features/include_tag.feature b/features/include_tag.feature
new file mode 100644
index 00000000..66f9d4a6
--- /dev/null
+++ b/features/include_tag.feature
@@ -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 "My awesome blog header: {{include.param}}"
+ And I have an "_includes/params.html" file that contains "Parameters:
{% for param in include %}
{{param[0]}} = {{param[1]}}
{% endfor %}
"
+ And I have an "_includes/ignore.html" file that contains ""
+ 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 "My awesome blog header: myparam" 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 "
date = today
" in "_site/2013/03/21/list-multiple-parameters.html"
+ And I should see "
start = tomorrow
" in "_site/2013/03/21/list-multiple-parameters.html"
+ And I should not see "My awesome blog header: myparam" in "_site/2013/03/21/dont-keep-parameters.html"
+ But I should see "My awesome blog header: " in "_site/2013/03/21/dont-keep-parameters.html"
+ And I should see "
cool = param with spaces
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
+ And I should see "
super = “quoted”
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
+ And I should see "
single = has “quotes”
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
+ And I should see "
escaped = ‘single’ quotes
" in "_site/2013/04/07/allow-params-with-spaces-and-quotes.html"
+ And I should see "
param1_or_2 = value
" in "_site/2013/04/12/parameter-syntax.html"
+ And I should see "
local = some text
" in "_site/2013/06/22/pass-a-variable.html"
+ And I should see "
layout = default
" in "_site/2013/06/22/pass-a-variable.html"
diff --git a/features/step_definitions/jekyll_steps.rb b/features/step_definitions/jekyll_steps.rb
index 1dca32d9..9208c4ec 100644
--- a/features/step_definitions/jekyll_steps.rb
+++ b/features/step_definitions/jekyll_steps.rb
@@ -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
diff --git a/lib/jekyll/tags/include.rb b/lib/jekyll/tags/include.rb
index 3c2fa984..ce1350b1 100644
--- a/lib/jekyll/tags/include.rb
+++ b/lib/jekyll/tags/include.rb
@@ -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
diff --git a/site/docs/templates.md b/site/docs/templates.md
index aa440ed4..b1152c3b 100644
--- a/site/docs/templates.md
+++ b/site/docs/templates.md
@@ -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
`/_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
diff --git a/test/source/_includes/params.html b/test/source/_includes/params.html
new file mode 100644
index 00000000..65a00018
--- /dev/null
+++ b/test/source/_includes/params.html
@@ -0,0 +1,7 @@
+{{include.param}}
+
+
+ {% for param in include %}
+
{{param[0]}} = {{param[1]}}
+ {% endfor %}
+
\ No newline at end of file
diff --git a/test/test_tags.rb b/test/test_tags.rb
index fd2df409..d0992873 100644
--- a/test/test_tags.rb
+++ b/test/test_tags.rb
@@ -347,4 +347,94 @@ CONTENT
end
end
end
+
+ context "include tag with parameters" do
+ context "with one parameter" do
+ setup do
+ content = < 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
+ end
+
+ should "correctly output include variable" do
+ assert_match "value", @result.strip
+ end
+
+ should "ignore parameters if unused" do
+ assert_match "\n
Tom Preston-Werner github.com/mojombo
\n", @result
+ end
+ end
+
+ context "with invalid parameter syntax" do
+ should "throw a SyntaxError" do
+ content = < 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
+ end
+
+ content = < 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
+ end
+ end
+ end
+
+ context "with several parameters" do
+ setup do
+ content = < 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
+ end
+
+ should "list all parameters" do
+ assert_match '
param1 = new_value
', @result
+ assert_match '
param2 = another
', @result
+ end
+
+ should "not include previously used parameters" do
+ assert_match "", @result
+ end
+ end
+
+ context "without parameters" do
+ setup do
+ content = < 'pretty', 'source' => source_dir, 'destination' => dest_dir, 'read_posts' => true})
+ end
+
+ should "include file with empty parameters" do
+ assert_match "", @result
+ end
+ end
+ end
end