daigaku 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +2 -3
  3. data/README.md +1 -1
  4. data/daigaku.gemspec +23 -26
  5. data/lib/daigaku.rb +0 -1
  6. data/lib/daigaku/chapter.rb +3 -4
  7. data/lib/daigaku/coloring.rb +13 -13
  8. data/lib/daigaku/configuration.rb +25 -28
  9. data/lib/daigaku/congratulator.rb +17 -5
  10. data/lib/daigaku/course.rb +9 -8
  11. data/lib/daigaku/exceptions.rb +0 -2
  12. data/lib/daigaku/generator.rb +13 -13
  13. data/lib/daigaku/github_client.rb +4 -4
  14. data/lib/daigaku/loadable.rb +11 -15
  15. data/lib/daigaku/loading/chapters.rb +0 -2
  16. data/lib/daigaku/loading/courses.rb +0 -2
  17. data/lib/daigaku/loading/units.rb +0 -2
  18. data/lib/daigaku/markdown.rb +1 -0
  19. data/lib/daigaku/markdown/printer.rb +89 -0
  20. data/lib/daigaku/markdown/ruby_doc.rb +6 -8
  21. data/lib/daigaku/solution.rb +18 -15
  22. data/lib/daigaku/storeable.rb +11 -12
  23. data/lib/daigaku/task.rb +1 -1
  24. data/lib/daigaku/terminal.rb +3 -4
  25. data/lib/daigaku/terminal/cli.rb +6 -8
  26. data/lib/daigaku/terminal/courses.rb +19 -16
  27. data/lib/daigaku/terminal/output.rb +0 -2
  28. data/lib/daigaku/terminal/setup.rb +13 -18
  29. data/lib/daigaku/terminal/solutions.rb +27 -32
  30. data/lib/daigaku/terminal/welcome.rb +9 -11
  31. data/lib/daigaku/test.rb +7 -10
  32. data/lib/daigaku/test_result.rb +13 -16
  33. data/lib/daigaku/unit.rb +1 -3
  34. data/lib/daigaku/version.rb +1 -1
  35. data/lib/daigaku/views.rb +4 -3
  36. data/lib/daigaku/views/chapters_menu.rb +16 -20
  37. data/lib/daigaku/views/courses_menu.rb +12 -15
  38. data/lib/daigaku/views/main_menu.rb +23 -23
  39. data/lib/daigaku/views/menu.rb +9 -13
  40. data/lib/daigaku/views/splash.rb +11 -13
  41. data/lib/daigaku/views/subscriber.rb +38 -0
  42. data/lib/daigaku/views/task_view.rb +80 -78
  43. data/lib/daigaku/views/top_bar.rb +4 -10
  44. data/lib/daigaku/views/units_menu.rb +16 -21
  45. data/lib/daigaku/window.rb +12 -70
  46. data/spec/daigaku/chapter_spec.rb +23 -18
  47. data/spec/daigaku/coloring_spec.rb +0 -1
  48. data/spec/daigaku/configuration_spec.rb +54 -50
  49. data/spec/daigaku/congratulator_spec.rb +11 -8
  50. data/spec/daigaku/course_spec.rb +70 -51
  51. data/spec/daigaku/generator_spec.rb +24 -25
  52. data/spec/daigaku/github_client_spec.rb +17 -18
  53. data/spec/daigaku/loading/chapters_spec.rb +2 -3
  54. data/spec/daigaku/loading/courses_spec.rb +2 -3
  55. data/spec/daigaku/loading/units_spec.rb +4 -5
  56. data/spec/daigaku/markdown/ruby_doc_spec.rb +3 -6
  57. data/spec/daigaku/reference_solution_spec.rb +8 -10
  58. data/spec/daigaku/solution_spec.rb +18 -20
  59. data/spec/daigaku/storeable_spec.rb +12 -10
  60. data/spec/daigaku/task_spec.rb +3 -4
  61. data/spec/daigaku/terminal/cli_spec.rb +29 -21
  62. data/spec/daigaku/terminal/courses_spec.rb +104 -99
  63. data/spec/daigaku/terminal/output_spec.rb +44 -39
  64. data/spec/daigaku/terminal/setup_spec.rb +1 -3
  65. data/spec/daigaku/terminal/solutions_spec.rb +0 -2
  66. data/spec/daigaku/terminal/welcome_spec.rb +0 -2
  67. data/spec/daigaku/terminal_spec.rb +5 -7
  68. data/spec/daigaku/test_example_spec.rb +16 -14
  69. data/spec/daigaku/test_result_spec.rb +21 -25
  70. data/spec/daigaku/test_spec.rb +11 -12
  71. data/spec/daigaku/unit_spec.rb +24 -27
  72. data/spec/daigaku/views/chapters_menu_spec.rb +0 -1
  73. data/spec/daigaku/views/courses_menu_spec.rb +0 -1
  74. data/spec/daigaku/views/menu_spec.rb +1 -2
  75. data/spec/daigaku/views/task_view_spec.rb +0 -2
  76. data/spec/daigaku/views/units_menu_spec.rb +0 -1
  77. data/spec/daigaku/views_spec.rb +0 -1
  78. data/spec/daigaku_spec.rb +9 -12
  79. data/spec/path_helpers_spec.rb +11 -12
  80. data/spec/resource_helpers_spec.rb +11 -12
  81. data/spec/spec_helper.rb +3 -4
  82. data/spec/support/macros/content_helpers.rb +16 -17
  83. data/spec/support/macros/mock_helpers.rb +6 -6
  84. data/spec/support/macros/path_helpers.rb +15 -15
  85. data/spec/support/macros/resource_helpers.rb +34 -35
  86. metadata +12 -10
