belajar 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -4
  3. data/CODE_OF_CONDUCT.md +76 -0
  4. data/Gemfile +1 -1
  5. data/README.md +76 -1
  6. data/Rakefile +1 -1
  7. data/belajar.gemspec +23 -26
  8. data/belajar.png +0 -0
  9. data/bin/belajar +6 -2
  10. data/lib/belajar.rb +0 -1
  11. data/lib/belajar/chapter.rb +3 -4
  12. data/lib/belajar/coloring.rb +32 -0
  13. data/lib/belajar/configuration.rb +25 -28
  14. data/lib/belajar/congratulator.rb +17 -5
  15. data/lib/belajar/course.rb +9 -8
  16. data/lib/belajar/exceptions.rb +0 -2
  17. data/lib/belajar/generator.rb +13 -15
  18. data/lib/belajar/github_client.rb +5 -5
  19. data/lib/belajar/loadable.rb +23 -14
  20. data/lib/belajar/loading/chapters.rb +0 -2
  21. data/lib/belajar/loading/courses.rb +0 -2
  22. data/lib/belajar/loading/units.rb +0 -2
  23. data/lib/belajar/markdown.rb +2 -0
  24. data/lib/belajar/markdown/printer.rb +89 -0
  25. data/lib/belajar/markdown/ruby_doc.rb +104 -0
  26. data/lib/belajar/solution.rb +23 -15
  27. data/lib/belajar/storeable.rb +11 -12
  28. data/lib/belajar/task.rb +1 -1
  29. data/lib/belajar/terminal.rb +3 -4
  30. data/lib/belajar/terminal/cli.rb +8 -8
  31. data/lib/belajar/terminal/courses.rb +24 -21
  32. data/lib/belajar/terminal/output.rb +46 -53
  33. data/lib/belajar/terminal/setup.rb +13 -18
  34. data/lib/belajar/terminal/solutions.rb +27 -32
  35. data/lib/belajar/terminal/texts/hint_course_download.txt +2 -2
  36. data/lib/belajar/terminal/welcome.rb +9 -11
  37. data/lib/belajar/test.rb +7 -10
  38. data/lib/belajar/test_result.rb +54 -21
  39. data/lib/belajar/unit.rb +2 -4
  40. data/lib/belajar/version.rb +1 -1
  41. data/lib/belajar/views.rb +29 -33
  42. data/lib/belajar/views/chapters_menu.rb +16 -20
  43. data/lib/belajar/views/courses_menu.rb +12 -15
  44. data/lib/belajar/views/main_menu.rb +23 -23
  45. data/lib/belajar/views/menu.rb +14 -18
  46. data/lib/belajar/views/splash.rb +11 -13
  47. data/lib/belajar/views/subscriber.rb +38 -0
  48. data/lib/belajar/views/task_view.rb +97 -80
  49. data/lib/belajar/views/top_bar.rb +4 -10
  50. data/lib/belajar/views/units_menu.rb +16 -21
  51. data/lib/belajar/window.rb +20 -140
  52. data/spec/belajar/chapter_spec.rb +23 -18
  53. data/spec/belajar/coloring_spec.rb +35 -0
  54. data/spec/belajar/configuration_spec.rb +55 -51
  55. data/spec/belajar/congratulator_spec.rb +11 -8
  56. data/spec/belajar/course_spec.rb +75 -52
  57. data/spec/belajar/generator_spec.rb +24 -25
  58. data/spec/belajar/github_client_spec.rb +17 -18
  59. data/spec/belajar/loading/chapters_spec.rb +2 -3
  60. data/spec/belajar/loading/courses_spec.rb +2 -3
  61. data/spec/belajar/loading/units_spec.rb +4 -5
  62. data/spec/belajar/markdown/ruby_doc_spec.rb +116 -0
  63. data/spec/belajar/reference_solution_spec.rb +8 -10
  64. data/spec/belajar/solution_spec.rb +21 -22
  65. data/spec/belajar/storeable_spec.rb +12 -10
  66. data/spec/belajar/task_spec.rb +3 -4
  67. data/spec/belajar/terminal/cli_spec.rb +29 -21
  68. data/spec/belajar/terminal/courses_spec.rb +104 -99
  69. data/spec/belajar/terminal/output_spec.rb +44 -39
  70. data/spec/belajar/terminal/setup_spec.rb +1 -3
  71. data/spec/belajar/terminal/solutions_spec.rb +0 -2
  72. data/spec/belajar/terminal/welcome_spec.rb +0 -2
  73. data/spec/belajar/terminal_spec.rb +5 -7
  74. data/spec/belajar/test_example_spec.rb +16 -14
  75. data/spec/belajar/test_result_spec.rb +21 -25
  76. data/spec/belajar/test_spec.rb +11 -12
  77. data/spec/belajar/unit_spec.rb +24 -27
  78. data/spec/belajar/views/chapters_menu_spec.rb +0 -1
  79. data/spec/belajar/views/courses_menu_spec.rb +0 -1
  80. data/spec/belajar/views/menu_spec.rb +1 -2
  81. data/spec/belajar/views/task_view_spec.rb +0 -2
  82. data/spec/belajar/views/units_menu_spec.rb +0 -1
  83. data/spec/belajar/views_spec.rb +0 -1
  84. data/spec/belajar_spec.rb +9 -12
  85. data/spec/path_helpers_spec.rb +11 -12
  86. data/spec/resource_helpers_spec.rb +11 -12
  87. data/spec/spec_helper.rb +3 -4
  88. data/spec/support/macros/content_helpers.rb +16 -17
  89. data/spec/support/macros/mock_helpers.rb +6 -6
  90. data/spec/support/macros/path_helpers.rb +15 -15
  91. data/spec/support/macros/resource_helpers.rb +34 -35
  92. metadata +56 -75
