bookmaker 0.1.0

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