burr 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +3 -0
  5. data/LICENSE.md +30 -0
  6. data/README.md +180 -0
  7. data/Rakefile +118 -0
  8. data/bin/burr +9 -0
  9. data/burr.gemspec +36 -0
  10. data/generators/Gemfile.txt +3 -0
  11. data/generators/config.yml +55 -0
  12. data/generators/contents/chapter1.md +7 -0
  13. data/generators/contents/chapter2.md +7 -0
  14. data/generators/stylesheets/pdf.css +569 -0
  15. data/generators/stylesheets/site.css +1 -0
  16. data/lib/burr.rb +56 -0
  17. data/lib/burr/book.rb +289 -0
  18. data/lib/burr/cli.rb +64 -0
  19. data/lib/burr/converter.rb +19 -0
  20. data/lib/burr/core_ext/blank.rb +107 -0
  21. data/lib/burr/dependency.rb +28 -0
  22. data/lib/burr/eeepub_ext/maker.rb +131 -0
  23. data/lib/burr/exporter.rb +137 -0
  24. data/lib/burr/exporters/epub.rb +163 -0
  25. data/lib/burr/exporters/pdf.rb +95 -0
  26. data/lib/burr/exporters/site.rb +101 -0
  27. data/lib/burr/generator.rb +41 -0
  28. data/lib/burr/kramdown_ext/converter.rb +145 -0
  29. data/lib/burr/kramdown_ext/options.rb +38 -0
  30. data/lib/burr/kramdown_ext/parser.rb +65 -0
  31. data/lib/burr/liquid_ext/block.rb +58 -0
  32. data/lib/burr/liquid_ext/extends.rb +114 -0
  33. data/lib/burr/plugin.rb +70 -0
  34. data/lib/burr/plugins/aside.rb +44 -0
  35. data/lib/burr/plugins/codeblock.rb +42 -0
  36. data/lib/burr/plugins/figure.rb +62 -0
  37. data/lib/burr/plugins/link.rb +47 -0
  38. data/lib/burr/plugins/parser_plugin.rb +18 -0
  39. data/lib/burr/plugins/table.rb +42 -0
  40. data/lib/burr/plugins/toc.rb +105 -0
  41. data/lib/burr/ruby_version_check.rb +4 -0
  42. data/lib/burr/server.rb +27 -0
  43. data/lib/burr/ui.rb +46 -0
  44. data/lib/burr/version.rb +8 -0
  45. data/resources/locales/labels/en.yml +45 -0
  46. data/resources/locales/labels/zh_CN.yml +46 -0
  47. data/resources/locales/titles/en.yml +21 -0
  48. data/resources/locales/titles/zh_CN.yml +21 -0
  49. data/resources/templates/epub/_layout.liquid +17 -0
  50. data/resources/templates/epub/acknowledgement.liquid +10 -0
  51. data/resources/templates/epub/afterword.liquid +10 -0
  52. data/resources/templates/epub/appendix.liquid +10 -0
  53. data/resources/templates/epub/author.liquid +10 -0
  54. data/resources/templates/epub/chapter.liquid +10 -0
  55. data/resources/templates/epub/conclusion.liquid +10 -0
  56. data/resources/templates/epub/cover.liquid +9 -0
  57. data/resources/templates/epub/dedication.liquid +10 -0
  58. data/resources/templates/epub/edition.liquid +1 -0
  59. data/resources/templates/epub/epilogue.liquid +1 -0
  60. data/resources/templates/epub/foreword.liquid +10 -0
  61. data/resources/templates/epub/glossary.liquid +1 -0
  62. data/resources/templates/epub/introduction.liquid +1 -0
  63. data/resources/templates/epub/license.liquid +1 -0
  64. data/resources/templates/epub/lof.liquid +24 -0
  65. data/resources/templates/epub/lot.liquid +24 -0
  66. data/resources/templates/epub/part.liquid +4 -0
  67. data/resources/templates/epub/preface.liquid +10 -0
  68. data/resources/templates/epub/prologue.liquid +1 -0
  69. data/resources/templates/epub/table.liquid +7 -0
  70. data/resources/templates/epub/title.liquid +3 -0
  71. data/resources/templates/epub/toc.liquid +10 -0
  72. data/resources/templates/pdf/_item.liquid +6 -0
  73. data/resources/templates/pdf/acknowledgement.liquid +1 -0
  74. data/resources/templates/pdf/afterword.liquid +1 -0
  75. data/resources/templates/pdf/appendix.liquid +4 -0
  76. data/resources/templates/pdf/author.liquid +1 -0
  77. data/resources/templates/pdf/blank.liquid +3 -0
  78. data/resources/templates/pdf/book.liquid +42 -0
  79. data/resources/templates/pdf/chapter.liquid +4 -0
  80. data/resources/templates/pdf/code.liquid +3 -0
  81. data/resources/templates/pdf/conclusion.liquid +1 -0
  82. data/resources/templates/pdf/cover.liquid +6 -0
  83. data/resources/templates/pdf/dedication.liquid +3 -0
  84. data/resources/templates/pdf/edition.liquid +1 -0
  85. data/resources/templates/pdf/epilogue.liquid +1 -0
  86. data/resources/templates/pdf/foreword.liquid +1 -0
  87. data/resources/templates/pdf/glossary.liquid +1 -0
  88. data/resources/templates/pdf/introduction.liquid +1 -0
  89. data/resources/templates/pdf/license.liquid +1 -0
  90. data/resources/templates/pdf/lof.liquid +24 -0
  91. data/resources/templates/pdf/lot.liquid +24 -0
  92. data/resources/templates/pdf/part.liquid +4 -0
  93. data/resources/templates/pdf/preface.liquid +1 -0
  94. data/resources/templates/pdf/prologue.liquid +1 -0
  95. data/resources/templates/pdf/table.liquid +7 -0
  96. data/resources/templates/pdf/title.liquid +3 -0
  97. data/resources/templates/pdf/toc.liquid +4 -0
  98. data/resources/templates/site/_layout.liquid +27 -0
  99. data/resources/templates/site/author.liquid +13 -0
  100. data/resources/templates/site/chapter.liquid +13 -0
  101. data/resources/templates/site/foreword.liquid +13 -0
  102. data/resources/templates/site/preface.liquid +13 -0
  103. metadata +232 -0
