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.
Files changed (88) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +7 -4
  3. data/CODE_OF_CONDUCT.md +77 -0
  4. data/README.md +11 -11
  5. data/bin/daigaku +6 -2
  6. data/daigaku.gemspec +22 -26
  7. data/lib/daigaku.rb +0 -1
  8. data/lib/daigaku/chapter.rb +3 -4
  9. data/lib/daigaku/coloring.rb +22 -27
  10. data/lib/daigaku/configuration.rb +25 -28
  11. data/lib/daigaku/congratulator.rb +17 -5
  12. data/lib/daigaku/course.rb +9 -8
  13. data/lib/daigaku/exceptions.rb +0 -2
  14. data/lib/daigaku/generator.rb +13 -15
  15. data/lib/daigaku/github_client.rb +5 -5
  16. data/lib/daigaku/loadable.rb +23 -14
  17. data/lib/daigaku/loading/chapters.rb +0 -2
  18. data/lib/daigaku/loading/courses.rb +0 -2
  19. data/lib/daigaku/loading/units.rb +0 -2
  20. data/lib/daigaku/markdown.rb +1 -0
  21. data/lib/daigaku/markdown/printer.rb +89 -0
  22. data/lib/daigaku/markdown/ruby_doc.rb +15 -15
  23. data/lib/daigaku/solution.rb +23 -15
  24. data/lib/daigaku/storeable.rb +11 -12
  25. data/lib/daigaku/task.rb +1 -1
  26. data/lib/daigaku/terminal.rb +3 -4
  27. data/lib/daigaku/terminal/cli.rb +8 -8
  28. data/lib/daigaku/terminal/courses.rb +22 -19
  29. data/lib/daigaku/terminal/output.rb +46 -53
  30. data/lib/daigaku/terminal/setup.rb +13 -18
  31. data/lib/daigaku/terminal/solutions.rb +27 -32
  32. data/lib/daigaku/terminal/welcome.rb +9 -11
  33. data/lib/daigaku/test.rb +7 -10
  34. data/lib/daigaku/test_result.rb +54 -21
  35. data/lib/daigaku/unit.rb +2 -4
  36. data/lib/daigaku/version.rb +1 -1
  37. data/lib/daigaku/views.rb +29 -33
  38. data/lib/daigaku/views/chapters_menu.rb +16 -20
  39. data/lib/daigaku/views/courses_menu.rb +12 -15
  40. data/lib/daigaku/views/main_menu.rb +23 -23
  41. data/lib/daigaku/views/menu.rb +14 -18
  42. data/lib/daigaku/views/splash.rb +11 -13
  43. data/lib/daigaku/views/subscriber.rb +38 -0
  44. data/lib/daigaku/views/task_view.rb +97 -80
  45. data/lib/daigaku/views/top_bar.rb +4 -10
  46. data/lib/daigaku/views/units_menu.rb +16 -21
  47. data/lib/daigaku/window.rb +12 -70
  48. data/spec/daigaku/chapter_spec.rb +23 -18
  49. data/spec/daigaku/coloring_spec.rb +0 -1
  50. data/spec/daigaku/configuration_spec.rb +54 -50
  51. data/spec/daigaku/congratulator_spec.rb +11 -8
  52. data/spec/daigaku/course_spec.rb +75 -52
  53. data/spec/daigaku/generator_spec.rb +24 -25
  54. data/spec/daigaku/github_client_spec.rb +17 -18
  55. data/spec/daigaku/loading/chapters_spec.rb +2 -3
  56. data/spec/daigaku/loading/courses_spec.rb +2 -3
  57. data/spec/daigaku/loading/units_spec.rb +4 -5
  58. data/spec/daigaku/markdown/ruby_doc_spec.rb +12 -6
  59. data/spec/daigaku/reference_solution_spec.rb +8 -10
  60. data/spec/daigaku/solution_spec.rb +21 -22
  61. data/spec/daigaku/storeable_spec.rb +12 -10
  62. data/spec/daigaku/task_spec.rb +3 -4
  63. data/spec/daigaku/terminal/cli_spec.rb +29 -21
  64. data/spec/daigaku/terminal/courses_spec.rb +104 -99
  65. data/spec/daigaku/terminal/output_spec.rb +44 -39
  66. data/spec/daigaku/terminal/setup_spec.rb +1 -3
  67. data/spec/daigaku/terminal/solutions_spec.rb +0 -2
  68. data/spec/daigaku/terminal/welcome_spec.rb +0 -2
  69. data/spec/daigaku/terminal_spec.rb +5 -7
  70. data/spec/daigaku/test_example_spec.rb +16 -14
  71. data/spec/daigaku/test_result_spec.rb +21 -25
  72. data/spec/daigaku/test_spec.rb +11 -12
  73. data/spec/daigaku/unit_spec.rb +24 -27
  74. data/spec/daigaku/views/chapters_menu_spec.rb +0 -1
  75. data/spec/daigaku/views/courses_menu_spec.rb +0 -1
  76. data/spec/daigaku/views/menu_spec.rb +1 -2
  77. data/spec/daigaku/views/task_view_spec.rb +0 -2
  78. data/spec/daigaku/views/units_menu_spec.rb +0 -1
  79. data/spec/daigaku/views_spec.rb +0 -1
  80. data/spec/daigaku_spec.rb +9 -12
  81. data/spec/path_helpers_spec.rb +11 -12
  82. data/spec/resource_helpers_spec.rb +11 -12
  83. data/spec/spec_helper.rb +3 -4
  84. data/spec/support/macros/content_helpers.rb +16 -17
  85. data/spec/support/macros/mock_helpers.rb +6 -6
  86. data/spec/support/macros/path_helpers.rb +15 -15
  87. data/spec/support/macros/resource_helpers.rb +34 -35
  88. metadata +32 -44
