New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support parameters for liquid include tags [take 2] #1204

Merged
merged 18 commits into from Jul 9, 2013

Conversation

Projects
None yet
7 participants
@maul-esel
Contributor

maul-esel commented Jun 11, 2013

This is a resubmission of #876 due to the heavy changes that have been made to master since it was submitted.

This PR adds the ability to send parameters to includes:

{% include foo.md bar="baz" %}

It can be used like this:

# in _includes/foo.md
{{ include.bar }} # outputs "baz" (without the quotes)
@parkr

This comment has been minimized.

Show comment
Hide comment
@parkr

parkr Jun 11, 2013

Member

Thank you!! Sorry to ask yet another thing of you but would you mind writing a quick comment outlining exactly how this works from a user standpoint?

Member

parkr commented Jun 11, 2013

Thank you!! Sorry to ask yet another thing of you but would you mind writing a quick comment outlining exactly how this works from a user standpoint?

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>"

This comment has been minimized.

@parkr

parkr Jun 11, 2013

Member

What if we made it so that we had this:

{% for param in include %}
{{ param.key }} = {{ param.value }}
{% endfor %}

I kind of like named accessors more than index accessors.

@parkr

parkr Jun 11, 2013

Member

What if we made it so that we had this:

{% for param in include %}
{{ param.key }} = {{ param.value }}
{% endfor %}

I kind of like named accessors more than index accessors.

This comment has been minimized.

This comment has been minimized.

@parkr

parkr Jun 11, 2013

Member

Ok cool :) Thanks!

@parkr

parkr Jun 11, 2013

Member

Ok cool :) Thanks!

@maul-esel

This comment has been minimized.

Show comment
Hide comment
@maul-esel

maul-esel Jun 11, 2013

Contributor

You can write your {% include %} tags like before, so all old things should still work.

But if, for a particular include, you want some extra variables available depending on where they are included from, you add parameters to the tag, like {% include param='value' %}.

Inside the include, you can then access these from the liquid variable include. You can for example use this for navigations (pass the "current" section and mark it) or conditional styling (pass the CSS class to use in the include) etc.

Contributor

maul-esel commented Jun 11, 2013

You can write your {% include %} tags like before, so all old things should still work.

But if, for a particular include, you want some extra variables available depending on where they are included from, you add parameters to the tag, like {% include param='value' %}.

Inside the include, you can then access these from the liquid variable include. You can for example use this for navigations (pass the "current" section and mark it) or conditional styling (pass the CSS class to use in the include) etc.

