Skip to content

Commit

Permalink
Exercise generator (#52)
Browse files Browse the repository at this point in the history
* Exercise generator

* spec and crystal tool format

* flip name logic
  • Loading branch information
bmulvihill authored and mhelmetag committed Nov 17, 2016
1 parent 154ed07 commit b38194d
Show file tree
Hide file tree
Showing 11 changed files with 177 additions and 11 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ bin/configlet
bin/configlet.exe
CHECKLIST
tmp/
bin/generate
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
ASSIGNMENT ?= ""
IGNOREDIRS := "^(\.git|.crystal|docs|bin|img|script)$$"
EXERCISESDIR ?= "exercises"
GENERATORDIR ?= "src/generator"
ASSIGNMENTS = $(shell find exercises -maxdepth 1 -mindepth 1 -type d | cut -d'/' -f2 | sort | grep -Ev $(IGNOREDIRS))

FILEEXT := "cr"
Expand All @@ -23,3 +24,8 @@ test-assignment:

test:
@for assignment in $(ASSIGNMENTS); do ASSIGNMENT=$$assignment $(MAKE) -s test-assignment || exit 1; done
@echo "running generator tests"
@cd $(GENERATORDIR) && crystal spec

build_generator:
@crystal build $(GENERATORDIR)/generate.$(FILEEXT) -o bin/generate
3 changes: 2 additions & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,8 @@
"ignored": [
"bin",
"docs",
"img"
"img",
"src"
],
"foregone": [
]
Expand Down
18 changes: 8 additions & 10 deletions exercises/hello-world/spec/hello_world_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,15 @@ require "spec"
require "../src/*"

describe "HelloWorld" do
describe "#hello" do
it "says hello with default 'World'" do
HelloWorld.hello.should eq "Hello, World!"
end
it "tests no name" do
HelloWorld.hello.should eq("Hello, World!")
end

pending "says hello with one name" do
HelloWorld.hello("Max").should eq "Hello, Max!"
end
pending "tests sample name" do
HelloWorld.hello("Alice").should eq("Hello, Alice!")
end

pending "says hello with another name" do
HelloWorld.hello("Alice").should eq "Hello, Alice!"
end
pending "tests other sample name" do
HelloWorld.hello("Bob").should eq("Hello, Bob!")
end
end
39 changes: 39 additions & 0 deletions src/generator/exercises/exercise_generator.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
require "ecr"
require "json"

abstract class ExerciseGenerator
METADATA_REPOSITORY = "x-common"

def self.generate
new.generate
end

def generate
File.write(test_file, to_s)
end

def describe_name
exercise_name.split('-').map(&.capitalize).join
end

abstract def exercise_name : String
abstract def test_cases : Array(ExerciseTestCase)

private def root
File.expand_path(File.join("..", "..", "..", ".."), __FILE__)
end

private def test_file
File.expand_path(File.join("exercises", exercise_name, "spec", "#{exercise_name.tr("-", "_")}_spec.cr"), root)
end

private def metadata_dir
File.expand_path(File.join("..", METADATA_REPOSITORY, "exercises", exercise_name), root)
end

private def data
File.read(File.join(metadata_dir, "canonical-data.json"))
end

ECR.def_to_s "#{__DIR__}/templates/example.tt"
end
8 changes: 8 additions & 0 deletions src/generator/exercises/exercise_test_case.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
abstract class ExerciseTestCase
abstract def workload
abstract def test_name

def pending?(index)
index == 0 ? "it" : "pending"
end
end
34 changes: 34 additions & 0 deletions src/generator/exercises/hello_world.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
require "./exercise_generator"
require "./exercise_test_case"

class HelloWorldGenerator < ExerciseGenerator
def exercise_name
"hello-world"
end

def test_cases
JSON.parse(data)["cases"].map do |test_case|
HelloWorldTestCase.from_json(test_case.to_json)
end
end
end

class HelloWorldTestCase < ExerciseTestCase
JSON.mapping(
description: String,
name: String | Nil,
expected: String
)

def workload
if name
"HelloWorld.hello(\"#{name}\").should eq(\"#{expected}\")"
else
"HelloWorld.hello.should eq(\"#{expected}\")"
end
end

def test_name
description
end
end
10 changes: 10 additions & 0 deletions src/generator/exercises/templates/example.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
require "spec"
require "../src/*"

describe <%= "#{describe_name.inspect} do" %>
<% test_cases.each_with_index do |test_case, index| %>
<%= test_case.pending?(index) %> "tests <%= test_case.test_name %>" do
<%= test_case.workload %>
end
<% end -%>
end
15 changes: 15 additions & 0 deletions src/generator/generate.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
require "./exercises/*"

if ARGV.empty?
STDERR.puts "Exercise name required!\n"
exit
end

exercise = ARGV[0]

klass = {{ExerciseGenerator.subclasses}}.find do |generator|
generator.to_s == "#{exercise.split('-').map(&.capitalize).join}Generator"
end

raise "Undefined Generator" unless klass
klass.generate
27 changes: 27 additions & 0 deletions src/generator/spec/exercise_generator_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require "spec"
require "../exercises/exercise_generator"
require "../exercises/exercise_test_case"

class DummyGenerator < ExerciseGenerator
def exercise_name
"dummy"
end

def test_cases
[] of ExerciseTestCase
end
end

describe "ExerciseGenerator" do
describe "#describe_name" do
it "will return the name of the exercise in camel case" do
DummyGenerator.new.describe_name.should eq("Dummy")
end
end

describe "#to_s" do
it "will output the generator test file using the example.tt" do
DummyGenerator.new.to_s.should eq("require \"spec\"\nrequire \"../src/*\"\n\ndescribe \"Dummy\" do\nend\n")
end
end
end
27 changes: 27 additions & 0 deletions src/generator/spec/exercise_test_case_spec.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require "spec"
require "json"
require "../exercises/exercise_test_case"

class DummyTestCase < ExerciseTestCase
JSON.mapping(
description: String
)

def workload; end

def test_name; end
end

describe "DummyTestCase" do
describe "#pending" do
it "outputs 'it' if the given integer is 0" do
dummy_test_case = DummyTestCase.from_json("{\"description\": \"hello\"}")
dummy_test_case.pending?(0).should eq("it")
end

it "outputs 'pending' if the given integer is greater than 0" do
dummy_test_case = DummyTestCase.from_json("{\"description\": \"hello\"}")
dummy_test_case.pending?(1).should eq("pending")
end
end
end

0 comments on commit b38194d

Please sign in to comment.