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,181 @@
1
+ module EpubForge
2
+ module Utils
3
+ class DirectoryBuilder
4
+ attr_accessor :current_path, :current_file
5
+
6
+ def initialize( path )
7
+ @paths = []
8
+ @current_path = path.fwf_filepath
9
+ make_path
10
+ end
11
+
12
+ def self.create( path, &block )
13
+ builder = self.new( path )
14
+ yield builder if block_given?
15
+ builder
16
+ end
17
+
18
+ def dir( *args, &block )
19
+ descend( *args ) do
20
+ yield if block_given?
21
+ end
22
+ end
23
+
24
+ # block must be given
25
+ def self.tmpdir( &block )
26
+ if block_given?
27
+ Dir.mktmpdir do |dir|
28
+ self.create( dir ) do |builder|
29
+ yield builder
30
+ end
31
+ end
32
+ else
33
+ self.create( Dir.mktmpdir )
34
+ end
35
+ end
36
+
37
+ # Copies the given source file into a file in the current_path.
38
+ # If a dest_name is given, the new file will be given that name.
39
+ def copy( src_filepath, dst_name = nil )
40
+ dst_filepath = dst_name ? @current_path.join( dst_name ) : @current_path
41
+ FileUtils.copy( src_filepath, dst_filepath )
42
+ end
43
+
44
+ def file( name = nil, content = nil, &block )
45
+ # if name && content
46
+ # begin
47
+ # f = open_file( name )
48
+ # f << content
49
+ # ensure
50
+ # close_file
51
+ # end
52
+ if name
53
+ open_file( name )
54
+ @current_file << content if content
55
+ if block_given?
56
+ begin
57
+ yield @current_file
58
+ ensure
59
+ close_file
60
+ end
61
+ end
62
+ else
63
+ @current_file
64
+ end
65
+ end
66
+
67
+ def current_file
68
+ @current_file ? FunWith::Files::FilePath.new( @current_file.path ) : nil
69
+ end
70
+
71
+ # if file not given, the result is appended to the current file.
72
+ def download( url, file = nil )
73
+ if file
74
+ if file.fwf_filepath.relative?
75
+ file = FunWith::Files::FilePath.new( @current_path, file )
76
+ end
77
+
78
+ File.open( file, "w" ) do |f|
79
+ download_to_target( url, f )
80
+ end
81
+ elsif @current_file
82
+ download_to_target( url, @current_file )
83
+ else
84
+ puts "No current file to append #{url} to."
85
+ end
86
+ end
87
+
88
+ def template( src, dst, vars = {} )
89
+ self.file( dst ) do |f|
90
+ f << Utils::TemplateEvaluator.new( src, vars ).result
91
+ end
92
+ end
93
+
94
+ protected
95
+ def make_path
96
+ FileUtils.mkdir_p( @current_path ) unless @current_path.exist?
97
+ end
98
+
99
+ def descend( *args, &block )
100
+ if @current_path.directory?
101
+ close_file
102
+ @paths << @current_path
103
+ @current_path = @paths.last.join( *args )
104
+ make_path
105
+ yield
106
+ @current_path = @paths.pop
107
+ close_file
108
+ else
109
+ raise "Cannot descend."
110
+ end
111
+ end
112
+
113
+ def open_file( name )
114
+ close_file
115
+ @current_file = File.open( @current_path.join( name ), "w" )
116
+ end
117
+
118
+ def close_file
119
+ if @current_file
120
+ @current_file.flush
121
+ @current_file.close
122
+ end
123
+
124
+ @current_file = nil
125
+ end
126
+
127
+ def download_to_target( url, file )
128
+ Downloader.new.download( url, file )
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+
135
+ # sample code
136
+ #
137
+ # DirBuilder.create( '~/project' ) do |b| # starts by creating directory. If parent
138
+ # # directories don't exist, they will soon.
139
+ # # if you use DirBuilder.tmp('~/project'), a tempdir
140
+ # # is created, and its contents relocated to ~/project when the
141
+ # # block terminates.
142
+ # b.dir("images") do # creates subdirectory "images"
143
+ # for img in src_dir.entries.select{|img| img.extension == ".png"}
144
+ # b.copy( src_dir.join( img.filename ) ) # copies a bunch of files from another directory
145
+ # end # rises back to the initial '~/project directory
146
+ #
147
+ # b.copy( src_dir.join( "rorshach.xml" ) )
148
+ # b.download( "dest.bash", "http://get.rvm.io" ) # downloads file directly beneath '~/project'
149
+ # # maybe someday, though
150
+ #
151
+ # b.dir("text", "scenes") do # creates ~/project/text/scenes subdir
152
+ # b.file( "adventure_time.txt" ) do |f|
153
+ # f << "Fill this in later"
154
+ # end
155
+ #
156
+ # # calling .file without feeding it a block leaves it open for writing,
157
+ # # until either the enclosing block terminates or .file is called
158
+ # # again with a string argument.
159
+ # b.file( "another_brick.txt" )
160
+ # b.file << "Hey, you!"
161
+ # b.file << "Yes, you!"
162
+ # b.file.push "Stand still, laddie!"
163
+ #
164
+ # b.template(templates_dir.join("blue_template.txt")) do |t|
165
+ # t.var(:fname, "John")
166
+ # t.var(:lname, "Macey")
167
+ # t.var(:state, "Ohio")
168
+ # t.vars(graduated: "2003")
169
+ # t.vars(quot: "That wasn't my duck.", photo: "john.png", css: "font-family: arial")
170
+ # end
171
+ #
172
+ # b.copy( [src_dir.join("abba.txt"), "baab.txt"] ) # contents of abba.txt copied into baab.txt
173
+ #
174
+ #
175
+ # b.file( ".lockfile" ) # creates an empty file
176
+ # end
177
+ #
178
+ # b.
179
+ # end
180
+ #
181
+
@@ -0,0 +1,58 @@
1
+ module EpubForge
2
+ module Utils
3
+ class Downloader
4
+ # stolen from:
5
+ # http://stackoverflow.com/questions/2263540/how-do-i-download-a-binary-file-over-http-using-ruby
6
+ def download( url, io )
7
+ @uri = URI.parse( url )
8
+ @io = io
9
+
10
+ open( url ) do |f|
11
+ @io << f.read
12
+ end
13
+
14
+ # @io << Net::HTTP.get( @uri )
15
+
16
+ # Net::HTTP.start( @uri.host, @uri.port ) do |http|
17
+ # http.request_get( @uri.path ) do |request|
18
+ # request.read_body do |seg|
19
+ # puts "============================== #{seg} ============================="
20
+ # io << seg
21
+ # #hack -- adjust to suit:
22
+ # sleep 0.005
23
+ # end
24
+ # end
25
+ # end
26
+ rescue Exception => e
27
+ handle_network_errors( e )
28
+ end
29
+
30
+ def handle_network_errors( e )
31
+ raise e
32
+ rescue URI::InvalidURIError => e
33
+ puts "Tried to get #{@uri.path} but failed with URI::InvalidURIError."
34
+ rescue OpenURI::HTTPError => e
35
+ STDERR.write( "Couldn't fetch podcast info from #{@uri.path}\n" )
36
+ STDERR.write( "#{e}\n\n" )
37
+ rescue SocketError => e
38
+ STDERR.write( "Problem connecting to server (Socket error) when downloading #{@uri.path}." )
39
+ STDERR.write( "#{e}\n\n" )
40
+ rescue URI::InvalidURIError => e
41
+ STDERR.write( "URI::InvalidURIError for #{@uri.path}." )
42
+ STDERR.write( "#{e}\n\n" )
43
+ # this may be too broad a filter
44
+ # TODO: retry?
45
+ rescue SystemCallError => e
46
+ STDERR.write( "Problem connecting to server (System call error) when downloading #{@uri.path}" )
47
+ STDERR.write( "#{e}\n\n" )
48
+ rescue OpenSSL::SSL::SSLError => e
49
+ STDERR.write( "OpenSSL::SSL::SSLError while downloading #{@uri.path}" )
50
+ STDERR.write( "#{e}\n\n" )
51
+ # rescue Timeout::Error
52
+ # STDERR.write( "Timeout error connecting to #{@uri.path}" )
53
+ # STDERR.write( "#{e}\n\n" )
54
+ end
55
+ end
56
+ end
57
+ end
58
+
@@ -0,0 +1,45 @@
1
+ module EpubForge
2
+ module Utils
3
+
4
+ # FileOrderer holds a set of strings/regexes, then reorders a set of files (FunWith::Files::FilePaths, actually)
5
+ # by matching the filenames against one regex after another. Allows you to say,
6
+ # "I want the title page, the foreward, then all the chapters, then all the appendixes, then the afterword."
7
+ # Ex: FileOrderer( ["title_page", "forward", "chapter-.*", "afterword", "appendix.*" ).reorder( pages )
8
+ # Only compares the basename minus extension. Files should come from the same directory
9
+ class FileOrderer
10
+ def initialize( matchers )
11
+ @matchers = matchers.map do |m|
12
+ case m
13
+ when Regexp
14
+ m
15
+ when String
16
+ /^#{m}$/
17
+ end
18
+ end
19
+
20
+ @matchers.push( /^.*$/ )
21
+ end
22
+
23
+ def reorder( files )
24
+ files = files.map(&:fwf_filepath)
25
+
26
+ files.sort_by!{ |f|
27
+ f.basename_no_ext.to_s
28
+ }
29
+
30
+
31
+ ordered_files = @matchers.inject( [] ) do |collector, matcher|
32
+ matched_files = files.select do |f|
33
+ name = f.basename_no_ext.to_s
34
+ matcher.match( name )
35
+ end
36
+
37
+ collector += matched_files
38
+ files -= matched_files
39
+
40
+ collector
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,152 @@
1
+ module EpubForge
2
+ module Utils
3
+ class FilePath < Pathname
4
+ def initialize( *args )
5
+ super( File.join( *args ) )
6
+ end
7
+
8
+ # args implicitly joined to cwd
9
+ def self.cwd( *args )
10
+ Dir.pwd.fwf_filepath.join( *args )
11
+ end
12
+
13
+ def self.pwd( *args )
14
+ self.cwd( *args )
15
+ end
16
+
17
+ def join( *args, &block )
18
+ if block_given?
19
+ yield self.class.new( super(*args) )
20
+ else
21
+ self.class.new( super(*args) )
22
+ end
23
+ end
24
+
25
+ alias :exists? :exist?
26
+
27
+ def up
28
+ self.class.new( self.join("..") ).expand
29
+ end
30
+
31
+ # opts:
32
+ # :class => [self.class] The class of objects you want returned (String, FilePath, ClassLoader, etc.)
33
+ # Should probably be a subclass of FilePath or String. Class.init must accept a string
34
+ # [representing a file path] as the sole argument.
35
+ #
36
+ # :recurse => [false]
37
+ # :ext => [] A single symbol, or a list containing strings/symbols representing file name extensions.
38
+ # No leading periods kthxbai.
39
+ #
40
+ # If opts not given, the user can still do it explicitly with arguments like .glob("**", "*.rb")
41
+ def glob( *args )
42
+ opts = args.last.is_a?(Hash) ? args.pop : {}
43
+
44
+ recurser = opts[:recurse] ? "**" : nil
45
+ extensions = case opts[:ext]
46
+ when Symbol, String
47
+ "*.#{opts[:ext]}"
48
+ when Array
49
+ extensions = opts[:ext].map(&:to_s).join(',')
50
+ "*.{#{extensions}}"
51
+ when NilClass
52
+ nil
53
+ end
54
+
55
+ args += [recurser, extensions]
56
+ args.compact!
57
+
58
+ opts[:class] ||= self.class
59
+ Dir.glob( self.join(*args) ).map{ |f| opts[:class].new(f) }
60
+ end
61
+
62
+ def expand
63
+ self.class.new( File.expand_path( self ) )
64
+ end
65
+
66
+ def touch
67
+ FileUtils.touch( self )
68
+ return true
69
+ rescue Errno::EACCESS
70
+ return false
71
+ end
72
+
73
+ def touch_dir
74
+ FileUtils.mkdir_p( self )
75
+ return true
76
+ rescue Errno::EEXIST
77
+ return true
78
+ rescue Errno::EACCESS
79
+ return false
80
+ end
81
+
82
+ def write( content = nil, &block )
83
+ File.open( self, "w" ) do |f|
84
+ f << content if content
85
+ if block_given?
86
+ yield f
87
+ end
88
+ end
89
+ end
90
+
91
+ def append( content = nil, &block )
92
+ File.open( self, "a" ) do |f|
93
+ f << content if content
94
+ if block_given?
95
+ yield f
96
+ end
97
+ end
98
+ end
99
+
100
+ def grep( regex )
101
+ return [] unless self.file?
102
+ matching = []
103
+ self.each_line do |line|
104
+ matching.push( line ) if line.match( regex )
105
+ end
106
+ matching
107
+ end
108
+
109
+ # Not the same as zero?
110
+ def empty?
111
+ raise Exceptions::FileDoesNotExist unless self.exist?
112
+
113
+ if self.file?
114
+ File.size( self ) == 0
115
+ elsif self.directory?
116
+ self.glob( "**", "*" ).length == 0
117
+ end
118
+ end
119
+
120
+ def basename_no_ext
121
+ self.basename.to_s.split(".")[0..-2].join(".").fwf_filepath
122
+ end
123
+
124
+ def without_ext
125
+ self.gsub(/\.#{self.ext}$/, '')
126
+ end
127
+
128
+ def ext
129
+ self.basename.to_s.split(".").last || ""
130
+ end
131
+
132
+ def relative_to( ancestor_dir )
133
+ depth = ancestor_dir.to_s.split(File::SEPARATOR).length
134
+ relative_path = self.to_s.split(File::SEPARATOR)
135
+ relative_path[(depth)..-1].join(File::SEPARATOR).fwf_filepath
136
+ end
137
+
138
+ def gsub( *args )
139
+ self.to_s.gsub(*args).fwf_filepath
140
+ end
141
+
142
+ def gsub!( *args )
143
+ new_str = self.to_s.gsub(*args)
144
+ self.instance_variable_set(:@path, new_str)
145
+ end
146
+
147
+ def fwf_filepath
148
+ self
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,99 @@
1
+ module EpubForge
2
+ module Utils
3
+ # An individual translator, which receives a filename, determines if it's up to the job
4
+ # then returns the resulting HTML translation.
5
+ class HtmlTranslator
6
+ GROUP_NAMES = [:preferred, :user, :default, :fallback]
7
+
8
+ def initialize
9
+ group( :user )
10
+ opts( "" )
11
+ end
12
+
13
+ def name( n = nil )
14
+ @name = n if n
15
+ @name
16
+ end
17
+
18
+ def group( g = nil )
19
+ if g
20
+ raise "group must be one of the following symbols: #{GROUP_NAMES.inspect}" unless GROUP_NAMES.include?(g)
21
+ @group = g
22
+ end
23
+
24
+ @group
25
+ end
26
+
27
+ def executable executable_name = nil
28
+ if executable_name
29
+ @executable_name = Htmlizer.instance.location( executable_name ) || `which #{executable_name}`.strip
30
+ end
31
+ @executable_name || ""
32
+ end
33
+
34
+ def format f = nil
35
+ @format = f if f
36
+ @format
37
+ end
38
+
39
+ def cmd c = nil
40
+ @cmd = c if c
41
+ @cmd
42
+ end
43
+
44
+ def custom_proc( p = nil, &block )
45
+ if block_given?
46
+ @custom_proc = block
47
+ else
48
+ @custom_proc = c if c
49
+ end
50
+
51
+ @custom_proc
52
+ end
53
+
54
+ def opts o = nil
55
+ @opts = o if o
56
+ @opts
57
+ end
58
+
59
+ def installed?
60
+ executable.length > 0
61
+ end
62
+
63
+ def handles_format?( f )
64
+ @format == determine_file_format( f ) || @format == :unknown
65
+ end
66
+
67
+ def can_do_job?( f )
68
+ installed? && handles_format?( f )
69
+ end
70
+
71
+ # opts allows you to override the normal command line arguments
72
+ # Maybe a description of the job's requirements should be more
73
+ # elaborate than just a filename. OTOH, simple can have its advantages.
74
+ def translate( filename, opts = "" )
75
+ return false unless can_do_job?( filename )
76
+
77
+ result = "<!-- generated from #{@format} by htmlizer #{@name} -->\n\n"
78
+ if @custom_proc
79
+ result += @custom_proc.call( filename, *opts )
80
+ elsif @cmd
81
+ exec_string = cmd.gsub( /\{\{f\}\}/, filename.to_s )
82
+ opts = @opts if opts.epf_blank?
83
+ exec_string.gsub!( /\{\{o\}\}/, opts )
84
+ exec_string.gsub!( /\{\{x\}\}/, executable )
85
+
86
+ result += `#{exec_string}`
87
+ else
88
+ return false
89
+ end
90
+
91
+ result
92
+ end
93
+
94
+ def determine_file_format( file )
95
+ file.fwf_filepath.ext.to_sym
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,70 @@
1
+ module EpubForge
2
+ module Utils
3
+ # A priority stack (like a priority queue, but FILO) with a simple job:
4
+ # keep track of the translators (by name and by group), and return them
5
+ # to the object user in the order they should be tried.
6
+ class HtmlTranslatorQueue
7
+ GROUP_NAMES = HtmlTranslator::GROUP_NAMES
8
+
9
+
10
+ def initialize
11
+ @translators = {}
12
+ @all_translators = []
13
+ @translators_named = {}
14
+ for name in GROUP_NAMES
15
+ @translators[name] = []
16
+ end
17
+ end
18
+
19
+ # def translators_handling_format( requested_format )
20
+ # htmlizers = GROUP_NAMES.map{ |group|
21
+ # (@translator_queue.keys - [:all, :named]).map do |format|
22
+ # htmlizers = @translator_queue[format][group]
23
+ # htmlizers ? htmlizers.select{|html| html.handles_format?(requested_format) } : []
24
+ # end
25
+ # }
26
+ #
27
+ # htmlizers.flatten
28
+ # end
29
+
30
+ # last installed, first yielded (within a given group)
31
+ #
32
+ # Returns them in priority order, user-defined ones first.
33
+ # At the moment, it is up to individual translators to accept or
34
+ # reject the translation job based on the file format (by extension, which is lame).
35
+ def each( &block )
36
+ ordered_translators = []
37
+ for group in GROUP_NAMES.map{|g| @translators[g].reverse }
38
+ ordered_translators += group
39
+ end
40
+
41
+ if block_given?
42
+ for translator in ordered_translators
43
+ yield translator
44
+ end
45
+ else
46
+ ordered_translators.to_enum
47
+ end
48
+ end
49
+
50
+ def length
51
+ @all_translators.length
52
+ end
53
+
54
+ def categorize( htmlizer )
55
+ unless GROUP_NAMES.include?( htmlizer.group )
56
+ puts "No group specified for htmlizer #{htmlizer}. Group must be one of the following symbols: #{GROUP_NAMES.map(&:inspect).inspect}"
57
+ return false
58
+ end
59
+
60
+ @all_translators << htmlizer
61
+ @translators_named[htmlizer.name] = htmlizer if htmlizer.name
62
+ @translators[htmlizer.group] << htmlizer
63
+ end
64
+
65
+ def named( sym )
66
+ @translators_named[sym]
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,92 @@
1
+ module EpubForge
2
+ module Utils
3
+
4
+ # Htmlizer coordinates the discovery, selection, and running of HtmlTranslators.
5
+ # It can be handed basically any supported filetype (markdown, textile, txt), and
6
+ # hand back an HTML translation of the file.
7
+ class Htmlizer
8
+ include Singleton
9
+
10
+ def setup_once
11
+ return false if @already_set_up
12
+ @already_set_up = true
13
+ @exec_location = {}
14
+
15
+ @translator_queue = HtmlTranslatorQueue.new
16
+
17
+ add_htmlizers( EpubForge.root( 'config', 'htmlizers.rb' ) )
18
+ add_htmlizers( EpubForge::USER_SETTINGS.join( 'htmlizers.rb' ) )
19
+
20
+ @already_set_up
21
+ end
22
+
23
+ public
24
+ def location( name, path = nil )
25
+ @exec_location[name] = path if path
26
+ @exec_location[name]
27
+ end
28
+
29
+ # Commenting out for the moment. Philosophically, maybe it shouldn't provide access to individual translators.
30
+ # def translators_named( name )
31
+ # @translator_queue[:named][name]
32
+ # end
33
+
34
+
35
+ def self.define( &block )
36
+ htmlizer = HtmlTranslator.new
37
+ yield htmlizer
38
+ self.instance.categorize( htmlizer )
39
+ end
40
+
41
+ def categorize( htmlizer )
42
+ @translator_queue.categorize( htmlizer )
43
+ end
44
+
45
+ def add_htmlizers( htmlizers_file )
46
+ if htmlizers_file.exist?
47
+ begin
48
+ require htmlizers_file.to_s
49
+ rescue Exception => e
50
+ puts e.message
51
+ puts e.backtrace.map{|line| "\t#{line}" }
52
+ puts "Failed to load htmlizers from project file #{htmlizers_file} Soldiering onward."
53
+ end
54
+ end
55
+ end
56
+
57
+
58
+ # available options
59
+ # :htmlizer => the sym for the requested htmlizer.
60
+ # :opts => a string representing options to execute cmd with
61
+ def translate( filename, opts = {} )
62
+ translator = opts[:translator]
63
+ translator = @translator_queue.named( translator ) if translator.is_a?( Symbol )
64
+ opts = opts[:opts] || ""
65
+
66
+ if translator
67
+ if result = translator.translate( filename, {opts: opts } )
68
+ return result
69
+ else
70
+ puts "Named Htmlizer #{htmlizer} did not return html. Falling back on other htmlizers"
71
+ end
72
+ end
73
+
74
+ for translator in @translator_queue
75
+ if result = translator.translate( filename, opts )
76
+ return result
77
+ end
78
+ end
79
+
80
+ "<!-- COULD NOT FIND HTMLIZER FOR #{filename} -->"
81
+ end
82
+
83
+ def self.format_from_filename( filename )
84
+ ext = filename.fwf_filepath.extname.gsub(/^\./, "")
85
+ ext.epf_blank? ? :unknown : ext.to_sym
86
+ end
87
+ end
88
+
89
+ Htmlizer.instance.setup_once
90
+ end
91
+ end
92
+