@@ -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 = solution_path(unit_path)
11
- @code = File.read(@path).strip if File.file?(@path)
12
- @verified = get_store_state
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
- result = Daigaku::Test.new(@unit_path).run(self.code)
17
- set_store_state(result.passed?)
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 = path.split('/')[-3..-1].join('/').gsub(FILE_SUFFIX, '')
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 = Daigaku.config.solutions_path
38
- sub_dirs = Storeable.key(path.split('/')[-3..-2].join('/').gsub(FILE_SUFFIX, ''))
39
- file = Storeable.key(File.basename(path)) + FILE_SUFFIX
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
- File.join(local_path, sub_dirs, file)
48
+ def directory_from(path)
49
+ path.split('/')[-3..-2].join('/').gsub(FILE_SUFFIX, '')
42
50
  end
43
51
 
44
- def set_store_state(verified)
52
+ def store_state=(verified)
45
53
  @verified = verified
46
54
  QuickStore.store.set(store_key, verified?)
47
55
  end
48
56
 
49
- def get_store_state
57
+ def store_state
50
58
  QuickStore.store.get(store_key)
51
59
  end
52
60
  end
53
- end
61
+ end
@@ -1,15 +1,14 @@
1
1
  module Daigaku
2
2
  module Storeable
3
-
4
3
  LEADING_NUMBERS = /^\d+[\_\-\s]+/
5
- PART_JOINTS = /[\_\-\s]+/
4
+ PART_JOINTS = /[\_\-\s]+/
6
5
 
7
6
  class << self
8
7
  def key(text, options = {})
