belajar 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +6 -0
  4. data/Gemfile +4 -0
  5. data/Guardfile +5 -0
  6. data/README.md +5 -0
  7. data/Rakefile +5 -0
  8. data/belajar.gemspec +38 -0
  9. data/bin/belajar +12 -0
  10. data/lib/belajar.rb +24 -0
  11. data/lib/belajar/chapter.rb +23 -0
  12. data/lib/belajar/configuration.rb +89 -0
  13. data/lib/belajar/congratulator.rb +13 -0
  14. data/lib/belajar/course.rb +80 -0
  15. data/lib/belajar/exceptions.rb +19 -0
  16. data/lib/belajar/generator.rb +60 -0
  17. data/lib/belajar/github_client.rb +30 -0
  18. data/lib/belajar/loadable.rb +23 -0
  19. data/lib/belajar/loading/chapters.rb +9 -0
  20. data/lib/belajar/loading/courses.rb +9 -0
  21. data/lib/belajar/loading/units.rb +9 -0
  22. data/lib/belajar/reference_solution.rb +18 -0
  23. data/lib/belajar/solution.rb +53 -0
  24. data/lib/belajar/storeable.rb +32 -0
  25. data/lib/belajar/task.rb +10 -0
  26. data/lib/belajar/terminal.rb +12 -0
  27. data/lib/belajar/terminal/cli.rb +59 -0
  28. data/lib/belajar/terminal/courses.rb +179 -0
  29. data/lib/belajar/terminal/output.rb +78 -0
  30. data/lib/belajar/terminal/setup.rb +115 -0
  31. data/lib/belajar/terminal/solutions.rb +46 -0
  32. data/lib/belajar/terminal/texts/about.txt +19 -0
  33. data/lib/belajar/terminal/texts/congratulations.txt +12 -0
  34. data/lib/belajar/terminal/texts/courses_empty.txt +3 -0
  35. data/lib/belajar/terminal/texts/hint_course_download.txt +13 -0
  36. data/lib/belajar/terminal/texts/welcome.txt +12 -0
  37. data/lib/belajar/terminal/welcome.rb +98 -0
  38. data/lib/belajar/test.rb +46 -0
  39. data/lib/belajar/test_result.rb +86 -0
  40. data/lib/belajar/unit.rb +28 -0
  41. data/lib/belajar/version.rb +3 -0
  42. data/lib/belajar/views.rb +51 -0
  43. data/lib/belajar/views/chapters_menu.rb +60 -0
  44. data/lib/belajar/views/courses_menu.rb +52 -0
  45. data/lib/belajar/views/main_menu.rb +37 -0
  46. data/lib/belajar/views/menu.rb +89 -0
  47. data/lib/belajar/views/splash.rb +57 -0
  48. data/lib/belajar/views/task_view.rb +236 -0
  49. data/lib/belajar/views/top_bar.rb +40 -0
  50. data/lib/belajar/views/units_menu.rb +61 -0
  51. data/lib/belajar/window.rb +215 -0
  52. data/spec/belajar/chapter_spec.rb +76 -0
  53. data/spec/belajar/configuration_spec.rb +161 -0
  54. data/spec/belajar/congratulator_spec.rb +24 -0
  55. data/spec/belajar/course_spec.rb +201 -0
  56. data/spec/belajar/generator_spec.rb +82 -0
  57. data/spec/belajar/github_client_spec.rb +53 -0
  58. data/spec/belajar/loading/chapters_spec.rb +16 -0
  59. data/spec/belajar/loading/courses_spec.rb +16 -0
  60. data/spec/belajar/loading/units_spec.rb +21 -0
  61. data/spec/belajar/reference_solution_spec.rb +41 -0
  62. data/spec/belajar/solution_spec.rb +86 -0
  63. data/spec/belajar/storeable_spec.rb +35 -0
  64. data/spec/belajar/task_spec.rb +23 -0
  65. data/spec/belajar/terminal/cli_spec.rb +51 -0
  66. data/spec/belajar/terminal/courses_spec.rb +293 -0
  67. data/spec/belajar/terminal/output_spec.rb +151 -0
  68. data/spec/belajar/terminal/setup_spec.rb +10 -0
  69. data/spec/belajar/terminal/solutions_spec.rb +8 -0
  70. data/spec/belajar/terminal/welcome_spec.rb +12 -0
  71. data/spec/belajar/terminal_spec.rb +24 -0
  72. data/spec/belajar/test_example_spec.rb +54 -0
  73. data/spec/belajar/test_result_spec.rb +91 -0
  74. data/spec/belajar/test_spec.rb +48 -0
  75. data/spec/belajar/unit_spec.rb +85 -0
  76. data/spec/belajar/views/chapters_menu_spec.rb +6 -0
  77. data/spec/belajar/views/courses_menu_spec.rb +6 -0
  78. data/spec/belajar/views/menu_spec.rb +19 -0
  79. data/spec/belajar/views/task_view_spec.rb +7 -0
  80. data/spec/belajar/views/units_menu_spec.rb +6 -0
  81. data/spec/belajar/views_spec.rb +21 -0
  82. data/spec/belajar_spec.rb +51 -0
  83. data/spec/path_helpers_spec.rb +60 -0
  84. data/spec/resource_helpers_spec.rb +33 -0
  85. data/spec/spec_helper.rb +28 -0
  86. data/spec/support/macros/content_helpers.rb +129 -0
  87. data/spec/support/macros/mock_helpers.rb +25 -0
  88. data/spec/support/macros/path_helpers.rb +139 -0
  89. data/spec/support/macros/resource_helpers.rb +157 -0
  90. data/spec/support/macros/test_helpers.rb +6 -0
  91. metadata +385 -0
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe Belajar::Solution do
4
+
5
+ it { is_expected.to respond_to :code }
6
+ it { is_expected.to respond_to :path }
7
+ it { is_expected.to respond_to :verify! }
8
+ it { is_expected.to respond_to :verified? }
9
+ it { is_expected.to respond_to :errors }
10
+
11
+ before(:all) do
12
+ prepare_solutions
13
+ Belajar.config.solutions_path = solutions_basepath
14
+ end
15
+
16
+ let(:solution_path) { all_solution_file_paths.first }
17
+ let(:unit_path) { all_unit_dirs.first }
18
+
19
+ subject { Belajar::Solution.new(unit_path) }
20
+
21
+ it "has the prescribed solution path from the given unit path" do
22
+ expect(subject.path).to eq solution_path
23
+ end
24
+
25
+ it "has the prescribed code" do
26
+ expect(subject.code).to eq solution_content
27
+ end
28
+
29
+ it "loads the verified state from the store on creation" do
30
+ Belajar::Solution.new(unit_path).verify!
31
+ solution = Belajar::Solution.new(unit_path)
32
+
33
+ expect(solution).to be_verified
34
+ end
35
+
36
+ describe "#store_key" do
37
+ it "returns the appropriate key string for the solution" do
38
+ key = "verified/course_a/chapter_a/unit_a"
39
+ expect(subject.store_key).to eq key
40
+ end
41
+ end
42
+
43
+ context "Verification" do
44
+ describe "#verify!" do
45
+ it "returns a TestResult" do
46
+ expect(subject.verify!).to be_a Belajar::TestResult
47
+ end
48
+
49
+ it "sets @verified true if Test passed" do
50
+ QuickStore.store.set(subject.store_key, false)
51
+ solution = Belajar::Solution.new(unit_path)
52
+
53
+ expect(solution.instance_variable_get(:@verified)).to be_falsey
54
+ solution.verify!
55
+ expect(solution.instance_variable_get(:@verified)).to be_truthy
56
+ end
57
+
58
+ it "sets the solution's state in the store to verified if passed" do
59
+ subject.verify!
60
+ mastered = QuickStore.store.get(subject.store_key)
61
+
62
+ expect(mastered).to be_truthy
63
+ end
64
+
65
+ it "sets the solution's state in the store to unverified unless passed" do
66
+ subject.instance_variable_set(:@code, 'puts "I ❤ Belajar!"')
67
+ subject.verify!
68
+ mastered = QuickStore.store.get(subject.store_key)
69
+
70
+ expect(mastered).to be_falsey
71
+ end
72
+ end
73
+
74
+ describe "#verified?" do
75
+ it "is false by default" do
76
+ expect(subject.verified?).to be_falsey
77
+ end
78
+
79
+ it "returns true if #verify! passed" do
80
+ subject.verify!
81
+ expect(subject.verified?).to be_truthy
82
+ end
83
+ end
84
+ end
85
+
86
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Belajar::Storeable do
4
+
5
+ it "responds to ::key" do
6
+ expect(Belajar::Storeable).to respond_to :key
7
+ end
8
+
9
+ describe '::key' do
10
+ it "creates a store key from the given string" do
11
+ key = Belajar::Storeable.key('1-_Raw content-Title')
12
+ expect(key).to eq 'raw_content_title'
13
+ end
14
+
15
+ it "creates a cleaned up store key from a given path string" do
16
+ key = Belajar::Storeable.key('path/to/the/1-_Raw content string')
17
+ expect(key).to eq 'path/to/the/raw_content_string'
18
+ end
19
+
20
+ it "creates a prefixed key when a prefix option is given" do
21
+ key = Belajar::Storeable.key('1-_Raw content-Title', prefix: 'courses')
22
+ expect(key).to eq 'courses/raw_content_title'
23
+ end
24
+
25
+ it "creates a suffixed key if a suffix option is given" do
26
+ key = Belajar::Storeable.key('1-_Raw content-Title', suffix: '1-author')
27
+ expect(key).to eq 'raw_content_title/author'
28
+ end
29
+
30
+ it "creates a multi suffixed key if a suffixes option is given" do
31
+ key = Belajar::Storeable.key('1-_Raw content-Title', suffixes: ['meta', '1-author'] )
32
+ expect(key).to eq 'raw_content_title/meta/author'
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe Belajar::Task do
4
+
5
+ it { is_expected.to respond_to :markdown }
6
+ it { is_expected.to respond_to :path }
7
+
8
+ let(:unit_path) do
9
+ course_name = course_dir_names.first
10
+ unit_dirs(course_name)[0].first
11
+ end
12
+
13
+ subject { Belajar::Task.new(unit_path) }
14
+
15
+ it "has the prescribed path" do
16
+ path = File.join(unit_path, task_name)
17
+ expect(subject.path).to eq path
18
+ end
19
+
20
+ it "has the prescribed markdown" do
21
+ expect(subject.markdown).to eq task_file_content
22
+ end
23
+ end
@@ -0,0 +1,51 @@
1
+ require 'spec_helper'
2
+
3
+ describe Belajar::Terminal::CLI do
4
+
5
+ it { is_expected.to be_a Thor }
6
+ it { is_expected.to respond_to :about }
7
+ it { is_expected.to respond_to :welcome }
8
+ it { is_expected.to respond_to :scaffold }
9
+ it { is_expected.to respond_to :learn }
10
+ it { is_expected.to respond_to :courses }
11
+ it { is_expected.to respond_to :solutions }
12
+ it { is_expected.to respond_to :setup }
13
+
14
+ describe "#learn" do
15
+ it "starts the belajar terminal app if there are courses" do
16
+ allow(Belajar::Loading::Courses).to receive(:load) { [1] }
17
+ allow(Belajar).to receive(:start) { true }
18
+ expect(Belajar).to receive(:start)
19
+
20
+ subject.learn
21
+ end
22
+
23
+ it "does not start the belajar terminal app if there are no courses" do
24
+ suppress_print_out
25
+ allow(Belajar::Loading::Courses).to receive(:load) { [] }
26
+ allow(Belajar).to receive(:start) { true }
27
+ expect(Belajar).not_to receive(:start)
28
+
29
+ subject.learn
30
+ end
31
+ end
32
+
33
+ describe "#welcome" do
34
+ it "runs the welcome routine" do
35
+ allow(Belajar::Terminal::Welcome).to receive(:run) { true }
36
+ expect(Belajar::Terminal::Welcome).to receive(:run).once
37
+
38
+ subject.welcome
39
+ end
40
+ end
41
+
42
+ describe "#scaffold" do
43
+ it "runs the scaffolding" do
44
+ allow($stdout).to receive(:puts) {}
45
+ allow_any_instance_of(Belajar::Generator).to receive(:scaffold) { true }
46
+ expect_any_instance_of(Belajar::Generator).to receive(:scaffold).once
47
+
48
+ subject.scaffold
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,293 @@
1
+ require 'spec_helper'
2
+
3
+ describe Belajar::Terminal::Courses do
4
+
5
+ before { suppress_print_out }
6
+
7
+ it { is_expected.to be_a Thor }
8
+
9
+ describe "commands" do
10
+ [:list, :download].each do |method|
11
+ it { is_expected.to respond_to method }
12
+ end
13
+ end
14
+
15
+ describe "#download" do
16
+ before do
17
+ Belajar.config.courses_path = local_courses_path
18
+
19
+ @zip_file_name = "repo.zip"
20
+ @file_content = prepare_download(@zip_file_name)
21
+ @url = "https://example.com/#{@zip_file_name}"
22
+
23
+ stub_request(:get, @url)
24
+ .with(headers: {
25
+ 'Accept' => '*/*',
26
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
27
+ 'User-Agent' => 'Ruby'
28
+ })
29
+ .to_return(status: 200, body: @file_content, headers: {})
30
+
31
+ stub_request(:get, "https://api.github.com/repos/wong-bejo/Get_started_with_Ruby")
32
+ .with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' })
33
+ .to_return(status: 200, body: "{}", headers: {})
34
+ end
35
+
36
+ after { cleanup_download(@zip_file_name) }
37
+
38
+ it "downloads the file from a given url" do
39
+ expect{ subject.download(@url) }.not_to raise_error
40
+ end
41
+
42
+ it "uses Courses.unzip to unzip the course" do
43
+ expect(Belajar::Course).to receive(:unzip).once
44
+ subject.download(@url)
45
+ end
46
+
47
+ it "creates a new courses folder in the belajar courses directory" do
48
+ target_path = File.join(Belajar.config.courses_path, File.basename(course_dirs.first))
49
+ dirs = Dir[File.join(Belajar.config.courses_path, '**')]
50
+ expect(dirs.include?(target_path)).to be_truthy
51
+ end
52
+
53
+ it "raises an error if param is no url" do
54
+ expect(subject).to receive(:say_warning)
55
+ subject.download('no-url')
56
+ end
57
+
58
+ it "raises an error if param is no url to a zip file" do
59
+ expect(subject).to receive(:say_warning)
60
+ subject.download('http://exmaple.com/something-else')
61
+ end
62
+
63
+ describe "stores download data:" do
64
+ before do
65
+ @github_url = "https://github.com/user/course_a/archive/master.zip"
66
+
67
+ stub_request(:get, @github_url)
68
+ .with(headers: {
69
+ 'Accept' => '*/*',
70
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
71
+ 'User-Agent' => 'Ruby'
72
+ })
73
+ .to_return(status: 200, body: @file_content, headers: {})
74
+
75
+ stub_request(:get, "https://api.github.com/repos/course_a/repo")
76
+ .with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' })
77
+ .to_return(status: 200, body: "{}", headers: {})
78
+ end
79
+
80
+ it "stores the course's author for courses from Github" do
81
+ subject.download(@github_url)
82
+ store_key = 'courses/course_a/author'
83
+ expect(QuickStore.store.get(store_key)).to eq 'user'
84
+ end
85
+
86
+ it "stores the course's repo for courses from Github" do
87
+ subject.download(@github_url)
88
+ store_key = 'courses/course_a/github'
89
+ expect(QuickStore.store.get(store_key)).to eq 'user/course_a'
90
+ end
91
+
92
+ it "stores the downloading timestamp" do
93
+ time = Time.now
94
+ allow(Time).to receive(:now) { time }
95
+ subject.download(@url)
96
+ expect(QuickStore.store.get('courses/course_a/updated_at')).to eq time.to_s
97
+ end
98
+
99
+ it "stores the course's download url" do
100
+ subject.download(@url)
101
+ expect(QuickStore.store.get('courses/course_a/url')).to eq @url
102
+ end
103
+ end
104
+ end
105
+
106
+ describe '#update' do
107
+ before do
108
+ Belajar.config.courses_path = local_courses_path
109
+
110
+ @zip_file_name = "repo.zip"
111
+ @file_content = prepare_download(@zip_file_name)
112
+ @url = "https://example.com/#{@zip_file_name}"
113
+
114
+ stub_request(:get, @url)
115
+ .with(headers: {
116
+ 'Accept' => '*/*',
117
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
118
+ 'User-Agent' => 'Ruby'
119
+ })
120
+ .to_return(status: 200, body: @file_content, headers: {})
121
+
122
+ stub_request(:get, "https://api.github.com/repos/wong-bejo/Get_started_with_Ruby")
123
+ .with(headers: { 'Accept' => '*/*', 'User-Agent' => 'Ruby' })
124
+ .to_return(status: 200, body: "{}", headers: {})
125
+
126
+ allow(subject).to receive(:download) { true }
127
+ allow(Belajar::GithubClient).to receive(:updated?) { |attr| true }
128
+ end
129
+
130
+ after { cleanup_download(@zip_file_name) }
131
+
132
+ it "updates a course that is not from Github on each call" do
133
+ url = 'https://example.com/repo.zip'
134
+ expect(subject).to receive(:download).with(url, 'updated').once
135
+ subject.update('Course_A')
136
+ end
137
+
138
+ it "updates a course from Github if there are new contents" do
139
+ url = "https://github.com/user/repo/archive/master.zip"
140
+
141
+ stub_request(:get, url)
142
+ .with(headers: {
143
+ 'Accept' => '*/*',
144
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
145
+ 'User-Agent' => 'Ruby'
146
+ })
147
+ .to_return(status: 200, body: @file_content, headers: {})
148
+
149
+ QuickStore.store.set('courses/course_a/url', url)
150
+
151
+ expect(subject).to receive(:download).with(url, 'updated').once
152
+ subject.update('Course_A')
153
+ end
154
+
155
+ it "does not update a course from Github if there are no new contents" do
156
+ allow(Belajar::GithubClient).to receive(:updated?) { false }
157
+ url = "https://github.com/user/repo/archive/master.zip"
158
+
159
+ stub_request(:get, url)
160
+ .with(headers: {
161
+ 'Accept' => '*/*',
162
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
163
+ 'User-Agent' => 'Ruby'
164
+ })
165
+ .to_return(status: 200, body: @file_content, headers: {})
166
+
167
+ QuickStore.store.set('courses/course_a/url', url)
168
+ QuickStore.store.set('courses/course_a/github', 'user/repo')
169
+
170
+ expect(subject).not_to receive(:download)
171
+ subject.update('Course_A')
172
+ end
173
+
174
+ it "updates all courses if --all option is given" do
175
+ file_content = prepare_download(@zip_file_name, multiple_courses: true)
176
+
177
+ stub_request(:get, @url)
178
+ .with(
179
+ headers: {
180
+ 'Accept' => '*/*',
181
+ 'Accept-Encoding' => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3',
182
+ 'User-Agent' => 'Ruby'
183
+ }
184
+ ).to_return(status: 200, body: file_content, headers: {})
185
+
186
+ allow(subject).to receive(:options) { { all: true } }
187
+ allow(subject).to receive(:download).exactly(course_dirs.count).times
188
+
189
+ subject.update
190
+ end
191
+ end
192
+
193
+ describe '#delete' do
194
+ before do
195
+ Belajar.config.courses_path = local_courses_path
196
+ @course_path = course_dirs.first
197
+ prepare_courses unless Dir.exist?(@course_path)
198
+ allow(subject).to receive(:get).and_return('yes')
199
+ end
200
+
201
+ describe "confirmation" do
202
+ it "is nessecary to delete all courses" do
203
+ allow(subject).to receive(:options) { { all: true } }
204
+ expect(subject).to receive(:get_confirm)
205
+ subject.delete
206
+ end
207
+
208
+ it "is nessecary to delete a certain course" do
209
+ expect(subject).to receive(:get_confirm)
210
+ subject.delete(course_dir_names.first)
211
+ end
212
+ end
213
+
214
+ context "when confirmed:" do
215
+ it "deletes the given course" do
216
+ expect(Dir.exist?(@course_path)).to be_truthy
217
+ subject.delete(course_dir_names.first)
218
+ expect(Dir.exist?(@course_path)).to be_falsey
219
+ end
220
+
221
+ it "deletes all courses with option --all" do
222
+ allow(subject).to receive(:options) { { all: true } }
223
+ expect(Dir.exist?(@course_path)).to be_truthy
224
+ subject.delete
225
+
226
+ course_dirs.each do |dir|
227
+ expect(Dir.exist?(dir)).to be_falsey
228
+ end
229
+ end
230
+ end
231
+
232
+ context "when not confirmed:" do
233
+ before { allow(subject).to receive(:get).and_return('no') }
234
+
235
+ it "does not delete the given course" do
236
+ expect(Dir.exist?(@course_path)).to be_truthy
237
+ subject.delete(course_dir_names.first)
238
+ expect(Dir.exist?(@course_path)).to be_truthy
239
+ end
240
+
241
+ it "does not delete all courses" do
242
+ allow(subject).to receive(:options) { { all: true } }
243
+ expect(Dir.exist?(@course_path)).to be_truthy
244
+ subject.delete
245
+
246
+ course_dirs.each do |dir|
247
+ expect(Dir.exist?(dir)).to be_truthy
248
+ end
249
+ end
250
+ end
251
+
252
+ describe "status information" do
253
+ it "is printed when a certain course was deleted" do
254
+ expect(subject).to receive(:say_info)
255
+ subject.delete(course_dir_names.first)
256
+ end
257
+
258
+ it "is printed when all courses were deleted" do
259
+ allow(subject).to receive(:options) { { all: true } }
260
+ expect(subject).to receive(:say_info)
261
+ subject.delete
262
+ end
263
+ end
264
+
265
+ describe "QuickStore data" do
266
+ it "is deleted for a certain course" do
267
+ key = Belajar::Storeable.key(course_dir_names.first, prefix: 'courses')
268
+ QuickStore.store.set(key, 'some value')
269
+ subject.delete(course_dir_names.first)
270
+ expect(QuickStore.store.get(key)).to be_nil
271
+ end
272
+
273
+ it "is deleted for all courses when option --all is set" do
274
+ allow(subject).to receive(:options) { { all: true } }
275
+
276
+ keys = course_dir_names.map do |course_name|
277
+ Belajar::Storeable.key(course_name, prefix: 'courses')
278
+ end
279
+
280
+ course_dir_names.each_with_index do |course_name, index|
281
+ QuickStore.store.set(keys[index], course_name)
282
+ end
283
+
284
+ subject.delete
285
+
286
+ course_dir_names.each_with_index do |course_name, index|
287
+ expect(QuickStore.store.get(keys[index])).to be_nil
288
+ end
289
+
290
+ end
291
+ end
292
+ end
293
+ end