jekyll-books 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: