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
@@ -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