bookshelf 1.2.1 → 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile.lock +23 -19
- data/README.md +6 -43
- data/bookshelf.gemspec +3 -4
- data/lib/bookshelf.rb +5 -18
- data/lib/bookshelf/adapters/markdown.rb +2 -20
- data/lib/bookshelf/cli.rb +2 -32
- data/lib/bookshelf/dependency.rb +0 -8
- data/lib/bookshelf/exporter.rb +1 -5
- data/lib/bookshelf/generator.rb +7 -6
- data/lib/bookshelf/parser.rb +1 -9
- data/lib/bookshelf/parser/epub.rb +166 -93
- data/lib/bookshelf/parser/html.rb +25 -102
- data/lib/bookshelf/parser/pdf.rb +2 -29
- data/lib/bookshelf/stats.rb +0 -8
- data/lib/bookshelf/version.rb +1 -1
- data/templates/cover.erb +6 -4
- data/templates/epub.erb +2 -2
- data/templates/helper.rb +0 -29
- data/templates/layout.erb +9 -17
- data/templates/toc.erb +20 -0
- metadata +179 -189
- data/lib/bookshelf/errors.rb +0 -3
- data/lib/bookshelf/extensions/redcloth.rb +0 -69
- data/lib/bookshelf/extensions/string.rb +0 -11
- data/lib/bookshelf/parser/mobi.rb +0 -14
- data/lib/bookshelf/parser/txt.rb +0 -18
- data/lib/bookshelf/stream.rb +0 -27
- data/lib/bookshelf/syntax.rb +0 -124
- data/lib/bookshelf/toc.rb +0 -6
- data/lib/bookshelf/toc/epub.rb +0 -41
- data/lib/bookshelf/toc/html.rb +0 -78
data/lib/bookshelf/errors.rb
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
module RedCloth
|
2
|
-
INLINE_FORMATTERS = [:textile, :footnote, :link]
|
3
|
-
|
4
|
-
def self.convert(text)
|
5
|
-
new(text).to_html(*INLINE_FORMATTERS)
|
6
|
-
end
|
7
|
-
|
8
|
-
module Inline
|
9
|
-
FN_RE = /
|
10
|
-
(\s+)? # getting spaces
|
11
|
-
(\\)?%\{ # opening
|
12
|
-
(.*?) # footnote
|
13
|
-
\}# # closing
|
14
|
-
/xm
|
15
|
-
|
16
|
-
def footnote(text)
|
17
|
-
text.gsub!(FN_RE) do |m|
|
18
|
-
if $2
|
19
|
-
%[#{$1}%{#{$3}}]
|
20
|
-
else
|
21
|
-
%(<span class="footnote">#{$3}</span>)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
LINK_RE = /
|
27
|
-
<
|
28
|
-
((?:https?|ftp):\/\/.*?)
|
29
|
-
>
|
30
|
-
/xm
|
31
|
-
|
32
|
-
def link(text)
|
33
|
-
text.gsub!(LINK_RE) do |m|
|
34
|
-
%(<a href="#{$1}">#{$1}</a>)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
module Formatters
|
40
|
-
module HTML
|
41
|
-
def figure(options = {})
|
42
|
-
%[<p class="figure"><img src="../images/#{options[:text]}" alt="#{options[:class]}" /><br/><span class="caption">#{options[:class]}</span></p>]
|
43
|
-
end
|
44
|
-
|
45
|
-
def note(options = {})
|
46
|
-
%[<p class="note">#{options[:text]}</p>]
|
47
|
-
end
|
48
|
-
|
49
|
-
def attention(options = {})
|
50
|
-
%[<p class="attention">#{options[:text]}</p>]
|
51
|
-
end
|
52
|
-
|
53
|
-
def file(options = {})
|
54
|
-
base_url = Bookshelf.config[:base_url]
|
55
|
-
|
56
|
-
if base_url
|
57
|
-
url = File.join(base_url, options[:text])
|
58
|
-
else
|
59
|
-
url = content
|
60
|
-
$stderr << "\nYou're using `file. #{content}` but didn't set base_url in your configuration file.\n"
|
61
|
-
end
|
62
|
-
|
63
|
-
%[<p class="file"><span><strong>Download</strong> <a href="#{url}">#{options[:text]}</a></span></p>]
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
RedCloth.send(:include, RedCloth::Inline)
|
@@ -1,11 +0,0 @@
|
|
1
|
-
class String
|
2
|
-
def to_permalink
|
3
|
-
str = ActiveSupport::Multibyte::Chars.new(self.dup)
|
4
|
-
str = str.normalize(:kd).gsub(/[^\x00-\x7F]/,'').to_s
|
5
|
-
str.gsub!(/[^-\w\d]+/xim, "-")
|
6
|
-
str.gsub!(/-+/xm, "-")
|
7
|
-
str.gsub!(/^-?(.*?)-?$/, '\1')
|
8
|
-
str.downcase!
|
9
|
-
str
|
10
|
-
end
|
11
|
-
end
|
data/lib/bookshelf/parser/txt.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
module Bookshelf
|
2
|
-
module Parser
|
3
|
-
class Txt < Base
|
4
|
-
def parse
|
5
|
-
spawn_command ["html2text", "-style", "pretty", "-nobs", "-o", txt_file.to_s, html_file.to_s]
|
6
|
-
end
|
7
|
-
|
8
|
-
def html_file
|
9
|
-
root_dir.join("output/#{name}.html")
|
10
|
-
end
|
11
|
-
|
12
|
-
def txt_file
|
13
|
-
root_dir.join("output/#{name}.txt")
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
data/lib/bookshelf/stream.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
module Bookshelf
|
2
|
-
class Stream
|
3
|
-
attr_accessor :listener, :content
|
4
|
-
attr_reader :html
|
5
|
-
|
6
|
-
def initialize(content, listener)
|
7
|
-
@content = content
|
8
|
-
@listener = listener
|
9
|
-
@html = Nokogiri::HTML.parse(content)
|
10
|
-
end
|
11
|
-
|
12
|
-
def parse
|
13
|
-
traverse(html)
|
14
|
-
end
|
15
|
-
|
16
|
-
def traverse(node)
|
17
|
-
node.children.each do |child|
|
18
|
-
emit(child)
|
19
|
-
traverse(child)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def emit(node)
|
24
|
-
listener.send(:tag, node) if node.name =~ /h[1-6]/
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
data/lib/bookshelf/syntax.rb
DELETED
@@ -1,124 +0,0 @@
|
|
1
|
-
module Bookshelf
|
2
|
-
class Syntax
|
3
|
-
autoload :Highlight, "bookshelf/syntax/highlight"
|
4
|
-
|
5
|
-
attr_reader :io
|
6
|
-
attr_reader :lines
|
7
|
-
attr_reader :book_dir
|
8
|
-
attr_reader :format
|
9
|
-
|
10
|
-
# Render syntax blocks from specified source code.
|
11
|
-
#
|
12
|
-
# dir = Pathname.new(File.dirname(__FILE__))
|
13
|
-
# text = File.read(dir.join("text/some_file.textile"))
|
14
|
-
# Bookshelf::Syntax.render(dir, :textile, text)
|
15
|
-
#
|
16
|
-
def self.render(book_dir, format, source_code, raw = false)
|
17
|
-
source_code.gsub(/@@@(.*?)@@@/m) do |match|
|
18
|
-
new(book_dir, format, $1, raw).process
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
# Process each syntax block individually.
|
23
|
-
#
|
24
|
-
def initialize(book_dir, format, code, raw = false)
|
25
|
-
@format = format
|
26
|
-
@book_dir = book_dir
|
27
|
-
@io = StringIO.new(code)
|
28
|
-
@lines = io.readlines.collect(&:chomp)
|
29
|
-
@language = 'text' if raw
|
30
|
-
end
|
31
|
-
|
32
|
-
# Return unprocessed line codes.
|
33
|
-
#
|
34
|
-
def raw
|
35
|
-
lines[1..-1].join("\n")
|
36
|
-
end
|
37
|
-
|
38
|
-
# Return meta data from syntax annotation.
|
39
|
-
#
|
40
|
-
def meta
|
41
|
-
@meta ||= begin
|
42
|
-
line = lines.first.squish
|
43
|
-
_, language, file, modifier, reference = *line.match(/^([^ ]+)(?: ([^:#]+)(?:(:|#)(.*?))?)?$/)
|
44
|
-
|
45
|
-
if modifier == "#"
|
46
|
-
type = :block
|
47
|
-
elsif modifier == ":"
|
48
|
-
type = :range
|
49
|
-
elsif file
|
50
|
-
type = :file
|
51
|
-
else
|
52
|
-
type = :inline
|
53
|
-
end
|
54
|
-
|
55
|
-
{
|
56
|
-
:language => language,
|
57
|
-
:file => file,
|
58
|
-
:type => type,
|
59
|
-
:reference => reference
|
60
|
-
}
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Process syntax block, returning a +pre+ HTML tag.
|
65
|
-
#
|
66
|
-
def process
|
67
|
-
code = raw.to_s.strip_heredoc
|
68
|
-
code = process_file.gsub(/\n^.*?@(begin|end):.*?$/, "") if meta[:file]
|
69
|
-
|
70
|
-
code = Highlight.apply(code, language)
|
71
|
-
|
72
|
-
# escape for textile
|
73
|
-
code = %[<notextile>#{code}</notextile>] if format == :textile
|
74
|
-
code
|
75
|
-
end
|
76
|
-
|
77
|
-
private
|
78
|
-
# Process line range as in <tt>@@@ ruby some_file.rb:15,20 @@@</tt>.
|
79
|
-
#
|
80
|
-
def process_range(code)
|
81
|
-
starts, ends = meta[:reference].split(",").collect(&:to_i)
|
82
|
-
code = StringIO.new(code).readlines[starts-1..ends-1].join("\n").strip_heredoc.chomp
|
83
|
-
end
|
84
|
-
|
85
|
-
# Process block name as in <tt>@@@ ruby some_file.rb#some_block @@@</tt>.
|
86
|
-
#
|
87
|
-
def process_block(code)
|
88
|
-
code.gsub!(/\r\n/, "\n")
|
89
|
-
re = %r[@begin: *\b(#{meta[:reference]})\b *[^\n]*\n(.*?)\n[^\n]*@end: \1]im
|
90
|
-
|
91
|
-
if code.match(re)
|
92
|
-
$2.strip_heredoc
|
93
|
-
else
|
94
|
-
"[missing '#{meta[:reference]}' block name]"
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# Process file and its relatives.
|
99
|
-
#
|
100
|
-
def process_file
|
101
|
-
file_path = book_dir.join("code/#{meta[:file]}")
|
102
|
-
|
103
|
-
if File.exist?(file_path)
|
104
|
-
code = File.read(file_path)
|
105
|
-
|
106
|
-
if meta[:type] == :range
|
107
|
-
process_range(code)
|
108
|
-
elsif meta[:type] == :block
|
109
|
-
process_block(code)
|
110
|
-
else
|
111
|
-
code
|
112
|
-
end
|
113
|
-
else
|
114
|
-
"[missing 'code/#{meta[:file]}' file]"
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
# Return the language used for this syntax block. Overrideable
|
119
|
-
# for epub generation.
|
120
|
-
def language
|
121
|
-
@language || meta[:language]
|
122
|
-
end
|
123
|
-
end
|
124
|
-
end
|
data/lib/bookshelf/toc.rb
DELETED
data/lib/bookshelf/toc/epub.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
module Bookshelf
|
2
|
-
module TOC
|
3
|
-
class Epub
|
4
|
-
attr_accessor :navigation
|
5
|
-
|
6
|
-
def initialize(navigation)
|
7
|
-
@navigation = navigation
|
8
|
-
end
|
9
|
-
|
10
|
-
def to_html
|
11
|
-
ERB.new(template).result OpenStruct.new(:navigation => navigation).instance_eval{ binding }
|
12
|
-
end
|
13
|
-
|
14
|
-
def template
|
15
|
-
<<-HTML.strip_heredoc.force_encoding("utf-8")
|
16
|
-
<?xml version="1.0" encoding="utf-8" ?>
|
17
|
-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
|
18
|
-
<html xml:lang="en" xmlns="http://www.w3.org/1999/xhtml">
|
19
|
-
<head>
|
20
|
-
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
21
|
-
<link rel="stylesheet" type="text/css" href="epub.css"/>
|
22
|
-
<meta http-equiv="Content-Type" content="application/xhtml+xml; charset=utf-8" />
|
23
|
-
<title>Table of Contents</title>
|
24
|
-
</head>
|
25
|
-
<body>
|
26
|
-
<div id="toc">
|
27
|
-
<ul>
|
28
|
-
<% navigation.each do |nav| %>
|
29
|
-
<li>
|
30
|
-
<a href="<%= nav[:content] %>"><%= nav[:label] %></a>
|
31
|
-
</li>
|
32
|
-
<% end %>
|
33
|
-
</ul>
|
34
|
-
</div>
|
35
|
-
</body>
|
36
|
-
</html>
|
37
|
-
HTML
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
data/lib/bookshelf/toc/html.rb
DELETED
@@ -1,78 +0,0 @@
|
|
1
|
-
module Bookshelf
|
2
|
-
module TOC
|
3
|
-
class HTML
|
4
|
-
# Return the table of contents in hash format.
|
5
|
-
#
|
6
|
-
attr_reader :toc
|
7
|
-
|
8
|
-
private_class_method :new
|
9
|
-
attr_reader :buffer # :nodoc:
|
10
|
-
attr_reader :attrs # :nodoc:
|
11
|
-
attr_accessor :content # :nodoc:
|
12
|
-
|
13
|
-
# Traverse every title and add a +id+ attribute.
|
14
|
-
# Return the modified content.
|
15
|
-
#
|
16
|
-
def self.normalize(content)
|
17
|
-
counter = {}
|
18
|
-
html = Nokogiri::HTML.parse(content)
|
19
|
-
html.search("h1, h2, h3, h4, h5, h6").each do |tag|
|
20
|
-
title = tag.inner_text
|
21
|
-
permalink = title.to_permalink
|
22
|
-
|
23
|
-
counter[permalink] ||= 0
|
24
|
-
counter[permalink] += 1
|
25
|
-
|
26
|
-
permalink = "#{permalink}-#{counter[permalink]}" if counter[permalink] > 1
|
27
|
-
|
28
|
-
tag.set_attribute("id", permalink)
|
29
|
-
end
|
30
|
-
|
31
|
-
html.css("body").to_xhtml.gsub(/<body>(.*?)<\/body>/m, "\\1")
|
32
|
-
end
|
33
|
-
|
34
|
-
# Traverse every title normalizing its content as a permalink.
|
35
|
-
#
|
36
|
-
def self.generate(content)
|
37
|
-
content = normalize(content)
|
38
|
-
listener = new
|
39
|
-
listener.content = content
|
40
|
-
Stream.new(content, listener).parse
|
41
|
-
listener
|
42
|
-
end
|
43
|
-
|
44
|
-
def initialize # :nodoc:
|
45
|
-
@toc = []
|
46
|
-
@counters = {}
|
47
|
-
end
|
48
|
-
|
49
|
-
def tag(node) # :nodoc:
|
50
|
-
toc << {
|
51
|
-
:level => node.name.gsub(/[^\d]/, "").to_i,
|
52
|
-
:text => node.text,
|
53
|
-
:permalink => node["id"]
|
54
|
-
}
|
55
|
-
end
|
56
|
-
|
57
|
-
# Return a hash with all normalized attributes.
|
58
|
-
#
|
59
|
-
def to_hash
|
60
|
-
{
|
61
|
-
:content => content,
|
62
|
-
:html => to_html,
|
63
|
-
:toc => toc
|
64
|
-
}
|
65
|
-
end
|
66
|
-
|
67
|
-
# Return the table of contents in HTML format.
|
68
|
-
#
|
69
|
-
def to_html
|
70
|
-
String.new.tap do |html|
|
71
|
-
toc.each do |options|
|
72
|
-
html << %[<div class="level#{options[:level]} #{options[:permalink]}"><a href="##{options[:permalink]}"><span>#{CGI.escape_html(options[:text])}</span></a></div>]
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|