daigaku 0.2.0 → 1.0.0
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/.travis.yml +7 -4
- data/CODE_OF_CONDUCT.md +77 -0
- data/README.md +11 -11
- data/bin/daigaku +6 -2
- data/daigaku.gemspec +22 -26
- data/lib/daigaku.rb +0 -1
- data/lib/daigaku/chapter.rb +3 -4
- data/lib/daigaku/coloring.rb +22 -27
- data/lib/daigaku/configuration.rb +25 -28
- data/lib/daigaku/congratulator.rb +17 -5
- data/lib/daigaku/course.rb +9 -8
- data/lib/daigaku/exceptions.rb +0 -2
- data/lib/daigaku/generator.rb +13 -15
- data/lib/daigaku/github_client.rb +5 -5
- data/lib/daigaku/loadable.rb +23 -14
- data/lib/daigaku/loading/chapters.rb +0 -2
- data/lib/daigaku/loading/courses.rb +0 -2
- data/lib/daigaku/loading/units.rb +0 -2
- data/lib/daigaku/markdown.rb +1 -0
- data/lib/daigaku/markdown/printer.rb +89 -0
- data/lib/daigaku/markdown/ruby_doc.rb +15 -15
- data/lib/daigaku/solution.rb +23 -15
- data/lib/daigaku/storeable.rb +11 -12
- data/lib/daigaku/task.rb +1 -1
- data/lib/daigaku/terminal.rb +3 -4
- data/lib/daigaku/terminal/cli.rb +8 -8
- data/lib/daigaku/terminal/courses.rb +22 -19
- data/lib/daigaku/terminal/output.rb +46 -53
- data/lib/daigaku/terminal/setup.rb +13 -18
- data/lib/daigaku/terminal/solutions.rb +27 -32
- data/lib/daigaku/terminal/welcome.rb +9 -11
- data/lib/daigaku/test.rb +7 -10
- data/lib/daigaku/test_result.rb +54 -21
- data/lib/daigaku/unit.rb +2 -4
- data/lib/daigaku/version.rb +1 -1
- data/lib/daigaku/views.rb +29 -33
- data/lib/daigaku/views/chapters_menu.rb +16 -20
- data/lib/daigaku/views/courses_menu.rb +12 -15
- data/lib/daigaku/views/main_menu.rb +23 -23
- data/lib/daigaku/views/menu.rb +14 -18
- data/lib/daigaku/views/splash.rb +11 -13
- data/lib/daigaku/views/subscriber.rb +38 -0
- data/lib/daigaku/views/task_view.rb +97 -80
- data/lib/daigaku/views/top_bar.rb +4 -10
- data/lib/daigaku/views/units_menu.rb +16 -21
- data/lib/daigaku/window.rb +12 -70
- data/spec/daigaku/chapter_spec.rb +23 -18
- data/spec/daigaku/coloring_spec.rb +0 -1
- data/spec/daigaku/configuration_spec.rb +54 -50
- data/spec/daigaku/congratulator_spec.rb +11 -8
- data/spec/daigaku/course_spec.rb +75 -52
- data/spec/daigaku/generator_spec.rb +24 -25
- data/spec/daigaku/github_client_spec.rb +17 -18
- data/spec/daigaku/loading/chapters_spec.rb +2 -3
- data/spec/daigaku/loading/courses_spec.rb +2 -3
- data/spec/daigaku/loading/units_spec.rb +4 -5
- data/spec/daigaku/markdown/ruby_doc_spec.rb +12 -6
- data/spec/daigaku/reference_solution_spec.rb +8 -10
- data/spec/daigaku/solution_spec.rb +21 -22
- data/spec/daigaku/storeable_spec.rb +12 -10
- data/spec/daigaku/task_spec.rb +3 -4
- data/spec/daigaku/terminal/cli_spec.rb +29 -21
- data/spec/daigaku/terminal/courses_spec.rb +104 -99
- data/spec/daigaku/terminal/output_spec.rb +44 -39
- data/spec/daigaku/terminal/setup_spec.rb +1 -3
- data/spec/daigaku/terminal/solutions_spec.rb +0 -2
- data/spec/daigaku/terminal/welcome_spec.rb +0 -2
- data/spec/daigaku/terminal_spec.rb +5 -7
- data/spec/daigaku/test_example_spec.rb +16 -14
- data/spec/daigaku/test_result_spec.rb +21 -25
- data/spec/daigaku/test_spec.rb +11 -12
- data/spec/daigaku/unit_spec.rb +24 -27
- data/spec/daigaku/views/chapters_menu_spec.rb +0 -1
- data/spec/daigaku/views/courses_menu_spec.rb +0 -1
- data/spec/daigaku/views/menu_spec.rb +1 -2
- data/spec/daigaku/views/task_view_spec.rb +0 -2
- data/spec/daigaku/views/units_menu_spec.rb +0 -1
- data/spec/daigaku/views_spec.rb +0 -1
- data/spec/daigaku_spec.rb +9 -12
- data/spec/path_helpers_spec.rb +11 -12
- data/spec/resource_helpers_spec.rb +11 -12
- data/spec/spec_helper.rb +3 -4
- data/spec/support/macros/content_helpers.rb +16 -17
- data/spec/support/macros/mock_helpers.rb +6 -6
- data/spec/support/macros/path_helpers.rb +15 -15
- data/spec/support/macros/resource_helpers.rb +34 -35
- metadata +32 -44
data/lib/daigaku/solution.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
module Daigaku
|
2
2
|
class Solution
|
3
|
-
|
4
|
-
FILE_SUFFIX = '_solution.rb'
|
3
|
+
FILE_SUFFIX = '_solution.rb'.freeze
|
5
4
|
|
6
5
|
attr_reader :code, :path, :errors
|
7
6
|
|
8
7
|
def initialize(unit_path)
|
9
8
|
@unit_path = unit_path
|
10
|
-
@path
|
11
|
-
@code
|
12
|
-
@verified
|
9
|
+
@path = solution_path(unit_path)
|
10
|
+
@code = load_code(@path)
|
11
|
+
@verified = store_state
|
13
12
|
end
|
14
13
|
|
15
14
|
def verify!
|
16
|
-
|
17
|
-
|
15
|
+
@code = load_code(@path)
|
16
|
+
result = Daigaku::Test.new(@unit_path).run(@code)
|
17
|
+
self.store_state = result.passed?
|
18
18
|
result
|
19
19
|
end
|
20
20
|
|
@@ -24,7 +24,7 @@ module Daigaku
|
|
24
24
|
|
25
25
|
def store_key
|
26
26
|
unless @store_key
|
27
|
-
part_path
|
27
|
+
part_path = path.split('/')[-3..-1].join('/').gsub(FILE_SUFFIX, '')
|
28
28
|
@store_key = Storeable.key(part_path, prefix: 'verified')
|
29
29
|
end
|
30
30
|
|
@@ -33,21 +33,29 @@ module Daigaku
|
|
33
33
|
|
34
34
|
private
|
35
35
|
|
36
|
+
def load_code(path)
|
37
|
+
File.read(path).strip if File.file?(path)
|
38
|
+
end
|
39
|
+
|
36
40
|
def solution_path(path)
|
37
|
-
local_path
|
38
|
-
|
39
|
-
file
|
41
|
+
local_path = Daigaku.config.solutions_path
|
42
|
+
sub_directory = Storeable.key(directory_from(path))
|
43
|
+
file = Storeable.key(File.basename(path)) + FILE_SUFFIX
|
44
|
+
|
45
|
+
File.join(local_path, sub_directory, file)
|
46
|
+
end
|
40
47
|
|
41
|
-
|
48
|
+
def directory_from(path)
|
49
|
+
path.split('/')[-3..-2].join('/').gsub(FILE_SUFFIX, '')
|
42
50
|
end
|
43
51
|
|
44
|
-
def
|
52
|
+
def store_state=(verified)
|
45
53
|
@verified = verified
|
46
54
|
QuickStore.store.set(store_key, verified?)
|
47
55
|
end
|
48
56
|
|
49
|
-
def
|
57
|
+
def store_state
|
50
58
|
QuickStore.store.get(store_key)
|
51
59
|
end
|
52
60
|
end
|
53
|
-
end
|
61
|
+
end
|
data/lib/daigaku/storeable.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
1
|
module Daigaku
|
2
2
|
module Storeable
|
3
|
-
|
4
3
|
LEADING_NUMBERS = /^\d+[\_\-\s]+/
|
5
|
-
PART_JOINTS
|
4
|
+
PART_JOINTS = /[\_\-\s]+/
|
6
5
|
|
7
6
|
class << self
|
8
7
|
def key(text, options = {})
|
9
|
-
separator
|
10
|
-
prefix
|
11
|
-
suffix
|
12
|
-
suffixes
|
8
|
+
separator = QuickStore.config.key_separator
|
9
|
+
prefix = options[:prefix]
|
10
|
+
suffix = clean(options[:suffix])
|
11
|
+
suffixes = options[:suffixes]
|
13
12
|
suffixes_items = suffixes ? suffixes.map { |s| clean(s) }.compact : nil
|
14
13
|
|
15
14
|
[prefix, clean(text), suffix || suffixes_items].compact.join(separator)
|
@@ -18,15 +17,15 @@ module Daigaku
|
|
18
17
|
private
|
19
18
|
|
20
19
|
def clean(text)
|
21
|
-
if text
|
22
|
-
|
23
|
-
|
24
|
-
end
|
20
|
+
return if text.nil?
|
21
|
+
parts(text.to_s).join(QuickStore.config.key_separator)
|
22
|
+
end
|
25
23
|
|
26
|
-
|
24
|
+
def parts(text)
|
25
|
+
text.split(QuickStore.config.key_separator).map do |key|
|
26
|
+
key.gsub(LEADING_NUMBERS, '').gsub(PART_JOINTS, '_').downcase
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
30
|
-
|
31
30
|
end
|
32
31
|
end
|
data/lib/daigaku/task.rb
CHANGED
data/lib/daigaku/terminal.rb
CHANGED
@@ -1,12 +1,11 @@
|
|
1
1
|
module Daigaku
|
2
2
|
module Terminal
|
3
|
-
|
4
3
|
# text should be of a width of 70 columns or less
|
5
4
|
def self.text(file_name)
|
6
5
|
texts_path = File.expand_path('../terminal/texts', __FILE__)
|
7
|
-
file
|
8
|
-
(File.exist?(file) ? File.read(file).to_s : '')
|
9
|
-
end
|
6
|
+
file = File.join(texts_path, "#{file_name}.txt")
|
10
7
|
|
8
|
+
File.exist?(file) ? File.read(file).to_s : ''
|
9
|
+
end
|
11
10
|
end
|
12
11
|
end
|
data/lib/daigaku/terminal/cli.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'thor'
|
2
|
+
require_relative 'courses'
|
3
|
+
require_relative 'solutions'
|
4
|
+
require_relative 'setup'
|
5
|
+
require_relative 'output'
|
2
6
|
|
3
7
|
module Daigaku
|
4
8
|
module Terminal
|
5
|
-
|
6
|
-
require_relative 'courses'
|
7
|
-
require_relative 'solutions'
|
8
|
-
require_relative 'setup'
|
9
|
-
require_relative 'output'
|
10
|
-
|
11
9
|
class CLI < Thor
|
12
10
|
include Terminal::Output
|
13
11
|
|
12
|
+
package_name 'Daigaku'
|
13
|
+
|
14
14
|
desc 'courses [COMMAND]', 'Handle daigaku courses'
|
15
15
|
subcommand 'courses', Terminal::Courses
|
16
16
|
|
@@ -21,7 +21,7 @@ module Daigaku
|
|
21
21
|
subcommand 'setup', Terminal::Setup
|
22
22
|
|
23
23
|
def self.start
|
24
|
-
Daigaku.config.import
|
24
|
+
Daigaku.config.import
|
25
25
|
super
|
26
26
|
end
|
27
27
|
|
@@ -40,7 +40,7 @@ module Daigaku
|
|
40
40
|
generator = Generator.new
|
41
41
|
generator.prepare
|
42
42
|
|
43
|
-
courses_path
|
43
|
+
courses_path = Daigaku.config.courses_path
|
44
44
|
solutions_path = Daigaku.config.solutions_path
|
45
45
|
|
46
46
|
generator.scaffold(courses_path, solutions_path)
|
@@ -1,14 +1,18 @@
|
|
1
|
+
require 'os'
|
2
|
+
require 'open-uri'
|
3
|
+
require 'zip'
|
4
|
+
require_relative 'output'
|
5
|
+
|
1
6
|
module Daigaku
|
2
7
|
module Terminal
|
3
|
-
|
4
|
-
require 'os'
|
5
|
-
require 'open-uri'
|
6
|
-
require 'zip'
|
7
|
-
require_relative 'output'
|
8
|
-
|
9
8
|
class Courses < Thor
|
10
9
|
include Terminal::Output
|
11
10
|
|
11
|
+
GITHUB = /github\.com/
|
12
|
+
MASTER_ZIP_URL = %r{github.com\/(.*)\/archive\/master.zip}
|
13
|
+
URL = /\A#{URI.regexp(%w(http https))}\z/
|
14
|
+
ZIP_FILE = /\.zip/
|
15
|
+
|
12
16
|
desc 'list', 'List your available daigaku courses'
|
13
17
|
def list
|
14
18
|
courses = Loading::Courses.load(Daigaku.config.courses_path)
|
@@ -22,22 +26,22 @@ module Daigaku
|
|
22
26
|
url = GithubClient.master_zip_url(Daigaku.config.initial_course) if use_initial_course
|
23
27
|
url = GithubClient.master_zip_url(options[:github]) if options[:github]
|
24
28
|
|
25
|
-
url_given = (url =~
|
26
|
-
github = use_initial_course || options[:github] || url.match(
|
29
|
+
url_given = (url =~ URL)
|
30
|
+
github = use_initial_course || options[:github] || url.match(GITHUB)
|
27
31
|
|
28
32
|
raise Download::NoUrlError unless url_given
|
29
|
-
raise Download::NoZipFileUrlError unless File.basename(url) =~
|
33
|
+
raise Download::NoZipFileUrlError unless File.basename(url) =~ ZIP_FILE
|
30
34
|
|
31
35
|
courses_path = Daigaku.config.courses_path
|
32
36
|
FileUtils.makedirs(courses_path) unless Dir.exist?(courses_path)
|
33
37
|
|
34
38
|
file_name = File.join(courses_path, url.split('/').last)
|
35
39
|
|
36
|
-
File.open(file_name, 'w') { |file| file << open(url).read }
|
40
|
+
File.open(file_name, 'w') { |file| file << URI.open(url).read }
|
37
41
|
course = Course.unzip(file_name, github_repo: github)
|
38
42
|
|
39
43
|
if github
|
40
|
-
user_and_repo = url.match(
|
44
|
+
user_and_repo = url.match(MASTER_ZIP_URL).captures.first
|
41
45
|
store_repo_data(options[:github] || user_and_repo)
|
42
46
|
end
|
43
47
|
|
@@ -46,11 +50,11 @@ module Daigaku
|
|
46
50
|
scaffold_solutions
|
47
51
|
|
48
52
|
say_info "Successfully #{action} the course \"#{course.title}\"!"
|
49
|
-
rescue Download::NoUrlError
|
53
|
+
rescue Download::NoUrlError
|
50
54
|
print_download_warning(url, "\"#{url}\" is not a valid URL!")
|
51
|
-
rescue Download::NoZipFileUrlError
|
55
|
+
rescue Download::NoZipFileUrlError
|
52
56
|
print_download_warning(url, "\"#{url}\" is not a URL of a *.zip file!")
|
53
|
-
rescue
|
57
|
+
rescue StandardError => e
|
54
58
|
print_download_warning(url, e.message)
|
55
59
|
ensure
|
56
60
|
FileUtils.rm(file_name) if File.exist?(file_name.to_s)
|
@@ -124,9 +128,9 @@ module Daigaku
|
|
124
128
|
end
|
125
129
|
|
126
130
|
def store_repo_data(user_and_repo)
|
127
|
-
parts
|
128
|
-
author = parts
|
129
|
-
course = parts
|
131
|
+
parts = (user_and_repo ||= Daigaku.config.initial_course).split('/')
|
132
|
+
author = parts[0]
|
133
|
+
course = parts[1]
|
130
134
|
|
131
135
|
course = Course.new(course)
|
132
136
|
QuickStore.store.set(course.key(:author), author)
|
@@ -164,7 +168,7 @@ module Daigaku
|
|
164
168
|
def print_course_not_available(course_name)
|
165
169
|
text = [
|
166
170
|
"The course \"#{course_name}\" is not available in",
|
167
|
-
"\"#{Daigaku.config.courses_path}\".\n"
|
171
|
+
"\"#{Daigaku.config.courses_path}\".\n"
|
168
172
|
]
|
169
173
|
|
170
174
|
say_warning text.join("\n")
|
@@ -174,6 +178,5 @@ module Daigaku
|
|
174
178
|
end
|
175
179
|
end
|
176
180
|
end
|
177
|
-
|
178
181
|
end
|
179
182
|
end
|
@@ -1,78 +1,71 @@
|
|
1
1
|
require 'thor'
|
2
|
-
require 'active_support/concern'
|
3
2
|
require 'colorize'
|
4
3
|
|
5
4
|
module Daigaku
|
6
5
|
module Terminal
|
7
|
-
|
8
6
|
module Output
|
9
|
-
|
10
|
-
|
11
|
-
included do
|
12
|
-
private
|
13
|
-
|
14
|
-
def say(text)
|
15
|
-
output = text.split("\n").map {|line| "\t#{line}" }.join("\n")
|
16
|
-
$stdout.puts output
|
17
|
-
end
|
7
|
+
private
|
18
8
|
|
19
|
-
|
20
|
-
|
21
|
-
|
9
|
+
def say(text)
|
10
|
+
output = text.split("\n").map { |line| "\t#{line}" }.join("\n")
|
11
|
+
$stdout.puts output
|
12
|
+
end
|
22
13
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
end
|
14
|
+
def empty_line
|
15
|
+
$stdout.puts ''
|
16
|
+
end
|
27
17
|
|
28
|
-
|
29
|
-
|
30
|
-
|
18
|
+
def get(string)
|
19
|
+
$stdout.print "\n\t#{string} "
|
20
|
+
$stdin.gets.strip
|
21
|
+
end
|
31
22
|
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
def say_info(text)
|
24
|
+
say_box(text, ' ℹ', :light_blue)
|
25
|
+
end
|
35
26
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
empty_line
|
27
|
+
def say_warning(text)
|
28
|
+
say_box(text, '⚠ ', :light_red)
|
29
|
+
end
|
40
30
|
|
41
|
-
|
42
|
-
|
31
|
+
def say_box(text, symbol, color)
|
32
|
+
empty_line
|
33
|
+
say line.send(color)
|
34
|
+
empty_line
|
43
35
|
|
44
|
-
|
45
|
-
|
46
|
-
empty_line
|
47
|
-
end
|
36
|
+
indented_text = text.split("\n").join("\n#{' ' * (symbol.length + 1)}")
|
37
|
+
say indented_text.prepend("#{symbol} ").send(color)
|
48
38
|
|
49
|
-
|
50
|
-
|
51
|
-
|
39
|
+
empty_line
|
40
|
+
say line.send(color)
|
41
|
+
empty_line
|
42
|
+
end
|
52
43
|
|
53
|
-
|
54
|
-
|
44
|
+
def line(symbol = '-')
|
45
|
+
symbol * 70
|
46
|
+
end
|
55
47
|
|
56
|
-
|
57
|
-
|
48
|
+
def get_command(command, description)
|
49
|
+
say description
|
58
50
|
|
59
|
-
|
60
|
-
|
61
|
-
next
|
62
|
-
end
|
51
|
+
loop do
|
52
|
+
cmd = get '>'
|
63
53
|
|
64
|
-
|
65
|
-
|
54
|
+
unless cmd == command
|
55
|
+
say "This was something else. Try \"#{command}\"."
|
56
|
+
next
|
66
57
|
end
|
67
|
-
end
|
68
58
|
|
69
|
-
|
70
|
-
|
71
|
-
confirm = get '(yes|no)'
|
72
|
-
yield if confirm == 'yes' && block_given?
|
59
|
+
system cmd
|
60
|
+
break
|
73
61
|
end
|
74
62
|
end
|
75
|
-
end
|
76
63
|
|
64
|
+
def get_confirm(description)
|
65
|
+
say_warning description
|
66
|
+
confirm = get '(yes|no)'
|
67
|
+
yield if confirm == 'yes' && block_given?
|
68
|
+
end
|
69
|
+
end
|
77
70
|
end
|
78
71
|
end
|
@@ -1,10 +1,8 @@
|
|
1
1
|
require 'thor'
|
2
|
+
require_relative 'output'
|
2
3
|
|
3
4
|
module Daigaku
|
4
5
|
module Terminal
|
5
|
-
|
6
|
-
require_relative 'output'
|
7
|
-
|
8
6
|
class Setup < Thor
|
9
7
|
include Terminal::Output
|
10
8
|
|
@@ -19,7 +17,7 @@ module Daigaku
|
|
19
17
|
path = get 'path:'
|
20
18
|
|
21
19
|
begin
|
22
|
-
@daigaku_path = File.expand_path(
|
20
|
+
@daigaku_path = File.expand_path(path.to_s, Dir.pwd)
|
23
21
|
rescue
|
24
22
|
say_warning "#{path} is no valid path name. Try another!"
|
25
23
|
next
|
@@ -29,7 +27,7 @@ module Daigaku
|
|
29
27
|
say "\"#{@daigaku_path}\""
|
30
28
|
|
31
29
|
confirmation = get '(yes|no)'
|
32
|
-
break if confirmation.
|
30
|
+
break if confirmation.casecmp('yes').zero?
|
33
31
|
|
34
32
|
empty_line
|
35
33
|
say 'No Problem. Just type another one!'
|
@@ -61,8 +59,8 @@ module Daigaku
|
|
61
59
|
solutions_path = options[:paths] || options[:solutions_path]
|
62
60
|
|
63
61
|
if courses_path.nil? && solutions_path.nil?
|
64
|
-
say_warning
|
65
|
-
say
|
62
|
+
say_warning 'Please specify options when using this command!'
|
63
|
+
say `daigaku setup help set`
|
66
64
|
return
|
67
65
|
end
|
68
66
|
|
@@ -76,11 +74,12 @@ module Daigaku
|
|
76
74
|
private
|
77
75
|
|
78
76
|
def prepare_directories(path)
|
79
|
-
courses_dir
|
77
|
+
courses_dir = Daigaku::Configuration::COURSES_DIR
|
80
78
|
courses_path = File.join(path, courses_dir)
|
79
|
+
|
81
80
|
Daigaku.config.courses_path = courses_path
|
82
81
|
|
83
|
-
solutions_dir
|
82
|
+
solutions_dir = Daigaku::Configuration::SOLUTIONS_DIR
|
84
83
|
solutions_path = File.join(path, solutions_dir)
|
85
84
|
|
86
85
|
if Dir.exist? solutions_path
|
@@ -92,7 +91,7 @@ module Daigaku
|
|
92
91
|
|
93
92
|
text = [
|
94
93
|
"Your Daigaku directory is now set up.\n",
|
95
|
-
|
94
|
+
'Daigaku created/updated following two paths for you:',
|
96
95
|
courses_path,
|
97
96
|
solutions_path
|
98
97
|
]
|
@@ -101,15 +100,11 @@ module Daigaku
|
|
101
100
|
end
|
102
101
|
|
103
102
|
def update_config(attribute, value)
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
say_warning e.message
|
109
|
-
end
|
103
|
+
path = File.expand_path(value, Dir.pwd)
|
104
|
+
Daigaku.config.send("#{attribute}=", path)
|
105
|
+
rescue StandardError => e
|
106
|
+
say_warning e.message
|
110
107
|
end
|
111
|
-
|
112
108
|
end
|
113
|
-
|
114
109
|
end
|
115
110
|
end
|