et 0.5.9 → 0.7.3

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.
@@ -1 +1 @@
1
- 2.2.3
1
+ 2.6.3
@@ -1,39 +1,44 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- et (0.5.9)
4
+ et (0.7.2)
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
 
10
13
  GEM
11
14
  remote: https://rubygems.org/
12
15
  specs:
13
- coderay (1.1.1)
14
- diff-lcs (1.2.5)
16
+ coderay (1.1.2)
17
+ diff-lcs (1.3)
18
+ faraday (0.17.3)
19
+ multipart-post (>= 1.2, < 3)
20
+ faraday_middleware (0.14.0)
21
+ faraday (>= 0.7.4, < 1.0)
15
22
  gli (2.11.0)
16
- method_source (0.8.2)
17
- multipart-post (2.0.0)
18
- pry (0.10.3)
19
- coderay (~> 1.1.0)
20
- method_source (~> 0.8.1)
21
- slop (~> 3.4)
23
+ method_source (1.0.0)
24
+ multipart-post (2.1.1)
25
+ pry (0.13.1)
26
+ coderay (~> 1.1)
27
+ method_source (~> 1.0)
22
28
  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)
29
+ rspec (3.9.0)
30
+ rspec-core (~> 3.9.0)
31
+ rspec-expectations (~> 3.9.0)
32
+ rspec-mocks (~> 3.9.0)
33
+ rspec-core (3.9.2)
34
+ rspec-support (~> 3.9.3)
35
+ rspec-expectations (3.9.2)
30
36
  diff-lcs (>= 1.2.0, < 2.0)
31
- rspec-support (~> 3.4.0)
32
- rspec-mocks (3.4.1)
37
+ rspec-support (~> 3.9.0)
38
+ rspec-mocks (3.9.1)
33
39
  diff-lcs (>= 1.2.0, < 2.0)
34
- rspec-support (~> 3.4.0)
35
- rspec-support (3.4.1)
36
- slop (3.6.0)
40
+ rspec-support (~> 3.9.0)
41
+ rspec-support (3.9.3)
37
42
 
38
43
  PLATFORMS
39
44
  ruby
@@ -41,7 +46,6 @@ PLATFORMS
41
46
  DEPENDENCIES
42
47
  et!
43
48
  pry (~> 0)
44
- rake (~> 10)
45
49
 
46
50
  BUNDLED WITH
47
- 1.12.5
51
+ 1.17.2
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
@@ -15,14 +15,15 @@ DESC
15
15
 
16
16
  s.files = `git ls-files`.split("\n")
17
17
  s.require_paths << "lib"
18
- s.has_rdoc = false
19
18
  s.bindir = "bin"
20
19
  s.executables << "et"
21
20
  s.license = "MIT"
22
- s.add_development_dependency("rake", "~> 10")
23
21
  s.add_development_dependency("pry", '~> 0')
24
22
  s.add_runtime_dependency("rspec", "~> 3.0")
23
+ s.add_runtime_dependency("rake", "~> 10")
25
24
  s.add_runtime_dependency("rspec-mocks", "~> 3.0")
26
25
  s.add_runtime_dependency("multipart-post", "~> 2.0")
27
26
  s.add_runtime_dependency("gli", "2.11.0")
27
+ s.add_runtime_dependency("faraday", "~> 0.9")
28
+ s.add_runtime_dependency("faraday_middleware", "~> 0.10")
28
29
  end
data/lib/et.rb CHANGED
@@ -3,9 +3,8 @@ 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"
9
- require_relative "et/challenge"
10
- require_relative "et/exercise"
11
10
  require_relative "et/archive_manager"
@@ -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
+ response = nil
19
+ api_client.open do |client|
20
+ response = client.get('/lessons.json', :submittable => 1)
21
+ end
22
+ response.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.open 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).open 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.open 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}), &block)
10
+ end
11
+
12
+ def open(&block)
13
+ begin
14
+ block.call(@connection)
15
+ rescue Faraday::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,24 +51,16 @@ 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
- def config
61
- @config ||= YAML.load_file(File.join(dir, ".lesson.yml"))
62
- end
63
-
64
56
  def random_archive_path
