epuber 0.7.4 → 0.8.0

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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -2
  3. data/README.md +2 -1
  4. data/epuber.gemspec +10 -12
  5. data/lib/epuber/book/contributor.rb +0 -1
  6. data/lib/epuber/book/file_request.rb +1 -1
  7. data/lib/epuber/book/target.rb +20 -23
  8. data/lib/epuber/book/toc_item.rb +2 -4
  9. data/lib/epuber/book.rb +19 -19
  10. data/lib/epuber/checker/bookspec_checker.rb +26 -0
  11. data/lib/epuber/checker/text_checker.rb +2 -1
  12. data/lib/epuber/checker.rb +16 -2
  13. data/lib/epuber/checker_transformer_base.rb +2 -5
  14. data/lib/epuber/command/build.rb +34 -24
  15. data/lib/epuber/command/init.rb +23 -23
  16. data/lib/epuber/command/server.rb +2 -2
  17. data/lib/epuber/command.rb +17 -20
  18. data/lib/epuber/compiler/compilation_context.rb +10 -8
  19. data/lib/epuber/compiler/file_database.rb +0 -2
  20. data/lib/epuber/compiler/file_finders/abstract.rb +33 -23
  21. data/lib/epuber/compiler/file_finders/imaginary.rb +40 -35
  22. data/lib/epuber/compiler/file_resolver.rb +77 -88
  23. data/lib/epuber/compiler/file_stat.rb +4 -4
  24. data/lib/epuber/compiler/file_types/abstract_file.rb +3 -4
  25. data/lib/epuber/compiler/file_types/bade_file.rb +12 -7
  26. data/lib/epuber/compiler/file_types/coffee_script_file.rb +1 -1
  27. data/lib/epuber/compiler/file_types/generated_file.rb +1 -1
  28. data/lib/epuber/compiler/file_types/image_file.rb +4 -2
  29. data/lib/epuber/compiler/file_types/nav_file.rb +0 -1
  30. data/lib/epuber/compiler/file_types/opf_file.rb +0 -1
  31. data/lib/epuber/compiler/file_types/source_file.rb +8 -3
  32. data/lib/epuber/compiler/file_types/xhtml_file.rb +67 -13
  33. data/lib/epuber/compiler/generator.rb +1 -2
  34. data/lib/epuber/compiler/meta_inf_generator.rb +1 -1
  35. data/lib/epuber/compiler/nav_generator.rb +5 -6
  36. data/lib/epuber/compiler/opf_generator.rb +22 -23
  37. data/lib/epuber/compiler/problem.rb +12 -21
  38. data/lib/epuber/compiler/xhtml_processor.rb +61 -31
  39. data/lib/epuber/compiler.rb +66 -19
  40. data/lib/epuber/config.rb +13 -7
  41. data/lib/epuber/dsl/attribute.rb +16 -17
  42. data/lib/epuber/dsl/attribute_support.rb +3 -3
  43. data/lib/epuber/dsl/object.rb +17 -15
  44. data/lib/epuber/dsl/tree_object.rb +2 -3
  45. data/lib/epuber/epubcheck.rb +15 -0
  46. data/lib/epuber/helper.rb +0 -1
  47. data/lib/epuber/lockfile.rb +7 -9
  48. data/lib/epuber/plugin.rb +1 -2
  49. data/lib/epuber/ruby_extensions/match_data.rb +1 -1
  50. data/lib/epuber/ruby_extensions/thread.rb +1 -0
  51. data/lib/epuber/server/base.styl +0 -1
  52. data/lib/epuber/server/basic.styl +1 -30
  53. data/lib/epuber/server/handlers.rb +1 -1
  54. data/lib/epuber/server.rb +67 -66
  55. data/lib/epuber/third_party/bower.rb +5 -5
  56. data/lib/epuber/transformer/text_transformer.rb +4 -2
  57. data/lib/epuber/transformer.rb +2 -2
  58. data/lib/epuber/user_interface.rb +49 -38
  59. data/lib/epuber/vendor/hash_binding.rb +9 -2
  60. data/lib/epuber/vendor/ruby_templater.rb +1 -5
  61. data/lib/epuber/vendor/version.rb +10 -10
  62. data/lib/epuber/version.rb +1 -1
  63. metadata +67 -69
  64. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Bold.ttf +0 -0
  65. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-BoldItalic.ttf +0 -0
  66. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Italic.ttf +0 -0
  67. data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Regular.ttf +0 -0
