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,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
+