et 0.5.5 → 0.5.6.beta

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: 878c7e0d9b0502f6a7d4e1b74eac6f3b74fd37d8
4
- data.tar.gz: 9de9207f81b70fc88e79b3789535c65852fa7946
3
+ metadata.gz: b2782275fe3bc5bae13bec94ba826d026cb9e7c0
4
+ data.tar.gz: 0e65bdf3385e33a0073aac29dbd10607227455e6
5
5
  SHA512:
6
- metadata.gz: 855269fa8bf46ca3e373da7364b35872a73356cd973098eaceabb535be52f7381cfc11347403d45f403a5887b6f41f912536cc442d70fcb2a4e5502e9c617cb3
7
- data.tar.gz: af29f4894062f35784fceb19e0316f7134208b53c3048591090a48ceff3eabf715d63b7f816cb1c4c7129af73335e5debf944b59cae35f92d0e221098e2e159f
6
+ metadata.gz: b7e91173a2eb580bd11ddfc80da3f05d7c78ce533c176722fcf5d16cd0f6dfffde7da84f1f05f96c08261bb29cc81f071fbbae54e10b036f7dd0bcbeb0e45fef
7
+ data.tar.gz: a9f8c1d3ca5ad1f8c4130f1670d5b07b84e5896b249804c9e5375af7da3966febb74a2c7717d9ad7b0971546ec2acfd896aee1d4268c7b7ab5657b4e39b93f73
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- 2.2.0
1
+ 2.2.3
data/bin/et CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- require "et"
2
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'et'))
3
3
 
4
4
  runner = ET::Runner.new
5
5
  exit runner.go(ARGV)
data/lib/et.rb CHANGED
@@ -1,9 +1,11 @@
1
1
  require "tmpdir"
2
- require "et/version"
3
- require "et/runner"
4
- require "et/api"
5
- require "et/config"
6
- require "et/formatter"
7
- require "et/lesson"
8
- require "et/challenge"
9
- require "et/exercise"
2
+ require_relative "et/version"
3
+ require_relative "et/runner"
4
+ require_relative "et/operating_system"
5
+ require_relative "et/api"
6
+ require_relative "et/config"
7
+ require_relative "et/formatter"
8
+ require_relative "et/lesson"
9
+ require_relative "et/challenge"
10
+ require_relative "et/exercise"
11
+ require_relative "et/archive_manager"
data/lib/et/api.rb CHANGED
@@ -2,6 +2,7 @@ require "net/http/post/multipart"
2
2
  require "securerandom"
3
3
  require "base64"
4
4
  require "json"
5
+ require "openssl"
5
6
 
6
7
  module ET
7
8
  class API
@@ -17,12 +18,7 @@ module ET
17
18
  request = Net::HTTP::Get.new(lessons_url)
18
19
  request["Authorization"] = auth_header
19
20
 
20
- response = nil
21
- Net::HTTP.start(lessons_url.host, lessons_url.port,
22
- use_ssl: lessons_url.scheme == "https") do |http|
23
-
24
- response = http.request(request)
25
- end
21
+ response = issue_request(request)
26
22
  JSON.parse(response.body, symbolize_names: true)[:lessons]
27
23
  end
28
24
 
@@ -30,12 +26,8 @@ module ET
30
26
  request = Net::HTTP::Get.new(lesson_url(slug))
31
27
  request["Authorization"] = auth_header
32
28
 
33
- response = nil
34
- Net::HTTP.start(lessons_url.host, lessons_url.port,
35
- use_ssl: lessons_url.scheme == "https") do |http|
29
+ response = issue_request(request)
36
30
 
37
- response = http.request(request)
38
- end
39
31
  body = JSON.parse(response.body, symbolize_names: true)
40
32
  body[:lesson]
41
33
  end
@@ -44,17 +36,16 @@ module ET
44
36
  uri = URI(url)
45
37
  dest = random_filename
46
38
 
47
- Net::HTTP.start(uri.host, uri.port,
48
- use_ssl: uri.scheme == "https") do |http|
49
-
50
- resp = http.get(uri.path)
51
-
39
+ request = Net::HTTP::Get.new(uri.path)
40
+ response = issue_request(request, url)
41
+ if response.code == "200"
52
42
  open(dest, 'wb') do |file|
