epubforge 0.0.5

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.
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
@@ -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,5 @@
1
+ class Array
2
+ def epf_blank?
3
+ length == 0
4
+ end
5
+ 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,5 @@
1
+ class NilClass
2
+ def epf_blank?
3
+ true
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Object
2
+ def umethods( regex = /.*/ )
3
+ (self.methods.sort - Object.new.methods).grep( regex )
4
+ end
5
+ 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,11 @@
1
+ module EpubForge
2
+ module Epub
3
+ module Assets
4
+ class Asset
5
+ def media_type
6
+ MEDIA_TYPES[@ext]
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,8 @@
1
+ module EpubForge
2
+ module Epub
3
+ module Assets
4
+ class HTML < Page
5
+ end
6
+ end
7
+ end
8
+ end
@@ -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,8 @@
1
+ module EpubForge
2
+ module Epub
3
+ module Assets
4
+ class Markdown < Page
5
+ end
6
+ end
7
+ end
8
+ 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
@@ -0,0 +1,8 @@
1
+ module EpubForge
2
+ module Epub
3
+ module Assets
4
+ class Textile < Page
5
+ end
6
+ end
7
+ end
8
+ end