A Ruby Gem to add content of Github files ot Jekyll
It’s been a while since last time I used Ruby, and I had the perfect excuse to come back to it when I was looking for a solution to inject content of Github files in Jekyll generated websites, and I could not find a github-pages solution to do that (there’s only a tag for including gists, but not a file or a part of it. See this)
I took some inspiration in a Gem I found for including gists
and I started developing my own gem. I never developed a gem before, so I decided to download RubyMine and use the
default template they have for that. With the help of the guides in
rubygems.org, I adapted some minor stuff to make the .gemfile ready for my gem, so I was ready to start developing.
I decided to create a single module (JekyllGithubContent) to encompass the classes of the Gem. Here is the resulting
file (injected using the gem :-):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# 3rd party libs
require 'open-uri'
require 'active_support'
require 'liquid'
module JekyllGithubContent
  class GithubUriParser
    GITHUB_URI_ROOT = 'https://github.com/'
    GITHUB_RAW_URI_ROOT = 'https://raw.githubusercontent.com'
    attr_reader :user, :repo, :file
    def initialize(path)
      relative_path = path.sub(/^https?\:\/\//, '').sub(/^github.com/, '')
      url_chunks = relative_path.split('/').delete_if { |e| e.empty? }
      @user, @repo, blob, @branch = url_chunks[0..3]
      @file = File.join(url_chunks[4..-1])
    end
    def get_raw_file_uri
      File.join(GITHUB_RAW_URI_ROOT, @user, @repo, @branch, @file)
    end
    def get_user_uri
      File.join(GITHUB_URI_ROOT, @user)
    end
    def get_repo_uri
      File.join(GITHUB_URI_ROOT, @user, @repo)
    end
  end
  class GithubContentRenderer < ::Liquid::Tag
    def initialize(tag_name, params, tokens)
      path, @initial_line, @end_line = params.split
      @github_file = GithubUriParser.new(path)
      @initial_line, @end_line = parse_line_boundaries(@initial_line, @end_line)
      super
    end
    def render(context)
      file_lines = cache.fetch(@github_file.get_raw_file_uri) do
        URI.open(@github_file.get_raw_file_uri).readlines
      end
      lines_to_print = file_lines[@initial_line..@end_line]
      lines_to_print.join
    end
    private # --------------------------------------------------------------------------------------------------------
    def cache
      @@cache ||= ActiveSupport::Cache::MemoryStore.new
    end
    def parse_line_boundaries(first_line, last_line)
      if first_line.nil? && last_line.nil?
        first_line = 0
        last_line = -1
      elsif last_line.nil?
        last_line = first_line
      end
      [first_line.to_i, last_line.to_i]
    end
  end
  class GithubMetaContentRenderer < GithubContentRenderer
    def render(context)
      <<DELIMITER.strip
<div class="github-file">
  <div class="meta-info">
    Github file by <a href="#{@github_file.get_user_uri}">#{@github_file.user}</a>
    <br>
    Repo: <a href="#{@github_file.get_repo_uri}">#{@github_file.repo}</a>
    <br>
    File: #{@github_file.file} <a href="#{@github_file.get_raw_file_uri}">Raw view</a>
  </div>
</div>
DELIMITER
    end
  end
end
# Register the tag names in Liquid to allow code & medadata insertion in Jekill markdown files
# e.g. {% github_file <my_file_url_in_github> [start_line] [end_line] %}
# e.g. {% github_fileref <my_file_url_in_github> %}
Liquid::Template.register_tag('github_file', JekyllGithubContent::GithubContentRenderer)
Liquid::Template.register_tag('github_fileref', JekyllGithubContent::GithubMetaContentRenderer)
Basically the gem is composed by three classes. The code is very simple and auto-commented, so it’s not worth to add something else:
- GithubUriParser - A parser that receives a path of a Github file and parses its contents for further use in the other 2 classes.
 - GithubContentRenderer - The responsible of rendering the file contents with the help of the previous class and the Liquid templating engine.
 - GithubMetaContentRenderer - An extension of the previous class that presents only the file metadata.
 
Finally the last two lines (94 and 95), are responsible the register the two new tags defined by the gem in order to inject the Github files. The Gem usage is described here.
Once the gem was ready, I opened an account in https://rubygems.org/ and I added my gem to the repo:
# Build the gem
$ gem build jekyll_github_content.gemspec
# Push it to the rubygems repo
$ gem push jekyll_github_content-0.0.3.gem
# Add your credentials and that's it!
At some point I had to create new version, and I wanted to get rid of the old ones in the public repo. We can achieve
that with gem yank jekyll_github_content -v 0.0.2.
After installing and testing the gem locally with my Jekyll website, I was ready to commit the integration in my
Jekyll website, which consists only in adding the gem to the Gemfile as it’s shown below, again using the gem:
1
2
3
4
5
source 'https://rubygems.org'
gem "jekyll"
gem "html-proofer"
gem 'github-pages', group: :jekyll_plugins
gem 'jekyll_github_content', '~> 0.0.4', group: :jekyll_plugins
In order to add the gem to the my github-pages website, as Github does not admit external gems, I had to generate the website statically as I described in this other blog post.