learn-open 1.2.20 → 1.2.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +4 -0
  3. data/Guardfile +70 -0
  4. data/learn-open.gemspec +7 -1
  5. data/lib/learn_open.rb +66 -0
  6. data/lib/learn_open/adapters/io_adapter.rb +24 -0
  7. data/lib/learn_open/adapters/learn_web_adapter.rb +103 -0
  8. data/lib/learn_open/adapters/system_adapter.rb +30 -0
  9. data/lib/learn_open/environments.rb +35 -0
  10. data/lib/learn_open/environments/base_environment.rb +70 -0
  11. data/lib/learn_open/environments/generic_environment.rb +9 -0
  12. data/lib/learn_open/environments/ide_environment.rb +63 -0
  13. data/lib/learn_open/environments/jupyter_container_environment.rb +28 -0
  14. data/lib/learn_open/environments/linux_environment.rb +17 -0
  15. data/lib/learn_open/environments/mac_environment.rb +75 -0
  16. data/lib/learn_open/lessons.rb +24 -0
  17. data/lib/learn_open/lessons/base_lesson.rb +57 -0
  18. data/lib/learn_open/lessons/ios_lesson.rb +14 -0
  19. data/lib/learn_open/lessons/jupyter_lesson.rb +14 -0
  20. data/lib/learn_open/lessons/lab_lesson.rb +9 -0
  21. data/lib/learn_open/lessons/readme_lesson.rb +13 -0
  22. data/lib/learn_open/opener.rb +26 -469
  23. data/lib/learn_open/services/dependency_installers.rb +19 -0
  24. data/lib/learn_open/services/dependency_installers/base_installer.rb +21 -0
  25. data/lib/learn_open/services/dependency_installers/gem_installer.rb +14 -0
  26. data/lib/learn_open/services/dependency_installers/jupyter_pip_installer.rb +14 -0
  27. data/lib/learn_open/services/dependency_installers/node_package_installer.rb +20 -0
  28. data/lib/learn_open/services/dependency_installers/pip_installer.rb +14 -0
  29. data/lib/learn_open/services/file_backup_starter.rb +20 -0
  30. data/lib/learn_open/services/lesson_downloader.rb +82 -0
  31. data/lib/learn_open/services/logger.rb +23 -0
  32. data/lib/learn_open/version.rb +1 -1
  33. data/spec/fakes/fake_git.rb +20 -0
  34. data/spec/fakes/fake_learn_client.rb +214 -0
  35. data/spec/fixtures/learn-config +3 -0
  36. data/spec/learn_open/environments/ide_environment_spec.rb +62 -0
  37. data/spec/learn_open/opener_spec.rb +789 -14
  38. data/spec/spec_helper.rb +41 -0
  39. metadata +121 -5
  40. data/spec/home_dir/.learn-config +0 -3