@@ -11,20 +11,19 @@ require_relative '../book/toc_item'
11
11
  module Epuber
12
12
  class Compiler
13
13
  class OPFGenerator < Generator
14
-
15
14
  EPUB2_NAMESPACES = {
16
- 'xmlns' => 'http://www.idpf.org/2007/opf',
17
- 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
15
+ 'xmlns' => 'http://www.idpf.org/2007/opf',
16
+ 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/',
18
17
  'xmlns:opf' => 'http://www.idpf.org/2007/opf',
19
18
  }.freeze
20
19
 
21
20
  EPUB3_NAMESPACES = {
22
- 'prefix' => 'rendition: http://www.idpf.org/vocab/rendition/',
21
+ 'prefix' => 'rendition: http://www.idpf.org/vocab/rendition/',
23
22
  'xmlns:epub' => 'http://www.idpf.org/2007/ops',
24
23
  }.freeze
25
24
 
26
25
  IBOOKS_NAMESPACES = {
27
- 'prefix' => 'rendition: http://www.idpf.org/vocab/rendition/# ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/',
26
+ 'prefix' => 'rendition: http://www.idpf.org/vocab/rendition/# ibooks: http://vocabulary.itunes.apple.com/rdf/ibooks/vocabulary-extensions-1.0/',
28
27
  'xmlns:ibooks' => 'http://apple.com/ibooks/html-extensions',
29
28
  }.freeze
30
29
 
@@ -32,32 +31,32 @@ module Epuber
32
31
  LANDMARKS_MAP = {
33
32
 
34
33
  # my favorite
35
- landmark_cover: 'cover',
34
+ landmark_cover: 'cover',
36
35
  landmark_start_page: 'text',
37
- landmark_copyright: 'copyright-page',
38
- landmark_toc: 'toc',
36
+ landmark_copyright: 'copyright-page',
37
+ landmark_toc: 'toc',
39
38
 
40
39
  # others
41
- landmark_title: 'title-page',
42
- landmark_index: 'index',
43
- landmark_glossary: 'glossary',
44
- landmark_acknowledgements: 'acknowledgements',
45
- landmark_bibliography: 'bibliography',
46
- landmark_colophon: 'colophon',
47
- landmark_dedication: 'dedication',
48
- landmark_epigraph: 'epigraph',
49
- landmark_foreword: 'foreword',
40
+ landmark_title: 'title-page',
41
+ landmark_index: 'index',
42
+ landmark_glossary: 'glossary',
43
+ landmark_acknowledgements: 'acknowledgements',
44
+ landmark_bibliography: 'bibliography',
45
+ landmark_colophon: 'colophon',
46
+ landmark_dedication: 'dedication',
47
+ landmark_epigraph: 'epigraph',
48
+ landmark_foreword: 'foreword',
50
49
  landmark_list_of_illustrations: 'loi',
51
- landmark_list_of_tables: 'lot',
52
- landmark_notes: 'notes',
53
- landmark_preface: 'preface',
50
+ landmark_list_of_tables: 'lot',
51
+ landmark_notes: 'notes',
52
+ landmark_preface: 'preface',
54
53
 
55
54
  }.freeze
56
55
 
57
56
  PROPERTIES_MAP = {
58
57
  cover_image: 'cover-image',
59
- navigation: 'nav',
60
- scripted: 'scripted',
58
+ navigation: 'nav',
59
+ scripted: 'scripted',
61
60
  remote_resources: 'remote-resources',
62
61
  mathml: 'mathml',
63
62
  }.freeze
@@ -164,7 +163,7 @@ module Epuber
164
163
  attrs['media-type'] = mime_type_for(file)
165
164
 
166
165
  properties = file.properties
167
- if properties.length > 0 && @target.epub_version >= 3
166
+ if properties.length.positive? && @target.epub_version >= 3
168
167
  pretty_properties = properties.to_a.map { |property| PROPERTIES_MAP[property] }.join(' ')
169
168
  attrs['properties'] = pretty_properties
170
169
  end
