et 0.5.5 → 0.5.6.beta

Sign up to get free protection for your applications and to get access to all the features.
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.