lessons_indexer 0.0.2.2 → 0.1.0

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.
@@ -0,0 +1,6 @@
1
+ module LessonsIndexer
2
+ module Models
3
+ class Heading < Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,17 @@
1
+ module LessonsIndexer
2
+ module Models
3
+ class Lesson < Base
4
+ NAME_PATTERN = /(?<minor_major>(?<major>(\d+))(?:\.|-)(?<minor>(\d+)))(?<ext>\.md)/i
5
+ attr_reader :name
6
+
7
+ def initialize(file_name)
8
+ super file_name
9
+ @name = file_name.gsub(NAME_PATTERN, ' \k<minor_major>').titlecase
10
+ end
11
+
12
+ def link(dir)
13
+ "* [#{name}](#{dir}/#{file_name})\n"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -18,14 +18,14 @@ module LessonsIndexer
18
18
  begin
19
19
  Slop.parse(argv, strict: true, help: true,
20
20
  banner: 'Welcome to Lessons Indexer. Here is the list of available options:') do |o|
21
- o.string '-p', '--path', 'Path to the directory with the course', default: '.'#, argument: true
21
+ o.string '-p', '--path', 'Path to the directory with the course', default: '.'
22
22
  o.bool '-s', '--skip_index', 'Skip index generation for the course', default: false
23
- o.string '-o', '--output', 'Output file', default: 'README.md'#, as: String, argument: true
23
+ o.string '-o', '--output', 'Output file', default: 'README.md'
24
24
  o.bool '-g', '--git', 'Push changes to the remote Git branch?', default: false
25
- o.string '-m', '--message', 'Commit message', default: 'Added index'#, as: String, argument: true
25
+ o.string '-m', '--message', 'Commit message', default: 'Added index'
26
26
  o.bool '-a', '--all', 'Work with all branches (except for master)', default: false
27
27
  o.bool '-i', '--headings', 'Add heading images to the beginning of the lesson files?', default: false
28
- o.string '-d', '--headings_dir', 'Relative path to the directory with heading images', default: 'headers'#, as: String, argument: true
28
+ o.string '-d', '--headings_dir', 'Relative path to the directory with heading images', default: 'headings'
29
29
  end.to_hash
30
30
  rescue Slop::Error => e
31
31
  exit_msg e.message
@@ -7,7 +7,7 @@ module LessonsIndexer
7
7
  end
8
8
 
9
9
  def start!
10
- with_messages("Welcome to Lessons Indexer ver#{LessonsIndexer::VERSION}!", "=== [ DONE. ] ===", false) do
10
+ with_messages("=== [ Welcome to Lessons Indexer ver#{LessonsIndexer::VERSION}! ] ===", "=== [ DONE. ] ===", false) do
11
11
  indexer = Indexer.new(options)
12
12
 
13
13
  within options.path do
@@ -15,27 +15,14 @@ module LessonsIndexer
15
15
  brancher = GitManager::Brancher.new
16
16
  brancher.get_branches.each do |branch|
17
17
  brancher.within_branch branch do
18
- work_with indexer
18
+ indexer.do_work!
19
19
  end
20
20
  end
21
21
  else
22
- work_with indexer
22
+ indexer.do_work!
23
23
  end
24
24
  end
25
25
  end
26
26
  end
27
-
28
- private
29
-
30
- def work_with(indexer)
31
- indexer.build_index! unless options.skip_index
32
- indexer.add_headings! if options.headings
33
- git_push! if options.git
34
- end
35
-
36
- def git_push!
37
- pusher = GitManager::Pusher.new(options.message)
38
- pusher.push!
39
- end
40
27
  end
41
28
  end
@@ -1,3 +1,3 @@
1
1
  module LessonsIndexer
2
- VERSION = "0.0.2.2"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -1,30 +1,28 @@
1
- module LessonsIndexer
2
- RSpec.describe Writer do
3
- before :each do
4
- @writer = Writer.new('test.txt')
5
- @writer << 'test'
6
- end
7
- after(:all) {File.delete('test.txt')}
1
+ RSpec.describe LessonsIndexer::Addons::FileManager::Writer do
2
+ before :each do
3
+ @writer = described_class.new('test.txt')
4
+ @writer << 'test'
5
+ end
6
+ after(:all) {File.delete('test.txt')}
8
7
 
