epubforge 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. data/Gemfile +26 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.rdoc +26 -0
  4. data/Rakefile +71 -0
  5. data/VERSION +1 -0
  6. data/bin/epubforge +10 -0
  7. data/config/actions/book_to_epub.rb +20 -0
  8. data/config/actions/generate.rb +24 -0
  9. data/config/actions/generate_chapter.rb +26 -0
  10. data/config/actions/git_backup.rb +23 -0
  11. data/config/actions/gitify.rb +72 -0
  12. data/config/actions/globals.rb +77 -0
  13. data/config/actions/help.rb +21 -0
  14. data/config/actions/init.rb +137 -0
  15. data/config/actions/kindle.rb +68 -0
  16. data/config/actions/notes_to_epub.rb +20 -0
  17. data/config/actions/notes_to_kindle.rb +17 -0
  18. data/config/actions/word_count.rb +126 -0
  19. data/config/actions/wrap_scene_notes_in_hidden_div.rb +118 -0
  20. data/config/htmlizers.rb +62 -0
  21. data/lib/action/actions_lookup.rb +41 -0
  22. data/lib/action/cli_command.rb +72 -0
  23. data/lib/action/cli_sequence.rb +55 -0
  24. data/lib/action/file_transformer.rb +59 -0
  25. data/lib/action/run_description.rb +24 -0
  26. data/lib/action/runner.rb +122 -0
  27. data/lib/action/thor_action.rb +149 -0
  28. data/lib/core_extensions/array.rb +5 -0
  29. data/lib/core_extensions/kernel.rb +42 -0
  30. data/lib/core_extensions/nil_class.rb +5 -0
  31. data/lib/core_extensions/object.rb +5 -0
  32. data/lib/core_extensions/string.rb +37 -0
  33. data/lib/custom_helpers.rb +60 -0
  34. data/lib/epub/assets/asset.rb +11 -0
  35. data/lib/epub/assets/html.rb +8 -0
  36. data/lib/epub/assets/image.rb +18 -0
  37. data/lib/epub/assets/markdown.rb +8 -0
  38. data/lib/epub/assets/page.rb +32 -0
  39. data/lib/epub/assets/stylesheet.rb +22 -0
  40. data/lib/epub/assets/textile.rb +8 -0
  41. data/lib/epub/builder.rb +270 -0
  42. data/lib/epub/packager.rb +16 -0
  43. data/lib/epubforge.rb +97 -0
  44. data/lib/errors.rb +8 -0
  45. data/lib/project/project.rb +65 -0
  46. data/lib/utils/action_loader.rb +7 -0
  47. data/lib/utils/class_loader.rb +83 -0
  48. data/lib/utils/directory_builder.rb +181 -0
  49. data/lib/utils/downloader.rb +58 -0
  50. data/lib/utils/file_orderer.rb +45 -0
  51. data/lib/utils/file_path.rb +152 -0
  52. data/lib/utils/html_translator.rb +99 -0
  53. data/lib/utils/html_translator_queue.rb +70 -0
  54. data/lib/utils/htmlizer.rb +92 -0
  55. data/lib/utils/misc.rb +20 -0
  56. data/lib/utils/root_path.rb +20 -0
  57. data/lib/utils/settings.rb +146 -0
  58. data/lib/utils/template_evaluator.rb +20 -0
  59. data/templates/default/book/afterword.markdown.template +4 -0
  60. data/templates/default/book/chapter-%i%.markdown.sequence +4 -0
  61. data/templates/default/book/foreword.markdown.template +6 -0
  62. data/templates/default/book/images/cover.png +0 -0
  63. data/templates/default/book/stylesheets/stylesheet.css.template +2 -0
  64. data/templates/default/book/title_page.markdown.template +4 -0
  65. data/templates/default/notes/character.named.markdown.template +4 -0
  66. data/templates/default/notes/stylesheets/stylesheet.css.template +2 -0
  67. data/templates/default/payload.rb +65 -0
  68. data/templates/default/settings/actions/local_action.rb.example +14 -0
  69. data/templates/default/settings/config.rb.form +55 -0
  70. data/templates/default/settings/htmlizers.rb +0 -0
  71. data/templates/default/settings/wordcount.template +6 -0
  72. data/test/helper.rb +22 -0
  73. data/test/misc/config.rb +7 -0
  74. data/test/sample_text/sample.markdown +30 -0
  75. data/test/sample_text/sample.textile +24 -0
  76. data/test/test_custom_helpers.rb +22 -0
  77. data/test/test_directory_builder.rb +141 -0
  78. data/test/test_epf_root.rb +9 -0
  79. data/test/test_epubforge.rb +164 -0
  80. data/test/test_htmlizers.rb +24 -0
  81. data/test/test_runner.rb +15 -0
  82. data/test/test_utils.rb +39 -0
  83. 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