Show outdated Hide outdated lib/jekyll/tags/include.rb
eos
end
while pos = markup.index(/[=\"\s]/, pos)

This comment has been minimized.

@parkr

parkr Jun 11, 2013

Member

Why not use regexp for this?

http://rubular.com/r/FbVfmDUIU6

@parkr

parkr Jun 11, 2013

Member

Why not use regexp for this?

http://rubular.com/r/FbVfmDUIU6

This comment has been minimized.

@maul-esel

maul-esel Jun 12, 2013

Contributor

😲 How did you come up with this? I tried, but couldn't think of any regex that does the job.

@maul-esel

maul-esel Jun 12, 2013

Contributor

😲 How did you come up with this? I tried, but couldn't think of any regex that does the job.

This comment has been minimized.

@maul-esel

maul-esel Jun 12, 2013

Contributor

One thing though: Your regex supports both single and double quote, but a double quote can terminate a string started with single quote. This leads to problems with code like the following:

param='and" dg=' abc="d"

With your current regex, the dg=' portion is leftover, though I'd expect it to be part of param.

Do you have a fix for this, god of regex? Or drop single-quote support?

@maul-esel

maul-esel Jun 12, 2013

Contributor

One thing though: Your regex supports both single and double quote, but a double quote can terminate a string started with single quote. This leads to problems with code like the following:

param='and" dg=' abc="d"

With your current regex, the dg=' portion is leftover, though I'd expect it to be part of param.

Do you have a fix for this, god of regex? Or drop single-quote support?

This comment has been minimized.

@parkr

parkr Jun 12, 2013

Member

What do you think of this?

http://rubular.com/r/6P8aGivX9g

@parkr

parkr Jun 12, 2013

Member

What do you think of this?

http://rubular.com/r/6P8aGivX9g

This comment has been minimized.

@maul-esel

maul-esel Jun 12, 2013

Contributor

That seems to fail on your other test data - it reads everything as one param.

@maul-esel

maul-esel Jun 12, 2013

Contributor

That seems to fail on your other test data - it reads everything as one param.

This comment has been minimized.

@parkr

parkr Jun 12, 2013

Member

@paulmsmith You seem pretty excited about this. Can you come up with a use-case for a param whose value includes the = character? I can't think of anything.

@parkr

parkr Jun 12, 2013

Member

@paulmsmith You seem pretty excited about this. Can you come up with a use-case for a param whose value includes the = character? I can't think of anything.

This comment has been minimized.

@paulmsmith

paulmsmith Jun 12, 2013

If I'm understanding you chaps correctly, then a use-case might just be a string of copy text. For example I could want to do:

{% include headline='Jekyll include parameters are here' strapline='Using them = EPIC WIN!' %} 

and from within the include:

<div class="somemodule">
<h1>{{ include.headline }}</h1>
<p>{{ include.strapline }}</p>
</div>

I'd expect that to output:

<div class="somemodule">
<h1>Jekyll include parameters are here</h1>
<p>Using them = EPIC WIN!</p>
</div>

Hope that makes sense.

@paulmsmith

paulmsmith Jun 12, 2013

If I'm understanding you chaps correctly, then a use-case might just be a string of copy text. For example I could want to do:

{% include headline='Jekyll include parameters are here' strapline='Using them = EPIC WIN!' %} 

and from within the include:

<div class="somemodule">
<h1>{{ include.headline }}</h1>
<p>{{ include.strapline }}</p>
</div>

I'd expect that to output:

<div class="somemodule">
<h1>Jekyll include parameters are here</h1>
<p>Using them = EPIC WIN!</p>
</div>

Hope that makes sense.

This comment has been minimized.

@maul-esel

maul-esel Jun 12, 2013

Contributor

That's the only thing I could think about as well. On the other side, most cases like that can be solved by using an HTML entity for the equal sign - we'd just need to document it.

@maul-esel

maul-esel Jun 12, 2013

Contributor

That's the only thing I could think about as well. On the other side, most cases like that can be solved by using an HTML entity for the equal sign - we'd just need to document it.

This comment has been minimized.

@parkr

parkr Jun 12, 2013

Member

So, guess what! @imathis (the true Regexp god) fixed everything: http://rubular.com/r/kmNhn2806o

@parkr

parkr Jun 12, 2013

Member

So, guess what! @imathis (the true Regexp god) fixed everything: http://rubular.com/r/kmNhn2806o

This comment has been minimized.

@maul-esel

maul-esel Jun 13, 2013

Contributor

Impressive! And indeed, the regex shortens the code immensely - definitly worth it.

@maul-esel

maul-esel Jun 13, 2013

Contributor

Impressive! And indeed, the regex shortens the code immensely - definitly worth it.

Show outdated Hide outdated site/docs/templates.md
{% highlight ruby %}
{{ "{% include sig.textile param=value " }}%}
{% endhighlight %}

This comment has been minimized.

@parkr

parkr Jun 11, 2013

Member

Let's use {% raw %} blocks in here instead of that weird {{ "{% syntax.

@parkr

parkr Jun 11, 2013

Member

Let's use {% raw %} blocks in here instead of that weird {{ "{% syntax.

This comment has been minimized.

@maul-esel

maul-esel Jun 12, 2013

Contributor

Done.

@maul-esel

maul-esel Jun 12, 2013

Contributor

Done.

@paulmsmith

This comment has been minimized.

Show comment
Hide comment
@paulmsmith

paulmsmith Jun 12, 2013

You guys are awesome!

You guys are awesome!

Show outdated Hide outdated lib/jekyll/tags/include.rb
pos = 0
# ensure the entire markup string from start to end is valid syntax, and params are separated by spaces
full_matcher = Regexp.compile('\A\s*(?:(?<=\s|\A)' + MATCHER.to_s + '\s*)*\z')

This comment has been minimized.

@maul-esel

maul-esel Jun 14, 2013

Contributor

I used this to make sure the entire markup is valid, not only portions of it. However, 1.8.7 doesn't like look-behind (?<=...) - any advice?

@maul-esel

maul-esel Jun 14, 2013

Contributor

I used this to make sure the entire markup is valid, not only portions of it. However, 1.8.7 doesn't like look-behind (?<=...) - any advice?

This comment has been minimized.

@parkr

parkr Jun 15, 2013

Member

Hm... not sure. What is the look-behind necessary for?

@parkr

parkr Jun 15, 2013

Member

Hm... not sure. What is the look-behind necessary for?

This comment has been minimized.

@maul-esel

maul-esel Jun 15, 2013

Contributor

To make sure there's a space between the individual params. I could solve this issue by using a look-ahead at the end of the pattern instead.

@maul-esel

maul-esel Jun 15, 2013

Contributor

To make sure there's a space between the individual params. I could solve this issue by using a look-ahead at the end of the pattern instead.

@maul-esel

This comment has been minimized.

Show comment
Hide comment
@maul-esel

maul-esel Jun 15, 2013

Contributor

OK, the tests finally pass. Anything else left to do?

Contributor

maul-esel commented Jun 15, 2013

OK, the tests finally pass. Anything else left to do?

@imathis

This comment has been minimized.

Show comment
Hide comment
@imathis

imathis Jun 15, 2013

What if we used this regex instead. ([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+)) that would add a few new options.

  1. Supports spaces around the equals sign
  2. Supports variable names with a dash (like YAML does).
  3. Supports existing variables in match[4](see next comment)

What if we used this regex instead. ([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+)) that would add a few new options.

  1. Supports spaces around the equals sign
  2. Supports variable names with a dash (like YAML does).
  3. Supports existing variables in match[4](see next comment)
@imathis

This comment has been minimized.

Show comment
Hide comment
@imathis

imathis Jun 15, 2013

If you set @markup = markup then you can move this regex parsing into the render method. Then using context, you can assign include variables using values from variables in the context. Using the new regex I commented about above, you'll have match[4] which will be an unquoted variable name derived from a match on something like foo=bar.baz. Then you can do the following:

elsif match[4]
  value = context[match[4]]
end

This will allow you to do something like this:

{% for post in site.posts reverse %}
  {% capture post_title %}{{ post.title | titlecase }}{% endcapture %}
  {% include post.html title=post_title %}
{% endfor %}

Then you can reference {{ include.title }} in your post template partial. By allowing variables as well as strings, template partials have to rely less on conditionals and global variables and aren't restricted to static strings like the current proposal would suggest.

If you set @markup = markup then you can move this regex parsing into the render method. Then using context, you can assign include variables using values from variables in the context. Using the new regex I commented about above, you'll have match[4] which will be an unquoted variable name derived from a match on something like foo=bar.baz. Then you can do the following:

elsif match[4]
  value = context[match[4]]
end

This will allow you to do something like this:

{% for post in site.posts reverse %}
  {% capture post_title %}{{ post.title | titlecase }}{% endcapture %}
  {% include post.html title=post_title %}
{% endfor %}

Then you can reference {{ include.title }} in your post template partial. By allowing variables as well as strings, template partials have to rely less on conditionals and global variables and aren't restricted to static strings like the current proposal would suggest.

@imathis

This comment has been minimized.

Show comment
Hide comment
@imathis

imathis Jun 15, 2013

Contributor

Oh, if you want to see this new regex in action, here's the rubular.

Contributor

imathis commented Jun 15, 2013

Oh, if you want to see this new regex in action, here's the rubular.

@maul-esel

This comment has been minimized.

Show comment
Hide comment
@maul-esel

maul-esel Jun 19, 2013

Contributor

Very interesting, thanks. I considered supporting variables, just didn't know how to. Unless @parkr or @mattr- have a different opinion, I'm happy to include it.

Contributor

maul-esel commented Jun 19, 2013

Very interesting, thanks. I considered supporting variables, just didn't know how to. Unless @parkr or @mattr- have a different opinion, I'm happy to include it.

Show outdated Hide outdated lib/jekyll/tags/include.rb
end
end
MATCHER = /(\w+)=(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)')/

This comment has been minimized.

@mattr-

mattr- Jun 22, 2013

Member

I'd prefer if the constant was at the top but I'm willing to be flexible.

@parkr do you have a preference here?

@mattr-

mattr- Jun 22, 2013

Member

I'd prefer if the constant was at the top but I'm willing to be flexible.

@parkr do you have a preference here?

This comment has been minimized.

@parkr

parkr Jun 22, 2013

Member

I also prefer the top if that's ok :)

@parkr

parkr Jun 22, 2013

Member

I also prefer the top if that's ok :)

@mattr-

This comment has been minimized.

Show comment
Hide comment
@mattr-

mattr- Jun 22, 2013

Member

I'm cool with supporting variables.

@parkr @mojombo Anything you're aware of that keeps us from supporting variables as in @imathis' example?

Member

mattr- commented Jun 22, 2013

I'm cool with supporting variables.

@parkr @mojombo Anything you're aware of that keeps us from supporting variables as in @imathis' example?

@parkr

This comment has been minimized.

Show comment
Hide comment
@parkr

parkr Jun 22, 2013

Member

@mattr- @mojombo I think supporting extraction of variable values if the string input is a variable is a good idea.

@imathis also suggested we try something more direct (albeit Rails-like) with something like this:

{% include post.html locals: { author_class: "blue icon-notice", title: "Herein Lies the Story" } %}

So we'd take everything after .extname and parse it as JSON. It definitely takes more work in terms of styling, but also offers hierarchies and such. Might very well be too complicated for any solution Jekyll should hold in its core, but it's a pretty neat suggestion nevertheless. I'd say go with our current implementation in order to be more designer-friendly.

Member

parkr commented Jun 22, 2013

@mattr- @mojombo I think supporting extraction of variable values if the string input is a variable is a good idea.

@imathis also suggested we try something more direct (albeit Rails-like) with something like this:

{% include post.html locals: { author_class: "blue icon-notice", title: "Herein Lies the Story" } %}

So we'd take everything after .extname and parse it as JSON. It definitely takes more work in terms of styling, but also offers hierarchies and such. Might very well be too complicated for any solution Jekyll should hold in its core, but it's a pretty neat suggestion nevertheless. I'd say go with our current implementation in order to be more designer-friendly.

maul-esel and others added some commits Jun 22, 2013

Support passing Liquid variables to includes
Change the regex matching to allow Liquid variables and object fields
to be passed to the include. Use the render context to retrieve the
variable value. Also, relax syntax checks by allowing surrounding spaces
and dashes in parameter names.
@maul-esel

This comment has been minimized.

Show comment
Hide comment
@maul-esel

maul-esel Jun 22, 2013

Contributor

Adjusted according to @imathis' suggestion, moved the regex up (@mattr-).

Contributor

maul-esel commented Jun 22, 2013

Adjusted according to @imathis' suggestion, moved the regex up (@mattr-).

@bcomnes

This comment has been minimized.

Show comment
Hide comment
@bcomnes

bcomnes Jun 30, 2013

Contributor

This seems similar to the extended include tag in shopify: {% include 'foo' with 'bar' %}

I can't seem to get that to work in Jekyll though, and this PR looks more useful. 👍 Can't wait to get my hands on it.

Contributor

bcomnes commented Jun 30, 2013

This seems similar to the extended include tag in shopify: {% include 'foo' with 'bar' %}

I can't seem to get that to work in Jekyll though, and this PR looks more useful. 👍 Can't wait to get my hands on it.

@paulmsmith

This comment has been minimized.

Show comment
Hide comment
@paulmsmith

paulmsmith Jul 4, 2013

Sorry to push. What's the latest on this @imathis @parkr ? Going to be in Jekyll one day soon?

Sorry to push. What's the latest on this @imathis @parkr ? Going to be in Jekyll one day soon?

Show outdated Hide outdated lib/jekyll/tags/include.rb
end
while match = MATCHER.match(markup) do
markup = markup[match.end(0)..-1]

This comment has been minimized.

@parkr

parkr Jul 4, 2013

Member

in practice, i think this will only ever execute once. @imathis's pattern above seems to encompass the entire call, no?

@parkr

parkr Jul 4, 2013

Member

in practice, i think this will only ever execute once. @imathis's pattern above seems to encompass the entire call, no?

This comment has been minimized.

@maul-esel

maul-esel Jul 4, 2013

Contributor

No. If you look at the rubular he linked, there are several matches, so this should execute for each of them.

@maul-esel

maul-esel Jul 4, 2013

Contributor

No. If you look at the rubular he linked, there are several matches, so this should execute for each of them.

Show outdated Hide outdated lib/jekyll/tags/include.rb
if markup.include?(' ')
separator = markup.index(' ')
@file = markup[0..separator].strip
@params = markup[separator..-1]

This comment has been minimized.

@parkr

parkr Jul 5, 2013

Member
@file, @params = markup.split(' ', 2)

should work :)