9
- specify "#name" do
10
- expect(@writer.name).to eq('test.txt')
11
- end
8
+ specify "#name" do
9
+ expect(@writer.name).to eq('test.txt')
10
+ end
12
11
 
13
- specify "#<<" do
14
- contents = IO.read(@writer.name)
15
- expect(contents).to eq('test')
16
- end
12
+ specify "#<<" do
13
+ contents = IO.read(@writer.name)
14
+ expect(contents).to eq('test')
15
+ end
17
16
 
18
- context "#prepend_data" do
19
- before(:each) {@writer.prepend_data('prepended')}
20
- it "should add data to the beginning" do
21
- expect(IO.read(@writer.name)).to eq('prependedtest')
22
- end
17
+ context "#prepend_data" do
18
+ before(:each) {@writer.prepend_data('prepended')}
19
+ it "should add data to the beginning" do
20
+ expect(IO.read(@writer.name)).to eq('prependedtest')
21
+ end
23
22
 
24
- it "should not add data to the beginning if data is already present" do
25
- @writer.prepend_data('prepended')
26
- expect(IO.read(@writer.name)).to eq('prependedtest')
27
- end
23
+ it "should not add data to the beginning if data is already present" do
24
+ @writer.prepend_data('prepended')
25
+ expect(IO.read(@writer.name)).to eq('prependedtest')
28
26
  end
29
27
  end
30
28
  end
@@ -0,0 +1,23 @@
1
+ RSpec.describe LessonsIndexer::Addons::GitManager::Pusher do
2
+ subject {described_class.new('test message')}
3
+
4
+ specify "#message" do
5
+ expect(subject.message).to eq('test message')
6
+ end
7
+
8
+ specify { expect(subject).to respond_to(:push!) }
9
+ end
10
+
11
+ RSpec.describe LessonsIndexer::Addons::GitManager::Brancher do
12
+ subject {described_class.new(false)}
13
+
14
+ specify "#ignore_master" do
15
+ expect(subject.ignore_master).to eq(false)
16
+ end
17
+
18
+ specify { expect(subject).to respond_to(:within_branch) }
19
+
20
+ specify "#get_branches" do
21
+ expect(subject.get_branches).to include 'master'
22
+ end
23
+ end
@@ -1,66 +1,64 @@
1
- module Kernel
2
- RSpec.describe "helper utils" do
3
- context "#within" do
4
- before(:all) {@original_path = Dir.getwd}
5
- after(:each) {Dir.chdir(@original_path)}
1
+ RSpec.describe Kernel do
2
+ context "#within" do
3
+ before(:all) {@original_path = Dir.getwd}
4
+ after(:each) {Dir.chdir(@original_path)}
6
5
 
7
- it "should return result of the block" do
8
- expect(Kernel.within('/') {1 + 1}).to eq(2)
9
- end
10
-
11
- it "should return immediately if no block is given" do
12
- expect(Kernel.within('/')).to eq(nil)
13
- end
14
-
15
- it "should raise an error if directory does not exist" do
16
- err = capture_stderr do
17
- expect(-> { Kernel.within('non_existent_dir') {1 + 1} }).to raise_error(SystemExit)
18
- end.uncolorize
19
- expect(err).to eq("[ERROR] The provided directory non_existent_dir was not found! Aborting...\n")
20
- end
6
+ it "should return result of the block" do
7
+ expect(described_class.within('/') {1 + 1}).to eq(2)
8
+ end
21
9
 
22
- it "should return to the original directory" do
23
- Kernel.within('/', true) {1 + 1}
24
- expect(Dir.getwd).to eq(@original_path)
25
- end
10
+ it "should return immediately if no block is given" do
11
+ expect(described_class.within('/')).to eq(nil)
12
+ end
26
13
 
27
- it "should not return to the original directory by default" do
28
- Kernel.within('/') {1 + 1}
29
- expect(Dir.getwd).not_to eq(@original_path)
30
- end
14
+ it "should raise an error if directory does not exist" do
15
+ err = capture_stderr do
16
+ expect(-> { described_class.within('non_existent_dir') {1 + 1} }).to raise_error(SystemExit)
17
+ end.uncolorize
18
+ expect(err).to eq("[ERROR] The provided directory non_existent_dir was not found! Aborting...\n")
31
19
  end
