jekyll-books 0.1.1 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fc3d76a327dca916cc3239711596d92d6c63a500cd9b57735d04fdb3b0b4aa18
4
- data.tar.gz: d1d66c85fe90d86d592ccda897d57789119987e3a1fab74dcecf6932853f2d78
3
+ metadata.gz: 4e17ad0be3b849ff375538efb12e5e4fd1cf350196fab71bc6ade5da7a15285c
4
+ data.tar.gz: 47e41c342c8ffced55ab9f23f73274e0d500bc7a66a16b368a828fec819d5279
5
5
  SHA512:
6
- metadata.gz: 8ef5730d173775277aa3245bb759e3def72a5dfae738f8940d361527cad99e95d0277cd96255000eb56ae3278f3c316fac99768834a2a7febc3b063e8063fe0d
7
- data.tar.gz: 3fa07efd4ede54d262fb4f38ece3c7fc0b9d9377fe336e21edfd38fa64d0036b205873e0e4ba5db8254dde652d4c93d08ac892cfd9cfcdf1b3e4a291da66d942
6
+ metadata.gz: ba138b49c0d3962b6d9ebf23ebd7ecd876a5f946e771fb9dc841af7de48c3c942b654ace7f0ad8d8bdf9a9c2233db3250e3938a3647fb2c3847a554cca933eaa
7
+ data.tar.gz: fdd260cb0c45a412bec8f2b24d4da966fc773818e958aa3e79b26da23b1e394aeed55a8d9bcbe167b3856f76049e77d8a77266d068fac335243bf96c876f387c
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = "jekyll-books"
5
- spec.version = "0.1.1"
5
+ spec.version = "0.2.0"
6
6
  spec.authors = ["Sharon Zhang"]
7
7
  spec.email = ["zhangshiyu1992@hotmail.com"]
8
8
 
