views:

283

answers:

3

I'm working on a Jekyll site and am trying to output three column divs nested in a row div. Liquid makes this pretty easy with their cycle filter:

{% for p in site.categories.post %}
    {% cycle 'add rows': '<div class="row">', nil, nil %}
        <div class="column">
            <a href="{{ p.url }}">{{ p.title }}</a>
        </div>
    {% cycle 'close rows': nil, nil, '</div>' %}
{% endfor %}

However, this only really works when there are 3, 6, 9, etc. posts. When the total number of posts is not a multiple of three, the <div class="row"> never gets closed--the for loop ends before the closing tag can be output as part of the close rows cycle.

In Ruby, PHP, or any other language I could easily fix this with a modulus operator, so in addition to close rows cycle I would output </div> when if site.categories.size % 3 == 0. However, Liquid, because it's a safe templating language, doesn't support the modulus.

What else can I do to properly close <div class="row"> when the total number of posts is not a multiple of three?

Thanks!

+1  A: 

IIRC Liquid doesn't block the modulo operation, only the % character. You can perform a modulus without using the % operator. For example, 14.modulo(3) => 2 instead of 14 % 3.

bta
That does make sense, since all the other arithmetic functions are abstracted out like that, but unfortunately neither `modulo` or `modulus` work…
Andrew
Yeah. Just checked the source code. Sadly, no `modulo`. I'll have to maybe fork it and add it in or something. My current solution is a mess: `unless total == 3 or total == 6 or total == 9 or total == 12 or total == 15…`
Andrew
`x.modulo(y)` is just an alias for `x.divmod(y)[1]`. If `divmod` is allowed, you could use that form. Or, you could always roll your own modulo function: `x - (x / y)` (using whatever abstracted versions of the arithmetic operators you need).
bta
+1  A: 

For your specific example, you could use {% cycle 'close rows': nil, '</div>', '</div>' %} after the {% endfor %}.

Grant Husbands
That works perfectly! Thanks!
Andrew
A: 

The only way for now is to write a liquid filter to accomplish this. Register the filter somewhere in your code where it's appropriate (it's in different places if using with rails and without them).

Liquid::Template.register_filter(LiquidFilters)

In you projects /lib directory add liquid_filters.rb:

module LiquidFilters  
  # makes modulus operation available to templates
  def mod(data, param)
    data % param
  end  
end

After that you can use it like following in your templates: {{ variable | mod:5 }}

And if you need to use it for some logic you can capture the value.

{% capture modulus %}{{ variable | mod:5 }}{% endcapture %}

Just I've noticed that captured value is a string so in order to compare it you use

{% if modulus == "0" %}
 ..
{% endif %}
Rytis Lukoševičius