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.