Skip to content

Instantly share code, notes, and snippets.

@jgatjens
Created February 10, 2014 22:03
Show Gist options
  • Save jgatjens/8925165 to your computer and use it in GitHub Desktop.
Save jgatjens/8925165 to your computer and use it in GitHub Desktop.
jekyll-plugin loop_directory
#usage:
#{% loop_directory directory:images iterator:image filter:*.jpg sort:descending %}
# <img src="{{ image }}" />
#{% endloop_directory %}
module Jekyll
class LoopDirectoryTag < Liquid::Block
include Liquid::StandardFilters
Syntax = /(#{Liquid::QuotedFragment}+)?/
def initialize(tag_name, markup, tokens)
@attributes = {}
@attributes['directory'] = '';
@attributes['iterator'] = 'item';
@attributes['filter'] = 'item';
@attributes['sort'] = 'ascending';
# Parse parameters
if markup =~ Syntax
markup.scan(Liquid::TagAttributes) do |key, value|
@attributes[key] = value
end
else
raise SyntaxError.new("Bad options given to 'loop_directory' plugin.")
end
#if @attributes['directory'].nil?
# raise SyntaxError.new("You did not specify a directory for loop_directory.")
#end
super
end
def render(context)
context.registers[:loop_directory] ||= Hash.new(0)
images = Dir.glob(File.join(@attributes['directory'], @attributes['filter']))
if @attributes['sort'].casecmp( "descending" ) == 0
# Find files and sort them reverse-lexically. This means
# that files whose names begin with YYYYMMDD are sorted newest first.
images.sort! {|x,y| y <=> x }
else
# sort normally in ascending order
images.sort!
end
result = []
context.stack do
# remove filename extension
images.each { |pathname|
context[@attributes['iterator']] = File.basename(pathname, @attributes['filter'].sub('*', ''))
result << render_all(@nodelist, context)
}
# return pathname
# images.each_with_index do |item, index|
# context[@attributes['iterator']] = item
# result << render_all(@nodelist, context)
# end
end
result
end
end
end
Liquid::Template.register_tag('loop_directory', Jekyll::LoopDirectoryTag)
@mikeritter
Copy link

Please add a README.md with install instructions for noobs and maybe even build testing if you're feeling adventurous.

I am trying following Jekyll's plugin instructions but not getting anywhere.

@ac70
Copy link

ac70 commented Jul 13, 2015

this is working well for me, THANKS!

I am trying to work out how to send the number of files in the directory as well (result.length ??). Can you point me in the right direction on what code to add? I'm a ruby noob.

mikeritter - basic instructions:
add the file above to the plug-ins folder
restart your jekyll server
add lines 2-4 to your html page

  • remove the # preceding the line
  • change the directory if needed
  • I changed filter:.jpg to filter: so it finds all files (not just .jpg, which is case sensitive as well)
  • try adding this line so you can see what the plug-in is outputting: {{ image }}

@lmmx
Copy link

lmmx commented Nov 8, 2015

@planemad
Copy link

@jgatjens this is an amazing plugin and should probably be probably be part of liquid!

I'm trying to pass a variable subdirectory to loop through but can't get it to work. Something like this:

{% loop_directory directory:{{page.url | remove_first: "/"}}thumbnail

I'm looking through stackoverflow questions on passing variables as arguments in liquid, but can't get any to work.

@adenta
Copy link

adenta commented Mar 26, 2016

@planemad, were you able to get this working? This seems like a common use case. I am trying to solve the same problem as well.

@adenta
Copy link

adenta commented Mar 26, 2016

For now I am using the solution presented here.

@skuzzymiglet
Copy link

I get a

> jekyll serve --trace 
Configuration file: /home/skuzzymiglet/ysgol/gwefan-jekyll/_config.yml 
            Source: /home/skuzzymiglet/ysgol/gwefan-jekyll 
       Destination: /home/skuzzymiglet/ysgol/gwefan-jekyll/_site 
 Incremental build: disabled. Enable with --incremental 
      Generating... 
  Liquid Exception: undefined method `render_all' for #<Jekyll::LoopDirectoryTag:0x000056284e5f07e0> in index.md 
Traceback (most recent call last): 
        41: from /home/skuzzymiglet/.gem/ruby/2.6.0/bin/jekyll:23:in `<main>' 
        40: from /home/skuzzymiglet/.gem/ruby/2.6.0/bin/jekyll:23:in `load' 
        39: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/exe/jekyll:15:in `<top (required)>' 
        38: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/mercenary-0.3.6/lib/mercenary.rb:19:in `program' 
        37: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/mercenary-0.3.6/lib/mercenary/program.rb:42:in `go' 
        36: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/mercenary-0.3.6/lib/mercenary/command.rb:220:in `execute' 
        35: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/mercenary-0.3.6/lib/mercenary/command.rb:220:in `each' 
        34: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/mercenary-0.3.6/lib/mercenary/command.rb:220:in `block in execute' 
        33: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/commands/serve.rb:86:in `block (2 levels) in init_with_program' 
        32: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/command.rb:89:in `process_with_graceful_fail' 
        31: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/command.rb:89:in `each' 
        30: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/command.rb:89:in `block in process_with_graceful_fail' 
        29: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/commands/build.rb:36:in `process' 
        28: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/commands/build.rb:65:in `build' 
        27: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/command.rb:28:in `process_site' 
        26: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/site.rb:76:in `process' 
        25: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/site.rb:202:in `render' 
        24: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/site.rb:515:in `render_pages' 
        23: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/site.rb:515:in `each' 
        22: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/site.rb:516:in `block in render_pages' 
        21: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/site.rb:523:in `render_regenerated' 
        20: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/renderer.rb:63:in `run' 
        19: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/renderer.rb:80:in `render_document' 
        18: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/renderer.rb:127:in `render_liquid' 
        17: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/liquid_renderer/file.rb:32:in `render!' 
        16: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/liquid_renderer/file.rb:60:in `measure_time' 
        15: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/liquid_renderer/file.rb:33:in `block in render!' 
        14: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/liquid_renderer/file.rb:53:in `measure_bytes' 
        13: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/liquid_renderer/file.rb:34:in `block (2 levels) in render!' 
        12: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/liquid_renderer/file.rb:49:in `measure_counts' 
        11: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/jekyll-4.0.0/lib/jekyll/liquid_renderer/file.rb:35:in `block (3 levels) in render!' 
        10: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/liquid-4.0.3/lib/liquid/template.rb:220:in `render!' 
         9: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/liquid-4.0.3/lib/liquid/template.rb:207:in `render' 
         8: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/liquid-4.0.3/lib/liquid/template.rb:242:in `with_profiling' 
         7: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/liquid-4.0.3/lib/liquid/template.rb:208:in `block in render' 
         6: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/liquid-4.0.3/lib/liquid/block_body.rb:82:in `render' 
         5: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/liquid-4.0.3/lib/liquid/block_body.rb:103:in `render_node_to_output' 
         4: from /home/skuzzymiglet/ysgol/gwefan-jekyll/_plugins/loop_dir.rb:52:in `render' 
         3: from /home/skuzzymiglet/.gem/ruby/2.6.0/gems/liquid-4.0.3/lib/liquid/context.rb:123:in `stack' 
         2: from /home/skuzzymiglet/ysgol/gwefan-jekyll/_plugins/loop_dir.rb:56:in `block in render' 
         1: from /home/skuzzymiglet/ysgol/gwefan-jekyll/_plugins/loop_dir.rb:56:in `each' 
/home/skuzzymiglet/ysgol/gwefan-jekyll/_plugins/loop_dir.rb:58:in `block (2 levels) in render': undefined method `render_all' for #<Jekyll::LoopDirectoryTag:0x000056284e5f07e0> (NoMethodError) 

my index.md:

---
layout: default
title: "Happy Jekylling!"
---

## You're ready to go!

{% loop_directory directory:images iterator:image filter:*.jpg sort:descending %}
   <img src="{{ image }}" />
{% endloop_directory %}

Clean, jekyll new --blank site

@jgatjens
Copy link
Author

jgatjens commented Nov 1, 2019

@skuzzymiglet do you have some repo with the code to see what's going on there?

@miko007
Copy link

miko007 commented Feb 24, 2020

i have come across the same problem. seems like Liquid::Block does not provide the render_all method anymore. Even the regular Liquid::Block::render() method is marked "For backwards compatibility". Likewise, the whole API for liquid blocks has changed.

I will have a more in-depth look into the problem, but never having wrote a liquid/jekyll module myself, it will not be very successful, i fear.

@photm5
Copy link

photm5 commented May 14, 2021

For a simpler version that works well with the built-in for block and works with current versions of liquid, see https://gitlab.com/baldrian/klimacamp-augsburg/-/blob/91244ae327380714e6d62778598a4bc69917c557/_plugins/list_files.rb. It creates a Page object for each of the files that were found, so if you don't want that, remove the .map do ... end part (you can also remove the site = ... and source = ... lines as well as the comment in that case) – without the map call, you'll get a list of filenames instead of a list of pages. See https://gitlab.com/baldrian/klimacamp-augsburg/-/blob/91244ae327380714e6d62778598a4bc69917c557/feeds/pressemitteilungen.xml#L14-15 for an example usage.

@meiadvanture
Copy link

meiadvanture commented May 22, 2024

I can't seem to get it work.
my directory for the image folder is /images under root directory.
The frontmatter in the post, I have "photos: /images"
and in my layout html, I have:

{% loop_directory directory: {{ page.photos }} iterator:image filter:*.jpg sort:descending %}
<img src="{{ image }}" />
{% endloop_directory %}

but nothing's showing. What did I do wrong?

@jgatjens
Copy link
Author

@meiadvanture this is like 8 years old or more, try this tho:

{% loop_directory directory:images iterator:image filter:*.jpg sort:descending %}
   <img src="{{ image }}" />
{% endloop_directory %}

Not sure about your {{ page.photos }} try it without that first and see.

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