Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create CodeTag plugin to render code files with and without solutions #29

Merged
merged 14 commits into from
Aug 16, 2024
Merged
12 changes: 12 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@ require:

AllCops:
NewCops: enable

Metrics/AbcSize:
Enabled: false

Metrics/CyclomaticComplexity:
Enabled: false

Metrics/MethodLength:
Max: 100

Metrics/PerceivedComplexity:
Enabled: false
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

source 'https://rubygems.org'
gem 'jekyll', '>= 4.0.0'
gem 'jekyll-sitemap'
gem 'just-the-docs'
gem 'kramdown-parser-gfm'
Expand Down
1 change: 1 addition & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ DEPENDENCIES
axe-core-capybara
axe-core-rspec
capybara
jekyll (>= 4.0.0)
jekyll-sitemap
just-the-docs
kramdown-parser-gfm
Expand Down
14 changes: 14 additions & 0 deletions _includes/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
def testing():
"""
doctests woohoo
"""
print('this is displayed')
print('this is also displayed')
phrdang marked this conversation as resolved.
Show resolved Hide resolved
# BEGIN SOLUTION
print('solution is only displayed if enabled')
phrdang marked this conversation as resolved.
Show resolved Hide resolved
# END SOLUTION

Check warning on line 10 in _includes/test.py

View workflow job for this annotation

GitHub Actions / runner / pylint

[pylint] reported by reviewdog 🐶 C0303: Trailing whitespace (trailing-whitespace) Raw Output: _includes/test.py:10:0: C0303: Trailing whitespace (trailing-whitespace)
print('this is also displayed')
phrdang marked this conversation as resolved.
Show resolved Hide resolved
# BEGIN SOLUTION
print('solution 2 is only displayed if enabled')
phrdang marked this conversation as resolved.
Show resolved Hide resolved
# END SOLUTION
70 changes: 70 additions & 0 deletions _plugins/python_tag.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# frozen_string_literal: true

module Jekyll
BEGIN_SOLUTION = '# BEGIN SOLUTION'
END_SOLUTION = '# END SOLUTION'

# Custom Liquid tag for including Python files in _includes/
# so that the solutions appear only when specified.
# Example format of .py file: _includes/test.py
#
# Inherits from Jekyll's built-in include tag:
# https://github.com/jekyll/jekyll/blob/master/lib/jekyll/tags/include.rb
# TODO: create superclass and specify comment character(s) to generalize behavior to other languages
class PythonTag < Jekyll::Tags::IncludeTag
def initialize(tag_name, params, tokens)
parse_params(params)
super(tag_name, @file_name, tokens)
end

def parse_params(params)
file_name, show_solution = params.strip.split

raise ArgumentError, "File name '#{file_name}' must end in .py" if file_name[-3..] != '.py'

unless %w[true false].include?(show_solution)
raise ArgumentError, "Second argument to python tag must be a boolean, not '#{show_solution}'"
end

show_solution = show_solution == 'true'

@file_name = file_name
@show_solution = show_solution
end

def render(context)
raw_lines = super.split("\n")
saw_begin = false
saw_end = false
parsed_lines = []

raw_lines.each_with_index do |line, index|
if line.include?(BEGIN_SOLUTION)
raise PythonTagError, "Duplicate '#{BEGIN_SOLUTION}' at _includes/#{@file_name}:#{index + 1}" if saw_begin

saw_begin = true
saw_end = false
elsif line.include?(END_SOLUTION)
unless saw_begin
raise PythonTagError,
"'#{END_SOLUTION}' without preceding '#{BEGIN_SOLUTION}' at _includes/#{@file_name}:#{index + 1}"
end

saw_begin = false
saw_end = true
elsif !saw_begin || (saw_begin && @show_solution)
parsed_lines.push(line)
end
end

raise PythonTagError, "'#{BEGIN_SOLUTION}' without matching '#{END_SOLUTION}'" if saw_begin && !saw_end

parsed_lines.join("\n")
end
end

class PythonTagError < StandardError
end
end

Liquid::Template.register_tag('python', Jekyll::PythonTag)
4 changes: 4 additions & 0 deletions home.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,7 @@ Just the Class has been used by instructors at Stanford University ([CS 161](htt
### Local development environment

Just the Class requires no special Jekyll plugins and can run on GitHub Pages' standard Jekyll compiler. To setup a local development environment, clone your template repository and follow the GitHub Docs on [Testing your GitHub Pages site locally with Jekyll](https://docs.github.com/en/pages/setting-up-a-github-pages-site-with-jekyll/testing-your-github-pages-site-locally-with-jekyll).

{% highlight python %}
{% python test.py true %}
{% endhighlight %}
Loading