@@ -0,0 +1 @@
1
+ @charset 'utf-8';
data/lib/burr.rb ADDED
@@ -0,0 +1,56 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ # Require all of the Ruby files in the given directory.
4
+ #
5
+ # path - The String relative path from here to the directory.
6
+ #
7
+ # Returns nothing.
8
+ def require_all(path)
9
+ glob = File.join(File.dirname(__FILE__), path, '*.rb')
10
+ Dir[glob].each do |f|
11
+ require f
12
+ end
13
+ end
14
+
15
+ require 'nokogiri'
16
+ require 'kramdown'
17
+ require 'liquid'
18
+ require 'thor'
19
+ require 'thor/group'
20
+ require 'eeepub'
21
+
22
+ require 'yaml'
23
+
24
+ require_all 'burr/core_ext'
25
+ require_all 'burr/kramdown_ext'
26
+ require 'burr/cli'
27
+ require 'burr/dependency'
28
+ require 'burr/generator'
29
+ require 'burr/version'
30
+ require 'burr/ui'
31
+ require 'burr/converter'
32
+ require 'burr/exporter'
33
+ require_all 'burr/exporters'
34
+ require 'burr/plugin'
35
+ require_all 'burr/plugins'
36
+ require_all 'burr/liquid_ext'
37
+ require_all 'burr/eeepub_ext'
38
+ require 'burr/book'
39
+ require 'burr/server'
40
+
41
+ Encoding.default_internal = 'utf-8'
42
+ Encoding.default_external = 'utf-8'
43
+
44
+ module Burr
45
+
46
+ def self.configuration
47
+ path = "#{Dir.pwd}/config.yml"
48
+
49
+ begin
50
+ YAML.load_file path
51
+ rescue => e
52
+ puts "#{e.message}"
53
+ end
54
+ end
55
+
56
+ end
data/lib/burr/book.rb ADDED
@@ -0,0 +1,289 @@
1
+ module Burr
2
+ class Book
3
+
4
+ attr_accessor :config, :format, :ui
5
+ attr_accessor :gem_dir, :root_dir, :outputs_dir, :caches_dir, :contents_dir, :plugins_dir, :templates_dir
6
+ attr_accessor :items, :toc, :images, :tables, :current_item
7
+ attr_accessor :labels, :titles, :ids
8
+ attr_accessor :uid, :slug
9
+
10
+ def initialize(config, format)
11
+ @config = config
12
+ @format = format
13
+ @ui = Burr::UI.new
14
+
15
+ # directory location
16
+ @gem_dir = File.expand_path('../../../' ,__FILE__)
17
+ @root_dir = Dir.pwd
18
+ @outputs_dir = "#{@root_dir}/outputs"
19
+ @caches_dir = "#{@root_dir}/caches"
20
+ @contents_dir = "#{@root_dir}/contents"
21
+ @plugins_dir = "#{@root_dir}/plugins"
22
+ @templates_dir = "#{@root_dir}/templates"
23
+
24
+ # publishing process variables
25
+ @items = []
26
+ @toc = ''
27
+ @images = []
28
+ @tables = []
29
+ @current_item = []
30
+
31
+ # labels and titles
32
+ book_labels # @labels = {}, @ids = []
33
+ book_titles # @titles = {}
34
+
35
+ # book information
36
+ book_uid # @uid = ''
37
+ book_slug # @slug = ''
38
+ end
39
+
40
+ # Export site files in outputs/site
41
+ #
42
+ def export_site
43
+ exporter = Burr::Site.new(self)
44
+ self.ui.confirm "Start exporting site files...."
45
+ exporter.run
46
+ self.ui.confirm "Exported site!"
47
+ end
48
+
49
+ # Export PDF files in outputs/pdf
50
+ #
51
+ def export_pdf
52
+ exporter = Burr::PDF.new(self)
53
+ self.ui.confirm "Start exporting pdf file...."
54
+ exporter.run
55
+ self.ui.confirm "Exported PDF!"
56
+ end
57
+
58
+ # Export Epub files in outputs/epub
59
+ #
60
+ def export_epub
61
+ exporter = Burr::Epub.new(self)
62
+ self.ui.confirm "Start exporting epub file...."
63
+ exporter.run
64
+ self.ui.confirm "Exported Epub!"
65
+ end
66
+
67
+ # Export Mobi files in outputs/mobi
68
+ #
69
+ def export_mobi
70
+ dest = File.join(self.outputs_dir, 'mobi')
71
+ FileUtils.mkdir_p(dest) unless File.exist?(dest)
72
+
73
+ self.ui.confirm "Start exporting mobi file...."
74
+
75
+ FileUtils.cd(File.join(self.outputs_dir, 'epub')) do
76
+ base = "#{self.config['slug']}-#{Time.new.strftime('%Y%m%d')}"
77
+ epub = "#{base}.epub"
78
+ mobi = "#{base}.mobi"
79
+ unless File.exist?(epub)
80
+ self.ui.error('Please export Epub first!')
81
+ exit 1
82
+ end
83
+
84
+ system "kindlegen #{epub} -c2"
85
+ FileUtils.cp(mobi, dest)
86
+ FileUtils.rm(mobi)
87
+ end
88
+
89
+ self.ui.confirm "Exported Mobi!"
90
+ end
91
+
92
+ # Export all formats
93
+ #
94
+ def export_all
95
+ self.export_pdf
96
+ self.export_epub
97
+ self.export_mobi
98
+ end
99
+
100
+ # Gets the template file for an element.
101
+ #
102
+ # - element The element name, such as 'chapter', 'appendix'
103
+ #
104
+ # Returns The absolute path of this element's template file.
105
+ #
106
+ def template_for(element)
107
+ base = File.join('templates', self.format, "#{element}.liquid")
108
+ default = File.join(self.gem_dir, 'resources', base)
109
+ custom = File.join(self.root_dir, base)
110
+
111
+ if File.exist?(custom)
112
+ custom
113
+ elsif !File.exist?(custom) && File.exist?(default)
114
+ default
115
+ else
116
+ self.ui.error("ERROR: Template #{self.format}/#{element}.liquid not found!")
117
+ exit 1
118
+ end
119
+ end
120
+
121
+ # Get the stylesheet file for a format.
122
+ #
123
+ # @param [String] format The format name, could be 'pdf', 'epub', 'site' and 'mobi'
124
+ # @return [String] The absolute path to this format's stylesheet
125
+ def stylesheet_for(format)
126
+ if %w(pdf epub site mobi).include?(format)
127
+ css = File.join(self.outputs_dir, format, "style.css")
128
+
129
+ if File.exist?(css)
130
+ css
131
+ else
132
+ self.ui.error("ERROR: Not found stylesheet for format #{format}.")
133
+ exit 1
134
+ end
135
+ else
136
+ self.ui.error("ERROR: #{format} is not support!")
137
+ exit 1
138
+ end
139
+ end
140
+
141
+ # Shortcut method to get the label of any element type.
142
+ #
143
+ # element - The element type (`chapter', `foreword', ...) in String format.
144
+ # variables - A variables Hash used to render the label.
145
+ #
146
+ # Returns the label String of the element or an empty String.
147
+ def render_label(element, variables = {})
148
+ c_labels = self.labels.include?(element) ? self.labels[element] : ''
149
+ # some elements (mostly chapters and appendices) have a different label for each level (h1, ..., h6)
150
+ if c_labels.is_a? Array
151
+ index = variables['item']['level'] - 1
152
+ if index == 0
153
+ label = c_labels[0]
154
+ else
155
+ label = c_labels[1]
156
+ end
157
+ else
158
+ label = c_labels
159
+ end
160
+
161
+ self.render_string(label, variables)
162
+ end
163
+
164
+ # Shortcut method to get the id of headings.
165
+ #
166
+ # variables - A variables Hash used to render the id.
167
+ #
168
+ # Returns the id String of the heading.
169
+ def render_id(variables = {})
170
+ index = variables['item']['level'] - 1
171
+ if index == 0
172
+ id = self.ids[0]
173
+ else
174
+ id = self.ids[1]
175
+ end
176
+
177
+ self.render_string(id, variables)
178
+ end
179
+
180
+ # Renders any string as a Liquid template.
181
+ #
182
+ # @param [String] text The original content to render
183
+ # @param [Array] variables Optional variables passed to the template
184
+ def render_string(text, variables = {})
185
+ registers = { :registers => { :book => self } }
186
+ Liquid::Template.parse(text).render(variables, registers)
187
+ end
188
+
189
+
190
+ # Renders any template (currently only supports Liquid templates).
191
+ #
192
+ # @param [String] template The template name, without the extension
193
+ # @param [Hash] parameters Optional variables passed to the template
194
+ # @param [String] target Optional output file path. If set, the rendered template is saved in this file.
195
+ # @return [String] The rendered content
196
+ def render(template, parameters = {}, target = nil)
197
+ defaults = {
198
+ 'config' => self.config,
199
+ 'format' => self.format,
200
+ 'generator' => { 'name' => 'Burr', 'version' => Burr::Version::STRING }
201
+ }
202
+ text = File.read(template)
203
+ registers = { :registers => { :book => self } }
204
+ content = Liquid::Template.parse(text).render(defaults.merge(parameters), registers)
205
+
206
+ if target
207
+ File.open(target, 'wb') { |f| f.puts content }
208
+ end
209
+
210
+ content
211
+ end
212
+
213
+ # Makes the liquid tags live.
214
+ #
215
+ # Returns Hash.
216
+ def to_liquid
217
+ #{ 'book' => self }
218
+ end
219
+
220
+ private
221
+
222
+ # Generates `@labels' for chapters, appendixes, etc.
223
+ #
224
+ # Returns nil.
225
+ def book_labels
226
+ base = File.join('locales', 'labels', "#{self.config['language']}.yml")
227
+ default = File.join(self.gem_dir, 'resources', base)
228
+ custom = File.join(self.root_dir, base)
229
+
230
+ labels = YAML::load_file(default)
231
+
232
+ #books can define their own labels files
233
+ if File.exist? custom
234
+ custom_labels = YAML::load_file(custom)
235
+ labels.merge!(custom_labels)
236
+ end
237
+
238
+ self.labels = labels
239
+ self.ids = labels['id']
240
+ nil
241
+ end
242
+
243
+ # Generates `@titles' for chapters, appendixes, etc.
244
+ #
245
+ # Returns nil.
246
+ def book_titles
247
+ base = File.join('locales', 'titles', "#{self.config['language']}.yml")
248
+ default = File.join(self.gem_dir, 'resources', base)
249
+ custom = File.join(self.root_dir, base)
250
+
251
+ titles = YAML::load_file(default)
252
+
253
+ #books can define their own titles files
254
+ if File.exist? custom
255
+ custom_titles = YAML::load_file(custom)
256
+ return titles.merge(custom_titles)
257
+ end
258
+
259
+ self.titles = titles
260
+ nil
261
+ end
262
+
263
+ # Generates a unique `@uid' for the book.
264
+ #
265
+ # Returns nil.
266
+ def book_uid
267
+ if @config['isbn']
268
+ @uid = @config['isbn']
269
+ else
270
+ @uid = Digest::MD5.hexdigest("#{Time.now}--#{rand}")
271
+ end
272
+ end
273
+
274
+ # Generates a `@slug' for the book.
275
+ #
276
+ # Uses the current directory name as slug, if no `slug' in `config.yml' provided.
277
+ #
278
+ # Returns String of the book's slug.
279
+ def book_slug
280
+ if @config['slug']
281
+ @slug = @config['slug']
282
+ else
283
+ dir = File.basename(self.root_dir)
284
+ @slug = CGI.escape(dir)
285
+ end
286
+ end
287
+
288
+ end
289
+ end
data/lib/burr/cli.rb ADDED
@@ -0,0 +1,64 @@
1
+ module Burr
2
+ class Cli < Thor
3
+
4
+ desc 'new [PATH]', 'Create a new book'
5
+ def new(path)
6
+ generator = Burr::Generator.new
7
+ generator.destination_root = path
8
+ generator.invoke_all
9
+ end
10
+
11
+ desc 'export [FORMAT]', 'Export a book format, or all formats'
12
+ def export(format)
13
+ valid = %w(site pdf epub mobi all)
14
+
15
+ if valid.include?(format)
16
+ book = Burr::Book.new(config, format)
17
+ case format
18
+ when 'site'
19
+ book.export_site
20
+ when 'pdf'
21
+ unless Dependency.prince_installed?
22
+ book.ui.warn "Please install PrinceXML first."
23
+ exit 1
24
+ end
25
+ book.export_pdf
26
+ when 'epub'
27
+ book.export_epub
28
+ when 'mobi'
29
+ unless Dependency.kindlegen_installed?
30
+ book.ui.warn "Please install kindelgen first."
31
+ exit 1
32
+ end
33
+ book.export_mobi
34
+ when 'all'
35
+ puts 'pending'
36
+ # book.export_all
37
+ end
38
+ else
39
+ raise "ERROR: invalid format. Formats: #{valid.join(', ')}."
40
+ end
41
+ end
42
+
43
+ desc 'server', 'Site preview'
44
+ def server
45
+ Burr::Server.start!
46
+ end
47
+
48
+ desc 'version', 'Show the burr version'
49
+ def version
50
+ puts Burr::Version::STRING
51
+ end
52
+
53
+ private
54
+
55
+ def config
56
+ @config ||= Burr.configuration
57
+ end
58
+
59
+ def book_root
60
+ @root ||= Dir.pwd
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,19 @@
1
+ module Burr
2
+ class Converter
3
+
4
+ attr_accessor :book
5
+
6
+ def initialize(book)
7
+ @book = book
8
+ end
9
+
10
+ def convert(text)
11
+ ::Kramdown::Document.new(text,
12
+ :input => 'Bsmarkdown',
13
+ :auto_ids => false,
14
+ :register => self.book
15
+ ).to_bshtml
16
+ end
17
+
18
+ end
19
+ end
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+
3
+ # This file is steal from Rails
4
+
5
+ class Object
6
+ # An object is blank if it's false, empty, or a whitespace string.
7
+ # For example, '', ' ', +nil+, [], and {} are all blank.
8
+ #
9
+ # This simplifies:
10
+ #
11
+ # if address.nil? || address.empty?
12
+ #
13
+ # ...to:
14
+ #
15
+ # if address.blank?
16
+ def blank?
17
+ respond_to?(:empty?) ? empty? : !self
18
+ end
19
+
20
+ # An object is present if it's not <tt>blank?</tt>.
21
+ def present?
22
+ !blank?
23
+ end
24
+
25
+ # Returns object if it's <tt>present?</tt> otherwise returns +nil+.
26
+ # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
27
+ #
28
+ # This is handy for any representation of objects where blank is the same
29
+ # as not present at all. For example, this simplifies a common check for
30
+ # HTTP POST/query parameters:
31
+ #
32
+ # state = params[:state] if params[:state].present?
33
+ # country = params[:country] if params[:country].present?
34
+ # region = state || country || 'US'
35
+ #
36
+ # ...becomes:
37
+ #
38
+ # region = params[:state].presence || params[:country].presence || 'US'
39
+ def presence
40
+ self if present?
41
+ end
42
+ end
43
+
44
+ class NilClass
45
+ # +nil+ is blank:
46
+ #
47
+ # nil.blank? # => true
48
+ def blank?
49
+ true
50
+ end
51
+ end
52
+
53
+ class FalseClass
54
+ # +false+ is blank:
55
+ #
56
+ # false.blank? # => true
57
+ def blank?
58
+ true
59
+ end
60
+ end
61
+
62
+ class TrueClass
63
+ # +true+ is not blank:
64
+ #
65
+ # true.blank? # => false
66
+ def blank?
67
+ false
68
+ end
69
+ end
70
+
71
+ class Array
72
+ # An array is blank if it's empty:
73
+ #
74
+ # [].blank? # => true
75
+ # [1,2,3].blank? # => false
76
+ alias_method :blank?, :empty?
77
+ end
78
+
79
+ class Hash
80
+ # A hash is blank if it's empty:
81
+ #
82
+ # {}.blank? # => true
83
+ # { key: 'value' }.blank? # => false
84
+ alias_method :blank?, :empty?
85
+ end
86
+
87
+ class String
88
+ # A string is blank if it's empty or contains whitespaces only:
89
+ #
90
+ # ''.blank? # => true
91
+ # ' '.blank? # => true
92
+ # ' '.blank? # => true
93
+ # ' something here '.blank? # => false
94
+ def blank?
95
+ self !~ /[^[:space:]]/
96
+ end
97
+ end
98
+
99
+ class Numeric #:nodoc:
100
+ # No number is blank:
101
+ #
102
+ # 1.blank? # => false
103
+ # 0.blank? # => false
104
+ def blank?
105
+ false
106
+ end
107
+ end