daigaku 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/Gemfile +4 -0
  4. data/Guardfile +5 -0
  5. data/README.md +62 -0
  6. data/Rakefile +2 -0
  7. data/bin/daigaku +12 -0
  8. data/daigaku.gemspec +37 -0
  9. data/lib/daigaku.rb +27 -0
  10. data/lib/daigaku/chapter.rb +23 -0
  11. data/lib/daigaku/configuration.rb +86 -0
  12. data/lib/daigaku/course.rb +23 -0
  13. data/lib/daigaku/database.rb +64 -0
  14. data/lib/daigaku/exceptions.rb +19 -0
  15. data/lib/daigaku/generator.rb +53 -0
  16. data/lib/daigaku/loadable.rb +23 -0
  17. data/lib/daigaku/loading/chapters.rb +9 -0
  18. data/lib/daigaku/loading/courses.rb +9 -0
  19. data/lib/daigaku/loading/units.rb +9 -0
  20. data/lib/daigaku/reference_solution.rb +10 -0
  21. data/lib/daigaku/solution.rb +42 -0
  22. data/lib/daigaku/task.rb +10 -0
  23. data/lib/daigaku/terminal.rb +11 -0
  24. data/lib/daigaku/terminal/cli.rb +59 -0
  25. data/lib/daigaku/terminal/courses.rb +114 -0
  26. data/lib/daigaku/terminal/output.rb +72 -0
  27. data/lib/daigaku/terminal/setup.rb +115 -0
  28. data/lib/daigaku/terminal/solutions.rb +46 -0
  29. data/lib/daigaku/terminal/texts/about.txt +19 -0
  30. data/lib/daigaku/terminal/texts/courses_empty.txt +3 -0
  31. data/lib/daigaku/terminal/texts/hint_course_download.txt +13 -0
  32. data/lib/daigaku/terminal/texts/welcome.txt +12 -0
  33. data/lib/daigaku/terminal/welcome.rb +98 -0
  34. data/lib/daigaku/test.rb +46 -0
  35. data/lib/daigaku/test_result.rb +69 -0
  36. data/lib/daigaku/unit.rb +28 -0
  37. data/lib/daigaku/version.rb +3 -0
  38. data/lib/daigaku/views.rb +59 -0
  39. data/lib/daigaku/views/chapters_menu.rb +91 -0
  40. data/lib/daigaku/views/courses_menu.rb +87 -0
  41. data/lib/daigaku/views/main_menu.rb +37 -0
  42. data/lib/daigaku/views/splash.rb +57 -0
  43. data/lib/daigaku/views/task_view.rb +206 -0
  44. data/lib/daigaku/views/top_bar.rb +48 -0
  45. data/lib/daigaku/views/units_menu.rb +92 -0
  46. data/lib/daigaku/window.rb +160 -0
  47. data/spec/daigaku/chapter_spec.rb +76 -0
  48. data/spec/daigaku/configuration_spec.rb +161 -0
  49. data/spec/daigaku/course_spec.rb +75 -0
  50. data/spec/daigaku/database_spec.rb +79 -0
  51. data/spec/daigaku/generator_spec.rb +82 -0
  52. data/spec/daigaku/loading/chapters_spec.rb +16 -0
  53. data/spec/daigaku/loading/courses_spec.rb +16 -0
  54. data/spec/daigaku/loading/units_spec.rb +21 -0
  55. data/spec/daigaku/reference_solution_spec.rb +23 -0
  56. data/spec/daigaku/solution_spec.rb +79 -0
  57. data/spec/daigaku/task_spec.rb +23 -0
  58. data/spec/daigaku/terminal/cli_spec.rb +51 -0
  59. data/spec/daigaku/terminal/courses_spec.rb +60 -0
  60. data/spec/daigaku/terminal/output_spec.rb +123 -0
  61. data/spec/daigaku/terminal/setup_spec.rb +10 -0
  62. data/spec/daigaku/terminal/solutions_spec.rb +8 -0
  63. data/spec/daigaku/terminal/welcome_spec.rb +12 -0
  64. data/spec/daigaku/terminal_spec.rb +14 -0
  65. data/spec/daigaku/test_example_spec.rb +54 -0
  66. data/spec/daigaku/test_result_spec.rb +81 -0
  67. data/spec/daigaku/test_spec.rb +48 -0
  68. data/spec/daigaku/unit_spec.rb +85 -0
  69. data/spec/daigaku/views/chapters_menu_spec.rb +8 -0
  70. data/spec/daigaku/views/courses_menu_spec.rb +8 -0
  71. data/spec/daigaku/views/task_view_spec.rb +7 -0
  72. data/spec/daigaku/views/units_menu_spec.rb +8 -0
  73. data/spec/daigaku/views_spec.rb +23 -0
  74. data/spec/daigaku_spec.rb +57 -0
  75. data/spec/path_helpers_spec.rb +60 -0
  76. data/spec/resource_helpers_spec.rb +33 -0
  77. data/spec/spec_helper.rb +28 -0
  78. data/spec/support/macros/content_helpers.rb +129 -0
  79. data/spec/support/macros/mock_helpers.rb +20 -0
  80. data/spec/support/macros/path_helpers.rb +133 -0
  81. data/spec/support/macros/resource_helpers.rb +119 -0
  82. data/spec/support/macros/test_helpers.rb +6 -0
  83. metadata +361 -0
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Daigaku::Views::ChaptersMenu, type: :view do
4
+
5
+ it { is_expected.to respond_to :enter_chapters_menu }
6
+ it { is_expected.to respond_to :reenter_chapters_menu }
7
+
8
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Daigaku::Views::CoursesMenu, type: :view do
4
+
5
+ it { is_expected.to respond_to :enter }
6
+ it { is_expected.to respond_to :reenter_courses_menu }
7
+
8
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Daigaku::Views::TaskView, type: :view do
4
+
5
+ it { is_expected.to respond_to :enter_task_view }
6
+
7
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe Daigaku::Views::UnitsMenu, type: :view do
4
+
5
+ it { is_expected.to respond_to :enter_units_menu }
6
+ it { is_expected.to respond_to :reenter_units_menu }
7
+
8
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Daigaku::Views do
4
+
5
+ subject do
6
+ class Test
7
+ include Daigaku::Views
8
+ end
9
+
10
+ Test.new
11
+ end
12
+
13
+ [
14
+ :default_window,
15
+ :top_bar,
16
+ :main_panel,
17
+ :sub_window_below_top_bar
18
+ ].each do |method|
19
+ it "has a private method ::#{method}" do
20
+ expect(subject.private_methods.include?(method)).to be_truthy
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Daigaku module" do
4
+
5
+ describe "::config" do
6
+
7
+ subject { Daigaku.config }
8
+
9
+ [:config, :configure, :start].each do |method|
10
+ it "responds to ::#{method}" do
11
+ expect(Daigaku.singleton_methods).to include method
12
+ end
13
+ end
14
+
15
+ it "returns Configuration of class Daigaku::Configuration" do
16
+ expect(subject).to be_an_instance_of Daigaku::Configuration
17
+ end
18
+
19
+ it "returns a singleton setting" do
20
+ expect(subject).to be Daigaku.config
21
+ end
22
+ end
23
+
24
+ describe "::configure" do
25
+
26
+ let(:configure) do
27
+ proc do
28
+ Daigaku.configure do |config|
29
+ config.solutions_path = test_basepath
30
+ end
31
+ end
32
+ end
33
+
34
+ it "allows to configure the app" do
35
+ expect { configure.call }.not_to raise_error
36
+ end
37
+
38
+ it "sets configutation properties" do
39
+ configure.call
40
+ expect(Daigaku.config.solutions_path).to eq test_basepath
41
+ end
42
+
43
+ it "allows to change the config during runtime" do
44
+ Daigaku.configure do |config|
45
+ config.solutions_path = courses_basepath
46
+ end
47
+
48
+ expect(Daigaku.config.solutions_path).to eq courses_basepath
49
+ end
50
+ end
51
+
52
+ describe "::database" do
53
+ it "returns a Diagaku::Database instance" do
54
+ expect(Daigaku.database).to be_a Daigaku::Database
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe "PathHelpers" do
4
+
5
+ it "provides a temp base path" do
6
+ puts "\n* temp base path:"
7
+ puts temp_basepath
8
+ end
9
+
10
+ it "provides a test base path" do
11
+ puts "\n* test base path:"
12
+ puts test_basepath
13
+ end
14
+
15
+ it "provides a courses base path" do
16
+ puts "\n* courses base path:"
17
+ puts courses_basepath
18
+ end
19
+
20
+ it "provides the course directories" do
21
+ puts "\n* course dirs:"
22
+ puts course_dirs
23
+ end
24
+
25
+ it "provides each course's chapter directories" do
26
+ puts "\n* chapter dirs: "
27
+
28
+ course_dir_names.each do |course_name|
29
+ puts chapter_dirs(course_name)
30
+ end
31
+ end
32
+
33
+ it "provides each chapter's unit directories" do
34
+ puts "\n* unit dirs:"
35
+
36
+ course_dir_names.each do |course_name|
37
+ puts unit_dirs(course_name)
38
+ end
39
+ end
40
+
41
+ it "provides all unit directories as flattened array" do
42
+ puts "\n* all unit dirs:"
43
+ puts all_unit_dirs
44
+ end
45
+
46
+ it "provides all solution file paths" do
47
+ puts "\n* all solution file paths:"
48
+ puts all_solution_file_paths
49
+ end
50
+
51
+ it "provides all test file paths" do
52
+ puts "\n* all test file paths:"
53
+ puts all_test_file_paths
54
+ end
55
+
56
+ it "provides the local_storage_file path" do
57
+ puts "\n* local storage file path:"
58
+ puts local_storage_file
59
+ end
60
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe "ResourceHelpers" do
4
+
5
+ let(:course) { course_dir_names.first }
6
+ let(:chapter) { chapter_dir_names.first }
7
+ let(:unit) { unit_dir_names.first }
8
+
9
+ it "provides the available courses" do
10
+ puts "\n* available courses:"
11
+ puts available_courses.map &:inspect
12
+ end
13
+
14
+ it "provides a course's available chapters" do
15
+ puts "\n* available chapters:"
16
+ puts available_chapters(course).map &:inspect
17
+ end
18
+
19
+ it "provides a chapter's avaliable units" do
20
+ puts "\n* available units:"
21
+ puts available_units(course, chapter).map &:inspect
22
+ end
23
+
24
+ it "provides a unit's task file" do
25
+ puts "\n* task:"
26
+ puts available_task(course, chapter, unit).map &:inspect
27
+ end
28
+
29
+ it "provides a unit's reference solution file" do
30
+ puts "\n* reference solution:"
31
+ puts available_reference_solution(course, chapter, unit).map &:inspect
32
+ end
33
+ end
@@ -0,0 +1,28 @@
1
+ require 'rspec'
2
+ require 'webmock/rspec'
3
+
4
+ def require_files_from(paths = [])
5
+ paths.each do |path|
6
+ Dir[File.join(File.expand_path("#{path}*.rb", __FILE__))].sort.each do |file|
7
+ require file
8
+ end
9
+ end
10
+ end
11
+
12
+ RSpec.configure do |config|
13
+ config.color = true
14
+
15
+ require File.expand_path('../../lib/daigaku', __FILE__)
16
+ require_files_from ["../support/**/"]
17
+
18
+ config.include TestHelpers
19
+
20
+ config.before(:each, type: :view) { mock_default_window }
21
+
22
+ config.before(:all) do
23
+ prepare_courses
24
+ use_test_storage_file
25
+ end
26
+
27
+ config.after(:all) { cleanup_temp_data }
28
+ end
@@ -0,0 +1,129 @@
1
+ module ContentHelpers
2
+
3
+ TASK_FILE_CONTENT = [
4
+ "Heading\n======", "##Task",
5
+ "Solve this task!",
6
+ "##Hints",
7
+ "Just do it right..."
8
+ ].join("\n\n")
9
+
10
+ SOLUTION_CONTENT = 'print "hello world".upcase'
11
+
12
+ TEST_CONTENT = [
13
+ "require 'rspec'\n",
14
+ "describe 'Your code' do",
15
+ " it 'prints out HELLO WORLD' do",
16
+ " expect { [['solution::code']] }.to output('HELLO WORLD').to_stdout",
17
+ " end\n",
18
+ " it 'uses the method #upcase to get capital letters' do",
19
+ " allow(self).to receive(:puts).and_return ''",
20
+ " allow(self).to receive(:print).and_return ''",
21
+ " expect_any_instance_of(String).to receive(:upcase).and_return('HELLO WORLD')\n",
22
+ " [['solution::code']]",
23
+ " end",
24
+ "end"
25
+ ].join("\n")
26
+
27
+ TEST_FAILED_JSON =
28
+ %Q#
29
+ { "examples":
30
+ [
31
+ { "description": "description 1",
32
+ "full_description": "full description 1",
33
+ "status": "failed",
34
+ "file_path": "./spec.rb",
35
+ "line_number": 4,
36
+ "run_time": 0.001,
37
+ "exception": {
38
+ "class": "RSpec::Expectations::ExpectationNotMetError",
39
+ "message": "message 1",
40
+ "backtrace": ["backtrace 1_a", "backtrace 1_b"]
41
+ }
42
+ },
43
+ { "description": "description 2",
44
+ "full_description": "full description 2",
45
+ "status": "passed",
46
+ "file_path": "./spec.rb",
47
+ "line_number": 8,
48
+ "run_time": 0.002
49
+ }
50
+ ],
51
+ "summary": {
52
+ "duration": 0.003,
53
+ "example_count": 2,
54
+ "failure_count": 1,
55
+ "pending_count": 0
56
+ },
57
+ "summary_line": "2 example, 1 failure"
58
+ }
59
+ #
60
+
61
+ TEST_PASSED_JSON =
62
+ %Q#
63
+ { "examples":
64
+ [
65
+ { "description":"description 1",
66
+ "full_description":"full description 1",
67
+ "status":"passed",
68
+ "file_path":"spec.rb",
69
+ "line_number":4,
70
+ "run_time":0.001
71
+ },
72
+ { "description":"description 2",
73
+ "full_description":"full description 2",
74
+ "status":"passed",
75
+ "file_path":"spec.rb",
76
+ "line_number":8,
77
+ "run_time":0.002
78
+ }
79
+ ],
80
+ "summary":{
81
+ "duration":0.003,
82
+ "example_count":2,
83
+ "failure_count":0,
84
+ "pending_count":0
85
+ },
86
+ "summary_line":"2 example, 0 failures"
87
+ }
88
+ #
89
+
90
+ TEST_PASSED_MESSAGE = "Your code passed all tests. Congratulations!"
91
+ EXAMPLE_PASSED_MESSAGE = "Your code passed this requirement."
92
+
93
+ def task_file_content
94
+ TASK_FILE_CONTENT
95
+ end
96
+
97
+ def solution_content
98
+ SOLUTION_CONTENT
99
+ end
100
+
101
+ def test_content
102
+ TEST_CONTENT
103
+ end
104
+
105
+ def test_failed_json
106
+ TEST_FAILED_JSON
107
+ end
108
+
109
+ def test_passed_json
110
+ TEST_PASSED_JSON
111
+ end
112
+
113
+ def test_passed_json_parsed
114
+ JSON.parse(test_passed_json, symbolize_names: true)
115
+ end
116
+
117
+ def test_failed_json_parsed
118
+ JSON.parse(test_failed_json, symbolize_names: true)
119
+ end
120
+
121
+ def test_passed_summary
122
+ TEST_PASSED_MESSAGE
123
+ end
124
+
125
+ def example_passed_message
126
+ EXAMPLE_PASSED_MESSAGE
127
+ end
128
+
129
+ end
@@ -0,0 +1,20 @@
1
+ module MockHelpers
2
+
3
+ def mock_default_window
4
+ allow_any_instance_of(described_class).to receive(:default_window) { true }
5
+ end
6
+
7
+ def use_test_storage_file
8
+ Daigaku::Configuration.send(:new)
9
+ Daigaku.config.instance_variable_set(:@storage_file, local_storage_file)
10
+ Daigaku::Database.send(:new)
11
+ end
12
+
13
+ def suppress_print_out
14
+ allow(described_class).to receive(:say_warning) {}
15
+ allow(described_class).to receive(:say_info) {}
16
+ allow(described_class).to receive(:say) {}
17
+ allow($stdout).to receive(:puts) {}
18
+ allow($stdout).to receive(:print) {}
19
+ end
20
+ end
@@ -0,0 +1,133 @@
1
+ module PathHelpers
2
+ LOCAL_DIR = '.daigaku'
3
+ CONFIGURATION_FILE = 'daigaku.settings'
4
+ COURSES = 'courses'
5
+ SOLUTIONS = 'solutions'
6
+ TEMP_PATH = File.expand_path("../../../../tmp/", __FILE__)
7
+ COURSE_DIR_NAMES = ['Course_A', 'Course_B']
8
+ CHAPTER_DIR_NAMES = ['Chapter-1', 'Chapter-2']
9
+ UNIT_DIR_NAMES = ['unit-1', 'unit-2']
10
+ TASK_NAME = 'task.md'
11
+ REFERENCE_SOLUTION_NAME = 'solution.rb'
12
+ TEST_NAME = 'solution_spec.rb'
13
+ STORAGE_FILE = 'daigaku.db.yml'
14
+
15
+ def temp_basepath
16
+ TEMP_PATH
17
+ end
18
+
19
+ def test_basepath
20
+ File.join(TEMP_PATH, 'test')
21
+ end
22
+
23
+ def course_dir_names
24
+ COURSE_DIR_NAMES
25
+ end
26
+
27
+ def chapter_dir_names
28
+ CHAPTER_DIR_NAMES
29
+ end
30
+
31
+ def unit_dir_names
32
+ UNIT_DIR_NAMES
33
+ end
34
+
35
+ def task_name
36
+ TASK_NAME
37
+ end
38
+
39
+ def reference_solution_name
40
+ REFERENCE_SOLUTION_NAME
41
+ end
42
+
43
+ def test_name
44
+ TEST_NAME
45
+ end
46
+
47
+ def courses_basepath
48
+ File.join(test_basepath, COURSES)
49
+ end
50
+
51
+ def solutions_basepath
52
+ File.join(test_basepath, SOLUTIONS)
53
+ end
54
+
55
+ def course_dirs
56
+ course_dir_names.map do |dir|
57
+ File.join(courses_basepath, dir)
58
+ end
59
+ end
60
+
61
+ def chapter_dirs(course_name)
62
+ chapter_dir_names.map do |chapter|
63
+ File.join(courses_basepath, course_name, chapter)
64
+ end
65
+ end
66
+
67
+ def unit_dirs(course_name)
68
+ chapter_dir_names.map do |chapter|
69
+ unit_dir_names.map do |unit|
70
+ File.join(courses_basepath, course_name, chapter, unit)
71
+ end
72
+ end
73
+ end
74
+
75
+ def all_unit_dirs
76
+ dirs = course_dirs.map do |course_dir|
77
+ chapter_dir_names.map do |chapter_name|
78
+ unit_dir_names.map do |unit_name|
79
+ File.join(course_dir, chapter_name, unit_name)
80
+ end
81
+ end
82
+ end
83
+
84
+ dirs.flatten
85
+ end
86
+
87
+ def all_solution_file_paths
88
+ all_unit_dirs.map do |unit_dir|
89
+ underscored_unit_dir = File.basename(unit_dir).gsub(/(\_+|\-+|\.+)/, '_')
90
+ file_name = underscored_unit_dir + Daigaku::Generator::SOLUTION_SUFFIX
91
+ unit_path = File.join(solutions_basepath, unit_dir.split('/')[-3..-1])
92
+
93
+ File.join(File.dirname(unit_path), file_name)
94
+ end
95
+ end
96
+
97
+ def all_test_file_paths
98
+ all_unit_dirs.map do |unit_dir|
99
+ File.join(unit_dir, test_name)
100
+ end
101
+ end
102
+
103
+ def course_titles
104
+ gsub_underscores(course_dir_names)
105
+ end
106
+
107
+ def chapter_titles
108
+ gsub_underscores(chapter_dir_names)
109
+ end
110
+
111
+ def unit_titles
112
+ gsub_underscores(unit_dir_names)
113
+ end
114
+
115
+ def gsub_underscores(names)
116
+ names.map { |unit| unit.gsub(/\_+/, ' ') }
117
+ end
118
+
119
+ def local_courses_path
120
+ local_path = File.join(test_basepath, LOCAL_DIR, COURSES)
121
+ File.expand_path(local_path, __FILE__)
122
+ end
123
+
124
+ def local_configuration_file
125
+ local_path = File.join(test_basepath, LOCAL_DIR, CONFIGURATION_FILE)
126
+ File.expand_path(local_path, __FILE__)
127
+ end
128
+
129
+ def local_storage_file
130
+ local_path = File.join(test_basepath, LOCAL_DIR, STORAGE_FILE)
131
+ File.expand_path(local_path, __FILE__)
132
+ end
133
+ end