@@ -4,9 +4,7 @@ module Epuber
4
4
  class Compiler
5
5
  class Problem
6
6
  class Location
7
- attr_reader :line
8
- attr_reader :column
9
- attr_reader :length
7
+ attr_reader :line, :column, :length
10
8
 
11
9
  def initialize(line, column, length = nil)
12
10
  @line = line
@@ -15,20 +13,14 @@ module Epuber
15
13
  end
16
14
  end
17
15
 
18
- attr_reader :level
19
- attr_reader :message
20
- attr_reader :source
21
- attr_reader :location
22
- attr_reader :file_path
16
+ attr_reader :level, :message, :source, :location, :file_path
23
17
 
24
18
  def initialize(level, message, source, location: nil, line: nil, column: nil, length: nil, file_path: nil)
25
19
  @level = level
26
20
  @message = message
27
21
  @source = source
28
22
  @location = location
29
- if @location.nil? && line && column
30
- @location = Location.new(line, column, length)
31
- end
23
+ @location = Location.new(line, column, length) if @location.nil? && line && column
32
24
 
33
25
  @file_path = file_path
34
26
  end
@@ -40,7 +32,7 @@ module Epuber
40
32
  # @return [String]
41
33
  #
42
34
  def self.caret_symbol(indent)
43
- ' ' * indent + '^'
35
+ "#{' ' * indent}^"
44
36
  end
45
37
 
46
38
  # Formats caret symbols for indent and length
@@ -53,7 +45,7 @@ module Epuber
53
45
  def self.caret_symbols(indent, length)
54
46
  start_sign = caret_symbol(indent)
55
47
  end_sign = if length > 1
56
- caret_symbol(length-2)
48
+ caret_symbol(length - 2)
57
49
  else
58
50
  ''
59
51
  end
@@ -74,10 +66,11 @@ module Epuber
74
66
  lines = text.split("\n")
75
67
 
76
68
  line = lines[line_index] || ''
77
- matched_text = line[column_index ... column_index + location.length] || ''
69
+ matched_text = line[column_index...column_index + location.length] || ''
78
70
 
79
- pre = (lines[0 ... line_index] + [line[0 ... column_index]]).join("\n")
80
- post = ([line[column_index + location.length .. line.length]] + (lines[location.line .. lines.count] || [])).join("\n")
71
+ pre = (lines[0...line_index] + [line[0...column_index]]).join("\n")
72
+ post = ([line[column_index + location.length..line.length]] + (lines[location.line..lines.count] || []))
73
+ .join("\n")
81
74
 
82
75
  [pre, matched_text, post]
83
76
  end
@@ -89,9 +82,7 @@ module Epuber
89
82
  post_line = post.split("\n").first || ''
90
83
 
91
84
  pre = match_pre_line = pre_line
92
- if remove_tabs(match_pre_line).length > 100
93
- pre = "#{match_pre_line.first(20)}...#{match_pre_line.last(30)}"
94
- end
85
+ pre = "#{match_pre_line.first(20)}...#{match_pre_line.last(30)}" if remove_tabs(match_pre_line).length > 100
95
86
 
96
87
  pre = remove_tabs(pre)
97
88
 
@@ -114,8 +105,8 @@ module Epuber
114
105
 
115
106
  [
116
107
  "#{@file_path}:#{line} column: #{column} --- #{@message}",
117
- ' ' + pre + colored_match_text + post,
118
- ' ' + pointers,
108
+ " #{pre}#{colored_match_text}#{post}",
109
+ " #{pointers}",
119
110
  ].join("\n")
120
111
  end
121
112
  end
@@ -13,7 +13,8 @@ module Epuber
13
13
 
14
14
  # Method for parsing incomplete XML, supports multiple root elements
15
15
  #
16
- # @warning Because of nature of XML, when input string don't contain root element, it will create own called `body`, since it will be used in next steps.
16
+ # @warning Because of nature of XML, when input string don't contain root element, it will create own called
17
+ # `body`, since it will be used in next steps.
17
18
  #
18
19
  # @param [String] text input XHTML text
19
20
  #
@@ -23,7 +24,8 @@ module Epuber
23
24
  text = text.dup
24
25
 
25
26
  if /\A[\n\r ]+(<\?xml)/ =~ text