@@ -1,46 +1,41 @@
1
+ require 'os'
2
+ require_relative 'output'
3
+
1
4
  module Daigaku
2
5
  module Terminal
3
-
4
- require 'os'
5
- require_relative 'output'
6
-
7
6
  class Solutions < Thor
8
7
  include Terminal::Output
9
8
 
10
9
  desc 'solutions open [COURSE NAME]', 'Open the solutions folder of a course in a GUI window'
11
10
  def open(course_name = '')
12
- begin
13
- path = File.join(Daigaku.config.solutions_path, course_name)
14
-
15
- unless Dir.exist?(path)
16
- text = [
17
- "The course directory \"#{File.basename(path)}\" is not available in",
18
- "\"#{File.dirname(path)}\".\n",
19
- 'Hint:',
20
- 'Run "daigaku scaffold" to create empty solution files for all courses.'
21
- ]
22
- say_warning text.join("\n")
23
-
24
- unless Loading::Courses.load(Daigaku.config.courses_path).empty?
25
- Terminal::Courses.new.list
26
- end
27
-
28
- return
11
+ path = File.join(Daigaku.config.solutions_path, course_name)
12
+
13
+ unless Dir.exist?(path)
14
+ text = [
15
+ "The course directory \"#{File.basename(path)}\" is not available in",
16
+ "\"#{File.dirname(path)}\".\n",
17
+ 'Hint:',
18
+ 'Run "daigaku scaffold" to create empty solution files for all courses.'
19
+ ]
20
+ say_warning text.join("\n")
21
+
22
+ unless Loading::Courses.load(Daigaku.config.courses_path).empty?
23
+ Terminal::Courses.new.list
29
24
  end
30
25
 
31
- if OS.windows?
32
- system "explorer '#{path}'"
33
- elsif OS.mac?
34
- system "open '#{path}'"
35
- elsif OS.linux?
36
- system "xdg-open '#{path}'"
37
- end
38
- rescue ConfigurationError => e
39
- say_warning e.message
26
+ return
40
27
  end
41
- end
42
28
 