@@ -1,11 +1,11 @@
1
1
  You can download a new belajar course by using the
2
2
  "belajar courses download [URL] [OPTIONS]" command, e.g.:
3
3
 
4
- $ belajar courses download https://github.com/wong-bejo/Get_started_with_Ruby/archive/master.zip
4
+ $ belajar courses download https://github.com/wong-bejo/Belajar_Ruby/archive/master.zip
5
5
 
6
6
  For Github resources you can also use the `--github` (short `-g`) option:
7
7
 
8
- $ belajar courses download -g wong-bejo/Get_started_with_Ruby
8
+ $ belajar courses download -g wong-bejo/Belajar_Ruby
9
9
 
10
10
  If you want to quick start with Daiaku's "Get started with Ruby"
11
11
  course just run:
@@ -1,22 +1,21 @@
1
1
  module Belajar
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 belajar paths."
18
+ say 'For now, lets setup the belajar paths.'
20
19
  Belajar::Terminal::Setup.new.init
21
20
 
22
21
  show_setup_list_announcement
@@ -37,7 +36,7 @@ module Belajar
37
36
  empty_line
38
37
  say Terminal.text :about
39
38
  empty_line
40
- say %x{belajar help}
39
+ say `belajar help`
41
40
  end
42
41
 
43
42
  private
@@ -45,7 +44,7 @@ module Belajar
45
44
  def show_setup_list_announcement
46
45
  command = 'belajar 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 Belajar
56
55
  command = 'belajar courses list'
57
56
  text = [
58
57
  "Well done. Now, type \"#{command}\" to see what courses are",
59
- "available in your belajar folder:"
58
+ 'available in your belajar folder:'
60
59
  ].join("\n")
61
60
 
62
61
  get_command(command, text)
@@ -65,7 +64,7 @@ module Belajar
65
64
  def show_courses_download_announcement
66
65
  command = 'belajar 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 Belajar course:"
70
69
  ].join("\n")
71
70
 
@@ -75,7 +74,7 @@ module Belajar
75
74
  def show_solutions_open_announcement
76
75
  command = 'belajar solutions open'