53
- file.write(resp.body)
43
+ file.write(response.body)
54
44
  end
45
+ dest
46
+ else
47
+ nil
55
48
  end
56
-
57
- dest
58
49
  end
59
50
 
60
51
  def submit_lesson(lesson)
@@ -66,16 +57,33 @@ module ET
66
57
  "submission[archive]" => UploadIO.new(f, "application/x-tar", "archive.tar.gz"))
67
58
  request["Authorization"] = auth_header
68
59
 
69
- Net::HTTP.start(url.host, url.port,
70
- use_ssl: url.scheme == "https") do |http|
60
+ issue_request(request)
61
+ end
62
+ end
63
+
64
+ 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|
71
70
 
72
71
  http.request(request)
73
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
74
84
  end
75
85
  end
76
86
 
77
- private
78
-
79
87
  def lesson_url(slug)
80
88
  URI.join(host, "lessons/#{slug}.json?submittable=1")
81
89
  end
@@ -99,5 +107,9 @@ module ET
99
107
  def auth_header
100
108
  "Basic #{credentials}"
101
109
  end
110
+
111
+ def operating_system
112
+ @os ||= ET::OperatingSystem.new
113
+ end
102
114
  end
103
115
  end
@@ -0,0 +1,49 @@
1
+ require "rubygems/package"
2
+ require "pry"
3
+
4
+ class ET::ArchiveManager
5
+ attr_reader :archive, :destination, :unpacked_files
6
+
7
+ def initialize(archive, destination)
8
+ @archive = archive
9
+ @destination = destination
10
+ end
11
+
12
+ def delete_archive
13
+ File.delete(archive)
14
+ end
15
+
16
+ def unpack
17
+ @unpacked_files = []
18
+ File.open(archive, "rb") do |tar_gz|
19
+ uncompress(tar_gz)
20
+ end
21
+ @unpacked_files
22
+ end
23
+
24
+ private
25
+
26
+ def uncompress(file)
27
+ Zlib::GzipReader.open(file) do |tar|
28
+ process_tar(tar)
29
+ end
30
+ end
31
+
32
+ def process_tar(tar)
33
+ Gem::Package::TarReader.new(tar) do |entries|
34
+ entries.each { |entry| create_file(entry) }
35
+ end
36
+ end
37
+
38
+ def create_file(entry)
39
+ if entry.file?
40
+ filename = File.join(destination, entry.full_name)
41
+ FileUtils.mkdir_p(File.dirname(filename))
42
+ File.open(filename, "wb") do |f|
43
+ f.write(entry.read)
44
+ end
45
+ File.chmod(entry.header.mode, filename)
46
+ @unpacked_files << filename
47
+ end
48
+ end
49
+ end
data/lib/et/lesson.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require "pathname"
2
2
  require "securerandom"
3
+ require "rubygems/package"
3
4
 
4
5
  module ET
5
6
  class Lesson
@@ -13,19 +14,26 @@ module ET
13
14
  if exists?
14
15
  filepath = random_archive_path
15
16
 
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
17
+ File.open(filepath, "wb") do |file|
18
+ Zlib::GzipWriter.wrap(file) do |gz|
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.length) do |io|
28
+ io.write(file_contents)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
28
35
  end
36
+ filepath
29
37
  else
30
38
  nil
31
39
  end
@@ -0,0 +1,37 @@
1
+ require 'rbconfig'
2
+
3
+ module ET
4
+ class OperatingSystem
5
+ attr_reader :raw_name, :version
6
+
7
+ def initialize(os = nil, version = nil)
8
+ @raw_name, @version = os, version
9
+ if @raw_name.nil?
10
+ derive_raw_name
11
+ end
12
+ end
13
+
14
+ #cribbed from Selenium
15
+ def name
16
+ case @raw_name
17
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
18
+ :windows
19
+ when /darwin|mac os/
20
+ :mac
21
+ when /linux/
22
+ :linux
23
+ when /solaris|bsd/
24
+ :unix
25
+ end
26
+ end
27
+
28
+ def platform_family?(platform)
29
+ name == platform.to_sym
30
+ end
31
+
32
+ protected
33
+ def derive_raw_name
34
+ @raw_name = RbConfig::CONFIG['host_os']
35
+ end
36
+ end
37
+ end
data/lib/et/runner.rb CHANGED
@@ -50,13 +50,14 @@ module ET
50
50
  cmdargs.each do |slug|