29
+ if OS.windows?
30
+ system "explorer '#{path}'"
31
+ elsif OS.mac?
32
+ system "open '#{path}'"
33
+ elsif OS.linux?
34
+ system "xdg-open '#{path}'"
35
+ end
36
+ rescue ConfigurationError => e
37
+ say_warning e.message
38
+ end
43
39
  end
44
-
45
40
  end
46
41
  end
@@ -1,22 +1,21 @@
1
1
  module Daigaku
2
2
  module Terminal
3
-
4
3
  class Welcome
5
4
  include Terminal::Output
6
5
 
7
6
  def self.run
8
- self.new.run
7
+ new.run
9
8
  end
10
9
 
11
10
  def self.about
12
- self.new.about
11
+ new.about
13
12
  end
14
13
 
15
14
  def run
16
15
  empty_line
17
16
  say Terminal.text :welcome
18
17
  empty_line
19
- say "For now, let's setup the daigaku paths."
18
+ say 'For now, lets setup the daigaku paths.'
20
19
  Daigaku::Terminal::Setup.new.init
21
20
 
22
21
  show_setup_list_announcement
@@ -37,7 +36,7 @@ module Daigaku
37
36
  empty_line
38
37
  say Terminal.text :about
39
38
  empty_line
40
- say %x{daigaku help}
39
+ say `daigaku help`
41
40
  end
42
41
 
43
42
  private
@@ -45,7 +44,7 @@ module Daigaku
45
44
  def show_setup_list_announcement
46
45
  command = 'daigaku setup list'
47
46
  text = [
48
- "The courses path and solutions path have been added to your settings.",
47
+ 'The courses path and solutions path have been added to your settings.',
49
48
  "Just list your current settings with the \"#{command}\" command:"
50
49
  ].join("\n")
51
50
 
@@ -56,7 +55,7 @@ module Daigaku
56
55
  command = 'daigaku courses list'
57
56
  text = [
58
57
  "Well done. Now, type \"#{command}\" to see what courses are",
59
- "available in your daigaku folder:"
58
+ 'available in your daigaku folder:'
60
59
  ].join("\n")
61
60
 
62
61
  get_command(command, text)
@@ -65,7 +64,7 @@ module Daigaku
65
64
  def show_courses_download_announcement
66
65
  command = 'daigaku courses download'
67
66
  text = [
68
- "Oh! You don't have any courses, yet?",
67
+ 'Oh! You dont have any courses, yet?',
69
68
  "Just enter \"#{command}\" to download the basic Daigaku course:"
70
69
  ].join("\n")
71
70
 
@@ -75,7 +74,7 @@ module Daigaku
75
74
  def show_solutions_open_announcement
76
75
  command = 'daigaku solutions open'
77
76
  text = [
78
- "When downloading a course, Daigaku scaffolds empty solution files",
77
+ 'When downloading a course, Daigaku scaffolds empty solution files',
79
78
  "for your code on the fly.\n",
80
79
  "Type \"#{command}\" to open your solutions folder:"
81
80
  ].join("\n")
@@ -86,13 +85,12 @@ module Daigaku
86
85
  def show_learn_announcement
87
86
  command = 'daigaku learn'
88
87
  text = [
89
- "Congratulations! You learned the first steps of using daigaku.",
88
+ 'Congratulations! You learned the first steps of using daigaku.',
90
89
  "To continue and start learning Ruby type \"#{command}\":"
91
90
  ].join("\n")
92
91
 
93
92
  get_command(command, text)
94
93
  end
95
94
  end
96
-
97
95
  end
98
96
  end
data/lib/daigaku/test.rb CHANGED
@@ -1,25 +1,24 @@
1
- module Daigaku
2
- require 'fileutils'
1
+ require 'fileutils'
3
2
 
3
+ module Daigaku
4
4
  class Test
5
+ CODE_REGEX = /\[\['solution::code'\]\]/
5
6
 
6
7
  attr_reader :path
