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
data/Gemfile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
# Add dependencies required to use your gem here.
|
3
|
+
# Example:
|
4
|
+
# gem "activesupport", ">= 2.3.5"
|
5
|
+
|
6
|
+
# TODO: How the hell do you say "any version of Ruby 1.9?" Google-fu fails me.
|
7
|
+
# ruby "1.9.x"
|
8
|
+
|
9
|
+
# Add dependencies to develop your gem here.
|
10
|
+
# Include everything needed to run rake, tests, features, etc.
|
11
|
+
|
12
|
+
gem "xdg"
|
13
|
+
gem "builder"
|
14
|
+
gem "nokogiri", "~> 1.5"
|
15
|
+
gem "thor"
|
16
|
+
# gem "configurator2"
|
17
|
+
gem 'fun_with_files'
|
18
|
+
gem 'fun_with_configurations'
|
19
|
+
|
20
|
+
group :development do
|
21
|
+
gem "rdoc", "~> 3.12"
|
22
|
+
gem "bundler", "~> 1.3.0"
|
23
|
+
gem "jeweler", "~> 1.8.4"
|
24
|
+
gem "shoulda", "~> 3.3"
|
25
|
+
gem "debugger"
|
26
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2013 Bryce Anderson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
= epubforge =
|
2
|
+
|
3
|
+
Write your book in markdown, then do all sorts of increasingly nifty things with it using this command-line utility.
|
4
|
+
|
5
|
+
== Project description ==
|
6
|
+
|
7
|
+
epubforge is a command-line utility for creating, tracking and managing longer (novella and book-length) writing projects. Write your text in markdown (http://whatismarkdown.com/), use the built in actions to convert your project to various ebook formats, track wordcount over the life of the project, manage a story bible, and back your project up using git.
|
8
|
+
|
9
|
+
Or go further and define your own formatters/converters and actions in Ruby. Have fun!
|
10
|
+
|
11
|
+
|
12
|
+
== Contributing to epubforge ==
|
13
|
+
|
14
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
15
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
|
16
|
+
* Fork the project.
|
17
|
+
* Start a feature/bugfix branch.
|
18
|
+
* Commit and push until you are happy with your contribution.
|
19
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
20
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
21
|
+
|
22
|
+
== Copyright ==
|
23
|
+
|
24
|
+
Copyright (c) 2013 Bryce Anderson. See LICENSE.txt for
|
25
|
+
further details.
|
26
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "epubforge"
|
18
|
+
gem.homepage = "http://github.com/darth_schmoo/epubforge"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{Write your book in markdown, then do all sorts of increasingly nifty things with it.}
|
21
|
+
gem.description = File.read( File.join( ".", "README.rdoc" ) )
|
22
|
+
gem.email = "keeputahweird@gmail.com"
|
23
|
+
gem.authors = ["Bryce Anderson"]
|
24
|
+
# dependencies defined in Gemfile
|
25
|
+
|
26
|
+
|
27
|
+
gem.files = Dir.glob( File.join( ".", "*.rb" ) ) +
|
28
|
+
Dir.glob( File.join( ".", "lib", "**", "*.rb" ) ) +
|
29
|
+
Dir.glob( File.join( ".", "templates", "**", "*.*" ) ) +
|
30
|
+
Dir.glob( File.join( ".", "test", "**", "*.*" ) ) +
|
31
|
+
Dir.glob( File.join( ".", "actions", "**", "*.rb" ) ) +
|
32
|
+
Dir.glob( File.join( ".", "config", "**", "*.rb" ) ) +
|
33
|
+
[ "Gemfile",
|
34
|
+
"Rakefile",
|
35
|
+
"LICENSE.txt",
|
36
|
+
"README.rdoc",
|
37
|
+
"VERSION",
|
38
|
+
File.join( ".", "bin", "epubforge" )
|
39
|
+
]
|
40
|
+
|
41
|
+
gem.default_executable = File.join( ".", "bin", "epubforge" )
|
42
|
+
end
|
43
|
+
|
44
|
+
Jeweler::RubygemsDotOrgTasks.new
|
45
|
+
|
46
|
+
require 'rake/testtask'
|
47
|
+
Rake::TestTask.new(:test) do |test|
|
48
|
+
test.libs << 'lib' << 'test'
|
49
|
+
test.pattern = 'test/**/test_*.rb'
|
50
|
+
test.verbose = true
|
51
|
+
end
|
52
|
+
|
53
|
+
# require 'rcov/rcovtask'
|
54
|
+
# Rcov::RcovTask.new do |test|
|
55
|
+
# test.libs << 'test'
|
56
|
+
# test.pattern = 'test/**/test_*.rb'
|
57
|
+
# test.verbose = true
|
58
|
+
# test.rcov_opts << '--exclude "gems/*"'
|
59
|
+
# end
|
60
|
+
|
61
|
+
task :default => :test
|
62
|
+
|
63
|
+
require 'rdoc/task'
|
64
|
+
Rake::RDocTask.new do |rdoc|
|
65
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
66
|
+
|
67
|
+
rdoc.rdoc_dir = 'rdoc'
|
68
|
+
rdoc.title = "epubforge #{version}"
|
69
|
+
rdoc.rdoc_files.include('README*')
|
70
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
71
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.5
|
data/bin/epubforge
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
raise "Requires Ruby 1.9 (or greater?)" unless RUBY_VERSION =~ /^(1\.9|2\.0|2\.1)/
|
4
|
+
|
5
|
+
exec_is_in = File.expand_path( File.dirname(__FILE__) )
|
6
|
+
epubforge_loader_file = File.join( exec_is_in, "..", "lib", "epubforge" )
|
7
|
+
|
8
|
+
require epubforge_loader_file
|
9
|
+
|
10
|
+
EpubForge::Action::Runner.new.exec( *ARGV )
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class BookToEpub < ThorAction
|
4
|
+
description "Create an epub book from the .markdown files in the project's book/ subdirectory."
|
5
|
+
keywords :forge, :book
|
6
|
+
usage "#{$PROGRAM_NAME} forge <project_directory (optional if current directory)>"
|
7
|
+
|
8
|
+
desc( "do:forge", "Wraps the project up in a .epub (ebook) file.")
|
9
|
+
def do( project, *args )
|
10
|
+
@project = project
|
11
|
+
builder = EpubForge::Epub::Builder.new( @project, :page_order => @project.config["pages"]["book"] )
|
12
|
+
|
13
|
+
builder.build
|
14
|
+
builder.package( @project.filename_for_epub_book )
|
15
|
+
builder.clean
|
16
|
+
puts "Done building epub <#{@project.filename_for_epub_book}>"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class Generate < ThorAction
|
4
|
+
keywords :new, :generate, :add
|
5
|
+
description "Add something to the project (a new chapter, or a new wiki entry, etc.)"
|
6
|
+
|
7
|
+
desc( "do:new:XXXXXXX", self.description )
|
8
|
+
def do( project, *args )
|
9
|
+
case @new_what = args.shift
|
10
|
+
when "chapter"
|
11
|
+
chapter( project, args )
|
12
|
+
else
|
13
|
+
puts "Unrecognized generator #{@new_what}. Quitting."
|
14
|
+
exit(0)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
protected
|
19
|
+
def chapter( project, args )
|
20
|
+
GenerateChapter.new.do( project, *args )
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class GenerateChapter < Generate
|
4
|
+
# examples:
|
5
|
+
#
|
6
|
+
# epubforge new chapter # adds a new chapter to the end of the book
|
7
|
+
# epubforge new chapter +5 # adds five new chapters to the end of the book
|
8
|
+
# epubforge new chapter 13 # Replaces 13 with a blank chapter, moving the old 13 to 14, the old 14 to 15, etc.
|
9
|
+
# epubforge new chapter 13+5 # Creates new chapter 13, 14, 15, 16, 17. Subsequent chapters get a +5
|
10
|
+
# I don't believe that more complex expressions are desirable.
|
11
|
+
desc( "do:new:chapter", "Add chapters to book" )
|
12
|
+
def do( project, *args )
|
13
|
+
expr = args.shift
|
14
|
+
|
15
|
+
puts expr
|
16
|
+
puts project.chapters
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
def chapter( project, args )
|
22
|
+
GenerateChapter.new.do( project, *args )
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class GitBackup < ThorAction
|
4
|
+
description "commit your project to the git repo and back it up"
|
5
|
+
keywords :backup, :save, :commit
|
6
|
+
usage "#{$PROGRAM_NAME} commit <project directory (optional if current dir)> \"optional message\""
|
7
|
+
|
8
|
+
desc( "do:save", "save to your git repository")
|
9
|
+
def do( project, *args )
|
10
|
+
@project = project
|
11
|
+
@message = args.length > 0 ? args.last : "incremental backup"
|
12
|
+
|
13
|
+
unless project_already_gitted?
|
14
|
+
say_error "Not a git-backed project. Aborting."
|
15
|
+
say_instruction "Run 'epubforge gitify <project>' to create a backup repository."
|
16
|
+
return false
|
17
|
+
end
|
18
|
+
|
19
|
+
`cd #{@project.target_dir} && git commit -a -m "#{@message}" && git push`
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class Gitify < ThorAction
|
4
|
+
description "create a remote git repository of the project folder"
|
5
|
+
keywords :gitify, :git_init
|
6
|
+
usage "#{$PROGRAM_NAME} gitify <project_directory>"
|
7
|
+
|
8
|
+
desc( "do:gitify", "create a git repository to hold backups" )
|
9
|
+
def do( project, *args )
|
10
|
+
@project = project
|
11
|
+
@conf = @project.config
|
12
|
+
@gitconf = @conf.git
|
13
|
+
@cli_sequence = CliSequence.new
|
14
|
+
@cli_sequence.default( :verbose, true )
|
15
|
+
@cli_sequence.default( :local_dir, @project.target_dir )
|
16
|
+
|
17
|
+
|
18
|
+
if project_already_gitted?
|
19
|
+
say_error "Already seems to be a git project. delete the .git folder if this is incorrect."
|
20
|
+
return false
|
21
|
+
end
|
22
|
+
|
23
|
+
project_name_with_folder = @gitconf["repo_folder"].fwf_filepath.expand
|
24
|
+
|
25
|
+
if @gitconf["remote_host"]
|
26
|
+
remote_host = "#{@gitconf['remote_user']}@#{@gitconf['remote_host']}"
|
27
|
+
@cli_sequence.default( :remote, remote_host )
|
28
|
+
project_url = "ssh://#{remote_host}#{project_name_with_folder}"
|
29
|
+
|
30
|
+
@cli_sequence.add_remote_command( "mkdir -p #{project_name_with_folder}", "rm -rf #{project_name_with_folder}" )
|
31
|
+
@cli_sequence.add_remote_command( "git --bare init #{project_name_with_folder}" )
|
32
|
+
identifier = project_name_with_folder.join( @gitconf["repo_id"] )
|
33
|
+
@cli_sequence.add_remote_command( "touch #{identifier}", "rm #{identifier}" ) # undo isn't needed here, since the directory will be wiped out.
|
34
|
+
else
|
35
|
+
project_url = "file://#{project_name_with_folder}"
|
36
|
+
@cli_sequence.add_local_command( "mkdir -p #{project_name_with_folder}", "rm -rf #{project_name_with_folder}" )
|
37
|
+
@cli_sequence.add_local_command( "git --bare init #{project_name_with_folder}" )
|
38
|
+
identifier = project_name_with_folder.join( @gitconf["repo_id"] )
|
39
|
+
@cli_sequence.add_local_command( "touch #{identifier}", "rm #{identifier}" )
|
40
|
+
end
|
41
|
+
|
42
|
+
# running locally
|
43
|
+
@cli_sequence.add_local_command "git init", "rm -rf .git"
|
44
|
+
@cli_sequence.add_local_command "git remote add origin #{project_url}"
|
45
|
+
@cli_sequence.add_local_command "git add ."
|
46
|
+
@cli_sequence.add_local_command "git commit -a -m \"Initial commit\""
|
47
|
+
@cli_sequence.add_local_command "git config branch.master.remote origin"
|
48
|
+
@cli_sequence.add_local_command "git config branch.master.merge refs/heads/master"
|
49
|
+
@cli_sequence.add_local_command "git push origin master" # need to be explicit about branch the first time
|
50
|
+
|
51
|
+
if @cli_sequence.execute
|
52
|
+
say_all_is_well( "All done. The url for this project is #{project_url}" )
|
53
|
+
else
|
54
|
+
say_error( "Command sequence failed." )
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
def git_remote_exec( cmd )
|
60
|
+
say_subtly( "attempting to run remotely: #{cmd}" )
|
61
|
+
`cd #{@project.target_dir} && ssh #{@gitconf["remote_user"]}@#{@gitconf["remote_host"]} "#{cmd}"`
|
62
|
+
say( "Success: #{$?.success?}")
|
63
|
+
end
|
64
|
+
|
65
|
+
def git_local_exec( cmd )
|
66
|
+
say_subtly( "attempting to run locally: #{cmd}" )
|
67
|
+
`cd #{@project.target_dir} && #{cmd}`
|
68
|
+
say( "Success: #{$?.success?}")
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class Globals < ThorAction
|
4
|
+
description "Set up global defaults for things like author name, publisher, etc."
|
5
|
+
keywords :global
|
6
|
+
usage "#{$PROGRAM_NAME} global <key>=\"<val>\" or global -i <asks you for defaults>"
|
7
|
+
project_not_required
|
8
|
+
|
9
|
+
desc( "do:global", "TODO: Words go here." )
|
10
|
+
def do( project, *args )
|
11
|
+
# project is ignored
|
12
|
+
@settings = EpubForge::Utils::Settings.new( EpubForge, EpubForge::USER_GLOBALS_FILE )
|
13
|
+
|
14
|
+
puts args
|
15
|
+
if args.first == "-i"
|
16
|
+
interactive
|
17
|
+
elsif args.first == "unset"
|
18
|
+
args.shift
|
19
|
+
args.each do |kv|
|
20
|
+
@settings.act_on_string( kv, :unset)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
args.each do |kv|
|
24
|
+
@settings.act_on_string( kv, :set )
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
@settings.write_settings_file
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
def interactive
|
33
|
+
keys = []
|
34
|
+
question = """
|
35
|
+
|
36
|
+
Enter 'Q' to quit, type
|
37
|
+
'set settinggroup:settingsubgroup:setting=value' to install or replace a setting, or type
|
38
|
+
'unset settinggroup:settingsubgroup:key' to remove a setting.
|
39
|
+
Examples:
|
40
|
+
set gopher:name=Woodchuck McGreggor
|
41
|
+
set gopher:handler=Ranger Rick
|
42
|
+
set gopher:domicile=Burrow #83612, Yellowstone
|
43
|
+
unset gopher # removes all the previous settings
|
44
|
+
set my:hotdog:has:a:firstname=It's o-s-c-a-r.
|
45
|
+
|
46
|
+
>>> """
|
47
|
+
while ( settings = ask( question ) ) != "Q"
|
48
|
+
if settings.match( /^unset / )
|
49
|
+
key = settings.gsub(/^unset /,'').strip
|
50
|
+
@settings.act_on_string( key, :unset )
|
51
|
+
key = "unset: #{key}"
|
52
|
+
elsif settings.match( /^set / )
|
53
|
+
key = settings.gsub(/^set /,'').strip
|
54
|
+
@settings.act_on_string( key, :set )
|
55
|
+
key = "set: #{key}"
|
56
|
+
else
|
57
|
+
say_error( "I didn't understand that." )
|
58
|
+
key = "???"
|
59
|
+
end
|
60
|
+
|
61
|
+
keys << key
|
62
|
+
|
63
|
+
|
64
|
+
interactive_print_user_keys keys
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def interactive_print_user_keys( keys )
|
69
|
+
say "\n\n"
|
70
|
+
for key in keys
|
71
|
+
say "\t#{key}"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class Help < ThorAction
|
4
|
+
description "The help menu."
|
5
|
+
keywords :help, :"-h", :"--help"
|
6
|
+
usage "#{$PROGRAM_NAME} -h"
|
7
|
+
project_not_required
|
8
|
+
|
9
|
+
desc( "do:help", "print out help for the various actions.")
|
10
|
+
def do( project, *args )
|
11
|
+
say_instruction "epubforge [action] [folder]"
|
12
|
+
say_instruction "\tActions:"
|
13
|
+
for action in Action::Runner.new.actions_lookup.actions
|
14
|
+
say_instruction "\t( #{action.keywords.join(" | ")} ) :"
|
15
|
+
say_instruction "\t\tDescription: #{action.description}"
|
16
|
+
say_instruction "\t\tUsage: #{action.usage}\n"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class Init < ThorAction
|
4
|
+
keywords :init, :initialize, :new
|
5
|
+
project_not_required
|
6
|
+
|
7
|
+
desc("do:init", "create a new epubforge project")
|
8
|
+
def do( project, *args )
|
9
|
+
unless project.nil?
|
10
|
+
say_error "Project already exists. Quitting."
|
11
|
+
return false
|
12
|
+
end
|
13
|
+
|
14
|
+
return false unless parse_args( *args )
|
15
|
+
|
16
|
+
@template_dir = EpubForge.root.join( "templates", @template_to_use )
|
17
|
+
src_entries = @template_dir.glob( "**", "*" ).map{ |entry|
|
18
|
+
entry.relative_to( @template_dir )
|
19
|
+
}
|
20
|
+
|
21
|
+
self.source_paths.push( @template_dir )
|
22
|
+
src_dirs = src_entries.select{ |d| @template_dir.join(d).directory? }.uniq
|
23
|
+
|
24
|
+
for dir in src_dirs
|
25
|
+
empty_directory( self.destination_root_filepath.join( dir ) )
|
26
|
+
end
|
27
|
+
|
28
|
+
for entry in src_entries - src_dirs
|
29
|
+
case entry.ext
|
30
|
+
when "template"
|
31
|
+
dst = self.destination_root_filepath.join( entry ).without_ext
|
32
|
+
template( entry, dst )
|
33
|
+
when "sequence"
|
34
|
+
@chapter_count ||= @opts[:answers][:chapter_count] if @opts[:answers]
|
35
|
+
@chapter_count ||= ask_prettily("Setting up chapter files.\n How many chapters will your book have (you can add more later)? >>> ").to_i
|
36
|
+
|
37
|
+
1.upto( @chapter_count ) do |i|
|
38
|
+
@i = i
|
39
|
+
dst = self.destination_root_filepath.join( entry ).gsub( /%i%/, sprintf( "%04i", @i) ).without_ext
|
40
|
+
template( entry, dst )
|
41
|
+
end
|
42
|
+
when "form"
|
43
|
+
configure_configuration( @opts[:answers] || {} )
|
44
|
+
dst = self.destination_root_filepath.join( entry ).without_ext
|
45
|
+
template( entry, dst, @template_options )
|
46
|
+
say_all_is_well( "Your configuration is all set up!" )
|
47
|
+
say_instruction( "run 'epubforge gitify' to initialize the backup repository." )
|
48
|
+
else
|
49
|
+
copy_file( entry, self.destination_root_filepath.join( entry ) )
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
def configure_configuration(opts = {})
|
56
|
+
say_instruction( "Don't think too hard about these next few questions. You can always change your mind by editing settings/config" )
|
57
|
+
|
58
|
+
opts[:title] ||= ask_prettily("What is the name of your book?")
|
59
|
+
opts[:author] ||= ask_prettily( "What is the name of the author?" )
|
60
|
+
opts[:license] ||= ask_from_menu "What license do you want your book under?", [ "All Rights Reserved",
|
61
|
+
"Creative Commons Non-Commercial, No Derivatives License",
|
62
|
+
"Creative Commons Non-Commercial, Share-Alike License",
|
63
|
+
"GNU Free Documentation License",
|
64
|
+
"Public Domain",
|
65
|
+
"Other" ]
|
66
|
+
if opts[:license] == "Other"
|
67
|
+
opts[:license] = ask_prettily( "Type in the license you wish to use : " )
|
68
|
+
end
|
69
|
+
|
70
|
+
@template_options = opts
|
71
|
+
|
72
|
+
if git_installed?
|
73
|
+
if opts[:use_git] || opts[:use_git].nil? && yes_prettily?( "Do you want to back up your project using git?" )
|
74
|
+
configure_git( opts[:git] || {} )
|
75
|
+
end
|
76
|
+
else
|
77
|
+
say_in_warning("The program 'git' must be installed and locatable if you want epubforge to back up your project.")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
def configure_git( opts = {})
|
84
|
+
opts[:remote] = "Back up to a remote host."
|
85
|
+
opts[:thumb] = "Back up to an external or thumb drive."
|
86
|
+
opts[:backup_type] ||= ask_from_menu( "Where would you like to back up your project to?",
|
87
|
+
[ opts[:remote],
|
88
|
+
opts[:thumb] ] )
|
89
|
+
|
90
|
+
opts[:repo_id] ||= rand(16**32).to_s(16)
|
91
|
+
|
92
|
+
if opts[:backup_type] == opts[:remote]
|
93
|
+
opts[:host] ||= ask_prettily("Enter the name of the remote host : ")
|
94
|
+
opts[:user] ||= ask_prettily("Enter your user name : ")
|
95
|
+
opts[:repo] ||= ask_prettily("Enter the name of the folder on the remote host. A folder called #{backup_folder_name} will be created there : ")
|
96
|
+
elsif opts[:backup_type] == opts[:thumb]
|
97
|
+
opts[:repo] ||= ask_prettily("Enter the full path to the backup folder. A folder called #{backup_folder_name} will be created there: ")
|
98
|
+
else
|
99
|
+
say_error("I'm confused by the requested backup style <#{opts[:backup_type]}>. Skipping git configuration.")
|
100
|
+
opts = nil
|
101
|
+
return false
|
102
|
+
end
|
103
|
+
|
104
|
+
# TODO: What if the target file system uses a different file separator?
|
105
|
+
opts[:repo] = opts[:repo].fwf_filepath.join( backup_folder_name ) if opts[:repo].is_a?(String)
|
106
|
+
@template_options[:git] = opts
|
107
|
+
true
|
108
|
+
end
|
109
|
+
|
110
|
+
def backup_folder_name
|
111
|
+
(@template_options[:title] || "").epf_underscorize + ".epubforge.git"
|
112
|
+
end
|
113
|
+
|
114
|
+
def parse_args( *args )
|
115
|
+
@opts = args.last.is_a?(Hash) ? args.pop : {}
|
116
|
+
root = args.shift
|
117
|
+
|
118
|
+
if root.nil?
|
119
|
+
say_error "No destination directory given."
|
120
|
+
return false
|
121
|
+
end
|
122
|
+
|
123
|
+
self.destination_root_filepath = root.fwf_filepath
|
124
|
+
debugger if self.destination_root_filepath != root.fwf_filepath.expand
|
125
|
+
|
126
|
+
if self.destination_root_filepath.exist? &&
|
127
|
+
(!(self.destination_root_filepath.empty?) || !(self.destination_root_filepath.directory?))
|
128
|
+
say_error "This action must create a new directory or act upon an empty directory. Quitting."
|
129
|
+
return false
|
130
|
+
end
|
131
|
+
|
132
|
+
@template_to_use = "default"
|
133
|
+
true
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class Kindle < ThorAction
|
4
|
+
description "Create a .mobi book and try to push it to your Kindle (conversion requires Calibre)"
|
5
|
+
keywords :kindle, :push, :b2k
|
6
|
+
usage "#{$PROGRAM_NAME} b2k <project_directory>"
|
7
|
+
|
8
|
+
# TODO: Hard-coded. Need a global settings file?
|
9
|
+
KINDLE_DEVICE_DIR = "/".fwf_filepath.join( "Volumes", "Kindle" )
|
10
|
+
KINDLE_PUSH_DIR = KINDLE_DEVICE_DIR.join("documents", "fic-mine")
|
11
|
+
|
12
|
+
desc( "do:kindle", "Turn your .epub file into a .mobi file. Check to see if your Kindle is connected, then pushes it." )
|
13
|
+
def do( project, *args )
|
14
|
+
@project = project
|
15
|
+
@src_epub = @project.filename_for_epub_book.fwf_filepath
|
16
|
+
@dst_mobi = @project.filename_for_mobi_book.fwf_filepath
|
17
|
+
|
18
|
+
mobify
|
19
|
+
end
|
20
|
+
|
21
|
+
protected
|
22
|
+
def mobify
|
23
|
+
return false unless fulfill_requirements
|
24
|
+
|
25
|
+
say "converting from #{@src_epub} to #{@dst_mobi}"
|
26
|
+
cmd = "ebook-convert #{@src_epub} #{@dst_mobi}"
|
27
|
+
say "executing: #{cmd}"
|
28
|
+
`#{cmd}`
|
29
|
+
|
30
|
+
if $?.success? && @dst_mobi.exist?
|
31
|
+
say_all_is_well "Formatted for Kindle (.mobi file). File at #{@dst_mobi}."
|
32
|
+
push_to_device @dst_mobi
|
33
|
+
else
|
34
|
+
say_warning( "Something went wrong during the conversion process." )
|
35
|
+
say_warning( "#{@dst_mobi} exists, but may not be complete or correct." ) if @dst_mobi.exist?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def push_to_device mobi_file
|
41
|
+
if KINDLE_DEVICE_DIR.directory? && KINDLE_PUSH_DIR.directory?
|
42
|
+
FileUtils.copy( mobi_file, KINDLE_PUSH_DIR )
|
43
|
+
say_all_is_well "File pushed to Kindle."
|
44
|
+
true
|
45
|
+
else
|
46
|
+
say_error "NOT installed on Kindle. It may not be plugged in."
|
47
|
+
false
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def fulfill_requirements
|
52
|
+
unless ebook_convert_installed?
|
53
|
+
say_error( "ebook-convert is not installed. Quitting." )
|
54
|
+
return false
|
55
|
+
end
|
56
|
+
|
57
|
+
BookToEpub.new.do( @project )
|
58
|
+
|
59
|
+
unless @src_epub.exist?
|
60
|
+
say_error( "Cannot find source .epub #{src_epub}" )
|
61
|
+
return false
|
62
|
+
end
|
63
|
+
|
64
|
+
true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module EpubForge
|
2
|
+
module Action
|
3
|
+
class NotesToEpub < ThorAction
|
4
|
+
description "Create an epub book from the .markdown files in the project's notes/ subdirectory."
|
5
|
+
keywords :notes, :forge_notes
|
6
|
+
usage "#{$PROGRAM_NAME} notes <project_directory> (optional if current directory)"
|
7
|
+
|
8
|
+
desc( "do:notes", "Wraps your story notes up in a .epub (ebook) file." )
|
9
|
+
def do( project, *args )
|
10
|
+
@project = project
|
11
|
+
builder = EpubForge::Epub::Builder.new( @project, book_dir: @project.target_dir.join("notes"),
|
12
|
+
page_order: @project.config[:pages][:notes] )
|
13
|
+
builder.build
|
14
|
+
builder.package( @project.filename_for_epub_notes )
|
15
|
+
builder.clean
|
16
|
+
puts "Done building epub <#{@project.filename_for_epub_notes}>"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|