26
- UI.warning('XML header must be at the beginning of document', location: UI::Location.new(file_path, 1))
27
+ UI.warning('XML header must be at the beginning of document',
28
+ location: UI::Location.new(path: file_path, lineno: 1))
27
29
 
28
30
  text = text.lstrip
29
31
  end
@@ -37,16 +39,14 @@ module Epuber
37
39
 
38
40
  doctypes = []
39
41
  while /(\n|\?>|\A)?(<!DOCTYPE [^>]*>\n*)/ =~ text
40
- doctypes << $2.strip
42
+ doctypes << ::Regexp.last_match(2).strip
41
43
 
42
44
  match = Regexp.last_match
43
45
  text[match.begin(2)...match.end(2)] = ''
44
46
  end
45
47
 
46
48
  before = ([xml_header] + doctypes).compact.join("\n")
47
- unless before.empty?
48
- before = before + "\n"
49
- end
49
+ before += "\n" unless before.empty?
50
50
 
51
51
  parse_options = Nokogiri::XML::ParseOptions::DEFAULT_XML |
52
52
  Nokogiri::XML::ParseOptions::NOERROR | # to silence any errors or warnings printing into console
@@ -82,11 +82,10 @@ module Epuber
82
82
  end
83
83
 
84
84
  def self.xml_document_from_string(text, file_path = nil)
85
- xml, errros = self.xml_doc_from_str_with_errors(text, file_path)
85
+ xml, = xml_doc_from_str_with_errors(text, file_path)
86
86
  xml
87
87
  end
88
88
 
89
-
90
89
  # Method to add all missing items in XML root
91
90
  #
92
91
  # Required items:
@@ -137,10 +136,10 @@ module Epuber
137
136
  end
138
137
 
139
138
  # https://github.com/IDPF/epubcheck/issues/631