@parkr

parkr Jul 5, 2013

Member
@file, @params = markup.split(' ', 2)

should work :)

This comment has been minimized.

@parkr

parkr Jul 5, 2013

Member

and it applies to both with and without params, so both of these work:

# No params
@markup = "some_fun_thing.html"
@file, @params = markup.split(" ", 2)
@file # => "some_fun_thing.html"
@params # => nil

# With params
@markup = "some_amazing_thing.html name='henry' title='king'"
@file, @params = markup.split(" ", 2)
@file # => "some_amazing_thing.html"
@params # => "name='henry' title='king'"
@parkr

parkr Jul 5, 2013

Member

and it applies to both with and without params, so both of these work:

# No params
@markup = "some_fun_thing.html"
@file, @params = markup.split(" ", 2)
@file # => "some_fun_thing.html"
@params # => nil

# With params
@markup = "some_amazing_thing.html name='henry' title='king'"
@file, @params = markup.split(" ", 2)
@file # => "some_amazing_thing.html"
@params # => "name='henry' title='king'"
Show outdated Hide outdated lib/jekyll/tags/include.rb
# ensure the entire markup string from start to end is valid syntax, and params are separated by spaces
full_matcher = Regexp.compile('\A\s*(?:' + MATCHER.to_s + '(?=\s|\z)\s*)*\z')
if not markup =~ full_matcher

