belajar 0.1.1

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 (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