65
57
  File.join(Dir.mktmpdir, "#{SecureRandom.hex}.tar.gz")
66
58
  end
67
59
 
68
60
  def find_lesson_dir(current_dir)
69
- path = File.join(current_dir, ".lesson.yml")
61
+ parent_directory = File.dirname(current_dir)
70
62
 
71
- if File.exists?(path)
63
+ if Config.new(parent_directory).exists?
72
64
  current_dir
73
65
  elsif current_dir == "." || Pathname.new(current_dir).root?
74
66
  nil
@@ -1,5 +1,4 @@
1
1
  require "gli"
2
- require "yaml"
3
2
 
4
3
  module ET
5
4
  class Runner
@@ -40,7 +39,7 @@ module ET
40
39
  desc "List available lessons."
41
40
  command :list do |c|
42
41
  c.action do |_global_options, _options, _cmdargs|
43
- Formatter.print_table(api.list_lessons, :slug, :title, :type)
42
+ Formatter.print_table(api.list_lessons, 'slug', 'title', 'type')
44
43
  end
45
44
  end
46
45
 
@@ -49,7 +48,7 @@ module ET
49
48
  c.action do |_global_options, _options, cmdargs|
50
49
  cmdargs.each do |slug|
51
50
  lesson = api.get_lesson(slug)
52
- archive = api.download_file(lesson[:archive_url])
51
+ archive = api.download_file(lesson['archive_url'])
53
52
  archive_manager = ET::ArchiveManager.new(archive, cwd)
54
53
  archive_manager.unpack
55
54
 
@@ -77,19 +76,6 @@ module ET
77
76
  end
78
77
  end
79
78
 
80
- desc "Run an exercise test suite."
81
- command :test do |c|
82
- c.action do |_global_options, _options, _cmdargs|
83
- exercise = Exercise.new(cwd)
84
-
85
- if exercise.exists?
86
- exercise.run_tests
87
- else
88
- raise StandardError.new("Not in an exercise directory.")
89
- end
90
- end
91
- end
92
-
93
79
  run(args)
94
80
  end
95
81
 
@@ -0,0 +1,58 @@
1
+ require "rake/file_list"
2
+
3
+ module ET
4
+ class SubmissionFileList
5
+ include Enumerable
6
+ DEFAULT_IGNORE_GLOBS = [
7
+ '.etignore',
8
+ 'node_modules/',
9
+ 'coverage/'
10
+ ]
11
+
12
+ def initialize(path)
13
+ @path = path
14
+ end
15
+
16
+ def files
17
+ unless @files
18
+ @files = Rake::FileList[File.join(@path, "**/*"), File.join(@path, ".etignore")]
19
+ ignore_globs.each do |glob|
20
+ filename = File.join(@path, glob)
21
+
22
+ if File.directory?(filename)
23
+ filename += glob.end_with?("/") ? "**/*" : "/**/*"
24
+ end
25
+
26
+ @files = @files.exclude(filename)
27
+ end
28
+ @files = @files.sub(File.join(@path, "/"), "")
29
+ end
30
+
31
+ @files
32
+ end
33
+
34
+ def each(&block)
35
+ files.each do |file|
36
+ block.call(file)
37
+ end
38
+ end
39
+
40
+ protected
41
+ def ignore_globs
42
+ etignore_globs + [] + DEFAULT_IGNORE_GLOBS
43
+ end
44
+
45
+ def etignore_globs
46
+ unless @etignore_globs
47
+ etignore = File.join(@path, '.etignore')
48
+ if FileTest.exists?(etignore)
49
+ @etignore_globs = File.read(etignore).split(/\n/)
50
+ @etignore_globs.delete_if { | string | string.start_with?("#") || string.empty?}
51
+ else
52
+ @etignore_globs = []
53
+ end
54
+ end
55
+ @etignore_globs
56
+ end
57
+ end
58
+ end