140
- if epub_version < 3.0
141
- xhtml_doc.internal_subset.remove unless xhtml_doc.internal_subset.nil?
142
- xhtml_doc.create_internal_subset('html', "-//W3C//DTD XHTML 1.1//EN", "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd")
143
- end
139
+ return unless epub_version < 3.0
140
+
141
+ xhtml_doc.internal_subset&.remove
142
+ xhtml_doc.create_internal_subset('html', '-//W3C//DTD XHTML 1.1//EN', 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd')
144
143
  end
145
144
 
146
145
  # Method for adding style sheets with links, method will not add duplicate items
@@ -151,7 +150,7 @@ module Epuber
151
150
  # @return nil
152
151
  #
153
152
  def self.add_styles(xhtml_doc, styles)
154
- head = xhtml_doc.at_css('html > head')
153
+ head = xhtml_doc.at_css('html > head')
155
154
  old_links = head.css('link[rel="stylesheet"]').map { |node| node['href'] }
156
155
 
157
156
  links_to_add = styles - old_links
@@ -169,7 +168,7 @@ module Epuber
169
168
  # @return nil
170
169
  #
171
170
  def self.add_scripts(xhtml_doc, scripts)
172
- head = xhtml_doc.at_css('html > head')
171
+ head = xhtml_doc.at_css('html > head')
173
172
  old_links = head.css('script').map { |node| node['src'] }
174
173
 
175
174
  links_to_add = scripts - old_links
@@ -195,7 +194,8 @@ module Epuber
195
194
  # Method which will resolve path to file from pattern
196
195
  #
197
196
  # @param [String] path pattern or path of the file
198
- # @param [Symbol | Array<Symbol>] groups groups of the searching file, could be for example :image when searching for file from tag <img>
197
+ # @param [Symbol | Array<Symbol>] groups groups of the searching file, could be for example :image when searching
198
+ # for file from tag <img>
199
199
  # @param [String] file_path path to file from which is searching for other file
200
200
  # @param [Epuber::Compiler::FileFinder] file_finder finder for searching for files
201
201
  #
@@ -233,7 +233,8 @@ module Epuber
233
233
  # @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
234
234
  # @param [String] tag_name CSS selector for tag
235
235
  # @param [String] attribute_name name of attribute
236
- # @param [Symbol | Array<Symbol>] groups groups of the searching file, could be for example :image when searching for file from tag <img>
236
+ # @param [Symbol | Array<Symbol>] groups groups of the searching file, could be for example :image when searching
237
+ # for file from tag <img>
237
238
  # @param [String] file_path path to file from which is searching for other file
238
239
  # @param [Epuber::Compiler::FileFinder] file_finder finder for searching for files
239
240
  #
@@ -243,22 +244,21 @@ module Epuber
243
244
  founded_links = []
244
245
 
245
246
  xhtml_doc.css("#{tag_name}[#{attribute_name}]").each do |node|
246
- begin
247
- src = node[attribute_name]
248
- # @type [String] src
247
+ src = node[attribute_name]
248
+ # @type [String] src
249
249
 
250
- next if src.nil?
250
+ next if src.nil?
251
+ next if src.start_with?('$')
251
252
 
252
- target_file = resolved_link_to_file(src, groups, file_path, file_finder)
253
- founded_links << target_file
253
+ target_file = resolved_link_to_file(src, groups, file_path, file_finder)
254
+ founded_links << target_file
254
255
 
255
- node[attribute_name] = target_file.to_s
256
- rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError => e
257
- UI.warning(e.to_s, location: node)
256
+ node[attribute_name] = target_file.to_s
257
+ rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError => e
258
+ UI.warning(e.to_s, location: node)
258
259
 
259
- # skip not found files
260
- next
261
- end
260
+ # skip not found files
261
+ next
262
262
  end
263
263
 
264
264
  founded_links
@@ -349,7 +349,6 @@ module Epuber
349
349
 
350
350
  begin
351
351
  new_path = file_resolver.dest_finder.find_file(path, groups: resource_group, context_path: dirname)
352
-
353
352
  rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError
354
353
  begin
355
354
  new_path = resolved_link_to_file(path, resource_group, dirname, file_resolver.source_finder).to_s
@@ -361,8 +360,7 @@ module Epuber
361
360
  file.path_type = :manifest
362
361
  file_resolver.add_file(file)
363
362
 
364
- new_path = FileResolver::renamed_file_with_path(new_path)
365
-
363
+ new_path = FileResolver.renamed_file_with_path(new_path)
366
364
  rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError => e
367
365
  UI.warning(e.to_s, location: img)
368
366
 
@@ -373,6 +371,38 @@ module Epuber
373
371
  img[attribute_name] = new_path
374
372
  end
375
373
  end
374
+
375
+ # @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
376
+ # @return [Array<Nokogiri::XML::Node>] list of nodes with global ids
377
+ #
378
+ def self.find_global_ids_nodes(xhtml_doc)
379
+ xhtml_doc
380
+ .css('[id^="$"]')
381
+ end
382
+
383
+ # @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
384
+ # @return [Array<string>] list of global ids (without dollar signs)
385
+ #
386
+ def self.find_global_ids(xhtml_doc)
387
+ find_global_ids_nodes(xhtml_doc)
388
+ .map { |node| node['id'][1..-1] }
389
+ end
390
+
391
+ # @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
392
+ # @return [Array<Nokogiri::XML::Node>] list of nodes with global links
393
+ #
394
+ def self.find_global_links_nodes(xhtml_doc)
395
+ xhtml_doc
396
+ .css('[href^="$"]')
397
+ end
398
+
399
+ # @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
400
+ # @return [Array<string>] list of global ids (without dollar signs)
401
+ #
402
+ def self.find_global_links(xhtml_doc)
403
+ find_global_links_nodes(xhtml_doc)
404
+ .map { |node| node['href'][1..-1] }
405
+ end
376
406
  end
377
407
  end
378
408
  end
@@ -71,22 +71,30 @@ module Epuber
71
71
 
72
72
  FileUtils.mkdir_p(build_folder)
73
73
 
74
- puts " handling target #{@target.name.inspect} in build dir `#{Config.instance.pretty_path_from_project(build_folder)}`"
74
+ UI.puts " #{<<~MSG}"
75
+ building target #{@target.name.inspect} (build dir: #{Config.instance.pretty_path_from_project(build_folder)})
76
+ MSG
75
77
 
76
- file_resolver.add_file(FileTypes::SourceFile.new(Config.instance.pretty_path_from_project(@book.file_path).to_s))
78
+ file_resolver.add_file(FileTypes::SourceFile.new(Config.instance.pretty_path_from_project(@book.file_path)))
77
79
  compilation_context.plugins
78
80
 
81
+ # validate bookspec
82
+ compilation_context.perform_plugin_things(Checker, :bookspec) do |checker|
83
+ checker.call(@book, compilation_context)
84
+ end
85
+
79
86
  parse_toc_item(@target.root_toc)
80
87
  parse_target_file_requests
81
88
 
82
89
  process_all_target_files
83
90
  generate_other_files
91
+ process_global_ids
84
92
 
85
93
  # build folder cleanup
86
94
  remove_unnecessary_files
87
95
  remove_empty_folders
88
96
 
89
- source_paths = file_resolver.files.select { |a| a.is_a?(FileTypes::SourceFile) }.map { |a| a.source_path }
97
+ source_paths = file_resolver.files.select { |a| a.is_a?(FileTypes::SourceFile) }.map(&:source_path)
90
98
  compilation_context.source_file_database.cleanup(source_paths)
91
99
  compilation_context.source_file_database.update_all_metadata
92
100
  compilation_context.source_file_database.save_to_file
@@ -118,7 +126,7 @@ module Epuber
118
126
  old_paths = zip_file.instance_eval { @entry_set.entries.map(&:name) }
119
127
  diff = old_paths - new_paths
120
128
  diff.each do |file_to_remove|
121
- puts "DEBUG: removing file from result EPUB: #{file_to_remove}" if compilation_context.verbose?
129
+ UI.puts "DEBUG: removing file from result EPUB: #{file_to_remove}" if compilation_context.verbose?
122
130
  zip_file.remove(file_to_remove)
123
131
  end
124
132
  end
@@ -147,7 +155,7 @@ module Epuber
147
155
  epub_name += @book.build_version.to_s unless @book.build_version.nil?
148
156
  epub_name += "-#{@target.name}" if @target != @book.default_target
149
157
  epub_name += "-#{configuration_suffix}" unless configuration_suffix.nil?
150
- epub_name + '.epub'
158
+ "#{epub_name}.epub"
151
159
  end
152
160
 
153
161
 
@@ -158,21 +166,25 @@ module Epuber
158
166
  def remove_empty_folders
159
167
  Dir.chdir(@file_resolver.destination_path) do
160
168
  Dir.glob('**/*')
161
- .select { |d| File.directory?(d) }
162
- .select { |d| (Dir.entries(d) - %w(. ..)).empty? }
163
- .each do |d|
164
- puts "DEBUG: removing empty folder `#{d}`" if compilation_context.verbose?
165
- Dir.rmdir(d)
166
- end
169
+ .select { |d| File.directory?(d) }
170
+ .select { |d| (Dir.entries(d) - %w[. ..]).empty? }
171
+ .each do |d|
172
+ UI.puts "DEBUG: removing empty folder `#{d}`" if compilation_context.verbose?
173
+ Dir.rmdir(d)
174
+ end
167
175
  end
168
176
  end
169
177
 
170
178
  # @return nil
171
179
  #
172
180
  def remove_unnecessary_files
173
- unnecessary_paths = @file_resolver.unneeded_files_in_destination.map { |path| File.join(@file_resolver.destination_path, path) }
181
+ unnecessary_paths = @file_resolver.unneeded_files_in_destination.map do |path|
182
+ File.join(@file_resolver.destination_path, path)
183
+ end
174
184
  unnecessary_paths.each do |path|
175
- puts "DEBUG: removing unnecessary file: `#{Config.instance.pretty_path_from_project(path)}`" if compilation_context.verbose?
185
+ if compilation_context.verbose?
186
+ UI.puts "DEBUG: removing unnecessary file: `#{Config.instance.pretty_path_from_project(path)}`"
187
+ end
176
188
 
177
189
  File.delete(path)
178
190
  end
@@ -201,11 +213,11 @@ module Epuber
201
213
  @file_resolver.add_file(container_xml)
202
214
  process_file(container_xml)
203
215
 
204
- if @target.epub_version >= 2.0 && @target.epub_version < 3.0
205
- ibooks_options = FileTypes::IBooksDisplayOptionsFile.new
206
- @file_resolver.add_file(ibooks_options)
207
- process_file(ibooks_options)
208
- end
216
+ return unless @target.epub_version >= 2.0 && @target.epub_version < 3.0
217
+
218
+ ibooks_options = FileTypes::IBooksDisplayOptionsFile.new
219
+ @file_resolver.add_file(ibooks_options)
220
+ process_file(ibooks_options)
209
221
  end
210
222
 
211
223
  # @return nil
@@ -239,6 +251,7 @@ module Epuber
239
251
  # add missing files to file_resolver
240
252
  paths.each do |path|
241
253
  next if file_resolver.file_with_source_path(path)
254
+
242
255
  file_resolver.add_file(FileTypes::SourceFile.new(path))
243
256
  end
244
257
 
@@ -247,7 +260,7 @@ module Epuber
247
260
 
248
261
  # add all activated plugin files
249
262
  paths += compilation_context.plugins.map do |plugin|
250
- plugin.files.map { |p_file| p_file.source_path }
263
+ plugin.files.map(&:source_path)
251
264
  end.flatten
252
265
 
253
266
  # add dependencies to databases
@@ -286,6 +299,40 @@ module Epuber
286
299
  end
287
300
  end
288
301
 
302
+ def process_global_ids
303
+ xhtml_files = @file_resolver.files.select { |file| file.is_a?(FileTypes::XHTMLFile) }
304
+ global_ids = validate_global_ids(xhtml_files)
305
+
306
+ xhtml_files.each do |file|
307
+ file.process_global_ids(compilation_context, global_ids)
308
+ end
309
+ end
310
+
311
+ # Validates duplicity of global ids in all files + returns map of global ids to files
312
+ #
313
+ # @param xhtml_files [Array<FileTypes::XHTMLFile>]
314
+ # @return [Hash<String, FileTypes::XHTMLFile>]
315
+ #
316
+ def validate_global_ids(xhtml_files)
317
+ map = {}
318
+ xhtml_files.each do |file|
319
+ file.global_ids.each do |id|
320
+ if map.key?(id)
321
+ message = "Duplicate global id `#{id}` in file `#{file.source_path}`."
322
+ if compilation_context.release_build?
323
+ UI.error!(message)
324
+ else
325
+ UI.warning("#{message} Will fail during release build.")
326
+ end
327
+ end
328
+
329
+ map[id] = file
330
+ end
331
+ end
332
+
333
+ map
334
+ end
335
+
289
336
  # @param cmd [String]
290
337
  #
291
338
  # @return [void]
data/lib/epuber/config.rb CHANGED
@@ -60,9 +60,11 @@ module Epuber
60
60
  #
61
61
  # @return [Epuber::Book]
62
62
  #
63
- def bookspec=(bookspec)
64
- @bookspec = bookspec
65
- end
63
+ attr_writer :bookspec
64
+
65
+ # @return [Boolean]
66
+ #
67
+ attr_accessor :release_build
66
68
 
67
69
  # @return [Epuber::Lockfile]
68
70
  #
@@ -122,12 +124,16 @@ module Epuber
122
124
 
123
125
  def warn_for_outdated_versions!
124
126
  if bookspec_lockfile.epuber_version > Epuber::VERSION
125
- UI.warning('Warning: the running version of Epuber is older than the version that created the lockfile. We suggest you upgrade to the latest version of Epuber by running `gem install epuber`.')
127
+ UI.warning(<<~MSG.rstrip)
128
+ Warning: the running version of Epuber is older than the version that created the lockfile. We suggest you upgrade to the latest version of Epuber by running `gem install epuber`.
129
+ MSG
126
130
  end
127
131
 
128
- if bookspec_lockfile.bade_version && bookspec_lockfile.bade_version > Bade::VERSION
129
- UI.warning('Warning: the running version of Bade is older than the version that created the lockfile. We suggest you upgrade to the latest version of Bade by running `gem install bade`.')
130
- end
132
+ return unless bookspec_lockfile.bade_version && bookspec_lockfile.bade_version > Bade::VERSION
133
+
134
+ UI.warning(<<~MSG.rstrip)
135
+ Warning: the running version of Bade is older than the version that created the lockfile. We suggest you upgrade to the latest version of Bade by running `gem install bade`.
136
+ MSG
131
137
  end
132
138
 
133
139
  def same_version_as_last_run?
@@ -109,25 +109,25 @@ module Epuber
109
109
  # is not specified.
110
110
  #
111
111
  attr_reader :required
112
- alias_method :required?, :required
112
+ alias required? required
113
113
 
114
114
  # @return [Bool] whether the attribute should be specified only on the root specification.
115
115
  #
116
116
  attr_reader :root_only
117
- alias_method :root_only?, :root_only
117
+ alias root_only? root_only
118
118
 
119
119
 
120
120
  # @return [Bool] whether there should be a singular alias for the attribute writer.
121
121
  #
122
122
  attr_reader :singularize
123
- alias_method :singularize?, :singularize
123
+ alias singularize? singularize
124
124
 
125
125
  # @return [Bool] whether the attribute describes file patterns.
126
126
  #
127
127
  # @note This is mostly used by the linter.
128
128
  #
129
129
  attr_reader :file_patterns
130
- alias_method :file_patterns?, :file_patterns
130
+ alias file_patterns? file_patterns
131
131
 
132
132
  # @return [Bool] defines whether the attribute reader should join the values with the parent.
133
133
  #
@@ -184,25 +184,24 @@ module Epuber
184
184
  raise StandardError, "Can't set `#{name}` attribute for subspecs (in `#{spec.name}`)."
185
185
  end
186
186
 
187
- if keys
188
- value.keys.each do |key|
189
- unless allowed_keys.include?(key)
190
- raise StandardError, "Unknown key `#{key}` for #{self}. Allowed keys: `#{allowed_keys.inspect}`"
191
- end
192
- end
193
- end
187
+ return unless keys
194
188
 
195
189
  # @return [Array] the flattened list of the allowed keys for the hash of a given specification.
196
190
  #
197
- def allowed_keys
191
+ allowed_keys = lambda do
198
192
  if keys.is_a?(Hash)
199
193
  keys.keys.concat(keys.values.flatten.compact)
200
194
  else
201
195
  keys
202
196
  end
203
197
  end
204
- end
205
198
 
199
+ value.each_key do |key|
200
+ unless allowed_keys.include?(key)
201
+ raise StandardError, "Unknown key `#{key}` for #{self}. Allowed keys: `#{allowed_keys.inspect}`"
202
+ end
203
+ end
204
+ end
206
205
 
207
206
  #---------------------------------------------------------------------#
208
207
 
@@ -221,15 +220,14 @@ module Epuber
221
220
  validate_type(value)
222
221
  rescue StandardError
223
222
  raise if @auto_convert.nil?
223
+
224
224
  dest_class = @auto_convert[value.class]
225
225
 
226
226
  if dest_class.nil?
227
227
  array_keys = @auto_convert.select { |k, _v| k.is_a?(Array) }
228
228
  array_keys_with_type = array_keys.select { |k, _v| k.any? { |klass| value.class <= klass } }
229
229
 
230
- if array_keys_with_type.count > 0
231
- dest_class = array_keys_with_type.values.first
232
- end
230
+ dest_class = array_keys_with_type.values.first if array_keys_with_type.count.positive?
233
231
  end
234
232
 
235
233
  if dest_class.respond_to?(:call)
@@ -241,7 +239,8 @@ module Epuber
241
239
  elsif dest_class.respond_to?(:new)
242
240
  return dest_class.new(value)
243
241
  else
244
- raise StandardError, "Object/class #{dest_class} doesn't support any convert method (#call, .parse or implicit .new)"
242
+ raise StandardError,
243
+ "Object/class #{dest_class} doesn't support any convert method (#call, .parse or implicit .new)"
245
244
  end
246
245
  end
247
246
 
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Epuber
5
4
  module DSL
6
5
  module AttributeSupport
@@ -86,8 +85,9 @@ module Epuber
86
85
  else
87
86
  begin
88
87
  @attributes_values[key] = attr.converted_value(value)
89
- rescue Exception => e
90
- UI.warning("Invalid value `#{value}` for attribute `#{name}`, original error `#{e}`", location: caller_locations[1])
88
+ rescue StandardError => e
89
+ UI.warning("Invalid value `#{value}` for attribute `#{name}`, original error `#{e}`",
90
+ location: caller_locations[1])
91
91
  end
92
92
  end
93
93
  end