Packages

Mix tasks for creating and running interactive workshops for teaching people how to program in Elixir, and other things.

Current section

Files

Jump to
workshop lib tasks new workshop.ex
Raw

lib/tasks/new/workshop.ex

defmodule Mix.Tasks.New.Workshop do
use Mix.Task
import Mix.Generator
import Mix.Utils, only: [camelize: 1]
@shortdoc "Create a new workshop"
@moduledoc """
Creates a new workshop.
It expects a path for the workshop
mix new.workshop PATH
The path will be named after the given PATH. Given `my_workshop` will
result in a workshop named *My Workshop*.
"""
@spec run(OptionParser.argv) :: :ok
def run(argv) do
{opts, argv, _} = OptionParser.parse(argv, switches: [])
case argv do
[] -> Mix.raise "Expected PATH to be given. Please use `mix new.workshop PATH`"
[path|_] ->
name = Path.basename(Path.expand(path))
check_workshop_name!(name)
mod = "Workshop"
title = opts[:title] || snake_case_to_headline(name)
case File.mkdir_p(path) do
:ok ->
File.cd!(path, fn ->
do_generate_workshop(path, title, mod, opts)
end)
end
end
end
defp check_workshop_name!(name) do
# taken from the `mix new` source code
unless name =~ ~r/^[a-z][\w_]*$/ do
Mix.raise "Workshop name must start with a letter and have only lowercase " <>
"letters, numbers and underscore, got: #{inspect name}"
end
end
defp do_generate_workshop(name, title, mod, _opts) do
assigns = [name: name, title: title, module: mod]
create_file "README.md", readme_template(assigns)
create_directory ".workshop"
create_file ".workshop/prerequisite.exs", prerequisite_template(assigns)
create_file ".workshop/workshop.exs", workshop_template(assigns)
create_directory ".workshop/exercises"
create_file ".workshop/exercises/.gitkeep", ""
end
defp snake_case_to_headline(name) do
name
|> String.split("_")
|> Enum.map(&String.capitalize/1)
|> Enum.join(" ")
end
embed_template :readme, """
<%= @title %>
<%= String.replace(@title, ~r/./, "=") %>
**TODO: add a short description of the workshop**
What's next?
------------
Type `mix workshop.start` in the terminal to start the workshop, and
have fun!
"""
embed_template :workshop, """
defmodule Workshop.Meta do
use Workshop.Info
# The human readable title of the workshop.
@title "<%= @title %>"
# The version number is used to verify what version of the workshop the user
# is running.
@version "0.0.1"
# An optional url to the website of the workshop. If the website is a Git repo
# on GitHub the help page will point to the GitHub issues page when the user
# types `mix workshop.help`
#
# ie: `https://github.com/foo/bar` becomes `https://github.com/foo/bar/issues`
@home nil
# An optional short description of the workshop. Will get shown at places
# where the long description would not fit, such as the `mix workshop` screen.
# Set this to `false` if you want to suppress the missing shortdesc warning.
@shortdesc nil
# The description should state what the user will learn from this workshop.
# Perhaps mention the intended audience.
@description \"""
Describe the workshop here.
\"""
# The introduction should welcome the user to the workshop, set the
# expectations, and inform the user to execute the next-command to get to the
# first exercise.
@introduction \"""
This is the introduction and it will get displayed when the workshop has been
started.
Don't forget to inform the user to write `mix workshop.next` to get to the
first exercise.
\"""
# The debriefing message will get shown when the workshop is over.
# This would be a good opportunity to congratulate the user; perhaps thank the
# user and; and ask for feedback.
@debriefing \"""
You have completed the last exercise. Congratulations!
\"""
end
"""
embed_template :prerequisite, """
defmodule Workshop.Prerequisite do
use Workshop.SolutionCheck
# just remove and replace this example check
verify "Should check the truth" do
case 1 + 1 do
2 ->
:ok
3 ->
{:warning, "Math doesn't seem to work, but we'll work with it"}
:otherwise ->
{:error, "Something is seriously wrong with the universe"}
end
end
end
"""
end