@@ -0,0 +1,19 @@
1
+ module LearnOpen
2
+ module DependencyInstallers
3
+ def self.installer_types
4
+ [
5
+ PipInstaller,
6
+ GemInstaller,
7
+ NodeInstaller
8
+ ]
9
+ end
10
+
11
+ def self.run_installers(lesson, location, environment, options)
12
+ installer_types.each do |type|
13
+ if type.detect(lesson, location)
14
+ type.call(lesson, location, environment, options)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ module LearnOpen
2
+ module DependencyInstallers
3
+ class BaseInstaller
4
+ attr_reader :lesson, :location, :system_adapter, :io, :environment
5
+
6
+ def self.call(lesson, location, environment, options)
7
+ if self.detect(lesson, location)
8
+ self.new(lesson, location, environment, options).run
9
+ end
10
+ end
11
+
12
+ def initialize(lesson, location, environment, options)
13
+ @lesson = lesson
14
+ @location = location
15
+ @environment = environment
16
+ @system_adapter = options.fetch(:system_adapter, LearnOpen.system_adapter)
17
+ @io = options.fetch(:io, LearnOpen.default_io)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module LearnOpen
2
+ module DependencyInstallers
3
+ class GemInstaller < BaseInstaller
4
+ def self.detect(lesson, location)
5
+ File.exists?("#{lesson.to_path}/Gemfile")
6
+ end
7
+
8
+ def run
9
+ io.puts "Bundling..."
10
+ system_adapter.run_command("bundle install")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module LearnOpen
2
+ module DependencyInstallers
3
+ class JupyterPipInstall < BaseInstaller
4
+ def self.detect(lesson, location)
5
+ File.exists?("#{lesson.to_path}/requirements.txt")
6
+ end
7
+
8
+ def run
9
+ io.puts "Installing pip dependencies..."
10
+ system_adapter.run_command("/opt/conda/bin/python -m pip install -r requirements.txt")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ module LearnOpen
2
+ module DependencyInstallers
3
+ class NodeInstaller < BaseInstaller
4
+ def self.detect(lesson, location)
5
+ File.exists?("#{lesson.to_path}/package.json")
6
+ end
7
+
8
+ def run
9
+ io.puts 'Installing npm dependencies...'
10
+
11
+ case environment
12
+ when LearnOpen::Environments::IDEEnvironment
13
+ system_adapter.run_command("yarn install --no-lockfile")
14
+ else
15
+ system_adapter.run_command("npm install")
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ module LearnOpen
2
+ module DependencyInstallers
3
+ class PipInstaller < BaseInstaller
4
+ def self.detect(lesson, location)
5
+ File.exists?("#{lesson.to_path}/requirements.txt")
6
+ end
7
+
8
+ def run
9
+ io.puts "Installing pip dependencies..."
10
+ system_adapter.run_command("python -m pip install -r requirements.txt")
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,20 @@
1
+ module LearnOpen
2
+ class FileBackupStarter
3
+ attr_reader :lesson, :location, :system_adapter
4
+
5
+ def self.call(lesson, location, options)
6
+ self.new(lesson, location, options).call
7
+ end
8
+
9
+ def initialize(lesson, location, options)
10
+ @lesson = lesson
11
+ @location = location
12
+ @system_adapter = options.fetch(:system_adapter, LearnOpen.system_adapter)
13
+ end
14
+
15
+ def call
16
+ system_adapter.spawn("restore-lab", block: true)
17
+ system_adapter.watch_dir("#{lesson.to_path}", "backup-lab")
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,82 @@
1
+ module LearnOpen
2
+ class LessonDownloader
3
+ attr_reader :lesson, :location, :io, :logger, :client, :git_adapter
4
+
5
+ def self.call(lesson, location, options = {})
6
+ self.new(lesson, location, options).call
7
+ end
8
+
9
+ def initialize(lesson, location, options = {})
10
+ @lesson = lesson
11
+ @location = location
12
+ @client = options.fetch(:learn_web_client) { LearnOpen.learn_web_client }
13
+ @logger = options.fetch(:logger) { LearnOpen.logger }
14
+ @io = options.fetch(:io) { LearnOpen.default_io }
15
+ @git_adapter = options.fetch(:git_adapter) { LearnOpen.git_adapter }
16
+ end
17
+
18
+ def call
19
+ if !repo_exists?
20
+ fork_repo
21
+ clone_repo
22
+ else
23
+ :noop
24
+ end
25
+ end
26
+
27
+ def fork_repo(retries = 3)
28
+ logger.log('Forking repository...')
29
+ io.puts "Forking lesson..."
30
+
31
+ begin
32
+ Timeout::timeout(15) do
33
+ client.fork_repo(repo_name: lesson.name)
34
+ end
35
+ rescue Timeout::Error
36
+ if retries > 0
37
+ io.puts "There was a problem forking this lesson. Retrying..."
38
+ fork_repo(retries - 1)
39
+ else
40
+ io.puts "There is an issue connecting to Learn. Please try again."
41
+ logger.log('ERROR: Error connecting to Learn')
42
+ exit
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ def clone_repo(retries = 3)
49
+ logger.log('Cloning to your machine...')
50
+ io.puts "Cloning lesson..."
51
+ begin
52
+ Timeout::timeout(15) do
53
+ git_adapter.clone("git@#{lesson.git_server}:#{lesson.repo_path}.git", lesson.name, path: location)
54
+ end
55
+ rescue Git::GitExecuteError
56
+ if retries > 0
57
+ io.puts "There was a problem cloning this lesson. Retrying..." if retries > 1
58
+ sleep(1)
59
+ clone_repo(retries - 1)
60
+ else
61
+ io.puts "Cannot clone this lesson right now. Please try again."
62
+ logger.log('ERROR: Error cloning. Try again.')
63
+ exit
64
+ end
65
+ rescue Timeout::Error
66
+ if retries > 0
67
+ io.puts "There was a problem cloning this lesson. Retrying..."
68
+ clone_repo(retries - 1)
69
+ else
70
+ io.puts "Cannot clone this lesson right now. Please try again."
71
+ logger.log('ERROR: Error cloning. Try again.')
72
+ exit
73
+ end
74
+ end
75
+
76
+ end
77
+
78
+ def repo_exists?
79
+ File.exists?("#{lesson.to_path}/.git")
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,23 @@
1
+ module LearnOpen
2
+ class Logger
3
+ attr_reader :log_path
4
+
5
+ def initialize(log_path)
6
+ @log_path = log_path
7
+ setup
8
+ end
9
+
10
+ def log(message)
11
+ File.write(log_path, message)
12
+ end
13
+
14
+ private
15
+
16
+ attr_reader :log_path
17
+
18
+ def setup
19
+ FileUtils.touch(log_path)
20
+ File.write(log_path, '')
21
+ end
22
+ end
23
+ end
@@ -1,3 +1,3 @@
1
1
  module LearnOpen