32
20
 
33
- context "#with_messages" do
34
- let(:after_msg) {"after"}
35
- let(:before_msg) {"before"}
36
- it "should handle block and display messages and delimiter" do
37
- info = capture_stdout do
38
- with_messages(before_msg, after_msg) { 1 + 1 }
39
- end.uncolorize
40
- expect(info).to eq("#{before_msg}\n#{after_msg}\n#{'=' * 50}\n")
41
- end
21
+ it "should return to the original directory" do
22
+ described_class.within('/', true) {1 + 1}
23
+ expect(Dir.getwd).to eq(@original_path)
24
+ end
42
25
 
43
- it "should not display delimiter when false is passed" do
44
- info = capture_stdout do
45
- with_messages(before_msg, after_msg, false) { 1 + 1 }
46
- end.uncolorize
47
- expect(info).to eq("#{before_msg}\n#{after_msg}\n")
48
- end
26
+ it "should not return to the original directory by default" do
27
+ described_class.within('/') {1 + 1}
28
+ expect(Dir.getwd).not_to eq(@original_path)
49
29
  end
30
+ end
50
31
 
51
- specify "#warning" do
52
- expect(Kernel).to respond_to(:warning)
53
- err = capture_stderr do
54
- warning "alert"
32
+ context "#with_messages" do
33
+ let(:after_msg) {"after"}
34
+ let(:before_msg) {"before"}
35
+ it "should handle block and display messages and delimiter" do
36
+ info = capture_stdout do
37
+ with_messages(before_msg, after_msg) { 1 + 1 }
55
38
  end.uncolorize
56
- expect(err).to eq("[WARNING] alert\n")
39
+ expect(info).to eq("#{before_msg}\n#{after_msg}\n#{'=' * 50}\n")
57
40
  end
58
41
 
59
- specify "#exit_msg" do
60
- err = capture_stderr do
61
- expect(-> {exit_msg('critical')}).to raise_error(SystemExit)
42
+ it "should not display delimiter when false is passed" do
43
+ info = capture_stdout do
44
+ with_messages(before_msg, after_msg, false) { 1 + 1 }
62
45
  end.uncolorize
63
- expect(err).to eq("[ERROR] critical\n")
46
+ expect(info).to eq("#{before_msg}\n#{after_msg}\n")
64
47
  end
65
48
  end
49
+
50
+ specify "#warning" do
51
+ expect(described_class).to respond_to(:warning)
52
+ err = capture_stderr do
53
+ warning "alert"
54
+ end.uncolorize
55
+ expect(err).to eq("[WARNING] alert\n")
56
+ end
57
+
58
+ specify "#exit_msg" do
59
+ err = capture_stderr do
60
+ expect(-> {exit_msg('critical')}).to raise_error(SystemExit)
61
+ end.uncolorize
62
+ expect(err).to eq("[ERROR] critical\n")
63
+ end
66
64
  end
