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,63 @@
|
|
1
|
+
module LearnOpen
|
2
|
+
module Environments
|
3
|
+
class IDEEnvironment < BaseEnvironment
|
4
|
+
def open_readme(lesson)
|
5
|
+
when_valid(lesson) do
|
6
|
+
warn_if_necessary(lesson)
|
7
|
+
io.puts "Opening readme..."
|
8
|
+
run_custom_command(:browser_open, {url: lesson.to_url})
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def open_jupyter_lab(lesson, location, editor)
|
13
|
+
when_valid(lesson) do
|
14
|
+
warn_if_necessary(lesson)
|
15
|
+
io.puts "Opening Jupyter Lesson..."
|
16
|
+
run_custom_command(:browser_open, {url: lesson.to_url})
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def open_lab(lesson, location, editor)
|
21
|
+
when_valid(lesson) do
|
22
|
+
warn_if_necessary(lesson)
|
23
|
+
case lesson
|
24
|
+
when LearnOpen::Lessons::IosLesson
|
25
|
+
super
|
26
|
+
when -> (l) { valid?(l) }
|
27
|
+
download_lesson(lesson, location)
|
28
|
+
open_editor(lesson, location, editor)
|
29
|
+
start_file_backup(lesson, location)
|
30
|
+
install_dependencies(lesson, location)
|
31
|
+
notify_of_completion
|
32
|
+
open_shell
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def when_valid(lesson, &block)
|
38
|
+
if valid?(lesson)
|
39
|
+
block.call
|
40
|
+
else
|
41
|
+
on_invalid(lesson)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def valid?(lesson)
|
46
|
+
lesson.name == environment_vars['LAB_NAME']
|
47
|
+
end
|
48
|
+
|
49
|
+
def on_invalid(lesson)
|
50
|
+
io.puts "Opening new window"
|
51
|
+
run_custom_command(:open_lab, {lab_name: lesson.name})
|
52
|
+
end
|
53
|
+
|
54
|
+
def run_custom_command(command, message)
|
55
|
+
home_dir = "/home/#{environment_vars['CREATED_USER']}"
|
56
|
+
custom_commands_log = "#{home_dir}/.custom_commands.log"
|
57
|
+
File.open(custom_commands_log, "a") do |f|
|
58
|
+
f.puts({:command => command}.merge(message).to_json)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module LearnOpen
|
2
|
+
module Environments
|
3
|
+
class JupyterContainerEnvironment < BaseEnvironment
|
4
|
+
def open_jupyter_lab(lesson, location, editor)
|
5
|
+
download_lesson(lesson, location)
|
6
|
+
open_editor(lesson, location, editor)
|
7
|
+
start_file_backup(lesson, location)
|
8
|
+
install_jupyter_dependencies(lesson, location)
|
9
|
+
notify_of_completion
|
10
|
+
open_shell
|
11
|
+
end
|
12
|
+
|
13
|
+
def open_editor(lesson, location, editor)
|
14
|
+
io.puts "Opening lesson..."
|
15
|
+
system_adapter.change_context_directory(lesson.to_path)
|
16
|
+
system_adapter.open_editor(editor, path: ".")
|
17
|
+
end
|
18
|
+
|
19
|
+
def install_jupyter_dependencies(lesson, location)
|
20
|
+
LearnOpen::DependencyInstallers::JupyterPipInstall.call(lesson, location, self, options)
|
21
|
+
end
|
22
|
+
|
23
|
+
def open_shell
|
24
|
+
system_adapter.open_login_shell(environment_vars['SHELL'])
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module LearnOpen
|
2
|
+
module Environments
|
3
|
+
class LinuxEnvironment < BaseEnvironment
|
4
|
+
def open_readme(lesson)
|
5
|
+
warn_if_necessary(lesson)
|
6
|
+
io.puts "Opening readme..."
|
7
|
+
system_adapter.run_command("xdg-open #{lesson.to_url}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def open_jupyter_lab(lesson, location, editor)
|
11
|
+
warn_if_necessary(lesson)
|
12
|
+
io.puts "Opening Jupyter Lesson..."
|
13
|
+
system_adapter.run_command("xdg-open #{lesson.to_url}")
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module LearnOpen
|
2
|
+
module Environments
|
3
|
+
class MacEnvironment < BaseEnvironment
|
4
|
+
def self.classify(options)
|
5
|
+
if chrome_installed?
|
6
|
+
MacWithChromeEnvironment.new(options)
|
7
|
+
else
|
8
|
+
self.new(options)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.chrome_installed?
|
13
|
+
File.exists?('/Applications/Google Chrome.app')
|
14
|
+
end
|
15
|
+
|
16
|
+
def open_readme(lesson)
|
17
|
+
warn_if_necessary(lesson)
|
18
|
+
io.puts "Opening readme..."
|
19
|
+
system_adapter.run_command("open -a Safari #{lesson.to_url}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def open_lab(lesson, location, editor)
|
23
|
+
warn_if_necessary(lesson)
|
24
|
+
case lesson
|
25
|
+
when LearnOpen::Lessons::IosLesson
|
26
|
+
download_lesson(lesson, location)
|
27
|
+
open_xcode(lesson)
|
28
|
+
notify_of_completion
|
29
|
+
open_shell
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def open_jupyter_lab(lesson, location, editor)
|
36
|
+
warn_if_necessary(lesson)
|
37
|
+
io.puts "Opening Jupyter Lesson..."
|
38
|
+
system_adapter.run_command("open -a Safari #{lesson.to_url}")
|
39
|
+
end
|
40
|
+
|
41
|
+
def xcodeproj_file?(lesson)
|
42
|
+
Dir.glob("#{lesson.to_path}/*.xcodeproj").any?
|
43
|
+
end
|
44
|
+
|
45
|
+
def xcworkspace_file?(lesson)
|
46
|
+
Dir.glob("#{lesson.to_path}/*.xcworkspace").any?
|
47
|
+
end
|
48
|
+
|
49
|
+
def open_xcode(lesson)
|
50
|
+
io.puts "Opening lesson..."
|
51
|
+
system_adapter.change_context_directory("#{lesson.to_path}")
|
52
|
+
if xcworkspace_file?(lesson)
|
53
|
+
system_adapter.run_command("cd #{lesson.to_path} && open *.xcworkspace")
|
54
|
+
elsif xcodeproj_file?(lesson)
|
55
|
+
system_adapter.run_command("cd #{lesson.to_path} && open *.xcodeproj")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class MacWithChromeEnvironment < MacEnvironment
|
61
|
+
def open_readme(lesson)
|
62
|
+
warn_if_necessary(lesson)
|
63
|
+
io.puts "Opening readme..."
|
64
|
+
system_adapter.run_command("open -a 'Google Chrome' #{lesson.to_url}")
|
65
|
+
end
|
66
|
+
|
67
|
+
def open_jupyter_lab(lesson, location, editor)
|
68
|
+
warn_if_necessary(lesson)
|
69
|
+
io.puts "Opening Jupyter Lesson..."
|
70
|
+
system_adapter.run_command("open -a 'Google Chrome' #{lesson.to_url}")
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module LearnOpen
|
2
|
+
module Lessons
|
3
|
+
|
4
|
+
def self.default
|
5
|
+
LabLesson
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.lesson_types
|
9
|
+
[
|
10
|
+
JupyterLesson,
|
11
|
+
ReadmeLesson,
|
12
|
+
IosLesson,
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.classify(lesson_data, options = {})
|
17
|
+
lesson = lesson_data[:lesson]
|
18
|
+
default = method(:default)
|
19
|
+
lesson_types.find(default) do |type|
|
20
|
+
type.detect(lesson)
|
21
|
+
end.new(lesson_data, options)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module LearnOpen
|
2
|
+
module Lessons
|
3
|
+
class BaseLesson
|
4
|
+
attr_reader :repo_path,
|
5
|
+
:organization,
|
6
|
+
:name,
|
7
|
+
:id,
|
8
|
+
:dot_learn,
|
9
|
+
:git_server,
|
10
|
+
:later_lesson,
|
11
|
+
:options,
|
12
|
+
:io,
|
13
|
+
:system_adapter,
|
14
|
+
:platform,
|
15
|
+
:environment_vars,
|
16
|
+
:logger,
|
17
|
+
:location
|
18
|
+
|
19
|
+
def initialize(lesson_data, options = {})
|
20
|
+
lesson = lesson_data[:lesson]
|
21
|
+
|
22
|
+
@repo_path = lesson.clone_repo
|
23
|
+
@organization, @name = repo_path.split('/')
|
24
|
+
|
25
|
+
@git_server = lesson.git_server
|
26
|
+
@dot_learn = lesson.dot_learn
|
27
|
+
@is_lab = lesson.lab
|
28
|
+
@later_lesson = lesson_data[:later_lesson]
|
29
|
+
@id = lesson_data[:id]
|
30
|
+
|
31
|
+
@logger = options.fetch(:logger, LearnOpen.logger)
|
32
|
+
@io = options.fetch(:io, LearnOpen.default_io)
|
33
|
+
@system_adapter = options.fetch(:system_adapter, LearnOpen.system_adapter)
|
34
|
+
@platform = options.fetch(:platform, LearnOpen.platform)
|
35
|
+
@environment_vars = options.fetch(:environment_vars, LearnOpen.environment_vars)
|
36
|
+
@location = options.fetch(:lessons_directory) {LearnOpen.lessons_directory}
|
37
|
+
@options = options
|
38
|
+
end
|
39
|
+
|
40
|
+
def lab?
|
41
|
+
@is_lab
|
42
|
+
end
|
43
|
+
|
44
|
+
def readme?
|
45
|
+
!lab?
|
46
|
+
end
|
47
|
+
|
48
|
+
def to_path
|
49
|
+
"#{location}/#{name}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def to_url
|
53
|
+
"https://learn.co/lessons/#{id}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module LearnOpen
|
2
|
+
module Lessons
|
3
|
+
class IosLesson < BaseLesson
|
4
|
+
def self.detect(lesson)
|
5
|
+
languages = Hash(lesson.dot_learn)[:languages]
|
6
|
+
(languages & ["swift", "objc"]).any?
|
7
|
+
end
|
8
|
+
|
9
|
+
def open(environment, editor)
|
10
|
+
environment.open_lab(self, location, editor)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module LearnOpen
|
2
|
+
module Lessons
|
3
|
+
class JupyterLesson < BaseLesson
|
4
|
+
def self.detect(lesson)
|
5
|
+
dot_learn = Hash(lesson.dot_learn)
|
6
|
+
!!dot_learn[:jupyter_notebook]
|
7
|
+
end
|
8
|
+
|
9
|
+
def open(environment, editor)
|
10
|
+
environment.open_jupyter_lab(self, location, editor)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/learn_open/opener.rb
CHANGED
@@ -1,484 +1,41 @@
|
|
1
1
|
module LearnOpen
|
2
2
|
class Opener
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
attr_reader :editor,
|
4
|
+
:target_lesson,
|
5
|
+
:get_next_lesson,
|
6
|
+
:io,
|
7
|
+
:logger,
|
8
|
+
:options
|
6
9
|
|
7
10
|
def self.run(lesson:, editor_specified:, get_next_lesson:)
|
8
11
|
new(lesson, editor_specified, get_next_lesson).run
|
9
12
|
end
|
10
13
|
|
11
|
-
def initialize(
|
12
|
-
|
13
|
-
@
|
14
|
-
|
15
|
-
@lesson = lesson
|
16
|
-
@editor = editor
|
14
|
+
def initialize(target_lesson, editor, get_next_lesson, options = {})
|
15
|
+
@target_lesson = target_lesson
|
16
|
+
@editor = editor
|
17
17
|
@get_next_lesson = get_next_lesson
|
18
|
-
@lessons_dir = YAML.load(File.read("#{HOME_DIR}/.learn-config"))[:learn_directory]
|
19
|
-
@file_path = "#{HOME_DIR}/.learn-open-tmp"
|
20
|
-
end
|
21
|
-
|
22
|
-
def run
|
23
|
-
setup_tmp_file
|
24
|
-
|
25
|
-
set_lesson
|
26
|
-
|
27
|
-
if ide_version_3?
|
28
|
-
if self.repo_dir != ENV['LAB_NAME']
|
29
|
-
home_dir = "/home/#{ENV['CREATED_USER']}"
|
30
|
-
File.open("#{home_dir}/.custom_commands.log", "a") do |f|
|
31
|
-
f.puts %Q{{"command": "open_lab", "lab_name": "#{self.repo_dir}"}}
|
32
|
-
end
|
33
|
-
exit
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
puts "Looking for lesson..."
|
38
|
-
|
39
|
-
if jupyter_notebook_environment?
|
40
|
-
git_tasks
|
41
|
-
file_tasks
|
42
|
-
restore_files
|
43
|
-
watch_for_changes
|
44
|
-
jupyter_pip_install
|
45
|
-
completion_tasks
|
46
|
-
else
|
47
|
-
warn_if_necessary
|
48
|
-
if lesson_is_readme?
|
49
|
-
open_readme
|
50
|
-
else
|
51
|
-
git_tasks
|
52
|
-
file_tasks
|
53
|
-
setup_backup_if_needed
|
54
|
-
dependency_tasks
|
55
|
-
completion_tasks
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
def repo_exists?
|
62
|
-
File.exists?("#{lessons_dir}/#{repo_dir}/.git")
|
63
|
-
end
|
64
|
-
|
65
|
-
private
|
66
|
-
|
67
|
-
def setup_backup_if_needed
|
68
|
-
if ide_environment? && ide_git_wip_enabled?
|
69
|
-
restore_files
|
70
|
-
watch_for_changes
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def ping_fork_completion(retries=3)
|
75
|
-
begin
|
76
|
-
Timeout::timeout(15) do
|
77
|
-
client.submit_event(
|
78
|
-
event: 'fork',
|
79
|
-
learn_oauth_token: token,
|
80
|
-
repo_name: repo_dir,
|
81
|
-
base_org_name: lesson.split('/')[0],
|
82
|
-
forkee: { full_name: nil }
|
83
|
-
)
|
84
|
-
end
|
85
|
-
rescue Timeout::Error
|
86
|
-
if retries > 0
|
87
|
-
puts "There was a problem forking and cloning this lesson. Retrying..."
|
88
|
-
ping_fork_completion(retries-1)
|
89
|
-
else
|
90
|
-
puts "There is an issue connecting to Learn. Please try again."
|
91
|
-
File.write(file_path, 'ERROR: Error connecting to Learn')
|
92
|
-
exit
|
93
|
-
end
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def warn_if_necessary
|
98
|
-
temp_args = nil
|
99
|
-
|
100
|
-
if self.later_lesson
|
101
|
-
puts 'WARNING: You are attempting to open a lesson that is beyond your current lesson.'
|
102
|
-
print 'Are you sure you want to continue? [Yn]: '
|
103
|
-
|
104
|
-
if ARGV.any?
|
105
|
-
temp_args = ARGV
|
106
|
-
ARGV.clear
|
107
|
-
end
|
108
|
-
|
109
|
-
warn_response = gets.chomp.downcase
|
110
|
-
|
111
|
-
if !warn_response.empty? && !['yes', 'y'].include?(warn_response)
|
112
|
-
exit
|
113
|
-
end
|
114
|
-
end
|
115
|
-
|
116
|
-
if temp_args
|
117
|
-
temp_args.each do |arg|
|
118
|
-
ARGV << arg
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
def setup_tmp_file
|
124
|
-
FileUtils.touch(file_path)
|
125
|
-
File.write(file_path, '')
|
126
|
-
end
|
127
|
-
|
128
|
-
def cleanup_tmp_file
|
129
|
-
File.write(file_path, 'Done.')
|
130
|
-
end
|
131
18
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
if !lesson && !get_next_lesson
|
136
|
-
self.lesson = get_current_lesson_forked_repo
|
137
|
-
self.lesson_is_lab = current_lesson.lab
|
138
|
-
self.lesson_id = current_lesson.id
|
139
|
-
self.later_lesson = false
|
140
|
-
self.dot_learn = current_lesson.dot_learn
|
141
|
-
elsif !lesson && get_next_lesson
|
142
|
-
self.lesson = get_next_lesson_forked_repo
|
143
|
-
self.lesson_is_lab = next_lesson.lab
|
144
|
-
self.lesson_id = next_lesson.id
|
145
|
-
self.later_lesson = false
|
146
|
-
self.dot_learn = next_lesson.dot_learn
|
147
|
-
else
|
148
|
-
self.lesson = ensure_correct_lesson.clone_repo
|
149
|
-
self.lesson_is_lab = correct_lesson.lab
|
150
|
-
self.lesson_id = correct_lesson.lesson_id
|
151
|
-
self.later_lesson = correct_lesson.later_lesson
|
152
|
-
self.dot_learn = correct_lesson.dot_learn
|
153
|
-
end
|
154
|
-
|
155
|
-
self.repo_dir = lesson.split('/').last
|
156
|
-
end
|
157
|
-
|
158
|
-
def current_lesson
|
159
|
-
@current_lesson ||= client.current_lesson
|
160
|
-
end
|
161
|
-
|
162
|
-
def next_lesson
|
163
|
-
@next_lesson ||= client.next_lesson
|
164
|
-
end
|
165
|
-
|
166
|
-
def get_current_lesson_forked_repo(retries=3)
|
167
|
-
begin
|
168
|
-
Timeout::timeout(15) do
|
169
|
-
current_lesson.clone_repo
|
170
|
-
end
|
171
|
-
rescue Timeout::Error
|
172
|
-
if retries > 0
|
173
|
-
puts "There was a problem getting your lesson from Learn. Retrying..."
|
174
|
-
get_current_lesson_forked_repo(retries-1)
|
175
|
-
else
|
176
|
-
puts "There seems to be a problem connecting to Learn. Please try again."
|
177
|
-
File.write(file_path, 'ERROR: Error connecting to Learn')
|
178
|
-
exit
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def get_next_lesson_forked_repo(retries=3)
|
184
|
-
begin
|
185
|
-
Timeout::timeout(15) do
|
186
|
-
next_lesson.clone_repo
|
187
|
-
end
|
188
|
-
rescue Timeout::Error
|
189
|
-
if retries > 0
|
190
|
-
puts "There was a problem getting your next lesson from Learn. Retrying..."
|
191
|
-
get_next_lesson_forked_repo(retries-1)
|
192
|
-
else
|
193
|
-
puts "There seems to be a problem connecting to Learn. Please try again."
|
194
|
-
File.write(file_path, 'ERROR: Error connecting to Learn')
|
195
|
-
exit
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
def ensure_correct_lesson
|
201
|
-
correct_lesson
|
202
|
-
end
|
203
|
-
|
204
|
-
def correct_lesson(retries=3)
|
205
|
-
@correct_lesson ||= begin
|
206
|
-
Timeout::timeout(15) do
|
207
|
-
client.validate_repo_slug(repo_slug: lesson)
|
208
|
-
end
|
209
|
-
rescue Timeout::Error
|
210
|
-
if retries > 0
|
211
|
-
puts "There was a problem connecting to Learn. Retrying..."
|
212
|
-
correct_lesson(retries-1)
|
213
|
-
else
|
214
|
-
puts "Cannot connect to Learn right now. Please try again."
|
215
|
-
File.write(file_path, 'ERROR: Error connecting to Learn')
|
216
|
-
exit
|
217
|
-
end
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
def fork_repo(retries=3)
|
222
|
-
if !repo_exists?
|
223
|
-
File.write(file_path, 'Forking repository...')
|
224
|
-
puts "Forking lesson..."
|
225
|
-
|
226
|
-
if !github_disabled?
|
227
|
-
begin
|
228
|
-
Timeout::timeout(15) do
|
229
|
-
client.fork_repo(repo_name: repo_dir)
|
230
|
-
end
|
231
|
-
rescue Timeout::Error
|
232
|
-
if retries > 0
|
233
|
-
puts "There was a problem forking this lesson. Retrying..."
|
234
|
-
fork_repo(retries-1)
|
235
|
-
else
|
236
|
-
puts "There is an issue connecting to Learn. Please try again."
|
237
|
-
File.write(file_path, 'ERROR: Error connecting to Learn')
|
238
|
-
exit
|
239
|
-
end
|
240
|
-
end
|
241
|
-
end
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
def clone_repo(retries=3)
|
246
|
-
if !repo_exists?
|
247
|
-
File.write(file_path, 'Cloning to your machine...')
|
248
|
-
puts "Cloning lesson..."
|
249
|
-
begin
|
250
|
-
Timeout::timeout(15) do
|
251
|
-
Git.clone("git@github.com:#{lesson}.git", repo_dir, path: lessons_dir)
|
252
|
-
end
|
253
|
-
rescue Git::GitExecuteError
|
254
|
-
if retries > 0
|
255
|
-
puts "There was a problem cloning this lesson. Retrying..." if retries > 1
|
256
|
-
sleep(1)
|
257
|
-
clone_repo(retries-1)
|
258
|
-
else
|
259
|
-
puts "Cannot clone this lesson right now. Please try again."
|
260
|
-
File.write(file_path, 'ERROR: Error cloning. Try again.')
|
261
|
-
exit
|
262
|
-
end
|
263
|
-
rescue Timeout::Error
|
264
|
-
if retries > 0
|
265
|
-
puts "There was a problem cloning this lesson. Retrying..."
|
266
|
-
clone_repo(retries-1)
|
267
|
-
else
|
268
|
-
puts "Cannot clone this lesson right now. Please try again."
|
269
|
-
File.write(file_path, 'ERROR: Error cloning. Try again.')
|
270
|
-
exit
|
271
|
-
end
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
if github_disabled?
|
276
|
-
ping_fork_completion
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
def open_with_editor
|
281
|
-
if ios_lesson?
|
282
|
-
open_ios_lesson
|
283
|
-
elsif editor
|
284
|
-
system("#{editor} .")
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
def ios_lesson?
|
289
|
-
begin
|
290
|
-
languages = YAML.load(File.read("#{lessons_dir}/#{repo_dir}/.learn"))['languages']
|
291
|
-
ios_lang = languages.any? {|l| ['objc', 'swift'].include?(l)}
|
292
|
-
|
293
|
-
ios_lang || xcodeproj_file? || xcworkspace_file?
|
294
|
-
rescue Psych::SyntaxError
|
295
|
-
if xcodeproj_file? || xcworkspace_file?
|
296
|
-
true
|
297
|
-
else
|
298
|
-
puts "Sorry, there seems to be a problem with this lesson. Please submit a bug report to bugs@learn.co and try again later."
|
299
|
-
puts "If you'd like to work on your next lesson now, type: learn next"
|
300
|
-
File.write(file_path, 'ERROR: Problem parsing lesson data. Try again.')
|
301
|
-
exit
|
302
|
-
end
|
303
|
-
rescue NoMethodError, Errno::ENOENT => e
|
304
|
-
if xcodeproj_file? || xcworkspace_file?
|
305
|
-
true
|
306
|
-
elsif e.message.match(/for false:FalseClass/) || e.message.match(/No such file or directory/)
|
307
|
-
false
|
308
|
-
else
|
309
|
-
puts "Sorry, there seems to be a problem with this lesson. Please submit a bug report to bugs@learn.co and try again later."
|
310
|
-
puts "If you'd like to work on your next lesson now, type: learn next"
|
311
|
-
File.write(file_path, 'ERROR: Problem parsing lesson data. Try again.')
|
312
|
-
exit
|
313
|
-
end
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
def open_ios_lesson
|
318
|
-
if can_open_ios_lesson?
|
319
|
-
open_xcode
|
320
|
-
else
|
321
|
-
puts "You need to be on a Mac to work on iOS lessons."
|
322
|
-
exit
|
323
|
-
end
|
324
|
-
end
|
19
|
+
@io = options.fetch(:io, LearnOpen.default_io)
|
20
|
+
@logger = options.fetch(:logger, LearnOpen.logger)
|
325
21
|
|
326
|
-
|
327
|
-
on_mac?
|
22
|
+
@options = options
|
328
23
|
end
|
329
24
|
|
330
|
-
def
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
end
|
345
|
-
|
346
|
-
def cd_to_lesson
|
347
|
-
puts "Opening lesson..."
|
348
|
-
Dir.chdir("#{lessons_dir}/#{repo_dir}")
|
349
|
-
end
|
350
|
-
|
351
|
-
def pip_install
|
352
|
-
if !ios_lesson? && File.exists?("#{lessons_dir}/#{repo_dir}/requirements.txt")
|
353
|
-
puts "Installing pip dependencies..."
|
354
|
-
system("python -m pip install -r requirements.txt")
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
def jupyter_pip_install
|
359
|
-
if !ios_lesson? && File.exists?("#{lessons_dir}/#{repo_dir}/requirements.txt")
|
360
|
-
puts "Installing pip dependencies..."
|
361
|
-
system("/opt/conda/bin/python -m pip install -r requirements.txt")
|
362
|
-
end
|
363
|
-
end
|
364
|
-
|
365
|
-
def bundle_install
|
366
|
-
if !ios_lesson? && File.exists?("#{lessons_dir}/#{repo_dir}/Gemfile")
|
367
|
-
puts "Bundling..."
|
368
|
-
system("bundle install")
|
369
|
-
end
|
370
|
-
end
|
371
|
-
|
372
|
-
def npm_install
|
373
|
-
if !ios_lesson? && File.exists?("#{lessons_dir}/#{repo_dir}/package.json")
|
374
|
-
puts 'Installing npm dependencies...'
|
375
|
-
|
376
|
-
if ide_environment?
|
377
|
-
system("yarn install --no-lockfile")
|
378
|
-
else
|
379
|
-
system("npm install")
|
380
|
-
end
|
381
|
-
end
|
382
|
-
end
|
383
|
-
|
384
|
-
def lesson_is_readme?
|
385
|
-
!lesson_is_lab
|
386
|
-
end
|
387
|
-
|
388
|
-
def open_readme
|
389
|
-
if ide_environment?
|
390
|
-
puts "Opening readme..."
|
391
|
-
File.open(".custom_commands.log", "a") do |f|
|
392
|
-
f.puts %Q{{"command": "browser_open", "url": "https://learn.co/lessons/#{lesson_id}"}}
|
393
|
-
end
|
394
|
-
elsif can_open_readme?
|
395
|
-
puts "Opening readme..."
|
396
|
-
launch_browser
|
397
|
-
else
|
398
|
-
puts "It looks like this lesson is a Readme. Please open it in your browser."
|
399
|
-
exit
|
400
|
-
end
|
401
|
-
end
|
402
|
-
|
403
|
-
def launch_browser
|
404
|
-
if chrome_installed?
|
405
|
-
open_chrome
|
406
|
-
else
|
407
|
-
open_safari
|
408
|
-
end
|
409
|
-
end
|
410
|
-
|
411
|
-
def chrome_installed?
|
412
|
-
File.exists?('/Applications/Google Chrome.app')
|
413
|
-
end
|
414
|
-
|
415
|
-
def open_chrome
|
416
|
-
system("open -a 'Google Chrome' https://learn.co/lessons/#{lesson_id}")
|
417
|
-
end
|
418
|
-
|
419
|
-
def open_safari
|
420
|
-
system("open -a Safari https://learn.co/lessons/#{lesson_id}")
|
421
|
-
end
|
422
|
-
|
423
|
-
def can_open_readme?
|
424
|
-
on_mac?
|
425
|
-
end
|
426
|
-
|
427
|
-
def on_mac?
|
428
|
-
!!RUBY_PLATFORM.match(/darwin/)
|
429
|
-
end
|
430
|
-
|
431
|
-
def github_disabled?
|
432
|
-
!dot_learn.nil? && dot_learn[:github] == false
|
433
|
-
end
|
434
|
-
|
435
|
-
def ide_environment?
|
436
|
-
ENV['IDE_CONTAINER'] == "true"
|
437
|
-
end
|
438
|
-
|
439
|
-
def ide_git_wip_enabled?
|
440
|
-
return false if github_disabled?
|
441
|
-
|
442
|
-
ENV['IDE_GIT_WIP'] == "true"
|
443
|
-
end
|
444
|
-
|
445
|
-
def ide_version_3?
|
446
|
-
ENV['IDE_VERSION'] == "3"
|
447
|
-
end
|
448
|
-
|
449
|
-
def jupyter_notebook_environment?
|
450
|
-
ENV['JUPYTER_CONTAINER'] == "true"
|
451
|
-
end
|
452
|
-
|
453
|
-
def git_tasks
|
454
|
-
fork_repo
|
455
|
-
clone_repo
|
456
|
-
end
|
457
|
-
|
458
|
-
def file_tasks
|
459
|
-
cd_to_lesson
|
460
|
-
open_with_editor
|
461
|
-
end
|
462
|
-
|
463
|
-
def dependency_tasks
|
464
|
-
bundle_install
|
465
|
-
npm_install
|
466
|
-
pip_install
|
467
|
-
end
|
468
|
-
|
469
|
-
def restore_files
|
470
|
-
pid = Process.spawn("restore-lab", [:out, :err] => File::NULL)
|
471
|
-
Process.waitpid(pid)
|
472
|
-
end
|
473
|
-
|
474
|
-
def watch_for_changes
|
475
|
-
Process.spawn("while inotifywait -e close_write,create,moved_to -r #{lessons_dir}/#{repo_dir}; do backup-lab; done", [:out, :err] => File::NULL)
|
476
|
-
end
|
477
|
-
|
478
|
-
def completion_tasks
|
479
|
-
cleanup_tmp_file
|
480
|
-
puts "Done."
|
481
|
-
exec("#{ENV['SHELL']} -l")
|
25
|
+
def run
|
26
|
+
logger.log('Getting lesson...')
|
27
|
+
io.puts "Looking for lesson..."
|
28
|
+
|
29
|
+
lesson_data = LearnOpen::Adapters::LearnWebAdapter
|
30
|
+
.new(options)
|
31
|
+
.fetch_lesson_data(
|
32
|
+
target_lesson: target_lesson,
|
33
|
+
fetch_next_lesson: get_next_lesson
|
34
|
+
)
|
35
|
+
|
36
|
+
lesson = Lessons.classify(lesson_data, options)
|
37
|
+
environment = LearnOpen::Environments.classify(options)
|
38
|
+
lesson.open(environment, editor)
|
482
39
|
end
|
483
40
|
end
|
484
41
|
end
|