2
- VERSION = '1.2.20'
2
+ VERSION = '1.2.21'
3
3
  end
@@ -0,0 +1,20 @@
1
+ class FakeGit
2
+ def clone(source, name, path:)
3
+ FileUtils.mkdir_p("#{path}/#{name}")
4
+ case name
5
+ when "jupyter_lab", "python_lab"
6
+ FileUtils.touch("#{path}/#{name}/requirements.txt")
7
+ when "ios_lab"
8
+ FileUtils.touch("#{path}/#{name}/project.xcodeproj")
9
+ when "ios_with_workspace_lab"
10
+ FileUtils.touch("#{path}/#{name}/project.xcworkspace")
11
+ when "ruby_lab"
12
+ FileUtils.touch("#{path}/#{name}/Gemfile")
13
+ when "node_lab"
14
+ FileUtils.touch("#{path}/#{name}/package.json")
15
+ else
16
+ nil
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,214 @@
1
+ require 'ostruct'
2
+ class FakeLearnClient
3
+ attr_reader :token
4
+ def initialize(token:)
5
+ @token = token
6
+ end
7
+ def current_lesson
8
+ OpenStruct.new({:id=>31322,
9
+ :title=>"Tic Tac Toe Board",
10
+ :link=>"https://learn.co/lessons/31322",
11
+ :github_repo=>"ttt-2-board-rb-v-000",
12
+ :forked_repo=>"StevenNunez/ttt-2-board-rb-v-000",
13
+ :clone_repo=>"StevenNunez/ttt-2-board-rb-v-000",
14
+ :git_server=>"github.com",
15
+ :dot_learn=>{:tags=>["variables", "arrays", "tictactoe"], :languages=>["ruby"], :resources=>0},
16
+ :lab=>true,
17
+ :ios_lab=>false,
18
+ :ruby_lab=>true,
19
+ :assessments=>
20
+ [{:type=>"fork", :passing=>true, :failing=>false, :started=>true, :message=>"You forked this lab."},
21
+ {:type=>"local_build", :passing=>false, :failing=>true, :started=>true, :message=>"Build failures."},
22
+ {:type=>"pull_request", :passing=>false, :failing=>false, :started=>false, :message=>"Submit a pull request on Github when you're done."}]})
23
+ end
24
+
25
+ def fork_repo(repo_name: ); :noop; end
26
+
27
+ def validate_repo_slug(repo_slug:)
28
+ case repo_slug
29
+ when "ios_lab"
30
+ OpenStruct.new({
31
+ :clone_repo=>"StevenNunez/ios_lab",
32
+ :repo_name=>"ios_lab",
33
+ :repo_slug=>"StevenNunez/ios_lab",
34
+ :lab=>true,
35
+ :lesson_id=>31322,
36
+ :later_lesson=>false,
37
+ :git_server=>"github.com",
38
+ :dot_learn=>{
39
+ :tags=>[
40
+ "UIView"
41
+ ],
42
+ :languages=>["swift"],
43
+ :resources=>0}
44
+ })
45
+ when "ios_with_workspace_lab"
46
+ OpenStruct.new({
47
+ :clone_repo=>"StevenNunez/ios_with_workspace_lab",
48
+ :repo_name=>"ios_with_workspace_lab",
49
+ :repo_slug=>"StevenNunez/ios_with_workspace_lab",
50
+ :lab=>true,
51
+ :lesson_id=>31322,
52
+ :later_lesson=>false,
53
+ :git_server=>"github.com",
54
+ :dot_learn=>{
55
+ :tags=>[
56
+ "UIView"
57
+ ],
58
+ :languages=>["swift"],
59
+ :resources=>0}
60
+ })
61
+ when "jupyter_lab"
62
+ OpenStruct.new({
63
+ :clone_repo=>"StevenNunez/jupyter_lab",
64
+ :repo_name=>"jupyter_lab",
65
+ :repo_slug=>"StevenNunez/jupyter_lab",
66
+ :lab=>true,
67
+ :lesson_id=>31322,
68
+ :later_lesson=>false,
69
+ :git_server=>"github.com",
70
+ :dot_learn=>{
71
+ :tags=>[
72
+ "jupyter_notebook"
73
+ ],
74
+ :languages=>["python"],
75
+ :jupyter_notebook => true,
76
+ :resources=>0}
77
+ })
78
+ when "ruby_lab"
79
+ OpenStruct.new({
80
+ :clone_repo=>"StevenNunez/ruby_lab",
81
+ :repo_name=>"ruby_lab",
82
+ :repo_slug=>"StevenNunez/ruby_lab",
83
+ :lab=>true,
84
+ :lesson_id=>31322,
85
+ :later_lesson=>false,
86
+ :git_server=>"github.com",
87
+ :dot_learn=>{
88
+ :tags=>[
89
+ "arrays"
90
+ ],
91
+ :languages=>["ruby"],
92
+ :resources=>0}
93
+ })
94
+ when "node_lab"
95
+ OpenStruct.new({
96
+ :clone_repo=>"StevenNunez/node_lab",
97
+ :repo_name=>"node_lab",
98
+ :repo_slug=>"StevenNunez/node_lab",
99
+ :lab=>true,
100
+ :lesson_id=>31322,
101
+ :later_lesson=>false,
102
+ :git_server=>"github.com",
103
+ :dot_learn=>{
104
+ :tags=>[
105
+ "arrays"
106
+ ],
107
+ :languages=>["javascript"],
108
+ :resources=>0}
109
+ })
110
+ when "python_lab"
111
+ OpenStruct.new({
112
+ :clone_repo=>"StevenNunez/python_lab",
113
+ :repo_name=>"python_lab",
114
+ :repo_slug=>"StevenNunez/python_lab",
115
+ :lab=>true,
116
+ :lesson_id=>31322,
117
+ :later_lesson=>false,
118
+ :git_server=>"github.com",
119
+ :dot_learn=>{
120
+ :tags=>[
121
+ "arrays"
122
+ ],
123
+ :languages=>["python"],
124
+ :resources=>0}
125
+ })
126
+ when "readme"
127
+ OpenStruct.new({
128
+ :clone_repo=>"StevenNunez/readme",
129
+ :repo_name=>"readme",
130
+ :repo_slug=>"StevenNunez/readme",
131
+ :lab=>false,
132
+ :lesson_id=>31322,
133
+ :later_lesson=>false,
134
+ :git_server=>"github.com",
135
+ :dot_learn=>{
136
+ :tags=>[
137
+ "Reading things"
138
+ ],
139
+ :languages=>["ruby"],
140
+ :resources=>0}
141
+ })
142
+ when "later_lesson"
143
+ OpenStruct.new({
144
+ :clone_repo=>"StevenNunez/later_lesson",
145
+ :repo_name=>"later_lesson",
146
+ :repo_slug=>"StevenNunez/later_lesson",
147
+ :lab=>false,
148
+ :lesson_id=>31322,
149
+ :later_lesson=>true,
150
+ :git_server=>"github.com",
151
+ :dot_learn=>{
152
+ :tags=>[
153
+ "Readable things"
154
+ ],
155
+ :languages=>["english"],
156
+ :resources=>0}
157
+ })
158
+ else
159
+ raise "Specify lab type"
160
+ end
161
+ end
162
+
163
+ def next_lesson
164
+ OpenStruct.new({
165
+ :id=>21304,
166
+ :title=>"Rails Dynamic Request Lab",
167
+ :link=>"https://learn.co/lessons/21304",
168
+ :github_repo=>"rails-dynamic-request-lab-cb-000",
169
+ :forked_repo=>"StevenNunez/rails-dynamic-request-lab-cb-000",
170
+ :clone_repo=>"StevenNunez/rails-dynamic-request-lab-cb-000",
171
+ :git_server=>"github.com",
172
+ :dot_learn=>
173
+ {
174
+ :tags=>[
175
+ "dynamic routes",
176
+ "controllers",
177
+ "rspec",
178
+ "capybara",
179
+ "mvc"
180
+ ],
181
+ :languages=>["ruby"],
182
+ :type=>["lab"],
183
+ :resources=>2},
184
+ :lab=>true,
185
+ :ios_lab=>false,
186
+ :ruby_lab=>true,
187
+ :assessments=>
188
+ [
189
+ {
190
+ :type=>"fork",
191
+ :passing=>true,
192
+ :failing=>false,
193
+ :started=>true,
194
+ :message=>"You forked this lab."
195
+ },
196
+ {
197
+ :type=>"local_build",
198
+ :passing=>false,
199
+ :failing=>false,
200
+ :started=>false,
201
+ :message=>"Run your tests locally to test your lab."
202
+ },
203
+ {
204
+ :type=>"pull_request",
205
+ :passing=>false,
206
+ :failing=>false,
207
+ :started=>false,
208
+ :message=>"Submit a pull request on Github when you're done."
209
+ }
210
+ ],
211
+ :later_lesson=>false
212
+ })
213
+ end
214
+ end