This comment has been minimized.

@parkr

parkr Jul 5, 2013

Member

unless is a bit more idiomatic here :)

@parkr

parkr Jul 5, 2013

Member

unless is a bit more idiomatic here :)

@parkr

This comment has been minimized.

Show comment
Hide comment
@parkr

parkr Jul 7, 2013

Member

@mattr-, would you please take another look at this? Looks pretty ready to me.

Member

parkr commented Jul 7, 2013

@mattr-, would you please take another look at this? Looks pretty ready to me.

Show outdated Hide outdated lib/jekyll/tags/include.rb
def parse_params(markup, context)
params = {}
pos = 0

This comment has been minimized.

@mattr-

mattr- Jul 8, 2013

Member

I think we can remove the pos variable. I'm not seeing it used anywhere.

@mattr-

mattr- Jul 8, 2013

Member

I think we can remove the pos variable. I'm not seeing it used anywhere.

@mattr-

This comment has been minimized.

Show comment
Hide comment
@mattr-

mattr- Jul 8, 2013

Member

👍 from me, even with the comment above. :shipit:

Member

mattr- commented Jul 8, 2013

👍 from me, even with the comment above. :shipit:

Show outdated Hide outdated lib/jekyll/tags/include.rb
params = {}
pos = 0
# ensure the entire markup string from start to end is valid syntax, and params are separated by spaces

