Search code examples
rubyjekyll

Parse data from an ICS file and expose it to a liquid variable in the template


I have searched tirelessly for this on Google but unfortunately every search collides with the fact Jekyll is a site generator and the results do not help.

I'm looking for a small example of how to read an ICS file from a plugin/generator that is then accessible with liquid from the templates.

I've tried creating collections and appending to them in plugins, I've tried creating site.data arrays. Nothing seems to work. Is there a small example of a jekyll plugin that reads a file or url and creates data that is then stored in a site variable and can be accessed via liquid? Specifically I'll be reading an ICS feed and creating a calendar.


Solution

  • Data files might not be able to fulfil your requirement. You need a Jekyll Plugin.

    Here's an ad-hoc solution I would implement to read ics files and expose calendar events to liquid variables in Jekyll:

    Add icalendar to your Gemfile and install it:

    bundle add icalendar
    bundle install
    

    Put this file in _plugins/calendar_reader.rb

    require "jekyll"
    require "icalendar"
    
    module Jekyll
      module ICSReader
        def read_calendar(input)
          begin
            calendar_file = File.open(input)
            events = Icalendar::Event.parse(calendar_file)
    
            hash = {}
            counter = 0
    
            # loop through the events in the calendars
            # and map the values you want into a variable and then return it:
    
            events.each do |event|
              hash[counter] = {
                "summary" => event.summary,
                "dtstart" => event.dtstart,
                "dtend" => event.dtend,
                "description" => event.description
              }
    
              counter += 1
            end
    
            return hash
          rescue
            # Handle errors
            Jekyll.logger.error "Calendar Reader:", "An error occurred!"
    
            return {}
          end
        end
      end
    end
    
    Liquid::Template.register_filter(Jekyll::ICSReader)
    

    The README.md docs of icalendar would help you understand how data is being read from the file. Basically, we parse the events in the file and map them to a dictionary and return it.

    Now take an ics file and put it into the _data folder.

    _data/my_calendar.ics

    BEGIN:VEVENT
    DTSTAMP:20050118T211523Z
    UID:bsuidfortestabc123
    DTSTART;TZID=US-Mountain:20050120T170000
    DTEND;TZID=US-Mountain:20050120T184500
    CLASS:PRIVATE
    GEO:37.386013;-122.0829322
    ORGANIZER:mailto:joebob@random.net
    PRIORITY:2
    SUMMARY:This is a really long summary to test the method of unfolding lines
     \, so I'm just going to make it a whole bunch of lines.
    ATTACH:http://bush.sucks.org/impeach/him.rhtml
    ATTACH:http://corporations-dominate.existence.net/why.rhtml
    RDATE;TZID=US-Mountain:20050121T170000,20050122T170000
    X-TEST-COMPONENT;QTEST="Hello, World":Shouldn't double double quotes
    END:VEVENT
    BEGIN:VEVENT
    DTSTAMP:20110118T211523Z
    UID:uid-1234-uid-4321
    DTSTART;TZID=US-Mountain:20110120T170000
    DTEND;TZID=US-Mountain:20110120T184500
    CLASS:PRIVATE
    GEO:37.386013;-122.0829322
    ORGANIZER:mailto:jmera@jmera.human
    PRIORITY:2
    SUMMARY:This is a very short summary.
    RDATE;TZID=US-Mountain:20110121T170000,20110122T170000
    END:VEVENT
    

    This sample ics file is taken from the icalendar repository.

    Now you can use the plugin filter from your markdown/html:

    {% assign events = "_data/my_calendar.ics" | read_calendar %}
    

    Here read_calendar is the function defined in _plugins/calendar_reader.rb and _data/my_calendar.ics is the file you want to get the data from. The plugin gets the input as _data/my_calendar.ics, reads it and returns a hash which is stored into the events variable itself.

    You can now use {{ events }} to access the hash of the data that you return from the function in the plugin file.

    // {{ events }}
    
    {0=>{“summary”=>”This is a really long summary to test the method of unfolding lines, so I’m just going to make it a whole bunch of lines.”, “dtstart”=>#<DateTime: 2005-01-20T17:00:00+00:00 ((2453391j,61200s,0n),+0s,2299161j)>, “dtend”=>#<DateTime: 2005-01-20T18:45:00+00:00 ((2453391j,67500s,0n),+0s,2299161j)>, “description”=>nil}, 1=>{“summary”=>”This is a very short summary.”, “dtstart”=>#<DateTime: 2011-01-20T17:00:00+00:00 ((2455582j,61200s,0n),+0s,2299161j)>, “dtend”=>#<DateTime: 2011-01-20T18:45:00+00:00 ((2455582j,67500s,0n),+0s,2299161j)>, “description”=>nil}}
    
    
    // {{ events[0] }}
    
    {“summary”=>”This is a really long summary to test the method of unfolding lines, so I’m just going to make it a whole bunch of lines.”, “dtstart”=>#<DateTime: 2005-01-20T17:00:00+00:00 ((2453391j,61200s,0n),+0s,2299161j)>, “dtend”=>#<DateTime: 2005-01-20T18:45:00+00:00 ((2453391j,67500s,0n),+0s,2299161j)>, “description”=>nil} 
    
    // {{ events[1] }}
    
    {“summary”=>”This is a very short summary.”, “dtstart”=>#<DateTime: 2011-01-20T17:00:00+00:00 ((2455582j,61200s,0n),+0s,2299161j)>, “dtend”=>#<DateTime: 2011-01-20T18:45:00+00:00 ((2455582j,67500s,0n),+0s,2299161j)>, “description”=>nil}
    

    This was the bare-bones of how a Jekyll Filter works. You can dive deeper into other types of Jekyll Plugins as explained in the docs.