et 0.0.1 → 0.1.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/.gitignore +3 -0
- data/.rspec +3 -0
- data/.travis.yml +3 -0
- data/Gemfile.lock +21 -0
- data/Rakefile +3 -38
- data/bin/et +2 -8
- data/et.gemspec +4 -1
- data/lib/et.rb +5 -4
- data/lib/et/api.rb +83 -0
- data/lib/et/config.rb +60 -0
- data/lib/et/formatter.rb +42 -0
- data/lib/et/runner.rb +135 -0
- data/lib/et/version.rb +1 -1
- data/spec/cli/get_challenge_spec.rb +42 -0
- data/spec/cli/init_spec.rb +27 -0
- data/spec/cli/list_challenges_spec.rb +30 -0
- data/spec/cli/submit_challenge_spec.rb +23 -0
- data/spec/data/challenges.json +1 -0
- data/spec/data/some-challenge.tar.gz +0 -0
- data/spec/lib/api_spec.rb +21 -0
- data/spec/lib/config_spec.rb +49 -0
- data/spec/spec_helper.rb +56 -0
- data/spec/support/helpers/output_catcher.rb +17 -0
- data/spec/support/helpers/path_helper.rb +7 -0
- data/spec/support/helpers/sample_files.rb +28 -0
- metadata +64 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f73dea1999d7fc46db767ea705e6022aceb45a24
|
4
|
+
data.tar.gz: 29df46b104d1484f458a5b9e60346a219544b4b7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4835233905faf16607d6fd5a37d264e814827b5be95b4477d43dd04588eaf3dfc03c6104be971f90a7e212a89c068e19dc51992c07582bcbd688c81f7694ac35
|
7
|
+
data.tar.gz: 14555a87bf0bd86fef92fcbb5256e7bd4cc82098c653d69566fc3f8aaae99f9dcf3232d34095228430b3e01c37eea759d63b8e7791f54dc59d9c29695cd66cd7
|
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile.lock
CHANGED
@@ -3,12 +3,31 @@ PATH
|
|
3
3
|
specs:
|
4
4
|
et (0.0.1)
|
5
5
|
gli (= 2.11.0)
|
6
|
+
rest-client (~> 1.7.2)
|
6
7
|
|
7
8
|
GEM
|
8
9
|
remote: https://rubygems.org/
|
9
10
|
specs:
|
11
|
+
diff-lcs (1.2.5)
|
10
12
|
gli (2.11.0)
|
13
|
+
mime-types (2.3)
|
14
|
+
netrc (0.7.7)
|
11
15
|
rake (10.3.2)
|
16
|
+
rest-client (1.7.2)
|
17
|
+
mime-types (>= 1.16, < 3.0)
|
18
|
+
netrc (~> 0.7)
|
19
|
+
rspec (3.0.0)
|
20
|
+
rspec-core (~> 3.0.0)
|
21
|
+
rspec-expectations (~> 3.0.0)
|
22
|
+
rspec-mocks (~> 3.0.0)
|
23
|
+
rspec-core (3.0.2)
|
24
|
+
rspec-support (~> 3.0.0)
|
25
|
+
rspec-expectations (3.0.2)
|
26
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
+
rspec-support (~> 3.0.0)
|
28
|
+
rspec-mocks (3.0.2)
|
29
|
+
rspec-support (~> 3.0.0)
|
30
|
+
rspec-support (3.0.2)
|
12
31
|
|
13
32
|
PLATFORMS
|
14
33
|
ruby
|
@@ -16,3 +35,5 @@ PLATFORMS
|
|
16
35
|
DEPENDENCIES
|
17
36
|
et!
|
18
37
|
rake (~> 10.3)
|
38
|
+
rspec (~> 3.0.0)
|
39
|
+
rspec-mocks (~> 3.0.0)
|
data/Rakefile
CHANGED
@@ -1,41 +1,6 @@
|
|
1
1
|
require "rake/clean"
|
2
|
-
require "
|
3
|
-
require "rubygems/package_task"
|
4
|
-
require "rdoc/task"
|
5
|
-
require "cucumber"
|
6
|
-
require "cucumber/rake/task"
|
2
|
+
require "rspec/core/rake_task"
|
7
3
|
|
8
|
-
|
9
|
-
rd.main = "README.rdoc"
|
10
|
-
rd.rdoc_files.include("README.rdoc", "lib/**/*.rb", "bin/**/*")
|
11
|
-
rd.title = "Your application title"
|
12
|
-
end
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
13
5
|
|
14
|
-
|
15
|
-
CLEAN << CUKE_RESULTS
|
16
|
-
desc "Run features"
|
17
|
-
Cucumber::Rake::Task.new(:features) do |t|
|
18
|
-
opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
|
19
|
-
opts += " --tags #{ENV["TAGS"]}" if ENV["TAGS"]
|
20
|
-
t.cucumber_opts = opts
|
21
|
-
t.fork = false
|
22
|
-
end
|
23
|
-
|
24
|
-
desc "Run features tagged as work-in-progress (@wip)"
|
25
|
-
Cucumber::Rake::Task.new("features:wip") do |t|
|
26
|
-
tag_opts = " --tags @wip"
|
27
|
-
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} " +
|
28
|
-
"--format pretty -x -s#{tag_opts}"
|
29
|
-
t.fork = false
|
30
|
-
end
|
31
|
-
|
32
|
-
task cucumber: :features
|
33
|
-
task "cucumber:wip" => "features:wip"
|
34
|
-
task wip: "features:wip"
|
35
|
-
require "rake/testtask"
|
36
|
-
Rake::TestTask.new do |t|
|
37
|
-
t.libs << "test"
|
38
|
-
t.test_files = FileList["test/*_test.rb"]
|
39
|
-
end
|
40
|
-
|
41
|
-
task default: [:test, :features]
|
6
|
+
task default: [:spec]
|
data/bin/et
CHANGED
data/et.gemspec
CHANGED
@@ -19,6 +19,9 @@ DESC
|
|
19
19
|
s.bindir = "bin"
|
20
20
|
s.executables << "et"
|
21
21
|
s.license = "MIT"
|
22
|
-
s.add_development_dependency("rake", "~> 10
|
22
|
+
s.add_development_dependency("rake", "~> 10")
|
23
|
+
s.add_development_dependency("rspec", "~> 3.0")
|
24
|
+
s.add_development_dependency("rspec-mocks", "~> 3.0")
|
25
|
+
s.add_runtime_dependency("rest-client", "~> 1.7")
|
23
26
|
s.add_runtime_dependency("gli", "2.11.0")
|
24
27
|
end
|
data/lib/et.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
require "et/version
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require "et/version"
|
2
|
+
require "et/runner"
|
3
|
+
require "et/api"
|
4
|
+
require "et/config"
|
5
|
+
require "et/formatter"
|
data/lib/et/api.rb
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
require "rest-client"
|
2
|
+
require "securerandom"
|
3
|
+
require "base64"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
module ET
|
7
|
+
class API
|
8
|
+
attr_reader :host, :username, :token
|
9
|
+
|
10
|
+
def initialize(options)
|
11
|
+
@host = options[:host]
|
12
|
+
@username = options[:username]
|
13
|
+
@token = options[:token]
|
14
|
+
end
|
15
|
+
|
16
|
+
def list_challenges
|
17
|
+
response = RestClient.get(challenges_url)
|
18
|
+
JSON.parse(response, symbolize_names: true)
|
19
|
+
end
|
20
|
+
|
21
|
+
def get_challenge(slug)
|
22
|
+
response = RestClient.get(challenge_url(slug))
|
23
|
+
body = JSON.parse(response, symbolize_names: true)
|
24
|
+
body[:challenge]
|
25
|
+
end
|
26
|
+
|
27
|
+
def download_file(url)
|
28
|
+
uri = URI(url)
|
29
|
+
dest = random_filename
|
30
|
+
|
31
|
+
Net::HTTP.start(uri.host, uri.port,
|
32
|
+
use_ssl: uri.scheme == "https") do |http|
|
33
|
+
|
34
|
+
resp = http.get(uri.path)
|
35
|
+
|
36
|
+
open(dest, 'wb') do |file|
|
37
|
+
file.write(resp.body)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
dest
|
42
|
+
end
|
43
|
+
|
44
|
+
def submit_challenge(dir)
|
45
|
+
Dir.mktmpdir do |tmpdir|
|
46
|
+
slug = File.basename(dir)
|
47
|
+
|
48
|
+
submission_file = File.join(tmpdir, "submission.tar.gz")
|
49
|
+
if system("tar zcf #{submission_file} -C #{dir} .")
|
50
|
+
RestClient.post(submission_url(slug),
|
51
|
+
{ submission: { archive: File.new(submission_file) }},
|
52
|
+
{ "Authorization" => auth_header })
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def challenge_url(slug)
|
60
|
+
URI.join(host, "challenges/#{slug}.json").to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
def challenges_url
|
64
|
+
URI.join(host, "challenges.json").to_s
|
65
|
+
end
|
66
|
+
|
67
|
+
def submission_url(slug)
|
68
|
+
URI.join(host, "challenges/#{slug}/submissions.json").to_s
|
69
|
+
end
|
70
|
+
|
71
|
+
def random_filename
|
72
|
+
File.join(Dir.mktmpdir, SecureRandom.hex)
|
73
|
+
end
|
74
|
+
|
75
|
+
def credentials
|
76
|
+
Base64.strict_encode64("#{username}:#{token}")
|
77
|
+
end
|
78
|
+
|
79
|
+
def auth_header
|
80
|
+
"Basic #{credentials}"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/et/config.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module ET
|
2
|
+
class Config
|
3
|
+
attr_reader :current_dir
|
4
|
+
|
5
|
+
def initialize(current_dir)
|
6
|
+
@current_dir = current_dir
|
7
|
+
end
|
8
|
+
|
9
|
+
def path
|
10
|
+
@path ||= find_config_file(current_dir)
|
11
|
+
end
|
12
|
+
|
13
|
+
def host
|
14
|
+
if options["host"].start_with?("http")
|
15
|
+
options["host"]
|
16
|
+
else
|
17
|
+
"http://" + options["host"]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def username
|
22
|
+
options["username"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def token
|
26
|
+
options["token"]
|
27
|
+
end
|
28
|
+
|
29
|
+
def exists?
|
30
|
+
!path.nil?
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](key)
|
34
|
+
options[key]
|
35
|
+
end
|
36
|
+
|
37
|
+
def update(options)
|
38
|
+
@options = options
|
39
|
+
File.write(path, options.to_yaml)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def options
|
45
|
+
@options ||= YAML.load(File.read(path))
|
46
|
+
end
|
47
|
+
|
48
|
+
def find_config_file(dir)
|
49
|
+
config_path = File.join(dir, ".et")
|
50
|
+
|
51
|
+
if File.exists?(config_path)
|
52
|
+
config_path
|
53
|
+
elsif dir == "/" || dir == "."
|
54
|
+
nil
|
55
|
+
else
|
56
|
+
find_config_file(File.dirname(dir))
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/et/formatter.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
module ET
|
2
|
+
class Formatter
|
3
|
+
def self.print_table(data, *headers)
|
4
|
+
column_widths = headers.map do |header|
|
5
|
+
data.map { |row| row[header].length }.max
|
6
|
+
end
|
7
|
+
|
8
|
+
total_width = column_widths.reduce(0) do |width, sum|
|
9
|
+
sum + width + 3
|
10
|
+
end
|
11
|
+
|
12
|
+
header = build_row(headers, column_widths, YELLOW)
|
13
|
+
|
14
|
+
puts header
|
15
|
+
puts "\e[34m" + ("-" * total_width) + "\e[0m"
|
16
|
+
|
17
|
+
data.each do |row|
|
18
|
+
values = headers.map { |header| row[header] }
|
19
|
+
puts build_row(values, column_widths, WHITE)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def self.build_row(row, widths, color)
|
26
|
+
row.zip(widths).map do |value, width|
|
27
|
+
" \e[#{color}m%-#{width}s\e[0m " % value
|
28
|
+
end.join("\e[34m|\e[0m")
|
29
|
+
end
|
30
|
+
|
31
|
+
CLEAR = 0
|
32
|
+
BOLD = 1
|
33
|
+
BLACK = 30
|
34
|
+
RED = 31
|
35
|
+
GREEN = 32
|
36
|
+
YELLOW = 33
|
37
|
+
BLUE = 34
|
38
|
+
MAGENTA = 35
|
39
|
+
CYAN = 36
|
40
|
+
WHITE = 37
|
41
|
+
end
|
42
|
+
end
|
data/lib/et/runner.rb
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
require "gli"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
module ET
|
5
|
+
class Runner
|
6
|
+
include GLI::App
|
7
|
+
|
8
|
+
attr_reader :cwd
|
9
|
+
|
10
|
+
def initialize(cwd = Dir.pwd)
|
11
|
+
@cwd = cwd
|
12
|
+
end
|
13
|
+
|
14
|
+
def go(args)
|
15
|
+
version VERSION
|
16
|
+
|
17
|
+
pre { |_, _, _, _| check_config! }
|
18
|
+
|
19
|
+
desc "Initialize current directory as challenge work area."
|
20
|
+
skips_pre
|
21
|
+
command :init do |c|
|
22
|
+
c.flag [:u, :user], desc: "Username"
|
23
|
+
c.flag [:t, :token], desc: "Login token"
|
24
|
+
c.flag [:h, :host], desc: "Server hosting the challenges"
|
25
|
+
|
26
|
+
c.action do |_global_options, options, _cmdargs|
|
27
|
+
settings = {
|
28
|
+
"username" => options[:user],
|
29
|
+
"token" => options[:token],
|
30
|
+
"host" => options[:host]
|
31
|
+
}
|
32
|
+
|
33
|
+
settings = prompt_for_missing(settings)
|
34
|
+
save_config(settings)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
desc "List available challenges."
|
39
|
+
command :list do |c|
|
40
|
+
c.action do |_global_options, _options, _cmdargs|
|
41
|
+
Formatter.print_table(api.list_challenges[:challenges], :slug, :title)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
desc "Download challenge to your working area."
|
46
|
+
command :get do |c|
|
47
|
+
c.action do |_global_options, _options, cmdargs|
|
48
|
+
cmdargs.each do |slug|
|
49
|
+
challenge = api.get_challenge(slug)
|
50
|
+
archive = api.download_file(challenge[:archive_url])
|
51
|
+
|
52
|
+
system("tar zxf #{archive} -C #{cwd}")
|
53
|
+
system("rm #{archive}")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
desc "Submit the challenge in this directory."
|
59
|
+
command :submit do |c|
|
60
|
+
c.action do |_global_options, _options, _cmdargs|
|
61
|
+
api.submit_challenge(cwd)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
run(args)
|
66
|
+
end
|
67
|
+
|
68
|
+
def api
|
69
|
+
@api ||= API.new(host: host, username: username, token: token)
|
70
|
+
end
|
71
|
+
|
72
|
+
def host
|
73
|
+
config.host
|
74
|
+
end
|
75
|
+
|
76
|
+
def username
|
77
|
+
config.username
|
78
|
+
end
|
79
|
+
|
80
|
+
def token
|
81
|
+
config.token
|
82
|
+
end
|
83
|
+
|
84
|
+
def setting(key)
|
85
|
+
config.exists? && config[key]
|
86
|
+
end
|
87
|
+
|
88
|
+
def config
|
89
|
+
@config ||= Config.new(cwd)
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def prompt_for_missing(settings)
|
95
|
+
settings.each do |key, value|
|
96
|
+
if value.nil?
|
97
|
+
existing_value = setting(key)
|
98
|
+
|
99
|
+
if existing_value
|
100
|
+
print "#{key.capitalize} (#{existing_value}): "
|
101
|
+
else
|
102
|
+
print "#{key.capitalize}: "
|
103
|
+
end
|
104
|
+
|
105
|
+
input = gets.chomp
|
106
|
+
|
107
|
+
if input && !input.empty?
|
108
|
+
settings[key] = input
|
109
|
+
else
|
110
|
+
settings[key] = existing_value
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
settings
|
116
|
+
end
|
117
|
+
|
118
|
+
def check_config!
|
119
|
+
if config.exists?
|
120
|
+
true
|
121
|
+
else
|
122
|
+
raise StandardError.new("Could not find configuration file. " +
|
123
|
+
"Run `et init` to create one.")
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def save_config(settings)
|
128
|
+
if config.exists?
|
129
|
+
config.update(settings)
|
130
|
+
else
|
131
|
+
File.write(File.join(cwd, ".et"), settings.to_yaml)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
data/lib/et/version.rb
CHANGED
@@ -0,0 +1,42 @@
|
|
1
|
+
describe "get challenge" do
|
2
|
+
let(:challenge_info) do
|
3
|
+
{
|
4
|
+
title: "Some Challenge",
|
5
|
+
slug: "some-challenge",
|
6
|
+
archive_url: "http://localhost:3000/some-challenge.tar.gz"
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
let(:sample_archive_path) do
|
11
|
+
project_root.join("spec/data/some-challenge.tar.gz")
|
12
|
+
end
|
13
|
+
|
14
|
+
context "when in a working area" do
|
15
|
+
it "downloads and extracts the challenge" do
|
16
|
+
tmp_archive_path = File.join(Dir.tmpdir, "some-challenge.tar.gz")
|
17
|
+
system("cp #{sample_archive_path} #{tmp_archive_path}")
|
18
|
+
|
19
|
+
expect_any_instance_of(ET::API).to receive(:get_challenge).
|
20
|
+
with("some-challenge").
|
21
|
+
and_return(challenge_info)
|
22
|
+
|
23
|
+
expect_any_instance_of(ET::API).to receive(:download_file).
|
24
|
+
with("http://localhost:3000/some-challenge.tar.gz").
|
25
|
+
and_return(tmp_archive_path)
|
26
|
+
|
27
|
+
Dir.mktmpdir("test") do |tmpdir|
|
28
|
+
write_sample_config_to(tmpdir)
|
29
|
+
|
30
|
+
runner = ET::Runner.new(tmpdir)
|
31
|
+
_, _ = capture_output do
|
32
|
+
expect(runner.go(["get", "some-challenge"])).to eq(0)
|
33
|
+
end
|
34
|
+
|
35
|
+
["some-challenge/README.md", "some-challenge/sample.rb"].each do |filename|
|
36
|
+
path = File.join(tmpdir, filename)
|
37
|
+
expect(File.exist?(path)).to eq(true)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
describe "init" do
|
4
|
+
it "writes the user credentials to a config file" do
|
5
|
+
Dir.mktmpdir("test") do |tmpdir|
|
6
|
+
arguments = [
|
7
|
+
"init",
|
8
|
+
"-u", "barry",
|
9
|
+
"-t", "foobar",
|
10
|
+
"-h", "http://localhost:3000"
|
11
|
+
]
|
12
|
+
|
13
|
+
runner = ET::Runner.new(tmpdir)
|
14
|
+
_, _ = capture_output do
|
15
|
+
expect(runner.go(arguments)).to eq(0)
|
16
|
+
end
|
17
|
+
|
18
|
+
filepath = File.join(tmpdir, ".et")
|
19
|
+
expect(File.exist?(filepath)).to eq(true)
|
20
|
+
|
21
|
+
config = YAML.load(File.read(filepath))
|
22
|
+
expect(config["username"]).to eq("barry")
|
23
|
+
expect(config["token"]).to eq("foobar")
|
24
|
+
expect(config["host"]).to eq("http://localhost:3000")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
describe "list challenges" do
|
2
|
+
let(:sample_challenges) do
|
3
|
+
{
|
4
|
+
challenges: [
|
5
|
+
{ title: "Guess the Number", slug: "guess-the-number" },
|
6
|
+
{ title: "Blackjack", slug: "blackjack" }
|
7
|
+
]
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
it "prints the titles and slug" do
|
12
|
+
expect_any_instance_of(ET::API).to receive(:list_challenges).
|
13
|
+
and_return(sample_challenges)
|
14
|
+
|
15
|
+
Dir.mktmpdir("test") do |tmpdir|
|
16
|
+
write_sample_config_to(tmpdir)
|
17
|
+
|
18
|
+
runner = ET::Runner.new(tmpdir)
|
19
|
+
stdout, _ = capture_output do
|
20
|
+
expect(runner.go(["list"])).to eq(0)
|
21
|
+
end
|
22
|
+
|
23
|
+
expect(stdout).to include("Guess the Number")
|
24
|
+
expect(stdout).to include("guess-the-number")
|
25
|
+
|
26
|
+
expect(stdout).to include("Blackjack")
|
27
|
+
expect(stdout).to include("blackjack")
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
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
|
+
with(challenge_dir).
|
14
|
+
and_return(true)
|
15
|
+
|
16
|
+
runner = ET::Runner.new(challenge_dir)
|
17
|
+
_, _ = capture_output do
|
18
|
+
expect(runner.go(["submit"])).to eq(0)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
{"challenges":[{"title":"Auto-Guesser","slug":"auto-guesser"},{"title":"Guess the Number","slug":"guess-the-number"}]}
|
Binary file
|
@@ -0,0 +1,21 @@
|
|
1
|
+
describe ET::API do
|
2
|
+
let(:api) { ET::API.new(host: "http://localhost:3000") }
|
3
|
+
|
4
|
+
describe "challenges" do
|
5
|
+
let(:challenges_response) do
|
6
|
+
File.read("spec/data/challenges.json")
|
7
|
+
end
|
8
|
+
|
9
|
+
it "queries for a list of challenges" do
|
10
|
+
expect(RestClient).to receive(:get).
|
11
|
+
with("http://localhost:3000/challenges.json").
|
12
|
+
and_return(challenges_response)
|
13
|
+
|
14
|
+
results = api.list_challenges
|
15
|
+
|
16
|
+
expect(results[:challenges].count).to eq(2)
|
17
|
+
expect(results[:challenges][0][:title]).to eq("Auto-Guesser")
|
18
|
+
expect(results[:challenges][0][:slug]).to eq("auto-guesser")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
describe ET::Config do
|
2
|
+
describe "#path" do
|
3
|
+
it "finds the config file in the given directory" do
|
4
|
+
Dir.mktmpdir("test") do |tmpdir|
|
5
|
+
write_sample_config_to(tmpdir)
|
6
|
+
|
7
|
+
config = ET::Config.new(tmpdir)
|
8
|
+
expect(config.path).to eq(File.join(tmpdir, ".et"))
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "finds the config file in parent directories" do
|
13
|
+
Dir.mktmpdir("test") do |parent_dir|
|
14
|
+
nested_dir = File.join(parent_dir, "nested")
|
15
|
+
Dir.mkdir(nested_dir)
|
16
|
+
|
17
|
+
write_sample_config_to(parent_dir)
|
18
|
+
|
19
|
+
config = ET::Config.new(nested_dir)
|
20
|
+
expect(config.path).to eq(File.join(parent_dir, ".et"))
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it "returns nil if no config file present" do
|
25
|
+
config = ET::Config.new(Dir.tmpdir)
|
26
|
+
expect(config.path).to eq(nil)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#host" do
|
31
|
+
it "reads the host from the config" do
|
32
|
+
Dir.mktmpdir("test") do |tmpdir|
|
33
|
+
write_sample_config_to(tmpdir, "host" => "http://example.com")
|
34
|
+
|
35
|
+
config = ET::Config.new(tmpdir)
|
36
|
+
expect(config.host).to eq("http://example.com")
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "preprends http if scheme not provided" do
|
41
|
+
Dir.mktmpdir("test") do |tmpdir|
|
42
|
+
write_sample_config_to(tmpdir, "host" => "example.com")
|
43
|
+
|
44
|
+
config = ET::Config.new(tmpdir)
|
45
|
+
expect(config.host).to eq("http://example.com")
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each do |f|
|
2
|
+
require f
|
3
|
+
end
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
# These two settings work together to allow you to limit a spec run
|
7
|
+
# to individual examples or groups you care about by tagging them with
|
8
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
9
|
+
# get run.
|
10
|
+
config.filter_run :focus
|
11
|
+
config.run_all_when_everything_filtered = true
|
12
|
+
|
13
|
+
# Run specs in random order to surface order dependencies. If you find an
|
14
|
+
# order dependency and want to debug it, you can fix the order by providing
|
15
|
+
# the seed, which is printed after each run.
|
16
|
+
# --seed 1234
|
17
|
+
config.order = :random
|
18
|
+
|
19
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
20
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
21
|
+
# test failures related to randomization by passing the same `--seed` value
|
22
|
+
# as the one that triggered the failure.
|
23
|
+
Kernel.srand config.seed
|
24
|
+
|
25
|
+
# rspec-expectations config goes here. You can use an alternate
|
26
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
27
|
+
# assertions if you prefer.
|
28
|
+
config.expect_with :rspec do |expectations|
|
29
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
30
|
+
# For more details, see:
|
31
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
32
|
+
expectations.syntax = :expect
|
33
|
+
end
|
34
|
+
|
35
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
36
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
37
|
+
config.mock_with :rspec do |mocks|
|
38
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
39
|
+
# For more details, see:
|
40
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
41
|
+
mocks.syntax = :expect
|
42
|
+
|
43
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
44
|
+
# a real object. This is generally recommended.
|
45
|
+
mocks.verify_partial_doubles = true
|
46
|
+
end
|
47
|
+
|
48
|
+
config.include OutputCatcher
|
49
|
+
config.include SampleFiles
|
50
|
+
config.include PathHelper
|
51
|
+
|
52
|
+
config.before :each do
|
53
|
+
allow(RestClient).to receive(:get)
|
54
|
+
allow(RestClient).to receive(:post)
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module OutputCatcher
|
2
|
+
def capture_output
|
3
|
+
original_stdout = $stdout
|
4
|
+
$stdout = fake_stdout = StringIO.new
|
5
|
+
original_stderr = $stderr
|
6
|
+
$stderr = fake_stderr = StringIO.new
|
7
|
+
|
8
|
+
begin
|
9
|
+
yield
|
10
|
+
ensure
|
11
|
+
$stdout = original_stdout
|
12
|
+
$stderr = original_stderr
|
13
|
+
end
|
14
|
+
|
15
|
+
[fake_stdout.string, fake_stderr.string]
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module SampleFiles
|
4
|
+
def write_sample_config_to(dir, additional_settings = nil)
|
5
|
+
config = {
|
6
|
+
"username" => "bob",
|
7
|
+
"token" => "1234",
|
8
|
+
"host" => "http://localhost:3000"
|
9
|
+
}
|
10
|
+
|
11
|
+
if additional_settings
|
12
|
+
config = config.merge(additional_settings)
|
13
|
+
end
|
14
|
+
|
15
|
+
path = File.join(dir, ".et")
|
16
|
+
File.write(path, config.to_yaml)
|
17
|
+
end
|
18
|
+
|
19
|
+
def write_sample_challenge_to(working_dir, slug)
|
20
|
+
challenge_dir = File.join(working_dir, slug)
|
21
|
+
system("mkdir #{challenge_dir}")
|
22
|
+
|
23
|
+
readme_path = File.join(challenge_dir, "README.md")
|
24
|
+
File.write(readme_path, "# README")
|
25
|
+
|
26
|
+
challenge_dir
|
27
|
+
end
|
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.0
|
4
|
+
version: 0.1.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-08-
|
11
|
+
date: 2014-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -16,14 +16,56 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '10
|
19
|
+
version: '10'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '10
|
26
|
+
version: '10'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec-mocks
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rest-client
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.7'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.7'
|
27
69
|
- !ruby/object:Gem::Dependency
|
28
70
|
name: gli
|
29
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -48,8 +90,10 @@ extensions: []
|
|
48
90
|
extra_rdoc_files: []
|
49
91
|
files:
|
50
92
|
- ".gitignore"
|
93
|
+
- ".rspec"
|
51
94
|
- ".ruby-gemset"
|
52
95
|
- ".ruby-version"
|
96
|
+
- ".travis.yml"
|
53
97
|
- Gemfile
|
54
98
|
- Gemfile.lock
|
55
99
|
- README.md
|
@@ -57,7 +101,23 @@ files:
|
|
57
101
|
- bin/et
|
58
102
|
- et.gemspec
|
59
103
|
- lib/et.rb
|
104
|
+
- lib/et/api.rb
|
105
|
+
- lib/et/config.rb
|
106
|
+
- lib/et/formatter.rb
|
107
|
+
- lib/et/runner.rb
|
60
108
|
- lib/et/version.rb
|
109
|
+
- spec/cli/get_challenge_spec.rb
|
110
|
+
- spec/cli/init_spec.rb
|
111
|
+
- spec/cli/list_challenges_spec.rb
|
112
|
+
- spec/cli/submit_challenge_spec.rb
|
113
|
+
- spec/data/challenges.json
|
114
|
+
- spec/data/some-challenge.tar.gz
|
115
|
+
- spec/lib/api_spec.rb
|
116
|
+
- spec/lib/config_spec.rb
|
117
|
+
- spec/spec_helper.rb
|
118
|
+
- spec/support/helpers/output_catcher.rb
|
119
|
+
- spec/support/helpers/path_helper.rb
|
120
|
+
- spec/support/helpers/sample_files.rb
|
61
121
|
homepage: http://www.launchacademy.com
|
62
122
|
licenses:
|
63
123
|
- MIT
|