This comment has been minimized.

@mattr-

mattr- Jul 8, 2013

Member

Looking at this again, the comment tells me that this should be in a separate method, something like below, perhaps?

def validate_syntax
  full_matcher = Regexp.compile('\A\s*(?:' + MATCHER.to_s + '(?=\s|\z)\s*)*\z')
  unless markup =~ full_matcher
    raise SyntaxError.new <<-eos
Invalid syntax for include tag:

  #{markup}

Valid syntax:

  {% include file.ext param='value' param2="value" %}

eos
  end
end

and then you can change the parse_params method to be like so:

def parse_params(markup, context)
  params = {}

  validate_syntax

  ...
end

Thoughts?

@mattr-

mattr- Jul 8, 2013

Member

Looking at this again, the comment tells me that this should be in a separate method, something like below, perhaps?

def validate_syntax
  full_matcher = Regexp.compile('\A\s*(?:' + MATCHER.to_s + '(?=\s|\z)\s*)*\z')
  unless markup =~ full_matcher
    raise SyntaxError.new <<-eos
Invalid syntax for include tag:

  #{markup}

Valid syntax:

  {% include file.ext param='value' param2="value" %}

eos
  end
end

and then you can change the parse_params method to be like so:

def parse_params(markup, context)
  params = {}

  validate_syntax

  ...