51
51
  lesson = api.get_lesson(slug)
52
52
  archive = api.download_file(lesson[:archive_url])
53
+ archive_manager = ET::ArchiveManager.new(archive, cwd)
54
+ archive_manager.unpack
53
55
 
54
- if system("tar zxf #{archive} -C #{cwd}")
55
- system("rm #{archive}")
56
- challenge_dir = File.join(cwd, slug)
57
- puts "Extracted challenge to #{challenge_dir}"
56
+ if !archive_manager.unpacked_files.empty?
57
+ archive_manager.delete_archive
58
+ puts "'#{slug}' extracted to '#{archive_manager.destination}'"
58
59
  else
59
- raise StandardError.new("Failed to extract the challenge archive.")
60
+ raise StandardError.new("Failed to extract the archive.")
60
61
  end
61
62
  end
62
63
  end
data/lib/et/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module ET
2
- VERSION = "0.5.5"
2
+ VERSION = "0.5.6.beta"
3
3
  end
data/spec/lib/api_spec.rb CHANGED
@@ -1,16 +1,18 @@
1
1
  describe ET::API do
2
2
  let(:api) { ET::API.new(host: "http://localhost:3000") }
3
+ let(:lessons_uri) do
4
+ URI("http://localhost:3000/lessons.json?submittable=1")
5
+ end
3
6
 
4
- describe "lessons" do
5
- let(:lessons_response) do
6
- File.read("spec/data/lessons.json")
7
- end
7
+ let(:lessons_response) do
8
+ File.read("spec/data/lessons.json")
9
+ end
8
10
 
11
+ describe "lessons" do
9
12
  it "queries for a list of lessons" do
10
13
  request = {}
11
14
  response = double
12
15
  http = double
13
- lessons_uri = URI("http://localhost:3000/lessons.json?submittable=1")
14
16
  expect(Net::HTTP::Get).to receive(:new).
15
17
  with(lessons_uri).
16
18
  and_return(request)
@@ -38,6 +40,7 @@ describe ET::API do
38
40
  request = {}
39
41
  response = double
40
42
  http = double
43
+
41
44
  lesson_uri = URI("http://localhost:3000/lessons/rock-paper-scissors.json?submittable=1")
42
45
  expect(Net::HTTP::Get).to receive(:new).
43
46
  with(lesson_uri).
@@ -56,4 +59,79 @@ describe ET::API do
56
59
  expect(result[:archive_url]).to eq("http://example.com/rock-paper-scissors.tar.gz")
57
60
  end
58
61
  end
62
+
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)
68
+
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
72
+
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
100
+ end
101
+ end
102
+
103
+ context 'downloading files' do
104
+ it 'returns nil when a 404 is encountered' do
105
+
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)
111
+
112
+ expect(api.download_file("http://example.com/somefile.tar.gz")).to be_nil
113
+ end
114
+ it 'returns a local file when a challenge is successfully downloaded' do
115
+ path = '/tmp/et'
116
+ filename = 'fab'
117
+
118
+ FileUtils.rm_rf(File.join(path, filename))
119
+ FileUtils.mkdir_p(path)
120
+
121
+ allow(Dir).to receive(:mktmpdir).and_return(path)
122
+ allow(SecureRandom).to receive(:hex).and_return(filename)
123
+
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'
133
+
134
+ expect(api.download_file(url)).to eql(File.join(path, filename))
135
+ end
136
+ end
59
137
  end