9
- separator = QuickStore.config.key_separator
10
- prefix = options[:prefix]
11
- suffix = clean(options[:suffix])
12
- suffixes = options[: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
- parts = text.to_s.split(QuickStore.config.key_separator).map do |key|
23
- key.gsub(LEADING_NUMBERS, '').gsub(PART_JOINTS, '_').downcase
24
- end
20
+ return if text.nil?
21
+ parts(text.to_s).join(QuickStore.config.key_separator)
22
+ end
25
23
 
26
- parts.join(QuickStore.config.key_separator)
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
@@ -3,7 +3,7 @@ module Daigaku
3
3
  attr_reader :markdown, :path
4
4
 
5
5
  def initialize(path)
6
- @path = Dir[File.join(path, '*.md')].first
6
+ @path = Dir[File.join(path, '*.md')].first
7
7
  @markdown = File.read(@path).strip
8
8
  end
9
9
  end
@@ -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 = File.join(texts_path, "#{file_name.to_s}.txt")
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
@@ -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 = Daigaku.config.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 =~ /\A#{URI::regexp(['http', 'https'])}\z/)
26
- github = use_initial_course || options[:github] || url.match(/github\.com/)
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) =~ /\.zip/
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(/github.com\/(.*)\/archive\/master.zip/).captures.first
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 => e
53
+ rescue Download::NoUrlError
50
54
  print_download_warning(url, "\"#{url}\" is not a valid URL!")
51
- rescue Download::NoZipFileUrlError => e
55
+ rescue Download::NoZipFileUrlError
52
56
  print_download_warning(url, "\"#{url}\" is not a URL of a *.zip file!")
53
- rescue Exception => e
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 = (user_and_repo ||= Daigaku.config.initial_course).split('/')
128
- author = parts.first
129
- course = parts.second
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
- extend ActiveSupport::Concern
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
- def empty_line
20
- $stdout.puts ''
21
- end
9
+ def say(text)
10
+ output = text.split("\n").map { |line| "\t#{line}" }.join("\n")
11
+ $stdout.puts output
12
+ end
22
13
 
23
- def get(string)
24
- $stdout.print "\n\t#{string} "
25
- $stdin.gets.strip
26
- end
14
+ def empty_line
15
+ $stdout.puts ''
16
+ end
27
17
 
28
- def say_info(text)
29
- say_box(text, ' ℹ', :light_blue)
30
- end
18
+ def get(string)
19
+ $stdout.print "\n\t#{string} "
20
+ $stdin.gets.strip
21
+ end
31
22
 
32
- def say_warning(text)
33
- say_box(text, ' ', :light_red)
34
- end
23
+ def say_info(text)
24
+ say_box(text, ' ', :light_blue)
25
+ end
35
26
 
36
- def say_box(text, symbol, color)
37
- empty_line
38
- say line.send(color)
39
- empty_line
27
+ def say_warning(text)
28
+ say_box(text, '⚠ ', :light_red)
29
+ end
40
30
 
41
- indented_text = text.split("\n").join("\n#{' ' * (symbol.length + 1)}")
42
- say indented_text.prepend("#{symbol} ").send(color)
31
+ def say_box(text, symbol, color)
32
+ empty_line
33
+ say line.send(color)
34
+ empty_line
43
35
 
44
- empty_line
45
- say line.send(color)
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
- def line(symbol = '-')
50
- symbol * 70
51
- end
39
+ empty_line
40
+ say line.send(color)
41
+ empty_line
42
+ end
52
43
 
53
- def get_command(command, description)
54
- say description
44
+ def line(symbol = '-')
45
+ symbol * 70
46
+ end
55
47
 
56
- loop do
57
- cmd = get '>'
48
+ def get_command(command, description)
49
+ say description
58
50
 
59
- unless cmd == command
60
- say "This was something else. Try \"#{command}\"."
61
- next
62
- end
51
+ loop do
52
+ cmd = get '>'
63
53
 
64
- system cmd
65
- break
54
+ unless cmd == command
55
+ say "This was something else. Try \"#{command}\"."
56
+ next
66
57
  end
67
- end
68
58
 
69
- def get_confirm(description)
70
- say_warning description
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("#{path}", Dir.pwd)
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.downcase == 'yes'
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 "Please specify options when using this command!"
65
- say %x{ daigaku setup help set }
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 = Daigaku::Configuration::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 = Daigaku::Configuration::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
- "Daigaku created/updated following two paths for you:",
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
- begin
105
- path = File.expand_path(value, Dir.pwd)
106
- Daigaku.config.send("#{attribute}=", path)
107
- rescue Exception => e
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