7
8
 
8
- CODE_REGEX = /\[\['solution::code'\]\]/
9
-
10
9
  def initialize(path)
11
10
  @unit_path = path
12
- @path = Dir[File.join(path, '*spec.rb')].first
11
+ @path = Dir[File.join(path, '*spec.rb')].first
13
12
  end
14
13
 
15
14
  def run(solution_code)
16
- spec_code = File.read(@path)
15
+ spec_code = File.read(@path)
17
16
  patched_spec_code = insert_code(spec_code, solution_code.to_s)
18
17
 
19
18
  temp_spec = File.join(File.dirname(@path), "temp_#{File.basename(@path)}")
20
19
  create_temp_spec(temp_spec, patched_spec_code)
21
20
 
22
- result = %x{ rspec --color --format j #{temp_spec} }
21
+ result = `rspec --color --format j #{temp_spec}`
23
22
  remove_file(temp_spec)
24
23
 
25
24
  TestResult.new(result)
@@ -33,14 +32,12 @@ module Daigaku
33
32
 
34
33
  def create_temp_spec(path, content)
35
34
  base_path = File.dirname(path)
36
- FileUtils.mkdir_p(base_path) unless Dir.exist?(base_path)
35
+ FileUtils.makedirs(base_path) unless Dir.exist?(base_path)
37
36
  File.open(path, 'w') { |f| f.puts content }
38
37
  end
39
38
 
40
39
  def remove_file(path)
41
40
  FileUtils.rm(path) if File.exist?(path)
42
41
  end
43
-
44
42
  end
45
-
46
43
  end
@@ -1,8 +1,7 @@
1
- module Daigaku
1
+ require 'json'
2
2
 
3
+ module Daigaku
3
4
  class TestResult
4
- require 'json'
5
-
6
5
  attr_reader :examples, :example_count, :failure_count
7
6
 
8
7
  def initialize(result_json)
@@ -17,9 +16,9 @@ module Daigaku
17
16
 
18
17
  @examples = @result[:examples].map do |example|
19
18
  description = example[:full_description]
20
- status = example[:status]
21
- exception = example[:exception]
22
- message = exception ? exception[:message] : nil
19
+ status = example[:status]
20
+ exception = example[:exception]
21
+ message = exception ? exception[:message] : nil
23
22
 
24
23
  TestExample.new(description: description, status: status, message: message)
25
24
  end
@@ -47,10 +46,10 @@ module Daigaku
47
46
 
48
47
  def build_failed_summary
49
48
  message = examples.map do |example|
50
- "#{example.description}\n#{example.status}: #{example.message}"
49
+ "#{example.description}\n#{example.status}: #{example.message}".strip
51
50
  end
52
51
 
53
- summary = message.map(&:strip).join("\n" * 3)
52
+ message.join("\n" * 3)
54
53
  end
55
54
 
56
55
  def syntax_error_json
@@ -59,7 +58,7 @@ module Daigaku
59
58
  examples: [
60
59
  {
61
60
  status: 'failed',
62
- exception: { message: ":( You got an error in your code!" }
61
+ exception: { message: ':( You got an error in your code!' }
63
62
  }
64
63
  ]
65
64
  }
@@ -67,20 +66,18 @@ module Daigaku
67
66
  end
68
67
 
69
68
  class TestExample
70
-
71
69
  attr_reader :description, :status, :message
72
70
 
73
- EXAMPLE_PASSED_MESSAGE = "Your code passed this requirement."
71
+ EXAMPLE_PASSED_MESSAGE = 'Your code passed this requirement.'.freeze
74
72
 
75
- def initialize(args = {})
76
- @description = args[:description]
77
- @status = args[:status]
78
- @message = args[:message] || EXAMPLE_PASSED_MESSAGE
73
+ def initialize(description:, status:, message: nil)
74
+ @description = description
75
+ @status = status
76
+ @message = message || EXAMPLE_PASSED_MESSAGE
79
77
  end