end

Thoughts?

more code improvements
Remove unused variable, extract validation to method (@mattr-).
Do not require markup to be passed to parse_params as argument.
Show outdated Hide outdated lib/jekyll/tags/include.rb
value = match[3].gsub(/\\'/, "'")
elsif match[4]
value = context[match[4]]
end

This comment has been minimized.

@parkr

parkr Jul 8, 2013

Member

what do you think about having this block return the value and setting value outside the block?

@parkr

parkr Jul 8, 2013

Member

what do you think about having this block return the value and setting value outside the block?

@maul-esel

This comment has been minimized.

Show comment
Hide comment
@maul-esel

maul-esel Jul 8, 2013

Contributor

Despite the build failure, this actually runs fine.

Contributor

maul-esel commented Jul 8, 2013

Despite the build failure, this actually runs fine.

parkr added a commit that referenced this pull request Jul 9, 2013

Merge pull request #1204 from maul-esel/include-params2
Support parameters for liquid include tags.

@parkr parkr merged commit 08f6f3c into jekyll:master Jul 9, 2013

1 check passed

default The Travis CI build passed
Details

parkr added a commit that referenced this pull request Jul 9, 2013

@parkr

This comment has been minimized.

Show comment
Hide comment
@parkr

parkr Jul 9, 2013

Member

MERGED!! Thanks so much for your hard work and patience with us on this @maul-esel and props to @imathis for crazy amazing Regexp-fu.

Member

parkr commented Jul 9, 2013

MERGED!! Thanks so much for your hard work and patience with us on this @maul-esel and props to @imathis for crazy amazing Regexp-fu.

@mattr-

This comment has been minimized.

Show comment
Hide comment
@mattr-

mattr- Jul 9, 2013

Member

🎉 🎆

Member

mattr- commented Jul 9, 2013

🎉 🎆

@maul-esel maul-esel deleted the maul-esel:include-params2 branch Jul 10, 2013

@paulmsmith

This comment has been minimized.

Show comment
Hide comment
@paulmsmith

paulmsmith Jul 10, 2013

Awesome!! Thanks for all the work @maul-esel, @parkr, @imathis and chums! Looking forward to using it!!

Awesome!! Thanks for all the work @maul-esel, @parkr, @imathis and chums! Looking forward to using it!!

@mattr-

This comment has been minimized.

Show comment
Hide comment
@mattr-

mattr- Jul 10, 2013

Member

@maul-esel Great work! Thanks so much for sticking with this through all the various revisions and comments.

Member

mattr- commented Jul 10, 2013

@maul-esel Great work! Thanks so much for sticking with this through all the various revisions and comments.

@maul-esel

This comment has been minimized.

Show comment
Hide comment
@maul-esel

maul-esel Jul 10, 2013

Contributor

Happy to help this awesome project grow and develop further. And thanks to you both for bringing it back to life❗️

Contributor

maul-esel commented Jul 10, 2013

Happy to help this awesome project grow and develop further. And thanks to you both for bringing it back to life❗️

@heyLu heyLu referenced this pull request Jul 26, 2013

Closed

Only works with latest Jekyll. #1

@jekyll jekyll locked and limited conversation to collaborators Feb 27, 2017

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.