bookmaker 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,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in bookmaker.gemspec
4
+ gemspec
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
File without changes
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- mode: ruby -*-
3
+
4
+ require "bookmaker"
5
+ Bookmaker::Cli.start
@@ -0,0 +1,37 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "bookmaker/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "bookmaker"
7
+ s.version = Bookmaker::VERSION
8
+ s.authors = ["Merovex"]
9
+ s.email = ["dausha@gmail.com"]
10
+ s.homepage = "http://dausha.net"
11
+ s.summary = %q{ODO: Write a gem summary}
12
+ s.description = %q{ODO: Write a gem description}
13
+
14
+ s.rubyforge_project = "bookmaker"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_dependency "activesupport"
23
+ s.add_development_dependency "aruba"
24
+ s.add_development_dependency "cucumber"
25
+ s.add_dependency "eeepub"
26
+ s.add_dependency "kramdown"
27
+ s.add_dependency "thor"
28
+ # s.add_dependency "erb"
29
+ # s.add_dependency "logger"
30
+ s.add_dependency "nokogiri"
31
+ s.add_dependency "notifier"
32
+ # s.add_dependency "open3"
33
+ # s.add_dependency "optparse"
34
+ # s.add_dependency "ostruct"
35
+ # s.add_dependency "tempfile"
36
+ # s.add_dependency "pathname"
37
+ end
@@ -0,0 +1,8 @@
1
+ Feature: Food
2
+ In order to portray or pluralize food
3
+ As a CLI
4
+ I want to be as objective as possible
5
+
6
+ Scenario: Do I work
7
+ When I run "bookmaker --version"
8
+ Then the output should contain "Bookmaker version"
@@ -0,0 +1,8 @@
1
+ Feature: Food
2
+ In order to portray or pluralize food
3
+ As a CLI
4
+ I want to be as objective as possible
5
+
6
+ Scenario: Create a PDF
7
+ When I run "bookmaker export --only html"
8
+ Then the output should contain "Bookmaker version"
@@ -0,0 +1 @@
1
+ require 'aruba/cucumber'
@@ -0,0 +1,54 @@
1
+ # require "bookmaker/version"
2
+ require "active_support/all"
3
+ require "awesome_print"
4
+ require "eeepub"
5
+ require "erb"
6
+ require "logger"
7
+ require "nokogiri"
8
+ require "notifier"
9
+ require "open3"
10
+ require "optparse"
11
+ require "ostruct"
12
+ require "tempfile"
13
+ require "pathname"
14
+ require "thor"
15
+ require "thor/group"
16
+ require "yaml"
17
+ require "cgi"
18
+
19
+ module Bookmaker
20
+
21
+ require "bookmaker/extensions/string"
22
+ ROOT = Pathname.new(File.dirname(__FILE__) + "/..")
23
+
24
+ autoload :Cli, "bookmaker/cli"
25
+ autoload :Dependency, "bookmaker/dependency"
26
+ autoload :Exporter, "bookmaker/exporter"
27
+ autoload :Generator, "bookmaker/generator"
28
+ autoload :Markdown, "bookmaker/adapters/markdown"
29
+ autoload :Parser, "bookmaker/parser"
30
+ autoload :Stats, "bookmaker/stats"
31
+ autoload :Stream, "bookmaker/stream"
32
+ autoload :TOC, "bookmaker/toc"
33
+ autoload :Version, "bookmaker/version"
34
+
35
+ Encoding.default_internal = "utf-8"
36
+ Encoding.default_external = "utf-8"
37
+
38
+ def self.config(root_dir = nil)
39
+ root_dir ||= Pathname.new(Dir.pwd)
40
+ path = root_dir.join("_bookmaker.yml")
41
+
42
+ raise "Invalid Bookmaker directory; couldn't found #{path} file." unless File.file?(path)
43
+ content = File.read(path)
44
+ erb = ERB.new(content).result
45
+
46
+ YAML.load(erb)#.with_indifferent_access
47
+ end
48
+ def self.logger
49
+ @logger ||= Logger.new(File.open("/tmp/bookmaker.log", "a"))
50
+ end
51
+ def self.hi
52
+ puts "hi"
53
+ end
54
+ end
@@ -0,0 +1,35 @@
1
+ module Kitabu
2
+ class Markdown
3
+ # Supported Markdown libraries
4
+ #
5
+ MARKDOWN_LIBRARIES = %w[Kramdown]
6
+
7
+ # Retrieve preferred Markdown processor.
8
+ # You'll need one of the following libraries:
9
+ #
10
+ # # RDiscount: https://rubygems.org/gems/rdiscount
11
+ # # Maruku: https://rubygems.org/gems/maruku
12
+ # # PEGMarkdown: https://rubygems.org/gems/rpeg-markdown
13
+ # # BlueCloth: https://rubygems.org/gems/bluecloth
14
+ # # Redcarpet: https://rubygems.org/gems/redcarpet
15
+ # # Kramdown: http://kramdown.rubyforge.org/
16
+ #
17
+ # Note: RDiscount will always be installed as Kitabu's dependency but only used when no
18
+ # alternative library is available.
19
+ #
20
+ def self.engine
21
+ @engine ||= Object.const_get(MARKDOWN_LIBRARIES.find {|lib| Object.const_defined?(lib)})
22
+ end
23
+
24
+ # Convert Markdown to HTML.
25
+ def self.to_html(content)
26
+ case engine.name
27
+ when "Redcarpet"
28
+ render = Redcarpet::Render::HTML.new(:hard_wrap => true, :xhtml => true)
29
+ Redcarpet::Markdown.new(render).render(content)
30
+ else
31
+ engine.new(content).to_html
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,74 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'thor'
3
+ module Bookmaker
4
+ class Cli < Thor
5
+ FORMATS = %w[pdf draft proof html epub mobi txt]
6
+ check_unknown_options!
7
+
8
+ def self.exit_on_failure?
9
+ true
10
+ end
11
+ def initialize(args = [], options = {}, config = {})
12
+ if config[:current_task].name == "new" && args.empty?
13
+ raise Error, "The e-Book path is required. For details run: bookmaker help new"
14
+ end
15
+ super
16
+ end
17
+
18
+ desc "create", "Start new work"
19
+ def create(path)
20
+ puts "A Million Monkeys Writing Your Masterpiece."
21
+ generator = Generator.new
22
+ generator.destination_root = path.squish.gsub(' ','-')
23
+ generator.invoke_all
24
+ end
25
+
26
+ desc "export [OPTIONS]", "Export e-book"
27
+ method_option :only, :type => :string, :desc => "Can be one of: #{FORMATS.join(", ")}"
28
+ method_option :open, :type => :boolean, :desc => "Automatically open PDF (Preview.app for Mac OS X and xdg-open for Linux)"
29
+ def export
30
+ inside_ebook!
31
+ if options[:only] && !FORMATS.include?(options[:only])
32
+ raise Error, "The --only option need to be one of: #{FORMATS.join(", ")}"
33
+ end
34
+ Bookmaker::Exporter.run(root_dir, options)
35
+ end
36
+
37
+ desc "version", "Prints the Bookmaker's version information"
38
+ map %w(-v --version) => :version
39
+ def version
40
+ say "Bookmaker version #{Bookmaker::VERSION}"
41
+ end
42
+
43
+ desc "stats", "Display some stats about your e-book"
44
+ def stats
45
+ inside_ebook!
46
+ stats = Bookmaker::Stats.new(root_dir)
47
+
48
+ say [
49
+ "Chapters: #{stats.chapters}",
50
+ "Words: #{stats.words}",
51
+ # "Images: #{stats.images}",
52
+ # "Links: #{stats.links}",
53
+ # "Footnotes: #{stats.footnotes}",
54
+ # "Code blocks: #{stats.code_blocks}"
55
+ ].join("\n")
56
+ end
57
+
58
+ private
59
+ def config
60
+ YAML.load_file(config_path).with_indifferent_access
61
+ end
62
+ def config_path
63
+ root_dir.join("_bookmaker.yml")
64
+ end
65
+ def root_dir
66
+ @root ||= Pathname.new(Dir.pwd)
67
+ end
68
+ def inside_ebook!
69
+ unless File.exist?(config_path)
70
+ raise Error, "You have to run this command from inside an e-book directory."
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,19 @@
1
+ module Bookmaker
2
+ class Dependency
3
+ def self.kindlegen?
4
+ @kindlegen ||= `which kindlegen` && $?.success?
5
+ end
6
+
7
+ def self.xelatex?
8
+ @xelatex ||= `which xelatex` && $?.success?
9
+ end
10
+
11
+ def self.html2text?
12
+ @html2text ||= `which html2text` && $?.success?
13
+ end
14
+
15
+ def self.pygments_rb?
16
+ @pygments_rb ||= defined?(Pygments)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,68 @@
1
+ module Bookmaker
2
+ class Exporter
3
+ def self.run(root_dir, options)
4
+ exporter = new(root_dir, options)
5
+ exporter.export!
6
+ end
7
+
8
+ attr_accessor :root_dir
9
+ attr_accessor :options
10
+
11
+ def initialize(root_dir, options)
12
+ @root_dir = root_dir
13
+ @options = options
14
+ end
15
+
16
+ def ui
17
+ @ui ||= Thor::Base.shell.new
18
+ end
19
+
20
+ def export!
21
+ helper = root_dir.join("config/helper.rb")
22
+ load(helper) if helper.exist?
23
+
24
+ export_pdf = [nil, "pdf"].include?(options[:only])
25
+ export_html = [nil, "html", "mobi", "epub"].include?(options[:only])
26
+ export_epub = [nil, "mobi", "epub"].include?(options[:only])
27
+ export_mobi = [nil, "mobi"].include?(options[:only])
28
+ export_txt = [nil, "txt"].include?(options[:only])
29
+
30
+ exported = []
31
+ exported << Parser::PDF.parse(root_dir) if export_pdf && Dependency.xelatex?# && Dependency.prince?
32
+ exported << Parser::HTML.parse(root_dir) if export_html
33
+ exported << Parser::Epub.parse(root_dir) if export_epub
34
+ exported << Parser::Mobi.parse(root_dir) if export_mobi && Dependency.kindlegen?
35
+ # exported << Parser::Txt.parse(root_dir) if export_txt && Dependency.html2text?
36
+
37
+ if exported.all?
38
+ color = :green
39
+ message = options[:auto] ? "exported!" : "** e-book has been exported"
40
+
41
+ if options[:open] && export_pdf
42
+ filepath = root_dir.join("output/#{File.basename(root_dir)}.pdf")
43
+
44
+ if RUBY_PLATFORM =~ /darwin/
45
+ IO.popen("open -a Preview.app '#{filepath}'").close
46
+ elsif RUBY_PLATFORM =~ /linux/
47
+ Process.detach(Process.spawn("xdg-open '#{filepath}'", :out => "/dev/null"))
48
+ end
49
+ end
50
+
51
+ Notifier.notify(
52
+ :image => Bookmaker::ROOT.join("templates/ebook.png"),
53
+ :title => "Bookmaker",
54
+ :message => "Your \"#{config[:title]}\" e-book has been exported!"
55
+ )
56
+ else
57
+ color = :red
58
+ message = options[:auto] ? "could not be exported!" : "** e-book couldn't be exported"
59
+ end
60
+
61
+ ui.say message, color
62
+ end
63
+
64
+ def config
65
+ Bookmaker.config(root_dir)
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,11 @@
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
@@ -0,0 +1,40 @@
1
+ module Bookmaker
2
+ class Generator < Thor::Group
3
+ include Thor::Actions
4
+ def self.source_root
5
+ File.dirname(__FILE__) + "/../../templates"
6
+ end
7
+ def build_config_file
8
+ @title = File.basename(destination_root).gsub('-', ' ')
9
+ @name = full_name
10
+ @uid = Digest::MD5.hexdigest("#{Time.now}--#{rand}")
11
+ @year = Date.today.year
12
+ template "config.erb", "_bookmaker .yml"
13
+ end
14
+ def copy_templates
15
+ copy_file "latex.erb", "templates/pdf/layout.erb"
16
+
17
+ copy_file "html.erb", "templates/html/layout.erb"
18
+ copy_file "user.css", "templates/html/user.css"
19
+ copy_file "syntax.css", "templates/html/syntax.css"
20
+
21
+ copy_file "epub.erb", "templates/epub/page.erb"
22
+ copy_file "epub.css", "templates/epub/user.css"
23
+ end
24
+ def copy_sample_text
25
+ copy_file "sample.md" , "text/01-Welcome.md"
26
+ end
27
+ def create_directories
28
+ empty_directory "output"
29
+ empty_directory "images"
30
+ end
31
+ private
32
+ # Retrieve user's name using finger.
33
+ # Defaults to <tt>John Doe</tt>.
34
+ #
35
+ def full_name
36
+ name = `finger $USER 2> /dev/null | grep Login | colrm 1 46`.chomp
37
+ name.empty? ? "John Doe" : name.squish
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,81 @@
1
+ require 'open3'
2
+
3
+ module Bookmaker
4
+ module Parser
5
+ autoload :HTML , "bookmaker/parser/html"
6
+ autoload :PDF , "bookmaker/parser/pdf"
7
+ autoload :Epub , "bookmaker/parser/epub"
8
+ autoload :Mobi , "bookmaker/parser/mobi"
9
+ # autoload :Txt , "bookmaker/parser/txt"
10
+
11
+ class Base
12
+ # The e-book directory.
13
+ #
14
+ attr_accessor :root_dir
15
+
16
+ # Where the text files are stored.
17
+ #
18
+ attr_accessor :source
19
+
20
+ def self.parse(root_dir)
21
+ new(root_dir).parse
22
+ end
23
+
24
+ def initialize(root_dir)
25
+ @root_dir = Pathname.new(root_dir)
26
+ @source = root_dir.join("text")
27
+ end
28
+
29
+ # Return directory's basename.
30
+ #
31
+ def name
32
+ File.basename(root_dir)
33
+ end
34
+
35
+ # Return the configuration file.
36
+ #
37
+ def config
38
+ Bookmaker.config(root_dir)
39
+ end
40
+ def entries
41
+ return @entries unless @entries.nil?
42
+ files = Dir["text/**/*.md"]
43
+ @entries = {}
44
+ files.each do |f|
45
+ k = File.dirname(f)
46
+ k.gsub!('text/','')
47
+ @entries[k] = [] if @entries[k].nil?
48
+ @entries[k] << f
49
+ end
50
+ return @entries
51
+ end
52
+ def render_template(file, locals = {})
53
+ ERB.new(File.read(file)).result OpenStruct.new(locals).instance_eval{ binding }
54
+ end
55
+ def read_content(file)
56
+ content = File.read(file)
57
+ begin
58
+ if content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
59
+ content = "\n#{$'}\n"
60
+ data = YAML.load($1)
61
+ end
62
+ return [content, data]
63
+ rescue => e
64
+ puts "Error reading file #{File.join(base, name)}: #{e.message}"
65
+ rescue SyntaxError => e
66
+ puts "YAML Exception reading #{File.join(base, name)}: #{e.message}"
67
+ end
68
+ end
69
+ def spawn_command(cmd)
70
+ begin
71
+ stdout_and_stderr, status = Open3.capture2e(*cmd)
72
+ rescue Errno::ENOENT => e
73
+ puts e.message
74
+ else
75
+ puts stdout_and_stderr unless status.success?
76
+ status.success?
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end