80
78
 
81
79
  def passed?
82
80
  @status == 'passed'
83
81
  end
84
82
  end
85
-
86
83
  end
data/lib/daigaku/unit.rb CHANGED
@@ -1,10 +1,9 @@
1
1
  module Daigaku
2
2
  class Unit
3
-
4
3
  attr_reader :title, :task
5
4
 
6
5
  def initialize(path)
7
- @path = path
6
+ @path = path
8
7
  @title = File.basename(path).gsub(/\_+/, ' ')
9
8
  end
10
9
 
@@ -23,6 +22,5 @@ module Daigaku
23
22
  def mastered?
24
23
  solution.verified?
25
24
  end
26
-
27
25
  end
28
26
  end
@@ -1,3 +1,3 @@
1
1
  module Daigaku
2
- VERSION = '0.3.0'
2
+ VERSION = '0.4.0'.freeze
3
3
  end
data/lib/daigaku/views.rb CHANGED
@@ -23,7 +23,7 @@ module Daigaku
23
23
  curs_set(0) # invisible cursor
24
24
 
25
25
  height ||= lines
26
- width ||= cols + 1
26
+ width ||= cols + 1
27
27
 
28
28
  window = Daigaku::Window.new(height, width, top, left)
29
29
 
@@ -40,12 +40,13 @@ module Daigaku
40
40
 
41
41
  def sub_window_below_top_bar(window, top_bar)
42
42
  top_bar.show
43
- top = top_bar.height
43
+
44
+ top = top_bar.height
44
45
  sub_window = window.subwin(window.maxy - top, window.maxx, top, 0)
46
+
45
47
  sub_window.keypad(true)
46
48
  sub_window
47
49
  end
48
50
  end
49
-
50
51
  end
51
52
  end
@@ -2,9 +2,7 @@ require 'daigaku/views/menu'
2
2
 
3
3
  module Daigaku
4
4
  module Views
5
-
6
5
  class ChaptersMenu < Menu
7
-
8
6
  private
9
7
 
10
8
  def before_enter(*args)
@@ -12,8 +10,8 @@ module Daigaku
12
10
  end
13
11
 
14
12
  def before_reenter(*args)
15
- @course = args[0]
16
- @chapter = args[1]
13
+ @course = args[0]
14
+ @chapter = args[1]
17
15
  @position = @course.chapters.find_index(@chapter)
18
16
  end
19
17
 
@@ -24,20 +22,20 @@ module Daigaku
24
22
  def interact_with(window)
25
23
  while char = window.getch
26
24
  case char
27
- when KEY_UP
28
- @position -= 1
29
- broadcast(:reset_menu_position)
30
- when KEY_DOWN
31
- @position += 1
32
- broadcast(:reset_menu_position)
33
- when 10 # Enter
34
- broadcast(:enter, @course, models[@position])
35
- return
36
- when 263 # Backspace
37
- broadcast(:reenter, @course)
38
- return
39
- when 27 # ESC
40
- exit
25
+ when KEY_UP
26
+ @position -= 1
27
+ broadcast(:reset_menu_position)
28
+ when KEY_DOWN
29
+ @position += 1
30
+ broadcast(:reset_menu_position)
31
+ when 10 # Enter
32
+ broadcast(:enter, @course, models[@position])
33
+ return
34
+ when 263 # Backspace
35
+ broadcast(:reenter, @course)
36
+ return
37
+ when 27 # ESC
38
+ exit
41
39
  end
42
40
 
43
41
  @position = items.length - 1 if @position < 0
@@ -53,8 +51,6 @@ module Daigaku
53
51
  def items
54
52
  models.map(&:title)
55
53
  end
56
-
57
54
  end
58
-
59
55
  end
60
56
  end
@@ -2,9 +2,7 @@ require 'daigaku/views/menu'
2
2
 
3
3
  module Daigaku
4
4
  module Views