@@ -0,0 +1,28 @@
1
+ RSpec.describe LessonsIndexer::Collections::HeadingsList do
2
+ subject { sample_headings }
3
+
4
+ context "#for" do
5
+ it "should return heading for a lesson" do
6
+ lesson = double('lesson', major: 2, minor: 5)
7
+ expect(subject.for(lesson)).to eq(subject.first)
8
+ end
9
+
10
+ it "should return nil if heading does not exist" do
11
+ lesson = double('lesson', major: 10, minor: 5)
12
+ expect(subject.for(lesson)).to be_nil
13
+ end
14
+ end
15
+
16
+ context "#list" do
17
+ it "should respond to #each" do
18
+ expect(subject).to respond_to(:each)
19
+ end
20
+
21
+ it "should be sorted properly" do
22
+ sorted_headings = subject.sort
23
+ %w(1.3 2.5 5.8 10.2).each_with_index do |version, index|
24
+ expect(sorted_headings[index].file_name).to eq "lesson#{version}.jpg"
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ RSpec.describe LessonsIndexer::Collections::LessonsList do
2
+ subject { sample_lessons }
3
+
4
+ context "#list" do
5
+ it "should respond to #each" do
6
+ expect(subject).to respond_to(:each)
7
+ end
8
+
9
+ it "should be sorted properly" do
10
+ sorted_lessons = subject.sort
11
+ %w(1.3 2.5 5.8 10.2).each_with_index do |version, index|
12
+ expect(sorted_lessons[index].file_name).to eq "lesson#{version}.md"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,89 @@
1
+ RSpec.describe LessonsIndexer::Course do
2
+ subject {described_class.new('my_course_handouts', 'headings')}
3
+
4
+ specify "#dir" do
5
+ expect(subject.dir).to eq('my_course_handouts')
6
+ end
7
+
8
+ specify "#title" do
9
+ expect(subject.title).to eq('My Course')
10
+ end
11
+
12
+ specify "#headings_dir" do
13
+ expect(subject.headings_dir).to eq('headings')
14
+ end
15
+
16
+ specify "#headings" do
17
+ expect(subject.headings).to eq([])
18
+ end
19
+
20
+ specify "#generate_index" do
21
+ expect(subject).to receive(:lessons).and_return(sample_lessons)
22
+ expect(subject.generate_index).to eq("# Index for the My Course course\n\n* [Lesson 1.3](my_course_handouts/lesson1.3.md)\n* [Lesson 2.5](my_course_handouts/lesson2.5.md)\n* [Lesson 5.8](my_course_handouts/lesson5.8.md)\n* [Lesson 10.2](my_course_handouts/lesson10.2.md)\n")
23
+ end
24
+
25
+ context "#generate_headings" do
26
+ it "should return formatted heading and path to file" do
27
+ expect(subject).to receive(:lessons).and_return(sample_lessons)
28
+ expect(subject).to receive(:headings).and_return(sample_headings).exactly(4).times
29
+ expect {|block| subject.generate_headings(&block)}.to yield_successive_args(
30
+ ["![](headings/lesson2.5.jpg)\n\n", "#{Dir.pwd}/lesson2.5.md"],
31
+ ["![](headings/lesson10.2.jpg)\n\n", "#{Dir.pwd}/lesson10.2.md"],
32
+ ["![](headings/lesson1.3.jpg)\n\n", "#{Dir.pwd}/lesson1.3.md"],
33
+ ["![](headings/lesson5.8.jpg)\n\n", "#{Dir.pwd}/lesson5.8.md"]
34
+ )
35
+ end
36
+
37
+ it "should warn if heading is not found" do
38
+ expect(subject).to receive(:lessons).and_return(sample_lessons(true))
39
+ expect(subject).to receive(:headings).and_return(sample_headings).exactly(5).times
40
+ err = capture_stderr do
41
+ expect {|block| subject.generate_headings(&block)}.to yield_successive_args(
42
+ ["![](headings/lesson2.5.jpg)\n\n", "#{Dir.pwd}/lesson2.5.md"],
43
+ ["![](headings/lesson10.2.jpg)\n\n", "#{Dir.pwd}/lesson10.2.md"],
44
+ ["![](headings/lesson1.3.jpg)\n\n", "#{Dir.pwd}/lesson1.3.md"],
45
+ ["![](headings/lesson5.8.jpg)\n\n", "#{Dir.pwd}/lesson5.8.md"]
46
+ )
47
+ end.uncolorize
48
+ expect(err).to eq("[WARNING] I was not able to find heading image for the Lesson 6.3\n")
49
+ end
50
+ end
51
+
52
+ context "file system access" do
53
+ before(:all) { setup_env! }
54
+
55
+ after(:all) { clear_env! }
56
+
57
+ context "#load_headings!" do
58
+ let(:headings) {subject.load_headings!}
59
+
60
+ specify { expect(headings).to be_a LessonsIndexer::Collections::HeadingsList }
61
+
62
+ specify { expect(headings.map {|l| l.file_name}).not_to include('test2.png') }
63
+
64
+ it "should have four items" do
65
+ expect(headings.map {|l| l.file_name}.length).to eq(4)
66
+ end
67
+
68
+ specify "heading should be a kind of LessonsIndexer::Models::Heading" do
69
+ expect(headings.first).to be_a LessonsIndexer::Models::Heading
70
+ end
71
+ end
72
+
73
+ context "#load_lessons!" do
74
+ let(:lessons) {subject.load_lessons!}
75
+
76
+ specify { expect(lessons).to be_a LessonsIndexer::Collections::LessonsList }
77
+
78
+ specify { expect(lessons.map {|l| l.name}).not_to include('Test') }
79
+
80
+ it "should have four items" do
81
+ expect(lessons.map {|l| l.name}.length).to eq(4)
82
+ end
83
+
84
+ specify "lesson should be a kind of LessonsIndexer::Models::Lesson" do
85
+ expect(lessons.first).to be_a LessonsIndexer::Models::Lesson
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,83 @@
1
+ RSpec.describe LessonsIndexer::Indexer do
2
+ before(:all) { setup_env! }
3
+ after(:all) { clear_env! }
4
+
5
+ subject { described_class.new(LessonsIndexer::Options.new(sample_options)) }
6
+ let(:course) {LessonsIndexer::Course.new(subject.get_course_dir, subject.options.headings_dir)}
7
+
8
+ context "#options" do
9
+ it "should return output" do
10
+ expect(subject.options.output).to eq('test.md')
11
+ end
12
+ end
13
+
14
+ context "#do_work!" do
15
+ it "should build index if --skip_index is not set" do
16
+ expect(subject).to receive(:build_index)
17
+ expect(subject).not_to receive(:add_headings)
18
+ expect(subject).not_to receive(:git_push!)
19
+ capture_stdout { subject.do_work! }
20
+ end
21
+
22
+ it "should add headings if --headings is set" do
23
+ allow(subject.options).to receive(:skip_index).and_return(true)
24
+ allow(subject.options).to receive(:headings).and_return(true)
25
+ expect(subject).to receive(:add_headings)
26
+ expect(subject).not_to receive(:build_index)
27
+ expect(subject).not_to receive(:git_push!)
28
+ capture_stdout { subject.do_work! }
29
+ end
30
+
31
+ it "should push to github if --git is set" do
32
+ allow(subject.options).to receive(:skip_index).and_return(true)
33
+ allow(subject.options).to receive(:git).and_return(true)
34
+ expect(subject).to receive(:git_push!)
35
+ expect(subject).not_to receive(:build_index)
36
+ expect(subject).not_to receive(:add_headings)
37
+ capture_stdout { subject.do_work! }
38
+ end
39
+ end
40
+
41
+ context "#get_course_dir" do
42
+ it "should return course dir" do
43
+ expect(subject.get_course_dir)
44
+ end
45
+
46
+ it "should abort if course dir is not found" do
47
+ Kernel.within 'my_course_handouts', true do
48
+ err = capture_stderr do
49
+ expect(-> {subject.get_course_dir}).to raise_error(SystemExit)
50
+ end.uncolorize
51
+ expect(err).to eq("[ERROR] Lesson files were not found inside the provided directory. Aborting...\n")
52
+ end
53
+ end
54
+ end
55
+
56
+ context "#build_index" do
57
+ it "should display proper info messages" do
58
+ info = capture_stdout do
59
+ subject.build_index(course)
60
+ end.uncolorize
61
+ expect(info).to eq("Starting to build index...\nIndex for the My Course course is generated!\n#{'=' * 50}\n")
62
+ end
63
+
64
+ it "should generate proper index" do
65
+ capture_stdout { subject.build_index(course) }
66
+ expect(IO.read('test.md')).to eq("# Index for the My Course course\n\n* [Lesson 1.3](my_course_handouts/lesson1.3.md)\n* [Lesson 2.5](my_course_handouts/lesson2.5.md)\n* [Lesson 5.8](my_course_handouts/lesson5.8.md)\n* [Lesson 10.2](my_course_handouts/lesson10.2.md)\n")
67
+ end
68
+ end
69
+
70
+ context "#add_headings" do
71
+ it "should display proper info messages" do
72
+ info = capture_stdout do
73
+ subject.add_headings(course)
74
+ end.uncolorize
75
+ expect(info).to eq("Starting to add headings...\nHeadings for the lesson files of My Course course were added!\n#{'=' * 50}\n")
76
+ end
77
+
78
+ it "should generate proper index" do
79
+ capture_stdout { subject.add_headings(course) }
80
+ expect(IO.read('my_course_handouts/lesson10.2.md')).to eq("![](headings/lesson10.2.jpg)\n\n")
81
+ end
82
+ end
83
+ end