@@ -0,0 +1,27 @@
1
+ describe ET::ArchiveManager do
2
+ let!(:archive) { File.join(File.dirname(__FILE__), "..", "data", "some-challenge.tar.gz") }
3
+ let!(:destination) { Dir.mktmpdir }
4
+ let!(:lesson_extractor) { ET::ArchiveManager.new(archive, destination) }
5
+
6
+ describe "#new" do
7
+ it "takes an archive and destination" do
8
+ expect(lesson_extractor).to be_a(ET::ArchiveManager)
9
+ end
10
+ end
11
+
12
+ describe "#unpack" do
13
+ it "extracts files from the archive" do
14
+ lesson_extractor.unpack
15
+ extracted_files = Dir.entries(File.join(destination, "some-challenge"))
16
+ expect(extracted_files).to include("README.md")
17
+ expect(extracted_files).to include("sample.rb")
18
+ end
19
+
20
+ it "returns an array of extracted filenames" do
21
+ result = lesson_extractor.unpack
22
+ expect(result).to be_an(Array)
23
+ expect(result.size).to eq(2)
24
+ expect(result.first).to match /sample.rb/
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ require "spec_helper"
2
+
3
+ describe ET::Lesson do
4
+ context "archive! method" do
5
+ it "creates a tar.gz file" do
6
+ path = '/tmp/et'
7
+ filename = 'fab'
8
+
9
+ FileUtils.rm_rf(File.join(path, filename))
10
+ FileUtils.mkdir_p(path)
11
+
12
+ allow(Dir).to receive(:mktmpdir).and_return(path)
13
+ allow(SecureRandom).to receive(:hex).and_return(filename)
14
+
15
+ exercise_path = File.expand_path(
16
+ File.join(File.dirname(__FILE__), '../data/sample-exercise'))
17
+ lesson = ET::Lesson.new(exercise_path)
18
+ resulting_file = lesson.archive!
19
+
20
+ expect(resulting_file).to_not be_nil
21
+ expect(FileTest).to exist(resulting_file)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,20 @@
1
+ describe ET::OperatingSystem do
2
+ context '10.10' do
3
+ let(:minor) { '10.10' }
4
+ let(:os) { ET::OperatingSystem.new('darwin', minor) }
5
+
6
+ it 'has a name' do
7
+ expect(os.name).to eq(:mac)
8
+ end
9
+
10
+ end
11
+
12
+ context '10.9' do
13
+ let(:minor) { '10.9' }
14
+ let(:os) { ET::OperatingSystem.new('darwin', minor + '.4') }
15
+
16
+ it 'has a minor version' do
17
+ expect(os.name).to eq(:mac)
18
+ end
19
+ end
20
+ 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.5.5
4
+ version: 0.5.6.beta
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Sheehan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-06-17 00:00:00.000000000 Z
11
+ date: 2016-06-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -116,11 +116,13 @@ files:
116
116
  - et.gemspec
117
117
  - lib/et.rb
118
118
  - lib/et/api.rb
119
+ - lib/et/archive_manager.rb
119
120
  - lib/et/challenge.rb
120
121
  - lib/et/config.rb
121
122
  - lib/et/exercise.rb
122
123
  - lib/et/formatter.rb
123
124
  - lib/et/lesson.rb
125
+ - lib/et/operating_system.rb
124
126
  - lib/et/runner.rb
125
127
  - lib/et/version.rb
126
128
  - spec/cli/get_lesson_spec.rb
@@ -139,9 +141,12 @@ files:
139
141
  - spec/data/sample-exercise/test/sample_exercise_test.rb
140
142
  - spec/data/some-challenge.tar.gz
141
143
  - spec/lib/api_spec.rb
144
+ - spec/lib/archive_manager_spec.rb
142
145
  - spec/lib/challenge_spec.rb
143
146
  - spec/lib/config_spec.rb
144
147
  - spec/lib/exercise_spec.rb
148
+ - spec/lib/lesson_spec.rb
149
+ - spec/lib/operating_system_spec.rb
145
150
  - spec/spec_helper.rb
146
151
  - spec/support/helpers/archive_helper.rb
147
152
  - spec/support/helpers/output_catcher.rb
@@ -163,12 +168,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
163
168
  version: '0'
164
169
  required_rubygems_version: !ruby/object:Gem::Requirement
165
170
  requirements:
166
- - - ">="
171
+ - - ">"
167
172
  - !ruby/object:Gem::Version
168
- version: '0'
173
+ version: 1.3.1
169
174
  requirements: []
170
175
  rubyforge_project:
171
- rubygems_version: 2.2.2
176
+ rubygems_version: 2.4.5.1
172
177
  signing_key:
173
178
  specification_version: 4
174
179
  summary: Command-line interface for the event horizon.