learn-open 1.2.20 → 1.2.21
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 +5 -5
- data/.gitignore +4 -0
- data/Guardfile +70 -0
- data/learn-open.gemspec +7 -1
- data/lib/learn_open.rb +66 -0
- data/lib/learn_open/adapters/io_adapter.rb +24 -0
- data/lib/learn_open/adapters/learn_web_adapter.rb +103 -0
- data/lib/learn_open/adapters/system_adapter.rb +30 -0
- data/lib/learn_open/environments.rb +35 -0
- data/lib/learn_open/environments/base_environment.rb +70 -0
- data/lib/learn_open/environments/generic_environment.rb +9 -0
- data/lib/learn_open/environments/ide_environment.rb +63 -0
- data/lib/learn_open/environments/jupyter_container_environment.rb +28 -0
- data/lib/learn_open/environments/linux_environment.rb +17 -0
- data/lib/learn_open/environments/mac_environment.rb +75 -0
- data/lib/learn_open/lessons.rb +24 -0
- data/lib/learn_open/lessons/base_lesson.rb +57 -0
- data/lib/learn_open/lessons/ios_lesson.rb +14 -0
- data/lib/learn_open/lessons/jupyter_lesson.rb +14 -0
- data/lib/learn_open/lessons/lab_lesson.rb +9 -0
- data/lib/learn_open/lessons/readme_lesson.rb +13 -0
- data/lib/learn_open/opener.rb +26 -469
- data/lib/learn_open/services/dependency_installers.rb +19 -0
- data/lib/learn_open/services/dependency_installers/base_installer.rb +21 -0
- data/lib/learn_open/services/dependency_installers/gem_installer.rb +14 -0
- data/lib/learn_open/services/dependency_installers/jupyter_pip_installer.rb +14 -0
- data/lib/learn_open/services/dependency_installers/node_package_installer.rb +20 -0
- data/lib/learn_open/services/dependency_installers/pip_installer.rb +14 -0
- data/lib/learn_open/services/file_backup_starter.rb +20 -0
- data/lib/learn_open/services/lesson_downloader.rb +82 -0
- data/lib/learn_open/services/logger.rb +23 -0
- data/lib/learn_open/version.rb +1 -1
- data/spec/fakes/fake_git.rb +20 -0
- data/spec/fakes/fake_learn_client.rb +214 -0
- data/spec/fixtures/learn-config +3 -0
- data/spec/learn_open/environments/ide_environment_spec.rb +62 -0
- data/spec/learn_open/opener_spec.rb +789 -14
- data/spec/spec_helper.rb +41 -0
- metadata +121 -5
- 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
|
data/lib/learn_open/version.rb
CHANGED
@@ -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
|