et 0.5.9 → 0.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 62871439d06d15d1b7b78b7493a5165f316d28d3
4
- data.tar.gz: 8a415980a7d4c513faec60ebf3b1f80b370d6592
3
+ metadata.gz: 6e414688a6eb0fc5a464071284eaad87cf914f16
4
+ data.tar.gz: 647e0fbd0c9fadbe733f480e4d29db3d06e6a18a
5
5
  SHA512:
6
- metadata.gz: 09f4f85e6be87779eb493370b0befc626507a581508e3558703ae5ff70f2470d77e3951fe816b565a2ac97dd2d69f8c3ac2841593062039dcab1f06c8e55bbb6
7
- data.tar.gz: c9d81101825e76f1a805845a2549b8baf46c9f8a8f55c2a6bf7be0cf6c27a695c6a27d9a659869b1a2d08303c80665080c923d06f81dc29d2c0d8ab2b2662f62
6
+ metadata.gz: f8b87ae613c19d680bc2219b596e980351f04ddf9baee09efc1ce1624f9885e150db28312b8430c983ceccb90e78a701128afb8868607274dd6bcbdcf5515ec0
7
+ data.tar.gz: 7cdb0f85e3fecc7aa82431e8d3e1cda5d745b65582ccfbefe8539868ab29e19e5ea161796e7d54c12d2ab6c7d019185d0f380e78e31208add0ec1a747e5d74b2
@@ -1,9 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- et (0.5.9)
4
+ et (0.6.0)
5
+ faraday (~> 0.9)
6
+ faraday_middleware (~> 0.10)
5
7
  gli (= 2.11.0)
6
8
  multipart-post (~> 2.0)
9
+ rake (~> 10)
7
10
  rspec (~> 3.0)
8
11
  rspec-mocks (~> 3.0)
9
12
 
@@ -12,6 +15,10 @@ GEM
12
15
  specs:
13
16
  coderay (1.1.1)
14
17
  diff-lcs (1.2.5)
18
+ faraday (0.9.2)
19
+ multipart-post (>= 1.2, < 3)
20
+ faraday_middleware (0.10.0)
21
+ faraday (>= 0.7.4, < 0.10)
15
22
  gli (2.11.0)
16
23
  method_source (0.8.2)
17
24
  multipart-post (2.0.0)
@@ -20,19 +27,19 @@ GEM
20
27
  method_source (~> 0.8.1)
21
28
  slop (~> 3.4)
22
29
  rake (10.5.0)
23
- rspec (3.4.0)
24
- rspec-core (~> 3.4.0)
25
- rspec-expectations (~> 3.4.0)
26
- rspec-mocks (~> 3.4.0)
27
- rspec-core (3.4.4)
28
- rspec-support (~> 3.4.0)
29
- rspec-expectations (3.4.0)
30
+ rspec (3.5.0)
31
+ rspec-core (~> 3.5.0)
32
+ rspec-expectations (~> 3.5.0)
33
+ rspec-mocks (~> 3.5.0)
34
+ rspec-core (3.5.4)
35
+ rspec-support (~> 3.5.0)
36
+ rspec-expectations (3.5.0)
30
37
  diff-lcs (>= 1.2.0, < 2.0)
31
- rspec-support (~> 3.4.0)
32
- rspec-mocks (3.4.1)
38
+ rspec-support (~> 3.5.0)
39
+ rspec-mocks (3.5.0)
33
40
  diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.4.0)
35
- rspec-support (3.4.1)
41
+ rspec-support (~> 3.5.0)
42
+ rspec-support (3.5.0)
36
43
  slop (3.6.0)
37
44
 
38
45
  PLATFORMS
@@ -41,7 +48,6 @@ PLATFORMS
41
48
  DEPENDENCIES
42
49
  et!
43
50
  pry (~> 0)
44
- rake (~> 10)
45
51
 
46
52
  BUNDLED WITH
47
- 1.12.5
53
+ 1.13.5
data/README.md CHANGED
@@ -28,3 +28,20 @@ COMMANDS
28
28
  submit - Submit the lesson in this directory.
29
29
  test - Run an exercise test suite.
