epubforge 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +26 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +26 -0
- data/Rakefile +71 -0
- data/VERSION +1 -0
- data/bin/epubforge +10 -0
- data/config/actions/book_to_epub.rb +20 -0
- data/config/actions/generate.rb +24 -0
- data/config/actions/generate_chapter.rb +26 -0
- data/config/actions/git_backup.rb +23 -0
- data/config/actions/gitify.rb +72 -0
- data/config/actions/globals.rb +77 -0
- data/config/actions/help.rb +21 -0
- data/config/actions/init.rb +137 -0
- data/config/actions/kindle.rb +68 -0
- data/config/actions/notes_to_epub.rb +20 -0
- data/config/actions/notes_to_kindle.rb +17 -0
- data/config/actions/word_count.rb +126 -0
- data/config/actions/wrap_scene_notes_in_hidden_div.rb +118 -0
- data/config/htmlizers.rb +62 -0
- data/lib/action/actions_lookup.rb +41 -0
- data/lib/action/cli_command.rb +72 -0
- data/lib/action/cli_sequence.rb +55 -0
- data/lib/action/file_transformer.rb +59 -0
- data/lib/action/run_description.rb +24 -0
- data/lib/action/runner.rb +122 -0
- data/lib/action/thor_action.rb +149 -0
- data/lib/core_extensions/array.rb +5 -0
- data/lib/core_extensions/kernel.rb +42 -0
- data/lib/core_extensions/nil_class.rb +5 -0
- data/lib/core_extensions/object.rb +5 -0
- data/lib/core_extensions/string.rb +37 -0
- data/lib/custom_helpers.rb +60 -0
- data/lib/epub/assets/asset.rb +11 -0
- data/lib/epub/assets/html.rb +8 -0
- data/lib/epub/assets/image.rb +18 -0
- data/lib/epub/assets/markdown.rb +8 -0
- data/lib/epub/assets/page.rb +32 -0
- data/lib/epub/assets/stylesheet.rb +22 -0
- data/lib/epub/assets/textile.rb +8 -0
- data/lib/epub/builder.rb +270 -0
- data/lib/epub/packager.rb +16 -0
- data/lib/epubforge.rb +97 -0
- data/lib/errors.rb +8 -0
- data/lib/project/project.rb +65 -0
- data/lib/utils/action_loader.rb +7 -0
- data/lib/utils/class_loader.rb +83 -0
- data/lib/utils/directory_builder.rb +181 -0
- data/lib/utils/downloader.rb +58 -0
- data/lib/utils/file_orderer.rb +45 -0
- data/lib/utils/file_path.rb +152 -0
- data/lib/utils/html_translator.rb +99 -0
- data/lib/utils/html_translator_queue.rb +70 -0
- data/lib/utils/htmlizer.rb +92 -0
- data/lib/utils/misc.rb +20 -0
- data/lib/utils/root_path.rb +20 -0
- data/lib/utils/settings.rb +146 -0
- data/lib/utils/template_evaluator.rb +20 -0
- data/templates/default/book/afterword.markdown.template +4 -0
- data/templates/default/book/chapter-%i%.markdown.sequence +4 -0
- data/templates/default/book/foreword.markdown.template +6 -0
- data/templates/default/book/images/cover.png +0 -0
- data/templates/default/book/stylesheets/stylesheet.css.template +2 -0
- data/templates/default/book/title_page.markdown.template +4 -0
- data/templates/default/notes/character.named.markdown.template +4 -0
- data/templates/default/notes/stylesheets/stylesheet.css.template +2 -0
- data/templates/default/payload.rb +65 -0
- data/templates/default/settings/actions/local_action.rb.example +14 -0
- data/templates/default/settings/config.rb.form +55 -0
- data/templates/default/settings/htmlizers.rb +0 -0
- data/templates/default/settings/wordcount.template +6 -0
- data/test/helper.rb +22 -0
- data/test/misc/config.rb +7 -0
- data/test/sample_text/sample.markdown +30 -0
- data/test/sample_text/sample.textile +24 -0
- data/test/test_custom_helpers.rb +22 -0
- data/test/test_directory_builder.rb +141 -0
- data/test/test_epf_root.rb +9 -0
- data/test/test_epubforge.rb +164 -0
- data/test/test_htmlizers.rb +24 -0
- data/test/test_runner.rb +15 -0
- data/test/test_utils.rb +39 -0
- metadata +328 -0
@@ -0,0 +1,149 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
module SharedActionInterface
|
4
|
+
def description( str = nil )
|
5
|
+
@description = str if str
|
6
|
+
@description
|
7
|
+
end
|
8
|
+
|
9
|
+
def keywords( *args )
|
10
|
+
if args.epf_blank?
|
11
|
+
@keywords ||= []
|
12
|
+
else
|
13
|
+
@keywords = args.map(&:to_s)
|
14
|
+
end
|
15
|
+
|
16
|
+
@keywords
|
17
|
+
end
|
18
|
+
|
19
|
+
def usage( str = nil )
|
20
|
+
@usage = str if str
|
21
|
+
@usage
|
22
|
+
end
|
23
|
+
|
24
|
+
def project_required?
|
25
|
+
@project_required = true if @project_required.nil?
|
26
|
+
@project_required
|
27
|
+
end
|
28
|
+
|
29
|
+
# Most actions require -- nay, demand! -- a project to act upon.
|
30
|
+
# Add the line 'project_not_required' to the class definition
|
31
|
+
# to keep it from failing out if it can't find an existing project.
|
32
|
+
# Used for things like initializing new projects, or... my imagination
|
33
|
+
# fails me.
|
34
|
+
def project_not_required
|
35
|
+
@project_required = false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ThorAction < Thor
|
40
|
+
include Thor::Actions
|
41
|
+
extend SharedActionInterface
|
42
|
+
|
43
|
+
|
44
|
+
CLEAR = Thor::Shell::Color::CLEAR
|
45
|
+
RED = Thor::Shell::Color::RED
|
46
|
+
BLUE = Thor::Shell::Color::BLUE
|
47
|
+
YELLOW = Thor::Shell::Color::YELLOW
|
48
|
+
GREEN = Thor::Shell::Color::GREEN
|
49
|
+
MAGENTA = Thor::Shell::Color::MAGENTA
|
50
|
+
ON_YELLOW = Thor::Shell::Color::ON_YELLOW
|
51
|
+
ON_BLUE = Thor::Shell::Color::ON_BLUE
|
52
|
+
|
53
|
+
|
54
|
+
protected
|
55
|
+
def say_error( statement )
|
56
|
+
say( "ERROR : #{statement}", RED + ON_BLUE )
|
57
|
+
end
|
58
|
+
|
59
|
+
def say_instruction( statement )
|
60
|
+
say( statement, YELLOW )
|
61
|
+
end
|
62
|
+
|
63
|
+
def say_all_is_well( statement )
|
64
|
+
say( statement, GREEN )
|
65
|
+
end
|
66
|
+
|
67
|
+
def say_in_warning( statement )
|
68
|
+
warn( statement, RED )
|
69
|
+
end
|
70
|
+
|
71
|
+
def say_subtly( statement )
|
72
|
+
say( statement, MAGENTA )
|
73
|
+
end
|
74
|
+
|
75
|
+
def yes_prettily?( statement )
|
76
|
+
yes?( statement, BLUE )
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
|
81
|
+
# choices = Array of Arrays(length:2) or Strings. Can be intermingled freely.
|
82
|
+
# when the user selects a string, returns the string. For the array,
|
83
|
+
# the user sees the first item, and the programmer gets back the last item
|
84
|
+
def ask_from_menu( statement, choices )
|
85
|
+
choices.map! do |choice|
|
86
|
+
choice.is_a?(String) ? [choice] : choice # I'm being too clever by half here. .first/.last still works.
|
87
|
+
end
|
88
|
+
|
89
|
+
choice_text = ""
|
90
|
+
choices.each_with_index{ |choice,i|
|
91
|
+
choice_text << "\t\t#{i}) #{choice.first}\n"
|
92
|
+
}
|
93
|
+
|
94
|
+
selection = ask( "#{statement}\n\tChoices:\n#{choice_text}>>> ", BLUE )
|
95
|
+
choices[selection.to_i].last
|
96
|
+
end
|
97
|
+
|
98
|
+
def ask_prettily( statement )
|
99
|
+
ask( statement, BLUE )
|
100
|
+
end
|
101
|
+
|
102
|
+
# hope this doesn't break anything. Sure enough, it broke a lot of things.
|
103
|
+
# def destination_root=( root )
|
104
|
+
# @destination_stack ||= []
|
105
|
+
# @destination_stack << (root ? root.fwf_filepath.expand : '')
|
106
|
+
# end
|
107
|
+
|
108
|
+
# Instead, use these instead of destination_root. Thor gets strings instead of
|
109
|
+
# filepaths, like it wants, and I get filepaths instead of strings, like I want.
|
110
|
+
def destination_root_filepath
|
111
|
+
self.destination_root.fwf_filepath
|
112
|
+
end
|
113
|
+
|
114
|
+
def destination_root_filepath=(root)
|
115
|
+
self.destination_root = root.to_s
|
116
|
+
end
|
117
|
+
|
118
|
+
def executable_installed?( name )
|
119
|
+
name = name.to_sym
|
120
|
+
|
121
|
+
if @executables.nil?
|
122
|
+
@executables = {}
|
123
|
+
for exe, path in (EpubForge.config[:exe_paths] || {})
|
124
|
+
@executables[exe] = path.fwf_filepath
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
@executables[name] ||= begin
|
129
|
+
_which = `which #{name}`.strip
|
130
|
+
(_which.length == 0) ? false : _which.fwf_filepath
|
131
|
+
end
|
132
|
+
|
133
|
+
@executables[name]
|
134
|
+
end
|
135
|
+
|
136
|
+
def git_installed?
|
137
|
+
executable_installed?('git')
|
138
|
+
end
|
139
|
+
|
140
|
+
def ebook_convert_installed?
|
141
|
+
executable_installed?('ebook-convert')
|
142
|
+
end
|
143
|
+
|
144
|
+
def project_already_gitted?
|
145
|
+
@project.target_dir.join( ".git" ).directory?
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Kernel
|
2
|
+
# def puts_nowhere( &block )
|
3
|
+
# collect_stdout( "/dev/null", &block )
|
4
|
+
# end
|
5
|
+
#
|
6
|
+
# def collect_stdout( dest, &block )
|
7
|
+
# @puts_nowhere_keep_old_stdout ||= []
|
8
|
+
# @puts_nowhere_keep_old_stdout << $stdout
|
9
|
+
# if dest.is_a?(String)
|
10
|
+
# $stdout = File.open( dest, "w" )
|
11
|
+
# else
|
12
|
+
# $stdout = dest
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# $stdout.sync = true
|
16
|
+
#
|
17
|
+
# yield
|
18
|
+
#
|
19
|
+
# $stdout = @puts_nowhere_keep_old_stdout.pop
|
20
|
+
# end
|
21
|
+
|
22
|
+
# yields an alternate reality block where instance methods
|
23
|
+
# are different from what they were. At the end of the block
|
24
|
+
# it resets the initial values of the instance variables.
|
25
|
+
def with_locals locals = {}, &block
|
26
|
+
old_local_vars = {}
|
27
|
+
|
28
|
+
for k, v in locals
|
29
|
+
var = :"@#{k}"
|
30
|
+
old_local_vars[k] = instance_variable_get(var)
|
31
|
+
instance_variable_set( var, v )
|
32
|
+
end
|
33
|
+
|
34
|
+
yield
|
35
|
+
ensure # make all as it once was
|
36
|
+
for k, v in old_local_vars
|
37
|
+
var = :"@#{k}"
|
38
|
+
instance_variable_set( var, v )
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class String
|
2
|
+
TITLE_WORDS_NOT_CAPITALIZED = %W(a an in the for and nor but or yet so also)
|
3
|
+
def epf_blank?
|
4
|
+
self.strip.length == 0
|
5
|
+
end
|
6
|
+
|
7
|
+
def epf_camelize
|
8
|
+
gsub(/(?:^|_)(.)/) { $1.upcase }
|
9
|
+
end
|
10
|
+
|
11
|
+
# TODO: Need comprehensive list of characters to be protected.
|
12
|
+
def epf_backhashed_filename
|
13
|
+
self.gsub(" ", "\\ ")
|
14
|
+
end
|
15
|
+
|
16
|
+
def epf_underscorize
|
17
|
+
self.downcase.gsub(/\s+/,"_").gsub(/[\W]/,"")
|
18
|
+
end
|
19
|
+
|
20
|
+
# def fwf_filepath
|
21
|
+
# EpubForge::FunWith::Files::FilePath.new(self)
|
22
|
+
# end
|
23
|
+
|
24
|
+
def to_pathname
|
25
|
+
Pathname.new( self )
|
26
|
+
end
|
27
|
+
|
28
|
+
def epf_deunderscorize_as_title
|
29
|
+
words = self.split("_")
|
30
|
+
|
31
|
+
words = [words[0].capitalize] + words[1..-1].map{|w|
|
32
|
+
TITLE_WORDS_NOT_CAPITALIZED.include?(w) ? w : w.capitalize
|
33
|
+
}
|
34
|
+
|
35
|
+
words.join(" ")
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module CustomHelpers
|
3
|
+
def ask question, opts = {}
|
4
|
+
opts[:menu] ||= [["Y", "Yes"], ["N", "No"]]
|
5
|
+
|
6
|
+
answer = nil
|
7
|
+
|
8
|
+
while answer.nil?
|
9
|
+
menu_string = opts[:menu].map{ |li| "#{li[0]}):\t#{li[1]}"}.join("\n\t")
|
10
|
+
puts "#{question} :\n\t#{ menu_string }"
|
11
|
+
line = Readline.readline(">> ",true).strip.upcase
|
12
|
+
|
13
|
+
opts[:menu].each{ |li|
|
14
|
+
if li[0].upcase == line.to_s
|
15
|
+
answer = li
|
16
|
+
end
|
17
|
+
}
|
18
|
+
|
19
|
+
puts "I don't understand that response" if answer.nil?
|
20
|
+
end
|
21
|
+
|
22
|
+
if opts[:return_value]
|
23
|
+
answer[1]
|
24
|
+
else
|
25
|
+
answer[0].upcase
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def collect_stdout( dest = StringIO.new, &block )
|
30
|
+
raise ArgumentError.new("No block given.") unless block_given?
|
31
|
+
|
32
|
+
prior_stdout = $stdout
|
33
|
+
# @epf_prior_stdout_stack ||= []
|
34
|
+
# @epf_prior_stdout_stack << $stdout
|
35
|
+
|
36
|
+
$stdout = begin
|
37
|
+
if dest.is_a?( String ) || dest.is_a?( Pathname )
|
38
|
+
File.open( dest, "a" )
|
39
|
+
elsif dest.is_a?( IO ) || dest.is_a?( StringIO )
|
40
|
+
dest
|
41
|
+
else
|
42
|
+
raise ArgumentError.new("collect_stdout cannot take a <#{dest.class.name}> as an argument.")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
$stdout.sync = true
|
47
|
+
yield
|
48
|
+
|
49
|
+
$stdout = prior_stdout
|
50
|
+
|
51
|
+
dest.is_a?( StringIO ) ? dest.string : nil
|
52
|
+
end
|
53
|
+
#
|
54
|
+
# def collect_stdout( *args, &block )
|
55
|
+
# yield
|
56
|
+
# end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
EpubForge.extend( EpubForge::CustomHelpers )
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Epub
|
3
|
+
module Assets
|
4
|
+
class Image < Asset
|
5
|
+
attr_reader :ext, :filename, :name
|
6
|
+
def initialize( filename, options = {} )
|
7
|
+
@filename = filename.fwf_filepath
|
8
|
+
@name = @filename.basename.to_s.split(".")[0..-2].join(".")
|
9
|
+
@ext = @filename.extname.gsub( /^\./, "" )
|
10
|
+
end
|
11
|
+
|
12
|
+
def link
|
13
|
+
IMAGES_DIR.join( @name )
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Epub
|
3
|
+
module Assets
|
4
|
+
class Page < Asset
|
5
|
+
attr_reader :html, :original_file, :title, :project, :media_type, :dest_extension
|
6
|
+
attr_accessor :section_id, :section_number
|
7
|
+
|
8
|
+
def initialize file, metadata, project
|
9
|
+
raise "NIL" if project.nil?
|
10
|
+
|
11
|
+
@metadata = metadata
|
12
|
+
@project = project
|
13
|
+
@original_file = file
|
14
|
+
@dest_extension = "xhtml"
|
15
|
+
|
16
|
+
@html = Utils::Htmlizer.instance.translate( file )
|
17
|
+
@title = File.basename( file ).split(".")[0..-2].map(&:capitalize).join(" : ")
|
18
|
+
@content = ""
|
19
|
+
puts "Initialized #{file} with title [#{@title}]"
|
20
|
+
end
|
21
|
+
|
22
|
+
def link
|
23
|
+
TEXT_DIR.join( "section#{ sprintf("%04i", @section_number) }.#{@dest_extension}" )
|
24
|
+
end
|
25
|
+
|
26
|
+
def media_type
|
27
|
+
MEDIA_TYPES[@dest_extension]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Epub
|
3
|
+
module Assets
|
4
|
+
class Stylesheet < Asset
|
5
|
+
attr_accessor :filename, :name, :contents
|
6
|
+
def initialize( filename )
|
7
|
+
@filename = filename.fwf_filepath
|
8
|
+
@name = @filename.basename
|
9
|
+
@contents = @filename.read
|
10
|
+
end
|
11
|
+
|
12
|
+
def link
|
13
|
+
STYLE_DIR.join( @name )
|
14
|
+
end
|
15
|
+
|
16
|
+
def media_type
|
17
|
+
MEDIA_TYPES["css"]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|