et 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +1 -1
- data/Gemfile.lock +3 -3
- data/lib/et.rb +2 -0
- data/lib/et/api.rb +10 -10
- data/lib/et/challenge.rb +2 -67
- data/lib/et/config.rb +7 -3
- data/lib/et/exercise.rb +15 -0
- data/lib/et/lesson.rb +72 -0
- data/lib/et/runner.rb +27 -22
- data/lib/et/version.rb +1 -1
- data/spec/cli/{get_challenge_spec.rb → get_lesson_spec.rb} +6 -6
- data/spec/cli/list_lessons_spec.rb +29 -0
- data/spec/cli/run_exercise_test_suite_spec.rb +31 -0
- data/spec/cli/submit_lesson_spec.rb +37 -0
- data/spec/data/challenge.json +8 -1
- data/spec/data/lessons.json +19 -0
- data/spec/data/sample-challenge/.lesson.yml +6 -0
- data/spec/data/sample-challenge/problem.rb +1 -0
- data/spec/data/sample-challenge/sample-challenge.md +3 -0
- data/spec/data/sample-exercise/.lesson.yml +6 -0
- data/spec/data/sample-exercise/lib/sample_exercise.rb +1 -0
- data/spec/data/sample-exercise/sample-exercise.md +3 -0
- data/spec/data/sample-exercise/test/sample_exercise_test.rb +7 -0
- data/spec/lib/api_spec.rb +15 -14
- data/spec/lib/challenge_spec.rb +20 -32
- data/spec/lib/config_spec.rb +48 -1
- data/spec/lib/exercise_spec.rb +28 -0
- data/spec/support/helpers/sample_files.rb +11 -13
- metadata +17 -6
- data/spec/cli/list_challenges_spec.rb +0 -25
- data/spec/cli/submit_challenge_spec.rb +0 -22
- data/spec/data/challenges.json +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 44921ec7a8dc12f41fa49bac47557a76f75de660
|
4
|
+
data.tar.gz: 074a87d6f612a8ac878f8608222f2a69b0eb6b34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5a6d4ac016be8a94adb73d46880ff5da19266734d6d41b814312d638d5a84ce3d832d3f114da3212b7e509bfbc181d7ec123edb7c5da7936addfc0257bf4ce4
|
7
|
+
data.tar.gz: 13b8218ceccd48c032c2ab8b158fa597b4505de41309c87b28796a0faafc6b80733cec84aad3d08467b6adacaedd5222f5860e6b9a3f8fec2dfa27b27a1aebda
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.1.
|
1
|
+
2.1.3
|
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
et (0.
|
4
|
+
et (0.5.0)
|
5
5
|
gli (= 2.11.0)
|
6
6
|
rest-client (~> 1.7)
|
7
7
|
|
@@ -10,8 +10,8 @@ GEM
|
|
10
10
|
specs:
|
11
11
|
diff-lcs (1.2.5)
|
12
12
|
gli (2.11.0)
|
13
|
-
mime-types (2.3)
|
14
|
-
netrc (0.
|
13
|
+
mime-types (2.4.3)
|
14
|
+
netrc (0.8.0)
|
15
15
|
rake (10.3.2)
|
16
16
|
rest-client (1.7.2)
|
17
17
|
mime-types (>= 1.16, < 3.0)
|
data/lib/et.rb
CHANGED
data/lib/et/api.rb
CHANGED
@@ -13,13 +13,13 @@ module ET
|
|
13
13
|
@token = options[:token]
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
17
|
-
response = RestClient.get(
|
16
|
+
def list_lessons
|
17
|
+
response = RestClient.get(lessons_url)
|
18
18
|
JSON.parse(response, symbolize_names: true)[:lessons]
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
response = RestClient.get(
|
21
|
+
def get_lesson(slug)
|
22
|
+
response = RestClient.get(lesson_url(slug))
|
23
23
|
body = JSON.parse(response, symbolize_names: true)
|
24
24
|
body[:lesson]
|
25
25
|
end
|
@@ -41,21 +41,21 @@ module ET
|
|
41
41
|
dest
|
42
42
|
end
|
43
43
|
|
44
|
-
def
|
45
|
-
submission_file =
|
46
|
-
RestClient.post(submission_url(
|
44
|
+
def submit_lesson(lesson)
|
45
|
+
submission_file = lesson.archive!
|
46
|
+
RestClient.post(submission_url(lesson.slug),
|
47
47
|
{ submission: { archive: File.new(submission_file) }},
|
48
48
|
{ "Authorization" => auth_header })
|
49
49
|
end
|
50
50
|
|
51
51
|
private
|
52
52
|
|
53
|
-
def
|
53
|
+
def lesson_url(slug)
|
54
54
|
URI.join(host, "lessons/#{slug}.json").to_s
|
55
55
|
end
|
56
56
|
|
57
|
-
def
|
58
|
-
URI.join(host, "lessons.json?
|
57
|
+
def lessons_url
|
58
|
+
URI.join(host, "lessons.json?submittable=1").to_s
|
59
59
|
end
|
60
60
|
|
61
61
|
def submission_url(slug)
|
data/lib/et/challenge.rb
CHANGED
@@ -1,72 +1,7 @@
|
|
1
|
-
require "pathname"
|
2
|
-
require "securerandom"
|
3
|
-
|
4
1
|
module ET
|
5
|
-
class Challenge
|
6
|
-
attr_reader :cwd
|
7
|
-
|
8
|
-
def initialize(cwd)
|
9
|
-
@cwd = cwd
|
10
|
-
end
|
11
|
-
|
12
|
-
def archive!
|
13
|
-
if exists?
|
14
|
-
filepath = random_archive_path
|
15
|
-
|
16
|
-
cmd = "tar zcf #{filepath} -C #{dir}"
|
17
|
-
|
18
|
-
ignored_files.each do |file|
|
19
|
-
cmd += " --exclude='#{file}'"
|
20
|
-
end
|
21
|
-
|
22
|
-
cmd += " ."
|
23
|
-
|
24
|
-
if system(cmd)
|
25
|
-
filepath
|
26
|
-
else
|
27
|
-
nil
|
28
|
-
end
|
29
|
-
else
|
30
|
-
nil
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
def dir
|
35
|
-
@dir ||= find_challenge_dir(cwd)
|
36
|
-
end
|
37
|
-
|
38
|
-
def slug
|
39
|
-
File.basename(dir)
|
40
|
-
end
|
41
|
-
|
2
|
+
class Challenge < Lesson
|
42
3
|
def exists?
|
43
|
-
!dir.nil?
|
44
|
-
end
|
45
|
-
|
46
|
-
def ignored_files
|
47
|
-
(config["ignore"] || []) + [".lesson.yml"]
|
48
|
-
end
|
49
|
-
|
50
|
-
private
|
51
|
-
|
52
|
-
def config
|
53
|
-
@config ||= YAML.load(File.read(File.join(dir, ".lesson.yml")))
|
54
|
-
end
|
55
|
-
|
56
|
-
def random_archive_path
|
57
|
-
File.join(Dir.mktmpdir, "#{SecureRandom.hex}.tar.gz")
|
58
|
-
end
|
59
|
-
|
60
|
-
def find_challenge_dir(current_dir)
|
61
|
-
path = File.join(current_dir, ".lesson.yml")
|
62
|
-
|
63
|
-
if File.exists?(path)
|
64
|
-
current_dir
|
65
|
-
elsif current_dir == "." || Pathname.new(current_dir).root?
|
66
|
-
nil
|
67
|
-
else
|
68
|
-
find_challenge_dir(File.dirname(current_dir))
|
69
|
-
end
|
4
|
+
!dir.nil? && config["type"] == "challenge"
|
70
5
|
end
|
71
6
|
end
|
72
7
|
end
|
data/lib/et/config.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "yaml"
|
1
2
|
require "pathname"
|
2
3
|
|
3
4
|
module ET
|
@@ -36,9 +37,12 @@ module ET
|
|
36
37
|
options[key]
|
37
38
|
end
|
38
39
|
|
39
|
-
def
|
40
|
-
|
41
|
-
|
40
|
+
def save!(options)
|
41
|
+
if exists?
|
42
|
+
File.write(path, options.to_yaml)
|
43
|
+
else
|
44
|
+
File.write(File.join(current_dir, ".et"), options.to_yaml)
|
45
|
+
end
|
42
46
|
end
|
43
47
|
|
44
48
|
private
|
data/lib/et/exercise.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
module ET
|
2
|
+
class Exercise < Lesson
|
3
|
+
def run_tests
|
4
|
+
system("rspec", "--color", "--fail-fast", spec_file)
|
5
|
+
end
|
6
|
+
|
7
|
+
def exists?
|
8
|
+
!dir.nil? && config["type"] == "exercise"
|
9
|
+
end
|
10
|
+
|
11
|
+
def spec_file
|
12
|
+
File.join(dir, "test", "#{slug.gsub("-", "_")}_test.rb")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/et/lesson.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "securerandom"
|
3
|
+
|
4
|
+
module ET
|
5
|
+
class Lesson
|
6
|
+
attr_reader :cwd
|
7
|
+
|
8
|
+
def initialize(cwd)
|
9
|
+
@cwd = cwd
|
10
|
+
end
|
11
|
+
|
12
|
+
def archive!
|
13
|
+
if exists?
|
14
|
+
filepath = random_archive_path
|
15
|
+
|
16
|
+
cmd = "tar zcf #{filepath} -C #{dir}"
|
17
|
+
|
18
|
+
ignored_files.each do |file|
|
19
|
+
cmd += " --exclude='#{file}'"
|
20
|
+
end
|
21
|
+
|
22
|
+
cmd += " ."
|
23
|
+
|
24
|
+
if system(cmd)
|
25
|
+
filepath
|
26
|
+
else
|
27
|
+
nil
|
28
|
+
end
|
29
|
+
else
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def dir
|
35
|
+
@dir ||= find_lesson_dir(cwd)
|
36
|
+
end
|
37
|
+
|
38
|
+
def slug
|
39
|
+
File.basename(dir)
|
40
|
+
end
|
41
|
+
|
42
|
+
def exists?
|
43
|
+
!dir.nil?
|
44
|
+
end
|
45
|
+
|
46
|
+
def ignored_files
|
47
|
+
(config["ignore"] || []) + [".lesson.yml"]
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
|
52
|
+
def config
|
53
|
+
@config ||= YAML.load_file(File.join(dir, ".lesson.yml"))
|
54
|
+
end
|
55
|
+
|
56
|
+
def random_archive_path
|
57
|
+
File.join(Dir.mktmpdir, "#{SecureRandom.hex}.tar.gz")
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_lesson_dir(current_dir)
|
61
|
+
path = File.join(current_dir, ".lesson.yml")
|
62
|
+
|
63
|
+
if File.exists?(path)
|
64
|
+
current_dir
|
65
|
+
elsif current_dir == "." || Pathname.new(current_dir).root?
|
66
|
+
nil
|
67
|
+
else
|
68
|
+
find_lesson_dir(File.dirname(current_dir))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/et/runner.rb
CHANGED
@@ -16,12 +16,12 @@ module ET
|
|
16
16
|
|
17
17
|
pre { |_, _, _, _| check_config! }
|
18
18
|
|
19
|
-
desc "Initialize current directory as
|
19
|
+
desc "Initialize current directory as a work area."
|
20
20
|
skips_pre
|
21
21
|
command :init do |c|
|
22
22
|
c.flag [:u, :user], desc: "Username"
|
23
23
|
c.flag [:t, :token], desc: "Login token"
|
24
|
-
c.flag [:h, :host], desc: "Server hosting the
|
24
|
+
c.flag [:h, :host], desc: "Server hosting the lessons"
|
25
25
|
|
26
26
|
c.action do |_global_options, options, _cmdargs|
|
27
27
|
settings = {
|
@@ -31,25 +31,25 @@ module ET
|
|
31
31
|
}
|
32
32
|
|
33
33
|
settings = prompt_for_missing(settings)
|
34
|
-
|
34
|
+
config.save!(settings)
|
35
35
|
|
36
36
|
puts "Saved configuration to #{config.path}"
|
37
37
|
end
|
38
38
|
end
|
39
39
|
|
40
|
-
desc "List available
|
40
|
+
desc "List available lessons."
|
41
41
|
command :list do |c|
|
42
42
|
c.action do |_global_options, _options, _cmdargs|
|
43
|
-
Formatter.print_table(api.
|
43
|
+
Formatter.print_table(api.list_lessons, :slug, :title, :type)
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
desc "Download
|
47
|
+
desc "Download lesson to your working area."
|
48
48
|
command :get do |c|
|
49
49
|
c.action do |_global_options, _options, cmdargs|
|
50
50
|
cmdargs.each do |slug|
|
51
|
-
|
52
|
-
archive = api.download_file(
|
51
|
+
lesson = api.get_lesson(slug)
|
52
|
+
archive = api.download_file(lesson[:archive_url])
|
53
53
|
|
54
54
|
if system("tar zxf #{archive} -C #{cwd}")
|
55
55
|
system("rm #{archive}")
|
@@ -62,16 +62,29 @@ module ET
|
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
|
-
desc "Submit the
|
65
|
+
desc "Submit the lesson in this directory."
|
66
66
|
command :submit do |c|
|
67
67
|
c.action do |_global_options, _options, _cmdargs|
|
68
|
-
|
68
|
+
lesson = Lesson.new(cwd)
|
69
69
|
|
70
|
-
if
|
71
|
-
api.
|
72
|
-
puts "
|
70
|
+
if lesson.exists?
|
71
|
+
api.submit_lesson(lesson)
|
72
|
+
puts "Lesson submitted"
|
73
73
|
else
|
74
|
-
raise StandardError.new("Not in a
|
74
|
+
raise StandardError.new("Not in a lesson directory.")
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
desc "Run an exercise test suite."
|
80
|
+
command :test do |c|
|
81
|
+
c.action do |_global_options, _options, _cmdargs|
|
82
|
+
exercise = Exercise.new(cwd)
|
83
|
+
|
84
|
+
if exercise.exists?
|
85
|
+
exercise.run_tests
|
86
|
+
else
|
87
|
+
raise StandardError.new("Not in an exercise directory.")
|
75
88
|
end
|
76
89
|
end
|
77
90
|
end
|
@@ -137,13 +150,5 @@ module ET
|
|
137
150
|
"Run `et init` to create one.")
|
138
151
|
end
|
139
152
|
end
|
140
|
-
|
141
|
-
def save_config(settings)
|
142
|
-
if config.exists?
|
143
|
-
config.update(settings)
|
144
|
-
else
|
145
|
-
File.write(File.join(cwd, ".et"), settings.to_yaml)
|
146
|
-
end
|
147
|
-
end
|
148
153
|
end
|
149
154
|
end
|
data/lib/et/version.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
describe "get
|
2
|
-
let(:
|
1
|
+
describe "get lesson" do
|
2
|
+
let(:lesson_info) do
|
3
3
|
{
|
4
4
|
title: "Some Challenge",
|
5
5
|
slug: "some-challenge",
|
@@ -12,13 +12,13 @@ describe "get challenge" do
|
|
12
12
|
end
|
13
13
|
|
14
14
|
context "when in a working area" do
|
15
|
-
it "downloads and extracts the
|
15
|
+
it "downloads and extracts the lesson" do
|
16
16
|
tmp_archive_path = File.join(Dir.tmpdir, "some-challenge.tar.gz")
|
17
|
-
system("cp
|
17
|
+
system("cp", sample_archive_path.to_s, tmp_archive_path)
|
18
18
|
|
19
|
-
expect_any_instance_of(ET::API).to receive(:
|
19
|
+
expect_any_instance_of(ET::API).to receive(:get_lesson).
|
20
20
|
with("some-challenge").
|
21
|
-
and_return(
|
21
|
+
and_return(lesson_info)
|
22
22
|
|
23
23
|
expect_any_instance_of(ET::API).to receive(:download_file).
|
24
24
|
with("http://localhost:3000/some-challenge.tar.gz").
|
@@ -0,0 +1,29 @@
|
|
1
|
+
describe "list lessons" do
|
2
|
+
let(:sample_lessons_file) { project_root.join("spec/data/lessons.json") }
|
3
|
+
|
4
|
+
let(:sample_lessons) do
|
5
|
+
JSON.parse(File.read(sample_lessons_file), symbolize_names: true)[:lessons]
|
6
|
+
end
|
7
|
+
|
8
|
+
it "prints the titles and slug" do
|
9
|
+
expect_any_instance_of(ET::API).to receive(:list_lessons).
|
10
|
+
and_return(sample_lessons)
|
11
|
+
|
12
|
+
Dir.mktmpdir("test") do |tmpdir|
|
13
|
+
write_sample_config_to(tmpdir)
|
14
|
+
|
15
|
+
runner = ET::Runner.new(tmpdir)
|
16
|
+
stdout, _ = capture_output do
|
17
|
+
expect(runner.go(["list"])).to eq(0)
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(stdout).to include("Max Number")
|
21
|
+
expect(stdout).to include("max-number")
|
22
|
+
expect(stdout).to include("exercise")
|
23
|
+
|
24
|
+
expect(stdout).to include("Optimal Guesser")
|
25
|
+
expect(stdout).to include("optimal-guesser")
|
26
|
+
expect(stdout).to include("challenge")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
describe "run exercise test suite" do
|
2
|
+
context "when in an exercise directory" do
|
3
|
+
it "runs the test suite" do
|
4
|
+
Dir.mktmpdir("test") do |tmpdir|
|
5
|
+
write_sample_config_to(tmpdir)
|
6
|
+
exercise_dir = add_sample_exercise(tmpdir)
|
7
|
+
|
8
|
+
expect_any_instance_of(ET::Exercise).to receive(:run_tests).and_return(true)
|
9
|
+
|
10
|
+
runner = ET::Runner.new(exercise_dir)
|
11
|
+
_, _ = capture_output do
|
12
|
+
expect(runner.go(["test"])).to eq(0)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context "when not in an exercise directory" do
|
19
|
+
it "fails to run the test suite" do
|
20
|
+
Dir.mktmpdir("test") do |tmpdir|
|
21
|
+
write_sample_config_to(tmpdir)
|
22
|
+
expect_any_instance_of(ET::Exercise).to_not receive(:run_tests)
|
23
|
+
|
24
|
+
runner = ET::Runner.new(tmpdir)
|
25
|
+
_, _ = capture_output do
|
26
|
+
expect(runner.go(["test"])).to eq(1)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
describe "submit lesson" do
|
2
|
+
let(:sample_archive_path) do
|
3
|
+
project_root.join("spec/data/archive.tar.gz")
|
4
|
+
end
|
5
|
+
|
6
|
+
context "when in a working area" do
|
7
|
+
it "packages and uploads a challenge directory" do
|
8
|
+
Dir.mktmpdir("test") do |tmpdir|
|
9
|
+
write_sample_config_to(tmpdir)
|
10
|
+
challenge_dir = add_sample_challenge(tmpdir)
|
11
|
+
|
12
|
+
expect_any_instance_of(ET::API).to receive(:submit_lesson).
|
13
|
+
and_return(true)
|
14
|
+
|
15
|
+
runner = ET::Runner.new(challenge_dir)
|
16
|
+
_, _ = capture_output do
|
17
|
+
expect(runner.go(["submit"])).to eq(0)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
it "packages and uploads an exercise directory" do
|
23
|
+
Dir.mktmpdir("test") do |tmpdir|
|
24
|
+
write_sample_config_to(tmpdir)
|
25
|
+
exercise_dir = add_sample_exercise(tmpdir)
|
26
|
+
|
27
|
+
expect_any_instance_of(ET::API).to receive(:submit_lesson).
|
28
|
+
and_return(true)
|
29
|
+
|
30
|
+
runner = ET::Runner.new(exercise_dir)
|
31
|
+
_, _ = capture_output do
|
32
|
+
expect(runner.go(["submit"])).to eq(0)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/spec/data/challenge.json
CHANGED
@@ -1 +1,8 @@
|
|
1
|
-
{
|
1
|
+
{
|
2
|
+
"lesson": {
|
3
|
+
"archive_url": "http://example.com/rock-paper-scissors.tar.gz",
|
4
|
+
"body": "body goes here...",
|
5
|
+
"slug": "rock-paper-scissors",
|
6
|
+
"title": "Rock, Paper, Scissors"
|
7
|
+
}
|
8
|
+
}
|
@@ -0,0 +1,19 @@
|
|
1
|
+
{
|
2
|
+
"lessons": [
|
3
|
+
{
|
4
|
+
"slug": "max-number",
|
5
|
+
"title": "Max Number",
|
6
|
+
"type": "exercise"
|
7
|
+
},
|
8
|
+
{
|
9
|
+
"slug": "guess-the-number",
|
10
|
+
"title": "Guess the Number",
|
11
|
+
"type": "challenge"
|
12
|
+
},
|
13
|
+
{
|
14
|
+
"slug": "optimal-guesser",
|
15
|
+
"title": "Optimal Guesser",
|
16
|
+
"type": "challenge"
|
17
|
+
}
|
18
|
+
]
|
19
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
# YOUR CODE GOES HERE
|
@@ -0,0 +1 @@
|
|
1
|
+
puts "hi"
|
data/spec/lib/api_spec.rb
CHANGED
@@ -1,33 +1,34 @@
|
|
1
1
|
describe ET::API do
|
2
2
|
let(:api) { ET::API.new(host: "http://localhost:3000") }
|
3
3
|
|
4
|
-
describe "
|
5
|
-
let(:
|
6
|
-
File.read("spec/data/
|
4
|
+
describe "lessons" do
|
5
|
+
let(:lessons_response) do
|
6
|
+
File.read("spec/data/lessons.json")
|
7
7
|
end
|
8
8
|
|
9
|
-
it "queries for a list of
|
9
|
+
it "queries for a list of lessons" do
|
10
10
|
expect(RestClient).to receive(:get).
|
11
|
-
with("http://localhost:3000/lessons.json?
|
12
|
-
and_return(
|
11
|
+
with("http://localhost:3000/lessons.json?submittable=1").
|
12
|
+
and_return(lessons_response)
|
13
13
|
|
14
|
-
results = api.
|
14
|
+
results = api.list_lessons
|
15
15
|
|
16
|
-
expect(results.count).to eq(
|
17
|
-
expect(results[0][:title]).to eq("
|
18
|
-
expect(results[0][:slug]).to eq("
|
16
|
+
expect(results.count).to eq(3)
|
17
|
+
expect(results[0][:title]).to eq("Max Number")
|
18
|
+
expect(results[0][:slug]).to eq("max-number")
|
19
|
+
expect(results[0][:type]).to eq("exercise")
|
19
20
|
end
|
20
21
|
|
21
|
-
let(:
|
22
|
+
let(:lesson_response) do
|
22
23
|
File.read("spec/data/challenge.json")
|
23
24
|
end
|
24
25
|
|
25
|
-
it "queries for a single
|
26
|
+
it "queries for a single lesson" do
|
26
27
|
expect(RestClient).to receive(:get).
|
27
28
|
with("http://localhost:3000/lessons/rock-paper-scissors.json").
|
28
|
-
and_return(
|
29
|
+
and_return(lesson_response)
|
29
30
|
|
30
|
-
result = api.
|
31
|
+
result = api.get_lesson("rock-paper-scissors")
|
31
32
|
|
32
33
|
expect(result[:title]).to eq("Rock, Paper, Scissors")
|
33
34
|
expect(result[:archive_url]).to eq("http://example.com/rock-paper-scissors.tar.gz")
|
data/spec/lib/challenge_spec.rb
CHANGED
@@ -1,18 +1,10 @@
|
|
1
1
|
require "yaml"
|
2
2
|
|
3
3
|
describe ET::Challenge do
|
4
|
-
let(:challenge_info) do
|
5
|
-
{
|
6
|
-
"title" => "Guess the Number",
|
7
|
-
"ignore" => ["README.md"]
|
8
|
-
}
|
9
|
-
end
|
10
|
-
|
11
4
|
describe "#dir" do
|
12
5
|
it "selects the directory containing the challenge file" do
|
13
|
-
Dir.mktmpdir do |
|
14
|
-
|
15
|
-
File.write(challenge_path, challenge_info.to_yaml)
|
6
|
+
Dir.mktmpdir do |tmpdir|
|
7
|
+
challenge_dir = add_sample_challenge(tmpdir)
|
16
8
|
|
17
9
|
challenge = ET::Challenge.new(challenge_dir)
|
18
10
|
expect(challenge.dir).to eq(challenge_dir)
|
@@ -20,20 +12,22 @@ describe ET::Challenge do
|
|
20
12
|
end
|
21
13
|
|
22
14
|
it "checks parent directories for the challenge file" do
|
23
|
-
Dir.mktmpdir do |
|
24
|
-
|
25
|
-
File.write(challenge_path, challenge_info.to_yaml)
|
15
|
+
Dir.mktmpdir do |tmpdir|
|
16
|
+
challenge_dir = add_sample_challenge(tmpdir)
|
26
17
|
|
27
|
-
child_dir = File.join(
|
18
|
+
child_dir = File.join(challenge_dir, "child")
|
19
|
+
Dir.mkdir(child_dir)
|
28
20
|
|
29
21
|
challenge = ET::Challenge.new(child_dir)
|
30
|
-
expect(challenge.dir).to eq(
|
22
|
+
expect(challenge.dir).to eq(challenge_dir)
|
31
23
|
end
|
32
24
|
end
|
33
25
|
|
34
26
|
it "returns nil if no challenge file found" do
|
35
|
-
|
36
|
-
|
27
|
+
Dir.mktmpdir do |tmpdir|
|
28
|
+
challenge = ET::Challenge.new(tmpdir)
|
29
|
+
expect(challenge.dir).to eq(nil)
|
30
|
+
end
|
37
31
|
end
|
38
32
|
end
|
39
33
|
|
@@ -42,18 +36,14 @@ describe ET::Challenge do
|
|
42
36
|
archive_path = nil
|
43
37
|
|
44
38
|
begin
|
45
|
-
Dir.mktmpdir do |
|
46
|
-
|
47
|
-
File.write(challenge_path, challenge_info.to_yaml)
|
48
|
-
|
49
|
-
file_path = File.join(challenge_dir, "file.rb")
|
50
|
-
File.write(file_path, "2 + 2 == 5")
|
39
|
+
Dir.mktmpdir do |tmpdir|
|
40
|
+
challenge_dir = add_sample_challenge(tmpdir)
|
51
41
|
|
52
42
|
challenge = ET::Challenge.new(challenge_dir)
|
53
43
|
archive_path = challenge.archive!
|
54
44
|
|
55
|
-
contents = read_file_from_gzipped_archive(archive_path, "./
|
56
|
-
expect(contents).to eq("
|
45
|
+
contents = read_file_from_gzipped_archive(archive_path, "./problem.rb")
|
46
|
+
expect(contents).to eq("# YOUR CODE GOES HERE\n")
|
57
47
|
end
|
58
48
|
ensure
|
59
49
|
if archive_path && File.exist?(archive_path)
|
@@ -66,18 +56,16 @@ describe ET::Challenge do
|
|
66
56
|
archive_path = nil
|
67
57
|
|
68
58
|
begin
|
69
|
-
Dir.mktmpdir do |
|
70
|
-
|
71
|
-
File.write(File.join(dir, "file.rb"), "2 + 2 == 5")
|
72
|
-
File.write(File.join(dir, "README.md"), "Ignore me!")
|
59
|
+
Dir.mktmpdir do |tmpdir|
|
60
|
+
challenge_dir = add_sample_challenge(tmpdir)
|
73
61
|
|
74
|
-
challenge = ET::Challenge.new(
|
62
|
+
challenge = ET::Challenge.new(challenge_dir)
|
75
63
|
archive_path = challenge.archive!
|
76
64
|
|
77
65
|
files = list_files_in_gzipped_archive(archive_path)
|
78
66
|
|
79
|
-
expect(files).to include("./
|
80
|
-
expect(files).to_not include("./
|
67
|
+
expect(files).to include("./problem.rb")
|
68
|
+
expect(files).to_not include("./sample-challenge.md")
|
81
69
|
expect(files).to_not include("./.lesson.yml")
|
82
70
|
end
|
83
71
|
ensure
|
data/spec/lib/config_spec.rb
CHANGED
@@ -1,4 +1,51 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
1
3
|
describe ET::Config do
|
4
|
+
describe "#save" do
|
5
|
+
let(:options) do
|
6
|
+
{
|
7
|
+
"username" => "foobar",
|
8
|
+
"token" => "supersecret",
|
9
|
+
"host" => "http://example.com"
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
it "writes a new config file if doesn't exist" do
|
14
|
+
Dir.mktmpdir("test") do |tmpdir|
|
15
|
+
config = ET::Config.new(tmpdir)
|
16
|
+
config.save!(options)
|
17
|
+
|
18
|
+
config_file = File.join(tmpdir, ".et")
|
19
|
+
expect(File.exists?(config_file)).to eq(true)
|
20
|
+
|
21
|
+
saved_options = YAML.load_file(config_file)
|
22
|
+
|
23
|
+
options.each do |key, value|
|
24
|
+
expect(saved_options[key]).to eq(value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it "updates an existing config file with new options" do
|
30
|
+
Dir.mktmpdir("test") do |parent_dir|
|
31
|
+
nested_dir = File.join(parent_dir, "nested")
|
32
|
+
Dir.mkdir(nested_dir)
|
33
|
+
|
34
|
+
write_sample_config_to(parent_dir)
|
35
|
+
|
36
|
+
config = ET::Config.new(nested_dir)
|
37
|
+
config.save!(options)
|
38
|
+
|
39
|
+
expect(File.exists?(File.join(nested_dir, ".et"))).to eq(false)
|
40
|
+
|
41
|
+
saved_options = YAML.load_file(File.join(parent_dir, ".et"))
|
42
|
+
options.each do |key, value|
|
43
|
+
expect(saved_options[key]).to eq(value)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
2
49
|
describe "#path" do
|
3
50
|
it "finds the config file in the given directory" do
|
4
51
|
Dir.mktmpdir("test") do |tmpdir|
|
@@ -37,7 +84,7 @@ describe ET::Config do
|
|
37
84
|
end
|
38
85
|
end
|
39
86
|
|
40
|
-
it "
|
87
|
+
it "prepends http if scheme not provided" do
|
41
88
|
Dir.mktmpdir("test") do |tmpdir|
|
42
89
|
write_sample_config_to(tmpdir, "host" => "example.com")
|
43
90
|
|
@@ -0,0 +1,28 @@
|
|
1
|
+
describe ET::Exercise do
|
2
|
+
describe "#exists?" do
|
3
|
+
it "is true if within an exercise directory" do
|
4
|
+
Dir.mktmpdir do |tmpdir|
|
5
|
+
exercise_dir = add_sample_exercise(tmpdir)
|
6
|
+
exercise = ET::Exercise.new(exercise_dir)
|
7
|
+
|
8
|
+
expect(exercise.exists?).to eq(true)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "is false when in a challenge directory" do
|
13
|
+
Dir.mktmpdir do |tmpdir|
|
14
|
+
challenge_dir = add_sample_challenge(tmpdir)
|
15
|
+
exercise = ET::Exercise.new(challenge_dir)
|
16
|
+
|
17
|
+
expect(exercise.exists?).to eq(false)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "is false when not in a lesson dir" do
|
22
|
+
Dir.mktmpdir do |tmpdir|
|
23
|
+
exercise = ET::Exercise.new(tmpdir)
|
24
|
+
expect(exercise.exists?).to eq(false)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -2,29 +2,27 @@ require "yaml"
|
|
2
2
|
|
3
3
|
module SampleFiles
|
4
4
|
def write_sample_config_to(dir, additional_settings = nil)
|
5
|
-
|
5
|
+
settings = {
|
6
6
|
"username" => "bob",
|
7
7
|
"token" => "1234",
|
8
8
|
"host" => "http://localhost:3000"
|
9
9
|
}
|
10
10
|
|
11
11
|
if additional_settings
|
12
|
-
|
12
|
+
settings = settings.merge(additional_settings)
|
13
13
|
end
|
14
14
|
|
15
|
-
|
16
|
-
|
15
|
+
config = ET::Config.new(dir)
|
16
|
+
config.save!(settings)
|
17
17
|
end
|
18
18
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
system("mkdir #{dir}")
|
24
|
-
|
25
|
-
File.write(File.join(dir, "README.md"), "# README")
|
26
|
-
File.write(File.join(dir, ".lesson.yml"), options.to_yaml)
|
19
|
+
def add_sample_exercise(dir)
|
20
|
+
system("cp", "-r", project_root.join("spec/data/sample-exercise").to_s, dir)
|
21
|
+
File.join(dir, "sample-exercise")
|
22
|
+
end
|
27
23
|
|
28
|
-
|
24
|
+
def add_sample_challenge(dir)
|
25
|
+
system("cp", "-r", project_root.join("spec/data/sample-challenge").to_s, dir)
|
26
|
+
File.join(dir, "sample-challenge")
|
29
27
|
end
|
30
28
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: et
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Sheehan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-12-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -104,19 +104,30 @@ files:
|
|
104
104
|
- lib/et/api.rb
|
105
105
|
- lib/et/challenge.rb
|
106
106
|
- lib/et/config.rb
|
107
|
+
- lib/et/exercise.rb
|
107
108
|
- lib/et/formatter.rb
|
109
|
+
- lib/et/lesson.rb
|
108
110
|
- lib/et/runner.rb
|
109
111
|
- lib/et/version.rb
|
110
|
-
- spec/cli/
|
112
|
+
- spec/cli/get_lesson_spec.rb
|
111
113
|
- spec/cli/init_spec.rb
|
112
|
-
- spec/cli/
|
113
|
-
- spec/cli/
|
114
|
+
- spec/cli/list_lessons_spec.rb
|
115
|
+
- spec/cli/run_exercise_test_suite_spec.rb
|
116
|
+
- spec/cli/submit_lesson_spec.rb
|
114
117
|
- spec/data/challenge.json
|
115
|
-
- spec/data/
|
118
|
+
- spec/data/lessons.json
|
119
|
+
- spec/data/sample-challenge/.lesson.yml
|
120
|
+
- spec/data/sample-challenge/problem.rb
|
121
|
+
- spec/data/sample-challenge/sample-challenge.md
|
122
|
+
- spec/data/sample-exercise/.lesson.yml
|
123
|
+
- spec/data/sample-exercise/lib/sample_exercise.rb
|
124
|
+
- spec/data/sample-exercise/sample-exercise.md
|
125
|
+
- spec/data/sample-exercise/test/sample_exercise_test.rb
|
116
126
|
- spec/data/some-challenge.tar.gz
|
117
127
|
- spec/lib/api_spec.rb
|
118
128
|
- spec/lib/challenge_spec.rb
|
119
129
|
- spec/lib/config_spec.rb
|
130
|
+
- spec/lib/exercise_spec.rb
|
120
131
|
- spec/spec_helper.rb
|
121
132
|
- spec/support/helpers/archive_helper.rb
|
122
133
|
- spec/support/helpers/output_catcher.rb
|
@@ -1,25 +0,0 @@
|
|
1
|
-
describe "list challenges" do
|
2
|
-
let(:sample_challenges) do
|
3
|
-
JSON.parse(File.read("spec/data/challenges.json"), symbolize_names: true)[:lessons]
|
4
|
-
end
|
5
|
-
|
6
|
-
it "prints the titles and slug" do
|
7
|
-
expect_any_instance_of(ET::API).to receive(:list_challenges).
|
8
|
-
and_return(sample_challenges)
|
9
|
-
|
10
|
-
Dir.mktmpdir("test") do |tmpdir|
|
11
|
-
write_sample_config_to(tmpdir)
|
12
|
-
|
13
|
-
runner = ET::Runner.new(tmpdir)
|
14
|
-
stdout, _ = capture_output do
|
15
|
-
expect(runner.go(["list"])).to eq(0)
|
16
|
-
end
|
17
|
-
|
18
|
-
expect(stdout).to include("Guess the Number")
|
19
|
-
expect(stdout).to include("guess-the-number")
|
20
|
-
|
21
|
-
expect(stdout).to include("Auto-Guesser")
|
22
|
-
expect(stdout).to include("auto-guesser")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
describe "submit challenge" do
|
2
|
-
let(:sample_archive_path) do
|
3
|
-
project_root.join("spec/data/archive.tar.gz")
|
4
|
-
end
|
5
|
-
|
6
|
-
context "when in a working area" do
|
7
|
-
it "packages and uploads directory" do
|
8
|
-
Dir.mktmpdir("test") do |tmpdir|
|
9
|
-
write_sample_config_to(tmpdir)
|
10
|
-
challenge_dir = write_sample_challenge_to(tmpdir, "some-challenge")
|
11
|
-
|
12
|
-
expect_any_instance_of(ET::API).to receive(:submit_challenge).
|
13
|
-
and_return(true)
|
14
|
-
|
15
|
-
runner = ET::Runner.new(challenge_dir)
|
16
|
-
_, _ = capture_output do
|
17
|
-
expect(runner.go(["submit"])).to eq(0)
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
data/spec/data/challenges.json
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
{"lessons":[{"title":"Auto-Guesser","slug":"auto-guesser"},{"title":"Guess the Number","slug":"guess-the-number"}]}
|