5
-
6
5
  class CoursesMenu < Menu
7
-
8
6
  private
9
7
 
10
8
  def header_text
@@ -14,17 +12,17 @@ module Daigaku
14
12
  def interact_with(window)
15
13
  while char = window.getch
16
14
  case char
17
- when KEY_UP
18
- @position -= 1
19
- broadcast(:reset_menu_position)
20
- when KEY_DOWN
21
- @position += 1
22
- broadcast(:reset_menu_position)
23
- when 10 # Enter
24
- broadcast(:enter, models[@position])
25
- return
26
- when 27 # ESC
27
- exit
15
+ when KEY_UP
16
+ @position -= 1
17
+ broadcast(:reset_menu_position)
18
+ when KEY_DOWN
19
+ @position += 1
20
+ broadcast(:reset_menu_position)
21
+ when 10 # Enter
22
+ broadcast(:enter, models[@position])
23
+ return
24
+ when 27 # ESC
25
+ exit
28
26
  end
29
27
 
30
28
  @position = items.length - 1 if @position < 0
@@ -41,12 +39,11 @@ module Daigaku
41
39
  non_empty_courses = models.select { |course| !course.chapters.empty? }
42
40
 
43
41
  non_empty_courses.map do |course|
44
- line = "#{course.title}"
42
+ line = course.title
45
43
  self.items_info <<= [(course.author ? "(by #{course.author})" : '')]
46
44
  line
47
45
  end
48
46
  end
49
47
  end
50
-
51
48
  end
52
49
  end
@@ -1,37 +1,37 @@
1
+ require_relative 'subscriber'
2
+
1
3
  module Daigaku
2
4
  module Views
3
-
5
+ # Subscription: `first.subscribe(second)` means
6
+ # first subscribes second on the first's broadcast.
7
+ # second has to have method that is broadcasted.
4
8
  class MainMenu
5
9
  include Views
6
10
 
7
- def initialize
8
- courses_menu = Views::CoursesMenu.new
9
- chapters_menu = Views::ChaptersMenu.new
10
- units_menu = Views::UnitsMenu.new
11
- task_view = Views::TaskView.new
11
+ attr_reader :courses_menu, :chapters_menu, :units_menu, :task_view
12
12
 
13
- # Subscription: `first.subscribe(second)` means
14
- # first subscribes second on the first's broadcast.
15
- # second has to have method that is broadcasted.
13
+ def initialize
14
+ @courses_menu = Views::CoursesMenu.new
15
+ @chapters_menu = Views::ChaptersMenu.new
16
+ @units_menu = Views::UnitsMenu.new
17
+ @task_view = Views::TaskView.new
16
18
 
17
- # top down navigation
18
- courses_menu.subscribe(chapters_menu, on: :enter)
19
- chapters_menu.subscribe(units_menu, on: :enter)
20
- units_menu.subscribe(task_view, on: :enter)
19
+ subscribe_events
20
+ courses_menu.enter
21
+ end
21
22
 
22
- # bottom up navigation
23
- chapters_menu.subscribe(courses_menu, on: :reenter)
24
- units_menu.subscribe(chapters_menu, on: :reenter)
25
- task_view.subscribe(units_menu, on: :reenter)
23
+ private
26
24
 
27
- # position reset
28
- courses_menu.subscribe(chapters_menu, on: :reset_menu_position)
29
- courses_menu.subscribe(units_menu, on: :reset_menu_position)
30
- chapters_menu.subscribe(units_menu, on: :reset_menu_position)
25
+ def subscribe_events
26
+ subscriber = Subscriber.new(
27
+ courses_menu: courses_menu,
28
+ chapters_menu: chapters_menu,
29
+ units_menu: units_menu,
30
+ task_view: task_view
31
+ )
31
32
 
32
- courses_menu.enter
33
+ subscriber.subscribe_events!
33
34
  end
34
35
  end
35
-
36
36
  end
37
37
  end