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.
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