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:" + 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}} + + \ 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