77
76
  text = [
78
- "When downloading a course, Belajar scaffolds empty solution files",
77
+ 'When downloading a course, Belajar 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 Belajar
86
85
  def show_learn_announcement
87
86
  command = 'belajar learn'
88
87
  text = [
89
- "Congratulations! You learned the first steps of using belajar.",
88
+ 'Congratulations! You learned the first steps of using belajar.',
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
@@ -1,25 +1,24 @@
1
- module Belajar
2
- require 'fileutils'
1
+ require 'fileutils'
3
2
 
3
+ module Belajar
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 --no-color --order defined --format j #{temp_spec}`
23
22
  remove_file(temp_spec)
24
23
 
25
24
  TestResult.new(result)
@@ -33,14 +32,12 @@ module Belajar
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,25 +1,33 @@
1
- module Belajar
1
+ require 'json'
2
2
 
3
+ module Belajar
3
4
  class TestResult
4
- require 'json'
5
+ CODE_ERROR_MESSAGE = ':( You got an error in your code!'.freeze
5
6
 
6
7
  attr_reader :examples, :example_count, :failure_count
7
8
 
8
9
  def initialize(result_json)
9
10
  @result = begin
10
11
  JSON.parse(result_json, symbolize_names: true)
11
- rescue
12
- syntax_error_json
12
+ rescue => error
13
+ syntax_error_json(error)
13
14
  end
14
15
 
15
- @example_count = @result[:summary][:example_count]
16
- @failure_count = @result[:summary][:failure_count]
16
+ @example_count = @result.dig(:summary, :example_count)
17
+ @failure_count = @result.dig(:summary, :failure_count)
18
+ error_count = @result.dig(:summary, :errors_outside_of_examples_count) || 0
19
+
20
+ if error_count > 0
21
+ @failure_count = error_count
22
+ details = error_details(@result)
23
+ @result = error_json(details)
24
+ end
17
25
 
18
26
  @examples = @result[:examples].map do |example|
19
27
  description = example[:full_description]
20
- status = example[:status]
21
- exception = example[:exception]
22
- message = exception ? exception[:message] : nil
28
+ status = example[:status]
29
+ exception = example[:exception]
30
+ message = exception ? exception[:message] : nil
23
31
 
24
32
  TestExample.new(description: description, status: status, message: message)
25
33
  end
@@ -47,40 +55,65 @@ module Belajar
47
55
 
48
56
  def build_failed_summary
49
57
  message = examples.map do |example|
50
- "#{example.description}\n#{example.status}: #{example.message}"
58
+ "#{example.description}\n#{example.status}: #{example.message}".strip
51
59
  end
52
60
 
53
- summary = message.map(&:strip).join("\n" * 3)
61
+ message.join("\n" * 3)
54
62
  end
55
63
 
56
- def syntax_error_json
64
+ def syntax_error_json(error)
65
+ details = failure_details(error)
66
+ error_json(details)
67
+ end
68
+
69
+ def error_details(result)
70
+ result[:messages]
71
+ .first
72
+ .split('\n')
73
+ .each_with_index
74
+ .select { |line, index| index > 0 && line.matches?(/temp_.+\.rb/) }
75
+ .first.to_s
76
+ end
77
+
78
+ def error_json(details)
57
79
  {
58
80
  summary: {},
59
81
  examples: [
60
82
  {
61
- status: 'failed',
62
- exception: { message: ":( You got a syntax error in your code!" }
83
+ status: TestExample::FAILED,
84
+ exception: { message: "#{CODE_ERROR_MESSAGE}\n\n#{details}" }
63
85
  }
64
86
  ]
65
87
  }
66
88
  end
89
+
90
+ def failure_details(error)
91
+ line = error.backtrace.first
92
+ error_message = remove_colorization(error.message)
93
+ "#{error.class} in #{line}:\n#{error_message}"
94
+ end
95
+
96
+ def remove_colorization(text)
97
+ text.gsub(/\x1b\[[0-9]*m/i, '')
98
+ end
67
99
  end
68
100
 
69
101
  class TestExample
102
+ PASSED = 'passed'.freeze
103
+ FAILED = 'failed'.freeze
70
104
 
71
105
  attr_reader :description, :status, :message
72
106
 
73
- EXAMPLE_PASSED_MESSAGE = "Your code passed this requirement."
107
+ EXAMPLE_PASSED_MESSAGE = 'Your code passed this requirement.'.freeze
74
108
 
75
- def initialize(args = {})
76
- @description = args[:description]
77
- @status = args[:status]
78
- @message = args[:message] || EXAMPLE_PASSED_MESSAGE
109
+ def initialize(status:, description: nil, message: nil)
110
+ @description = description
111
+ @status = status
112
+ @message = message || EXAMPLE_PASSED_MESSAGE
79
113
  end
80
114
 
81
115
  def passed?
82
- @status == 'passed'
116
+ @status == PASSED
83
117
  end
84
118
  end
85
-
86
119
  end
@@ -1,10 +1,9 @@
1
1
  module Belajar
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
 
@@ -17,12 +16,11 @@ module Belajar
17
16
  end
18
17
 
19
18
  def solution
20
- @solution = Solution.new(@path)
19
+ @solution ||= Solution.new(@path)
21
20
  end
22
21
 
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 Belajar
2
- VERSION = "0.1.1"
2
+ VERSION = '1.0.0'.freeze
3
3
  end
@@ -1,51 +1,47 @@
1
1
  require 'curses'
2
- require 'active_support/concern'
3
2
  require 'belajar/views/top_bar'
4
3
 
5
4
  module Belajar
6
5
  module Views
7
- extend ActiveSupport::Concern
6
+ include Curses
8
7
 
9
- included do
10
- include Curses
11
-
12
- def reset_menu_position
13
- @position = 0
14
- end
15
-
16
- private
8
+ def reset_menu_position
9
+ @position = 0
10
+ end
17
11
 
18
- def default_window(height = nil, width = nil, top = 0, left = 0)
19
- init_screen
12
+ private
20
13
 
21
- noecho
22
- crmode
23
- curs_set(0) # invisible cursor
14
+ def default_window(height = nil, width = nil, top = 0, left = 0)
15
+ init_screen
24
16
 
25
- height ||= lines
26
- width ||= cols + 1
17
+ noecho
18
+ crmode
19
+ curs_set(0) # invisible cursor
27
20
 
28
- window = Belajar::Window.new(height, width, top, left)
21
+ height ||= lines
22
+ width ||= cols + 1
29
23
 
30
- Curses.lines.times do |line|
31
- window.setpos(line, 0)
32
- window.clear_line
33
- end
24
+ window = Belajar::Window.new(height, width, top, left)
34
25
 
35
- window.keypad(true)
36
- window.scrollok(true)
37
- window.refresh
38
- window
26
+ Curses.lines.times do |line|
27
+ window.setpos(line, 0)
28
+ window.clear_line
39
29
  end
40
30
 
41
- def sub_window_below_top_bar(window, top_bar)
42
- top_bar.show
43
- top = top_bar.height
44
- sub_window = window.subwin(window.maxy - top, window.maxx, top, 0)
45
- sub_window.keypad(true)
46
- sub_window
47
- end
31
+ window.keypad(true)
32
+ window.scrollok(true)
33
+ window.refresh
34
+ window
48
35
  end
49
36
 
37
+ def sub_window_below_top_bar(window, top_bar)
38
+ top_bar.show
39
+
40
+ top = top_bar.height
41
+ sub_window = window.subwin(window.maxy - top, window.maxx, top, 0)
42
+
43
+ sub_window.keypad(true)
44
+ sub_window
45
+ end
50
46
  end
51
47
  end
@@ -2,9 +2,7 @@ require 'belajar/views/menu'
2
2
 
3
3
  module Belajar
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 Belajar
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 Belajar
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 Belajar
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 'belajar/views/menu'
2
2
 
3
3
  module Belajar
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 Belajar
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 Belajar
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