30
30
  ```
31
+
32
+ ### Releasing New Versions
33
+
34
+ Bundler provided `gem_tasks` have been incorporated into this libraries
35
+ `Rakefile`. Thankfully these tasks make releasing new gem versions a snap!
36
+
37
+ To release a new version of this gem:
38
+
39
+ 1. Bump the version according to [semantic versioning](http://semver.org/)
40
+ 2. Perform a git commit
41
+ 3. Run `rake release` from the project root
42
+
43
+ Bundler's provided rake task will appropriately push a tag to GitHub and the gem
44
+ itself to [rubygems.org](https://rubygems.org)
45
+
46
+ _Note:_ in order to release the gem you must be an authorized owner of it on
47
+ [rubygems.org](https://rubygems.org)
data/et.gemspec CHANGED
@@ -19,10 +19,12 @@ DESC
19
19
  s.bindir = "bin"
20
20
  s.executables << "et"
21
21
  s.license = "MIT"
22
- s.add_development_dependency("rake", "~> 10")
23
22
  s.add_development_dependency("pry", '~> 0')
24
23
  s.add_runtime_dependency("rspec", "~> 3.0")
24
+ s.add_runtime_dependency("rake", "~> 10")
25
25
  s.add_runtime_dependency("rspec-mocks", "~> 3.0")
26
26
  s.add_runtime_dependency("multipart-post", "~> 2.0")
27
27
  s.add_runtime_dependency("gli", "2.11.0")
28
+ s.add_runtime_dependency("faraday", "~> 0.9")
29
+ s.add_runtime_dependency("faraday_middleware", "~> 0.10")
28
30
  end
data/lib/et.rb CHANGED
@@ -3,6 +3,7 @@ require_relative "et/version"
3
3
  require_relative "et/runner"
4
4
  require_relative "et/operating_system"
5
5
  require_relative "et/api"
6
+ require_relative "et/submission_file_list"
6
7
  require_relative "et/config"
7
8
  require_relative "et/formatter"
8
9
  require_relative "et/lesson"
@@ -1,9 +1,9 @@
1
- require "net/http/post/multipart"
2
1
  require "securerandom"
3
2
  require "base64"
4
3
  require "json"
5
4
  require "openssl"
6
5
 
6
+ require_relative "fallback_connection"
7
7
  module ET
8
8
  class API
9
9
  attr_reader :host, :username, :token
@@ -15,30 +15,29 @@ module ET
15
15
  end
16
16
 
17
17
  def list_lessons
18
- request = Net::HTTP::Get.new(lessons_url)
19
- request["Authorization"] = auth_header
20
-
21
- response = issue_request(request)
22
- JSON.parse(response.body, symbolize_names: true)[:lessons]
18
+ resp = nil
19
+ api_client.with_ssl_fallback do |client|
20
+ resp = client.get('/lessons.json', :submittable => 1)
21
+ end
22
+ resp.body['lessons']
23
23
  end
24
24
 
25
25
  def get_lesson(slug)
26
- request = Net::HTTP::Get.new(lesson_url(slug))
27
- request["Authorization"] = auth_header
28
-
29
- response = issue_request(request)
30
-
31
- body = JSON.parse(response.body, symbolize_names: true)
32
- body[:lesson]
26
+ resp = nil
27
+ api_client.with_ssl_fallback do |client|
28
+ resp = client.get(lesson_url(slug), :submittable => 1)
29
+ end
30
+ resp.body['lesson']
33
31
  end
34
32
 
35
33
  def download_file(url)
36
- uri = URI(url)
34
+ response = nil
37
35
  dest = random_filename
38
36
 
39
- request = Net::HTTP::Get.new(uri.path)
40
- response = issue_request(request, url)
41
- if response.code == "200"
37
+ download_client(url).with_ssl_fallback do |client|
38
+ response = client.get(URI(url).path)
39
+ end
40
+ if response.status == 200
42
41
  open(dest, 'wb') do |file|
43
42
  file.write(response.body)
44
43
  end
@@ -50,66 +49,47 @@ module ET
50
49
 
51
50
  def submit_lesson(lesson)
52
51
  submission_file = lesson.archive!
53
- url = submission_url(lesson.slug)
54
-
55
- File.open(submission_file) do |f|
56
- request = Net::HTTP::Post::Multipart.new(url.path,
57
- "submission[archive]" => UploadIO.new(f, "application/x-tar", "archive.tar.gz"))
58
- request["Authorization"] = auth_header
59
-
60
- issue_request(request)
52
+ io = Faraday::UploadIO.new(submission_file, "application/x-tar")
53
+ resp = nil
54
+ api_client.with_ssl_fallback do |client|
55
+ resp = client.post(submission_url(lesson.slug),
56
+ "submission" => { "archive" => io})
61
57
  end
58
+ resp
62
59
  end
63
60
 
64
61
  private
65
- def issue_request(request, url = nil)
66
- uri = URI.parse(url || @host)
67
- begin
68
- Net::HTTP.start(uri.host, uri.port,
69
- use_ssl: uri.scheme == "https") do |http|
70
-
71
- http.request(request)
72
- end
73
- rescue OpenSSL::SSL::SSLError => e
74
- if operating_system.platform_family?(:windows)
75
- https = Net::HTTP.new(uri.host, uri.port)
76
- https.verify_mode = OpenSSL::SSL::VERIFY_NONE
77
- https.use_ssl = uri.scheme == 'https'
78
- https.start do |http|
79
- http.request(request)
80
- end
81
- else
82
- raise e
83
- end
84
- end
85
- end
86
-
87
62
  def lesson_url(slug)
88
- URI.join(host, "lessons/#{slug}.json?submittable=1")
89
- end
90
-
91
- def lessons_url
92
- URI.join(host, "lessons.json?submittable=1")
63
+ "/lessons/#{slug}.json"
93
64
  end
94
65
 
95
66
  def submission_url(slug)
96
- URI.join(host, "lessons/#{slug}/submissions.json")
67
+ "/lessons/#{slug}/submissions.json"
97
68
  end
98
69
 
99
70
  def random_filename
100
71
  File.join(Dir.mktmpdir, SecureRandom.hex)
101
72
  end
102
73
 
103
- def credentials
104
- Base64.strict_encode64("#{username}:#{token}")
105
- end
74
+ def api_client
75
+ @api_client ||= ET::FallbackConnection.new(:url => @host) do |client|
76
+ client.request :multipart
106
77
 
107
- def auth_header
108
- "Basic #{credentials}"
78
+ client.request :url_encoded
79
+ client.request :basic_auth, username, token
80
+
81
+ client.response :json, :content_type => /\bjson$/
82
+
83
+ client.adapter Faraday.default_adapter
84
+ end
109
85
  end
110
86
 
111
- def operating_system
112
- @os ||= ET::OperatingSystem.new
87
+ def download_client(url)
88
+ uri = URI(url)
89
+ scheme_and_host = [uri.scheme, uri.host].join('://')
90
+ ET::FallbackConnection.new(:url => scheme_and_host) do |client|
91
+ client.adapter Faraday.default_adapter
92
+ end
113
93
  end
114
94
  end
115
95
  end
@@ -0,0 +1,29 @@
1
+ require "faraday"
2
+ require "faraday_middleware"
3
+
4
+ module ET
5
+ class FallbackConnection
6
+ def initialize(opts = {}, &block)
7
+ @connection = Faraday.new(opts, &block)
8
+
9
+ @fallback_connection = Faraday.new(opts.merge(:ssl => {:verify => false}))
10
+ end
11
+
12
+ def with_ssl_fallback(&block)
13
+ begin
14
+ block.call(@connection)
15
+ rescue OpenSSL::SSL::SSLError => e
16
+ if operating_system.platform_family?(:windows)
17
+ block.call(@fallback_connection)
18
+ else
19
+ raise e
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+ def operating_system
26
+ @os ||= ET::OperatingSystem.new
27
+ end
28
+ end
29
+ end
@@ -17,16 +17,16 @@ module ET
17
17
  File.open(filepath, "wb") do |file|
18
18
  Zlib::GzipWriter.wrap(file) do |gz|
19
19
  Gem::Package::TarWriter.new(gz) do |tar|
20
- Dir.glob(File.join(dir, "**/*")).each do |file|
21
- relative_path = file.gsub(dir + "/", "")
22
- if !ignored_files.include?(relative_path)
23
- if FileTest.directory?(file)
24
- tar.mkdir(relative_path, 0755)
25
- else
26
- file_contents = File.read(file)
27
- tar.add_file_simple("./" + relative_path, 0555, file_contents.bytesize) do |io|
28
- io.write(file_contents)
29
- end
20
+ ET::SubmissionFileList.new(dir).each do |file|
21
+ relative_path = file
22
+ absolute_path = File.join(dir, file)
23
+
24
+ if FileTest.directory?(absolute_path)
25
+ tar.mkdir(relative_path, 0755)
26
+ else
27
+ file_contents = File.read(absolute_path)
28
+ tar.add_file_simple("./" + relative_path, 0555, file_contents.bytesize) do |io|
29
+ io.write(file_contents)
30
30
  end
31
31
  end
32
32
  end
@@ -51,10 +51,6 @@ module ET
51
51
  !dir.nil?
52
52
  end
53
53
 
54
- def ignored_files
55
- (config["ignore"] || []) + [".lesson.yml"]
56
- end
57
-
58
54
  protected
59
55
 
60
56
  def config
@@ -40,7 +40,7 @@ module ET
40
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.list_lessons, :slug, :title, :type)
43
+ Formatter.print_table(api.list_lessons, 'slug', 'title', 'type')
44
44
  end
45
45
  end
46
46
 
@@ -49,7 +49,7 @@ module ET
49
49
  c.action do |_global_options, _options, cmdargs|
50
50
  cmdargs.each do |slug|
51
51
  lesson = api.get_lesson(slug)
52
- archive = api.download_file(lesson[:archive_url])
52
+ archive = api.download_file(lesson['archive_url'])
53
53
  archive_manager = ET::ArchiveManager.new(archive, cwd)
54
54
  archive_manager.unpack
55
55
 
@@ -0,0 +1,50 @@
1
+ require "rake/file_list"
2
+
3
+ module ET
4
+ class SubmissionFileList
5
+ include Enumerable
6
+ DEFAULT_IGNORE_GLOBS = [
7
+ '.lesson.yml',
8
+ 'node_modules/**/*'
9
+ ]
10
+
11
+ def initialize(path)
12
+ @path = path
13
+ end
14
+
15
+ def files
16
+ unless @files
17
+ @files = Rake::FileList[File.join(@path, "**/*")]
18
+ ignore_globs.each do |glob|
19
+ @files = @files.exclude(File.join(@path, glob))
20
+ end
21
+ @files = @files.sub(File.join(@path, "/"), "")
22
+ end
23
+
24
+ @files
25
+ end
26
+
27
+ def each(&block)
28
+ files.each do |file|
29
+ block.call(file)
30
+ end
31
+ end
32
+
33
+ protected
34
+ def ignore_globs
35
+ lesson_ignore_globs + [] + DEFAULT_IGNORE_GLOBS
36
+ end
37
+
38
+ def lesson_ignore_globs
39
+ unless @lesson_ignore_globs
40
+ lesson_yml = File.join(@path, '.lesson.yml')
41
+ if FileTest.exists?(lesson_yml)
42
+ @lesson_ignore_globs = YAML.load_file(lesson_yml)['ignore'] || []
43
+ else
44
+ @lesson_ignore_globs = []
45
+ end
46
+ end
47
+ @lesson_ignore_globs
48
+ end
49
+ end
50
+ end
@@ -1,3 +1,3 @@
1
1
  module ET
2
- VERSION = "0.5.9"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -1,38 +1,38 @@
1
- describe "get lesson" do
1
+ describe 'get lesson' do
2
2
  let(:lesson_info) do
3
3
  {
4
- title: "Some Challenge",
5
- slug: "some-challenge",
6
- archive_url: "http://localhost:3000/some-challenge.tar.gz"
4
+ 'title' => 'Some Challenge',
5
+ 'slug' => 'some-challenge',
6
+ 'archive_url' => "http://localhost:3000/some-challenge.tar.gz"
7
7
  }
8
8
  end
9
9
 
10
10
  let(:sample_archive_path) do
11
- project_root.join("spec/data/some-challenge.tar.gz")
11
+ project_root.join('spec/data/some-challenge.tar.gz')
12
12
  end
13
13
 
14
- context "when in a working area" do
15
- it "downloads and extracts the lesson" do
16
- tmp_archive_path = File.join(Dir.tmpdir, "some-challenge.tar.gz")
17
- system("cp", sample_archive_path.to_s, tmp_archive_path)
14
+ context 'when in a working area' do
15
+ it 'downloads and extracts the lesson' do
16
+ tmp_archive_path = File.join(Dir.tmpdir, 'some-challenge.tar.gz')
17
+ system('cp', sample_archive_path.to_s, tmp_archive_path)
18
18
 
19
19
  expect_any_instance_of(ET::API).to receive(:get_lesson).
20
- with("some-challenge").
20
+ with('some-challenge').
21
21
  and_return(lesson_info)
22
22
 
23
23
  expect_any_instance_of(ET::API).to receive(:download_file).
24
- with("http://localhost:3000/some-challenge.tar.gz").
24
+ with('http://localhost:3000/some-challenge.tar.gz').
25
25
  and_return(tmp_archive_path)
26
26
 
27
- Dir.mktmpdir("test") do |tmpdir|
27
+ Dir.mktmpdir('test') do |tmpdir|
28
28
  write_sample_config_to(tmpdir)
29
29
 
30
30
  runner = ET::Runner.new(tmpdir)
31
31
  _, _ = capture_output do
32
- expect(runner.go(["get", "some-challenge"])).to eq(0)
32
+ expect(runner.go(['get', 'some-challenge'])).to eq(0)
33
33
  end
34
34
 
35
- ["some-challenge/README.md", "some-challenge/sample.rb"].each do |filename|
35
+ ['some-challenge/README.md', 'some-challenge/sample.rb'].each do |filename|
36
36
  path = File.join(tmpdir, filename)
37
37
  expect(File.exist?(path)).to eq(true)
38
38
  end
@@ -2,7 +2,7 @@ describe "list lessons" do
2
2
  let(:sample_lessons_file) { project_root.join("spec/data/lessons.json") }
3
3
 
4
4
  let(:sample_lessons) do
5
- JSON.parse(File.read(sample_lessons_file), symbolize_names: true)[:lessons]
5
+ JSON.parse(File.read(sample_lessons_file))['lessons']
6
6
  end
7
7
 
8
8
  it "prints the titles and slug" do
@@ -0,0 +1,7 @@
1
+ ---
2
+ title: "Bloated Challenge"
3
+ type: "challenge"
4
+ description: "Some description."
5
+ ignore:
6
+ - "bloated-challenge.md"
7
+ - "rando_folder/**/*"
@@ -0,0 +1,3 @@
1
+ ### Instructions
2
+
3
+ Do something.
@@ -0,0 +1 @@
1
+ alert('please do not include this file');
@@ -0,0 +1 @@
1
+ # YOUR CODE GOES HERE
@@ -0,0 +1 @@
1
+ console.log('I am random');
@@ -10,26 +10,24 @@ describe ET::API do
10
10
 
11
11
  describe "lessons" do
12
12
  it "queries for a list of lessons" do
13
- request = {}
14
- response = double
15
- http = double
16
- expect(Net::HTTP::Get).to receive(:new).
17
- with(lessons_uri).
18
- and_return(request)
19
- expect(Net::HTTP).to receive(:start).with(
20
- lessons_uri.host,
21
- lessons_uri.port,
22
- use_ssl: lessons_uri.scheme == "https").
23
- and_yield(http)
24
- expect(http).to receive(:request).and_return(response)
25
- expect(response).to receive(:body).and_return(lessons_response)
13
+ client = Faraday.new do |builder|
14
+ builder.response :json
15
+
16
+ builder.adapter :test do |stubs|
17
+ stubs.get("/lessons.json") do
18
+ [200, {}, lessons_response]
19
+ end
20
+ end
21
+ end
22
+
23
+ allow_any_instance_of(ET::FallbackConnection).to receive(:with_ssl_fallback).and_yield(client)
26
24
 
27
25
  results = api.list_lessons
28
26
 
29
27
  expect(results.count).to eq(3)
30
- expect(results[0][:title]).to eq("Max Number")
31
- expect(results[0][:slug]).to eq("max-number")
32
- expect(results[0][:type]).to eq("exercise")
28
+ expect(results[0]['title']).to eq("Max Number")
29
+ expect(results[0]['slug']).to eq("max-number")
30
+ expect(results[0]['type']).to eq("exercise")
33
31
  end
34
32
 
35
33
  let(:lesson_response) do
@@ -37,99 +35,67 @@ describe ET::API do
37
35
  end
38
36
 
39
37
  it "queries for a single lesson" do
40
- request = {}
41
- response = double
42
- http = double
43
-
44
- lesson_uri = URI("http://localhost:3000/lessons/rock-paper-scissors.json?submittable=1")
45
- expect(Net::HTTP::Get).to receive(:new).
46
- with(lesson_uri).
47
- and_return(request)
48
- expect(Net::HTTP).to receive(:start).with(
49
- lesson_uri.host,
50
- lesson_uri.port,
51
- use_ssl: lesson_uri.scheme == "https").
52
- and_yield(http)
53
- expect(http).to receive(:request).and_return(response)
54
- expect(response).to receive(:body).and_return(lesson_response)
55
-
56
- result = api.get_lesson("rock-paper-scissors")
38
+ client = Faraday.new do |builder|
39
+ builder.response :json
57
40
 
58
- expect(result[:title]).to eq("Rock, Paper, Scissors")
59
- expect(result[:archive_url]).to eq("http://example.com/rock-paper-scissors.tar.gz")
60
- end
61
- end
41
+ builder.adapter :test do |stubs|
42
+ stubs.get("/lessons/rock-paper-scissors.json") do
43
+ [200, {}, lesson_response]
44
+ end
45
+ end
46
+ end
62
47
 
63
- context 'ssl verification' do
64
- it 're-raises an exception for non Windows machines' do
65
- dbl_os = double
66
- allow(dbl_os).to receive(:platform_family?).with(:windows).and_return(false)
67
- expect(ET::OperatingSystem).to receive(:new).and_return(dbl_os)
48
+ allow_any_instance_of(ET::FallbackConnection).
49
+ to receive(:with_ssl_fallback).and_yield(client)
68
50
 
69
- expect(Net::HTTP).to receive(:start).and_raise(OpenSSL::SSL::SSLError)
70
- expect{ api.list_lessons }.to raise_error(OpenSSL::SSL::SSLError)
71
- end
51
+ result = api.get_lesson("rock-paper-scissors")
72
52
 
73
- it 'swallows the exception for windows machines and reissues' do
74
- http = double
75
- allow(http).to receive(:start).and_yield(http)
76
- allow(http).to receive(:verify_mode=)
77
- allow(http).to receive(:use_ssl=)
78
- response = double
79
-
80
- allow(http).to receive(:request).and_return(response)
81
- allow(response).to receive(:body).and_return(lessons_response)
82
-
83
- allow(Net::HTTP).to receive(:start).
84
- with(
85
- lessons_uri.host,
86
- lessons_uri.port,
87
- use_ssl: lessons_uri.scheme == 'https').
88
- and_raise(OpenSSL::SSL::SSLError)
89
-
90
- expect(Net::HTTP).to receive(:new).with(
91
- lessons_uri.host,
92
- lessons_uri.port).
93
- and_return(http)
94
-
95
- dbl_os = double
96
- allow(dbl_os).to receive(:platform_family?).and_return(:windows)
97
- allow(ET::OperatingSystem).to receive(:new).and_return(dbl_os)
98
-
99
- api.list_lessons
53
+ expect(result['title']).to eq("Rock, Paper, Scissors")
54
+ expect(result['archive_url']).to eq('http://example.com/rock-paper-scissors.tar.gz')
100
55
  end
101
- end
56
+ end
102
57
 
103
58
  context 'downloading files' do
104
59
  it 'returns nil when a 404 is encountered' do
60
+ filename = 'somefile.tar.gz'
61
+ client = Faraday.new do |builder|
62
+ builder.response :json
105
63
 
106
- http = double
107
- response = double
108
- allow(response).to receive(:code).and_return("404")
109
- allow(http).to receive(:request).and_return(response)
110
- allow(Net::HTTP).to receive(:start).and_yield(http)
64
+ builder.adapter :test do |stubs|
65
+ stubs.get("/#{filename}") do
66
+ [404, {}, '']
67
+ end
68
+ end
69
+ end
111
70
 
112
- expect(api.download_file("http://example.com/somefile.tar.gz")).to be_nil
71
+ allow_any_instance_of(ET::FallbackConnection).
72
+ to receive(:with_ssl_fallback).and_yield(client)
73
+
74
+ expect(api.download_file("http://example.com/#{filename}")).to be_nil
113
75
  end
76
+
114
77
  it 'returns a local file when a challenge is successfully downloaded' do
115
78
  path = '/tmp/et'
116
- filename = 'fab'
79
+ filename = 'some-challenge.tar.gz'
117
80
 
118
81
  FileUtils.rm_rf(File.join(path, filename))
119
82
  FileUtils.mkdir_p(path)
120
83
 
84
+ filename = 'somefile.tar.gz'
85
+ client = Faraday.new do |builder|
86
+ builder.adapter :test do |stubs|
87
+ stubs.get("/#{filename}") do
88
+ [200, {}, '']
89
+ end
90
+ end
91
+ end
92
+
93
+ allow_any_instance_of(ET::FallbackConnection).
94
+ to receive(:with_ssl_fallback).and_yield(client)
121
95
  allow(Dir).to receive(:mktmpdir).and_return(path)
122
96
  allow(SecureRandom).to receive(:hex).and_return(filename)
123
97
 
124
- http = double
125
- response = double
126
- file_contents = File.read(File.join(File.dirname(__FILE__), "../data/some-challenge.tar.gz"))
127
- allow(response).to receive(:body).and_return(file_contents)
128
- allow(response).to receive(:code).and_return("200")
129
- allow(http).to receive(:request).and_return(response)
130
- allow(Net::HTTP).to receive(:start).and_yield(http)
131
-
132
- url = 'http://example.com/some-challenge.tar.gz'
98
+ url = "http://example.com/#{filename}"
133
99
 
134
100
  expect(api.download_file(url)).to eql(File.join(path, filename))
135
101
  end
@@ -0,0 +1,42 @@
1
+ describe ET::FallbackConnection do
2
+ let(:cnn) do
3
+ ET::FallbackConnection.new(:url => 'http://localhost')
4
+ end
5
+
6
+ context 'ssl verification' do
7
+ it 're-raises an exception for non Windows machines' do
8
+ dbl_os = double
9
+ allow(dbl_os).to receive(:platform_family?).with(:windows).and_return(false)
10
+ expect(ET::OperatingSystem).to receive(:new).and_return(dbl_os)
11
+
12
+ allow_any_instance_of(Faraday::Connection).
13
+ to receive(:get).and_raise(OpenSSL::SSL::SSLError)
14
+
15
+ expect{ cnn.with_ssl_fallback{|client| client.get('/') }}.
16
+ to raise_error(OpenSSL::SSL::SSLError)
17
+ end
18
+
19
+ it 'swallows the exception for windows machines and reissues' do
20
+ dbl_os = double
21
+ allow(dbl_os).to receive(:platform_family?).and_return(:windows)
22
+ allow(ET::OperatingSystem).to receive(:new).and_return(dbl_os)
23
+
24
+ called_twice = false
25
+ allow_any_instance_of(Faraday::Connection).to receive(:get) do |*args|
26
+ if args[0].ssl.verify != false
27
+ #simulate a windows SSL verification failed
28
+ raise OpenSSL::SSL::SSLError
29
+ elsif args[0].ssl.verify == false
30
+ #flip the switch that the request was reissued without SSL verification
31
+ called_twice = true
32
+ double(:body => '{}')
33
+ end
34
+ end
35
+
36
+ cnn.with_ssl_fallback do |client|
37
+ client.get('/')
38
+ end
39
+ expect(called_twice).to be(true)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,27 @@
1
+ describe ET::SubmissionFileList do
2
+ let(:file_list) do
3
+ path = File.expand_path(File.join(
4
+ File.dirname(__FILE__), "../data/bloated-challenge"))
5
+ ET::SubmissionFileList.new(path)
6
+ end
7
+
8
+ it 'includes a relevant file' do
9
+ expect(file_list).to include('problem.rb')
10
+ end
11
+
12
+ it 'ignores a standard file' do
13
+ expect(file_list).to_not include('sample-challenge.md')
14
+ end
15
+
16
+ it 'ignores a file included in the default globs' do
17
+ expect(file_list).to_not include('node_modules/somefile.js')
18
+ end
19
+
20
+ it 'ignores a file included in a glob' do
21
+ expect(file_list).to_not include('node_modules/boo/somefile.js')
22
+ end
23
+
24
+ it 'ignores a file included in the lesson.yml ignore' do
25
+ expect(file_list).to_not include('rando_folder/rando_file.js')
26
+ end
27
+ end
@@ -49,9 +49,4 @@ RSpec.configure do |config|
49
49
  config.include SampleFiles
50
50
  config.include PathHelper
51
51
  config.include ArchiveHelper
52
-
53
- config.before :each do
54
- allow(Net::HTTP).to receive(:start)
55
- allow(Net::HTTP).to receive(:get)
56
- end
57
52
  end
metadata CHANGED
@@ -1,57 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: et
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.9
4
+ version: 0.6.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: 2016-06-15 00:00:00.000000000 Z
11
+ date: 2016-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: rake
14
+ name: pry
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '10'
19
+ version: '0'
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: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: pry
28
+ name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
33
+ version: '3.0'
34
+ type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0'
40
+ version: '3.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '3.0'
47
+ version: '10'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '3.0'
54
+ version: '10'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: rspec-mocks
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +94,34 @@ dependencies:
94
94
  - - '='
95
95
  - !ruby/object:Gem::Version
96
96
  version: 2.11.0
97
+ - !ruby/object:Gem::Dependency
98
+ name: faraday
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '0.9'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '0.9'
111
+ - !ruby/object:Gem::Dependency
112
+ name: faraday_middleware
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.10'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '0.10'
97
125
  description: |
98
126
  Users can download challenges and submit their solutions via the command-line
99
127
  interface.
@@ -120,16 +148,23 @@ files:
120
148
  - lib/et/challenge.rb
121
149
  - lib/et/config.rb
122
150
  - lib/et/exercise.rb
151
+ - lib/et/fallback_connection.rb
123
152
  - lib/et/formatter.rb
124
153
  - lib/et/lesson.rb
125
154
  - lib/et/operating_system.rb
126
155
  - lib/et/runner.rb
156
+ - lib/et/submission_file_list.rb
127
157
  - lib/et/version.rb
128
158
  - spec/cli/get_lesson_spec.rb
129
159
  - spec/cli/init_spec.rb
130
160
  - spec/cli/list_lessons_spec.rb
131
161
  - spec/cli/run_exercise_test_suite_spec.rb
132
162
  - spec/cli/submit_lesson_spec.rb
163
+ - spec/data/bloated-challenge/.lesson.yml
164
+ - spec/data/bloated-challenge/bloated-challenge.md
165
+ - spec/data/bloated-challenge/node_modules/boo/somefile.js
166
+ - spec/data/bloated-challenge/problem.rb
167
+ - spec/data/bloated-challenge/rando_folder/rando_file.js
133
168
  - spec/data/challenge.json
134
169
  - spec/data/lessons.json
135
170
  - spec/data/sample-challenge/.lesson.yml
@@ -145,8 +180,10 @@ files:
145
180
  - spec/lib/challenge_spec.rb
146
181
  - spec/lib/config_spec.rb
147
182
  - spec/lib/exercise_spec.rb
183
+ - spec/lib/fallback_connection_spec.rb
148
184
  - spec/lib/lesson_spec.rb
149
185
  - spec/lib/operating_system_spec.rb
186
+ - spec/lib/submission_file_list_spec.rb
150
187
  - spec/spec_helper.rb
151
188
  - spec/support/helpers/archive_helper.rb
152
189
  - spec/support/helpers/output_catcher.rb
@@ -173,7 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
210
  version: '0'
174
211
  requirements: []
175
212
  rubyforge_project:
176
- rubygems_version: 2.4.5
213
+ rubygems_version: 2.4.5.1
177
214
  signing_key:
178
215
  specification_version: 4
179
216
  summary: Command-line interface for the event horizon.