@@ -0,0 +1,10 @@
1
+ books_settings:
2
+ # 自动读取书籍的目录
3
+ source: "_books"
4
+ # 生成书籍的根目录
5
+ destination: "/"
6
+ # 书籍首页的默认布局
7
+ book:
8
+ layout: "book"
9
+ chapter:
10
+ layout: "chapter"
@@ -1,186 +1,2 @@
1
- module Jekyll
2
- require_relative "util"
3
-
4
- class BookPage < Page
5
- def initialize(site, base, dir)
6
- @site = site
7
- @base = base
8
- @dir = dir.gsub(/^_/, "").downcase
9
- @name = "index.md"
10
-
11
- self.process(@name)
12
-
13
- read_yaml(File.join(@base, "_books", @dir), @name)
14
-
15
- self.data["layout"] = 'book'
16
-
17
- Util.init_date_of_book(self.data)
18
- end
19
- end
20
-
21
- class ChapterPage < Page
22
- def initialize(site, base, dir, name, book, config)
23
- @site = site
24
- @base = base
25
-
26
- if name.include?("/")
27
- names = File.split(name)
28
- @dir = File.join(dir, names[0])
29
- @name = names[1]
30
- else
31
- @dir = dir
32
- @name = name
33
- end
34
-
35
- self.process(@name)
36
-
37
- read_yaml(File.join(base, "_books", @dir), @name)
38
-
39
- self.data["layout"] = 'chapter'
40
- self.data["book"] = book
41
- self.data["title"] = config["title"]
42
- self.data["level"] = config["level"]
43
- end
44
-
45
- def is_top_level?
46
- self.data["level"] == 1
47
- end
48
- end
49
-
50
- class BooksGenerator < Generator
51
- def generate(site)
52
-
53
- books = []
54
-
55
- dir = "_books"
56
- begin
57
- Dir.foreach(dir) do |book_dir|
58
- book_path = File.join(dir, book_dir)
59
- if File.directory?(book_path) and book_dir.chars.first != "."
60
- book = BookPage.new(site, site.source, book_dir)
61
-
62
- summary = File.read(File.join(book_path, "SUMMARY.md"))
63
-
64
- parts = get_parts_from_summary(summary)
65
-
66
- chapters = create_chapters(site, parts, book_dir, book)
67
-
68
- add_prev_and_next_to_every_chapter(chapters)
69
-
70
- push_chapters_to_site_pages(site, chapters)
71
-
72
- add_next_to_book_and_prev_to_chapter(book, chapters.first)
73
-
74
- books << book
75
- site.pages << book
76
- end
77
- end
78
-
79
- books += get_books_from_data(site)
80
-
81
- sort_books_by_date(books)
82
-
83
- site.config["books"] = books
84
- rescue Exception => e
85
- puts e
86
- end
87
- end
88
-
89
- private
90
- def get_parts_from_summary(summary)
91
- require "nokogiri"
92
-
93
- html = Kramdown::Document.new(summary).to_html
94
- parsed_html = Nokogiri::HTML(html)
95
-
96
- chapters = []
97
-
98
- list = parsed_html.xpath("//body/ul/li")
99
- list.each do |li|
100
- chapters.push( get_chapter_from_li(li, 1) )
101
-
102
- li.xpath("ul/li").each do |sub_li|
103
- chapters.push( get_chapter_from_li(sub_li, 2) )
104
- end
105
- end
106
- return chapters
107
- end
108
-
109
- def get_chapter_from_li(li, level)
110
- chapter = Hash.new
111
- chapter["link"] = li.xpath("a/@href").to_s
112
- chapter["title"] = li.xpath("a/text()").to_s
113
- chapter["level"] = level
114
- return chapter
115
- end
116
-
117
- def add_prev_and_next_to_every_chapter(chapters)
118
- chapters.each_with_index do |chapter, index|
119
- if index > 0
120
- chapter.data["prev"] = chapters[index - 1]
121
- end
122
- if index < chapters.size - 1
123
- chapter.data["next"] = chapters[index + 1]
124
- end
125
- end
126
- end
127
-
128
- def push_chapters_to_site_pages(site, chapters)
129
- chapters.each do |chapter|
130
- site.pages << chapter
131
- end
132
- end
133
-
134
- def add_next_to_book_and_prev_to_chapter(book, chapter)
135
- book.data["next"] = chapter
136
- chapter.data["prev"] = book
137
- end
138
-
139
- def create_index_page(site, books)
140
- book_index = IndexPage.new(site, site.source, "", books[0..3])
141
- book_index.render(site.layouts, site.site_payload)
142
- book_index.write(site.dest)
143
- return book_index
144
- end
145
-
146
- def create_chapters(site, parts, book_dir, book)
147
- chapters = []
148
- book.data["parts"] = []
149
-
150
- current = nil
151
-
152
- parts.each do |part|
153
- chapter = ChapterPage.new(site, site.source, book_dir, part["link"], book, part)
154
-
155
- if chapter.is_top_level?
156
- book.data["parts"].push(chapter)
157
- current = chapter
158
- chapter.data["parts"] = []
159
- else
160
- current.data["parts"].push(chapter)
161
- end
162
-
163
- chapters.push(chapter)
164
- end
165
- return chapters
166
- end
167
-
168
- def sort_books_by_date(books)
169
- books.sort_by! do |book|
170
- if book.respond_to? "data"
171
- [book.data["end"], book.data["start"]]
172
- else
173
- [book["end"], book["end"]]
174
- end
175
- end
176
- books.reverse!
177
- end
178
-
179
- def get_books_from_data(site)
180
- books = site.data["books"]
181
- books.each do |book|
182
- Util.init_date_of_book(book)
183
- end
184
- end
185
- end
186
- end
1
+ require "jekyll/generators/book"
2
+ require "jekyll/commands/book"
@@ -0,0 +1,85 @@
1
+ require "jekyll/pages/chapter"
2
+ require "jekyll/pages/book"
3
+ require_relative "summary"
4
+
5
+ class BookReader
6
+ attr_reader :root, :site, :name, :destination, :params
7
+ attr_accessor :index, :pages
8
+
9
+ def initialize(params, site, name)
10
+ @root = params["source"]
11
+ @destination = params["destination"]
12
+ @params = params
13
+ @site = site
14
+ @name = name
15
+ @index = 0
16
+ @pages = []
17
+ end
18
+
19
+ def read
20
+ @pages = [book_page]
21
+ index = 0
22
+
23
+ recursive_read_chapters(summary.read, 1, book_page)
24
+ add_prev_and_next
25
+
26
+ pages
27
+ end
28
+
29
+ def recursive_read_chapters(chapters, level, parent)
30
+ page = parent
31
+ chapter = parent
32
+
33
+ begin
34
+ chapter = chapters[index]
35
+ return unless chapter
36
+
37
+ if chapter["level"] > level
38
+ recursive_read_chapters(chapters, chapter["level"], page)
39
+ else
40
+ page = read_chapter(chapter)
41
+ pages << page
42
+ parent.data["parts"] << page
43
+ end
44
+
45
+ @index += 1
46
+ end while chapter["level"] >= level
47
+ end
48
+
49
+ def add_prev_and_next
50
+ pages.each_with_index do |page, i|
51
+ if i > 0
52
+ page.data["prev"] = pages[i - 1]
53
+ end
54
+ if i < pages.size - 1
55
+ page.data["next"] = pages[i + 1]
56
+ end
57
+ end
58
+ end
59
+
60
+ def read_chapter(chapter)
61
+ page = Jekyll::ChapterPage.new(
62
+ site,
63
+ params.merge({"name" => name}).merge(chapter)
64
+ )
65
+ page.data["book"] = book_page
66
+ page
67
+ end
68
+
69
+ def summary
70
+ Summary.new(book_folder)
71
+ end
72
+
73
+ def book_folder
74
+ File.join(site.source, root, name)
75
+ end
76
+
77
+ def book_page
78
+ @book_page ||= Jekyll::BookPage.new(
79
+ site,
80
+ params.merge({
81
+ "name" => name
82
+ })
83
+ )
84
+ end
85
+ end
@@ -0,0 +1,70 @@
1
+ require_relative "summary"
2
+ require "util"
3
+
4
+ class BookWriter
5
+ attr_reader :name, :root, :params, :summary
6
+ def initialize(name, root, params)
7
+ @name = name
8
+ @root = root
9
+ @params = params
10
+ @summary = Summary.new(book_folder)
11
+ end
12
+
13
+ def run
14
+ mkdir
15
+ create_index
16
+ create_summary
17
+ end
18
+
19
+ def book_folder
20
+ File.join(root, name)
21
+ end
22
+
23
+ def summary_file
24
+ summary.file_path
25
+ end
26
+
27
+ def index_file
28
+ summary.index_path
29
+ end
30
+
31
+ def mkdir
32
+ unless File.directory?(book_folder)
33
+ FileUtils.mkdir_p(book_folder)
34
+ end
35
+ end
36
+
37
+ def create_index
38
+ return if File.file?(index_file)
39
+
40
+ f = File.new(index_file, "w+")
41
+ f.write(content)
42
+ end
43
+
44
+ def create_summary
45
+ return if File.file?(summary_file) && !params["forced"]
46
+
47
+ summary.write(true)
48
+ end
49
+
50
+ def content
51
+ front_matter = YAML.dump(
52
+ Util.cover_default({
53
+ "title" => name.capitalize,
54
+ "start" => default_year,
55
+ "end" => default_year,
56
+ "img" => img_url
57
+ }, params)
58
+ )
59
+
60
+ front_matter + "---\n"
61
+ end
62
+
63
+ def default_year
64
+ Date.today.year
65
+ end
66
+
67
+ def img_url
68
+ "/img/home/#{name}.jpg"
69
+ end
70
+ end
@@ -0,0 +1,27 @@
1
+ require_relative "book_reader"
2
+
3
+ class BooksParser
4
+ attr_reader :root, :site, :books, :pages
5
+
6
+ def initialize(root, site)
7
+ @root = root
8
+ @site = site
9
+ @books = []
10
+ @pages = []
11
+ end
12
+
13
+ def root_path
14
+ File.join(site.source, root)
15
+ end
16
+
17
+ def parse(params)
18
+ Dir.foreach(root_path) do |book_dir|
19
+ book_path = File.join(root_path, book_dir)
20
+ if File.directory?(book_path) and book_dir.chars.first != "."
21
+ pages = BookReader.new(params, site, book_dir).read
22
+ books << pages[0]
23
+ @pages += pages
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,95 @@
1
+ require "find"
2
+ require "nokogiri"
3
+ require "kramdown"
4
+
5
+ class Summary
6
+ attr_reader :folder
7
+
8
+ def initialize(folder)
9
+ @folder = folder
10
+ end
11
+
12
+ def file_path
13
+ File.join(folder, "SUMMARY.md")
14
+ end
15
+
16
+ def index_path
17
+ File.join(folder, "index.md")
18
+ end
19
+
20
+ def chapter_files
21
+ files = []
22
+ Find.find(folder) do |f|
23
+ if File.file?(f) and
24
+ ![file_path, index_path].include?(f)
25
+ files << f if File.file?(f)
26
+ end
27
+ end
28
+ files.sort! do |f1, f2|
29
+ File.ctime(f1) <=> File.ctime(f2)
30
+ end
31
+ end
32
+
33
+ def chapters
34
+ chapter_files.map do |f|
35
+ {
36
+ :title => get_title(f),
37
+ :path => get_relative_path(f)
38
+ }
39
+ end
40
+ end
41
+
42
+ def get_title(file)
43
+ content = File.read(file)
44
+ html = Kramdown::Document.new(content).to_html
45
+ parsed_html = Nokogiri::HTML(html)
46
+ parsed_html.xpath("//h1/text()").to_s
47
+ end
48
+
49
+ def get_relative_path(file)
50
+ file.sub(folder + "/", "")
51
+ end
52
+
53
+ def write(autoload = false)
54
+ File.open(file_path, "w+") do |f|
55
+ f.write summary_title
56
+ if autoload
57
+ chapters.each do |chapter|
58
+ f.write "* [#{chapter[:title]}](#{chapter[:path]})\n"
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def read
65
+ content = File.read(file_path)
66
+ html = Kramdown::Document.new(content).to_html
67
+ parsed_html = Nokogiri::HTML(html)
68
+
69
+ chapters = []
70
+ parsed_html.xpath("//body/ul/li").each do |li|
71
+ recursive_parse_chapter(li, 1, chapters)
72
+ end
73
+ chapters
74
+ end
75
+
76
+ def recursive_parse_chapter(content, level, result)
77
+ result << parse_chapter(content, level)
78
+
79
+ content.xpath("ul/li").each do |li|
80
+ recursive_parse_chapter(li, level + 1, result)
81
+ end
82
+ end
83
+
84
+ def parse_chapter(chapter, level)
85
+ {
86
+ "link" => chapter.xpath("a/@href").to_s,
87
+ "title" => chapter.xpath("a/text()").to_s,
88
+ "level" => level
89
+ }
90
+ end
91
+
92
+ def summary_title
93
+ "# Summary\n\n"
94
+ end
95
+ end
@@ -0,0 +1,31 @@
1
+ require "jekyll-books/book_writer"
2
+
3
+ module Jekyll
4
+ module Commands
5
+ class Book < Command
6
+ def self.init_with_program(prog)
7
+ prog.command(:book) do |c|
8
+ c.syntax "book [options]"
9
+ c.description 'Create a new book.'
10
+
11
+ add_options(c)
12
+
13
+ c.action do |args, options|
14
+ configs = Jekyll.configuration()
15
+ BookWriter.new(
16
+ options["name"],
17
+ File.join(configs["source"], "_books"),
18
+ options
19
+ ).run
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.add_options(cmd)
25
+ cmd.option "name", "-n", "--name NAME"
26
+ cmd.option "title", "-t", "--title TITLE"
27
+ cmd.option "forced", "-f", "--forced"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,55 @@
1
+ require "jekyll-books/books_parser"
2
+ require "util"
3
+
4
+ module Jekyll
5
+ class BooksGenerator < Generator
6
+ def generate(site)
7
+ params = get_params(site)
8
+
9
+ parser = BooksParser.new(params["source"], site)
10
+ parser.parse(params)
11
+ site.pages += parser.pages
12
+
13
+ books = parser.books
14
+ books += get_books_from_data(site)
15
+ sort_books_by_date(books)
16
+ site.config["books"] = books
17
+ end
18
+
19
+ def get_params(site)
20
+ default_setting = {
21
+ "source" => "_books",
22
+ "destination" => "/",
23
+ "book" => {
24
+ "layout" => "book"
25
+ },
26
+ "chapter" => {
27
+ "layout" => "chapter"
28
+ }
29
+ }
30
+
31
+ default_setting.merge(site.config["books_settings"] || {})
32
+ end
33
+
34
+ private
35
+ def get_books_from_data(site)
36
+ books = site.data["books"]
37
+ return [] unless books
38
+
39
+ books.each do |book|
40
+ Util.init_date_of_book(book)
41
+ end
42
+ end
43
+
44
+ def sort_books_by_date(books)
45
+ books.sort_by! do |book|
46
+ if book.respond_to? "data"
47
+ [book.data["end"], book.data["start"]]
48
+ else
49
+ [book["end"], book["start"]]
50
+ end
51
+ end
52
+ books.reverse!
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,24 @@
1
+ require "util"
2
+
3
+ module Jekyll
4
+ class BookPage < Page
5
+ def initialize(site, params)
6
+ @site = site
7
+ @base = site.source
8
+ @dir = File.join(params["destination"], params["name"])
9
+ @name = "index.md"
10
+
11
+ self.process(@name)
12
+
13
+ read_yaml(
14
+ File.join(@base, params["source"], params["name"]),
15
+ @name
16
+ )
17
+
18
+ self.data = params["book"].merge(self.data)
19
+ self.data["parts"] = []
20
+
21
+ Util.init_date_of_book(self.data)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ module Jekyll
2
+ class ChapterPage < Page
3
+ def initialize(site, params)
4
+ @site = site
5
+ @base = site.source
6
+
7
+ path = File.join(params["name"], File.dirname(params["link"]))
8
+ @dir = File.join(params["destination"], path)
9
+
10
+ @name = File.basename(params["link"])
11
+
12
+ self.process(name)
13
+
14
+ read_yaml(
15
+ File.dirname(
16
+ File.join(@base, params["source"], path)
17
+ ),
18
+ name
19
+ )
20
+
21
+ self.data = params["chapter"].merge(self.data)
22
+ self.data["parts"] = []
23
+ end
24
+ end
25
+ end
@@ -14,4 +14,11 @@ module Util
14
14
  book["date"] = "#{book["start"]}-#{book["end"]}"
15
15
  end
16
16
  end
17
+
18
+ def Util.cover_default(default, params)
19
+ for key in default
20
+ default[key] = params[key] unless params[key].nil?
21
+ end
22
+ default
23
+ end
17
24
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-books
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Zhang
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-11 00:00:00.000000000 Z
11
+ date: 2020-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: jekyll
@@ -50,7 +50,16 @@ files:
50
50
  - LICENSE.txt
51
51
  - README.md
52
52
  - jekyll-books.gemspec
53
+ - lib/_config.yml
53
54
  - lib/jekyll-books.rb
55
+ - lib/jekyll-books/book_reader.rb
56
+ - lib/jekyll-books/book_writer.rb
57
+ - lib/jekyll-books/books_parser.rb
58
+ - lib/jekyll-books/summary.rb
59
+ - lib/jekyll/commands/book.rb
60
+ - lib/jekyll/generators/book.rb
61
+ - lib/jekyll/pages/book.rb
62
+ - lib/jekyll/pages/chapter.rb
54
63
  - lib/util.rb
55
64
  homepage: https://github.com/erlzhang/jekyll-books
56
65
  licenses: