epuber 0.7.4 → 0.9.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.
- checksums.yaml +4 -4
- data/Gemfile +10 -2
- data/LICENSE.txt +1 -1
- data/README.md +4 -3
- data/epuber.gemspec +11 -16
- data/lib/epuber/book/contributor.rb +10 -7
- data/lib/epuber/book/file_request.rb +3 -3
- data/lib/epuber/book/target.rb +30 -33
- data/lib/epuber/book/toc_item.rb +2 -4
- data/lib/epuber/book.rb +21 -21
- data/lib/epuber/checker/bookspec_checker.rb +26 -0
- data/lib/epuber/checker/text_checker.rb +16 -7
- data/lib/epuber/checker.rb +16 -2
- data/lib/epuber/checker_transformer_base.rb +3 -6
- data/lib/epuber/command/build.rb +40 -25
- data/lib/epuber/command/from_file.rb +39 -0
- data/lib/epuber/command/init.rb +34 -32
- data/lib/epuber/command/server.rb +3 -3
- data/lib/epuber/command.rb +18 -20
- data/lib/epuber/compiler/compilation_context.rb +10 -8
- data/lib/epuber/compiler/file_database.rb +2 -4
- data/lib/epuber/compiler/file_finders/abstract.rb +36 -26
- data/lib/epuber/compiler/file_finders/imaginary.rb +40 -35
- data/lib/epuber/compiler/file_resolver.rb +79 -89
- data/lib/epuber/compiler/file_stat.rb +4 -4
- data/lib/epuber/compiler/file_types/abstract_file.rb +4 -7
- data/lib/epuber/compiler/file_types/bade_file.rb +20 -15
- data/lib/epuber/compiler/file_types/coffee_script_file.rb +1 -1
- data/lib/epuber/compiler/file_types/css_file.rb +103 -0
- data/lib/epuber/compiler/file_types/generated_file.rb +1 -1
- data/lib/epuber/compiler/file_types/image_file.rb +4 -2
- data/lib/epuber/compiler/file_types/nav_file.rb +0 -1
- data/lib/epuber/compiler/file_types/opf_file.rb +0 -1
- data/lib/epuber/compiler/file_types/source_file.rb +8 -3
- data/lib/epuber/compiler/file_types/stylus_file.rb +4 -3
- data/lib/epuber/compiler/file_types/xhtml_file.rb +67 -13
- data/lib/epuber/compiler/generator.rb +1 -2
- data/lib/epuber/compiler/meta_inf_generator.rb +1 -1
- data/lib/epuber/compiler/nav_generator.rb +10 -11
- data/lib/epuber/compiler/opf_generator.rb +26 -27
- data/lib/epuber/compiler/problem.rb +12 -21
- data/lib/epuber/compiler/xhtml_processor.rb +63 -32
- data/lib/epuber/compiler.rb +77 -25
- data/lib/epuber/config.rb +16 -10
- data/lib/epuber/dsl/attribute.rb +17 -18
- data/lib/epuber/dsl/attribute_support.rb +7 -7
- data/lib/epuber/dsl/object.rb +19 -17
- data/lib/epuber/dsl/tree_object.rb +2 -3
- data/lib/epuber/epubcheck.rb +15 -0
- data/lib/epuber/from_file/bookspec_generator.rb +371 -0
- data/lib/epuber/from_file/encryption_handler.rb +146 -0
- data/lib/epuber/from_file/from_file_executor.rb +140 -0
- data/lib/epuber/from_file/nav_file.rb +163 -0
- data/lib/epuber/from_file/opf_file.rb +219 -0
- data/lib/epuber/helper.rb +0 -1
- data/lib/epuber/lockfile.rb +7 -9
- data/lib/epuber/plugin.rb +2 -3
- data/lib/epuber/ruby_extensions/match_data.rb +1 -1
- data/lib/epuber/ruby_extensions/thread.rb +1 -0
- data/lib/epuber/server/base.styl +0 -1
- data/lib/epuber/server/basic.styl +1 -30
- data/lib/epuber/server/handlers.rb +1 -1
- data/lib/epuber/server.rb +81 -80
- data/lib/epuber/third_party/bower.rb +5 -5
- data/lib/epuber/transformer/book_transformer.rb +108 -0
- data/lib/epuber/transformer/text_transformer.rb +4 -2
- data/lib/epuber/transformer.rb +4 -2
- data/lib/epuber/user_interface.rb +49 -38
- data/lib/epuber/vendor/hash_binding.rb +9 -2
- data/lib/epuber/vendor/ruby_templater.rb +4 -8
- data/lib/epuber/vendor/version.rb +12 -12
- data/lib/epuber/version.rb +1 -1
- metadata +79 -100
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Bold.ttf +0 -0
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-BoldItalic.ttf +0 -0
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Italic.ttf +0 -0
- data/lib/epuber/server/fonts/AvenirNext/AvenirNext-Regular.ttf +0 -0
@@ -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
|
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',
|
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,20 +39,19 @@ module Epuber
|
|
37
39
|
|
38
40
|
doctypes = []
|
39
41
|
while /(\n|\?>|\A)?(<!DOCTYPE [^>]*>\n*)/ =~ text
|
40
|
-
doctypes <<
|
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
|
53
|
-
Nokogiri::XML::ParseOptions::NOWARNING
|
53
|
+
Nokogiri::XML::ParseOptions::NOWARNING |
|
54
|
+
Nokogiri::XML::ParseOptions::NOENT
|
54
55
|
|
55
56
|
doc = Nokogiri::XML("#{before}<root>#{text}</root>", file_path, nil, parse_options)
|
56
57
|
text_for_errors = before + text
|
@@ -82,11 +83,10 @@ module Epuber
|
|
82
83
|
end
|
83
84
|
|
84
85
|
def self.xml_document_from_string(text, file_path = nil)
|
85
|
-
xml,
|
86
|
+
xml, = xml_doc_from_str_with_errors(text, file_path)
|
86
87
|
xml
|
87
88
|
end
|
88
89
|
|
89
|
-
|
90
90
|
# Method to add all missing items in XML root
|
91
91
|
#
|
92
92
|
# Required items:
|
@@ -137,10 +137,10 @@ module Epuber
|
|
137
137
|
end
|
138
138
|
|
139
139
|
# https://github.com/IDPF/epubcheck/issues/631
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
140
|
+
return unless epub_version < 3.0
|
141
|
+
|
142
|
+
xhtml_doc.internal_subset&.remove
|
143
|
+
xhtml_doc.create_internal_subset('html', '-//W3C//DTD XHTML 1.1//EN', 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd')
|
144
144
|
end
|
145
145
|
|
146
146
|
# Method for adding style sheets with links, method will not add duplicate items
|
@@ -151,7 +151,7 @@ module Epuber
|
|
151
151
|
# @return nil
|
152
152
|
#
|
153
153
|
def self.add_styles(xhtml_doc, styles)
|
154
|
-
head
|
154
|
+
head = xhtml_doc.at_css('html > head')
|
155
155
|
old_links = head.css('link[rel="stylesheet"]').map { |node| node['href'] }
|
156
156
|
|
157
157
|
links_to_add = styles - old_links
|
@@ -169,7 +169,7 @@ module Epuber
|
|
169
169
|
# @return nil
|
170
170
|
#
|
171
171
|
def self.add_scripts(xhtml_doc, scripts)
|
172
|
-
head
|
172
|
+
head = xhtml_doc.at_css('html > head')
|
173
173
|
old_links = head.css('script').map { |node| node['src'] }
|
174
174
|
|
175
175
|
links_to_add = scripts - old_links
|
@@ -195,7 +195,8 @@ module Epuber
|
|
195
195
|
# Method which will resolve path to file from pattern
|
196
196
|
#
|
197
197
|
# @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
|
198
|
+
# @param [Symbol | Array<Symbol>] groups groups of the searching file, could be for example :image when searching
|
199
|
+
# for file from tag <img>
|
199
200
|
# @param [String] file_path path to file from which is searching for other file
|
200
201
|
# @param [Epuber::Compiler::FileFinder] file_finder finder for searching for files
|
201
202
|
#
|
@@ -233,7 +234,8 @@ module Epuber
|
|
233
234
|
# @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
|
234
235
|
# @param [String] tag_name CSS selector for tag
|
235
236
|
# @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
|
237
|
+
# @param [Symbol | Array<Symbol>] groups groups of the searching file, could be for example :image when searching
|
238
|
+
# for file from tag <img>
|
237
239
|
# @param [String] file_path path to file from which is searching for other file
|
238
240
|
# @param [Epuber::Compiler::FileFinder] file_finder finder for searching for files
|
239
241
|
#
|
@@ -243,22 +245,21 @@ module Epuber
|
|
243
245
|
founded_links = []
|
244
246
|
|
245
247
|
xhtml_doc.css("#{tag_name}[#{attribute_name}]").each do |node|
|
246
|
-
|
247
|
-
|
248
|
-
# @type [String] src
|
248
|
+
src = node[attribute_name]
|
249
|
+
# @type [String] src
|
249
250
|
|
250
|
-
|
251
|
+
next if src.nil?
|
252
|
+
next if src.start_with?('$')
|
251
253
|
|
252
|
-
|
253
|
-
|
254
|
+
target_file = resolved_link_to_file(src, groups, file_path, file_finder)
|
255
|
+
founded_links << target_file
|
254
256
|
|
255
|
-
|
256
|
-
|
257
|
-
|
257
|
+
node[attribute_name] = target_file.to_s
|
258
|
+
rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError => e
|
259
|
+
UI.warning(e.to_s, location: node)
|
258
260
|
|
259
|
-
|
260
|
-
|
261
|
-
end
|
261
|
+
# skip not found files
|
262
|
+
next
|
262
263
|
end
|
263
264
|
|
264
265
|
founded_links
|
@@ -349,7 +350,6 @@ module Epuber
|
|
349
350
|
|
350
351
|
begin
|
351
352
|
new_path = file_resolver.dest_finder.find_file(path, groups: resource_group, context_path: dirname)
|
352
|
-
|
353
353
|
rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError
|
354
354
|
begin
|
355
355
|
new_path = resolved_link_to_file(path, resource_group, dirname, file_resolver.source_finder).to_s
|
@@ -361,8 +361,7 @@ module Epuber
|
|
361
361
|
file.path_type = :manifest
|
362
362
|
file_resolver.add_file(file)
|
363
363
|
|
364
|
-
new_path = FileResolver
|
365
|
-
|
364
|
+
new_path = FileResolver.renamed_file_with_path(new_path)
|
366
365
|
rescue UnparseableLinkError, FileFinders::FileNotFoundError, FileFinders::MultipleFilesFoundError => e
|
367
366
|
UI.warning(e.to_s, location: img)
|
368
367
|
|
@@ -373,6 +372,38 @@ module Epuber
|
|
373
372
|
img[attribute_name] = new_path
|
374
373
|
end
|
375
374
|
end
|
375
|
+
|
376
|
+
# @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
|
377
|
+
# @return [Array<Nokogiri::XML::Node>] list of nodes with global ids
|
378
|
+
#
|
379
|
+
def self.find_global_ids_nodes(xhtml_doc)
|
380
|
+
xhtml_doc
|
381
|
+
.css('[id^="$"]')
|
382
|
+
end
|
383
|
+
|
384
|
+
# @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
|
385
|
+
# @return [Array<string>] list of global ids (without dollar signs)
|
386
|
+
#
|
387
|
+
def self.find_global_ids(xhtml_doc)
|
388
|
+
find_global_ids_nodes(xhtml_doc)
|
389
|
+
.map { |node| node['id'][1..-1] }
|
390
|
+
end
|
391
|
+
|
392
|
+
# @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
|
393
|
+
# @return [Array<Nokogiri::XML::Node>] list of nodes with global links
|
394
|
+
#
|
395
|
+
def self.find_global_links_nodes(xhtml_doc)
|
396
|
+
xhtml_doc
|
397
|
+
.css('[href^="$"]')
|
398
|
+
end
|
399
|
+
|
400
|
+
# @param [Nokogiri::XML::Document] xhtml_doc input XML document to work with
|
401
|
+
# @return [Array<string>] list of global ids (without dollar signs)
|
402
|
+
#
|
403
|
+
def self.find_global_links(xhtml_doc)
|
404
|
+
find_global_links_nodes(xhtml_doc)
|
405
|
+
.map { |node| node['href'][1..-1] }
|
406
|
+
end
|
376
407
|
end
|
377
408
|
end
|
378
409
|
end
|
data/lib/epuber/compiler.rb
CHANGED
@@ -39,8 +39,8 @@ module Epuber
|
|
39
39
|
#
|
40
40
|
attr_reader :compilation_context
|
41
41
|
|
42
|
-
# @param
|
43
|
-
# @param
|
42
|
+
# @param [Epuber::Book::Book] book
|
43
|
+
# @param [Epuber::Book::Target] target
|
44
44
|
#
|
45
45
|
def initialize(book, target)
|
46
46
|
@book = book
|
@@ -50,7 +50,7 @@ module Epuber
|
|
50
50
|
|
51
51
|
# Compile target to build folder
|
52
52
|
#
|
53
|
-
# @param
|
53
|
+
# @param [String] build_folder path to folder, where will be stored all compiled files
|
54
54
|
# @param [Bool] check should run non-release checkers
|
55
55
|
# @param [Bool] write should perform transformations of source files and write them back
|
56
56
|
# @param [Bool] release this is release build
|
@@ -71,22 +71,35 @@ module Epuber
|
|
71
71
|
|
72
72
|
FileUtils.mkdir_p(build_folder)
|
73
73
|
|
74
|
-
puts "
|
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)
|
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
|
84
91
|
|
92
|
+
compilation_context.perform_plugin_things(Transformer, :after_all_text_files) do |transformer|
|
93
|
+
transformer.call(@book, compilation_context)
|
94
|
+
end
|
95
|
+
|
96
|
+
process_global_ids
|
97
|
+
|
85
98
|
# build folder cleanup
|
86
99
|
remove_unnecessary_files
|
87
100
|
remove_empty_folders
|
88
101
|
|
89
|
-
source_paths = file_resolver.files.select { |a| a.is_a?(FileTypes::SourceFile) }.map
|
102
|
+
source_paths = file_resolver.files.select { |a| a.is_a?(FileTypes::SourceFile) }.map(&:source_path)
|
90
103
|
compilation_context.source_file_database.cleanup(source_paths)
|
91
104
|
compilation_context.source_file_database.update_all_metadata
|
92
105
|
compilation_context.source_file_database.save_to_file
|
@@ -101,7 +114,7 @@ module Epuber
|
|
101
114
|
|
102
115
|
# Archives current target files to epub
|
103
116
|
#
|
104
|
-
# @param
|
117
|
+
# @param [String] path path to created archive
|
105
118
|
#
|
106
119
|
# @return [String] path
|
107
120
|
#
|
@@ -118,7 +131,7 @@ module Epuber
|
|
118
131
|
old_paths = zip_file.instance_eval { @entry_set.entries.map(&:name) }
|
119
132
|
diff = old_paths - new_paths
|
120
133
|
diff.each do |file_to_remove|
|
121
|
-
puts "DEBUG: removing file from result EPUB: #{file_to_remove}" if compilation_context.verbose?
|
134
|
+
UI.puts "DEBUG: removing file from result EPUB: #{file_to_remove}" if compilation_context.verbose?
|
122
135
|
zip_file.remove(file_to_remove)
|
123
136
|
end
|
124
137
|
end
|
@@ -147,7 +160,7 @@ module Epuber
|
|
147
160
|
epub_name += @book.build_version.to_s unless @book.build_version.nil?
|
148
161
|
epub_name += "-#{@target.name}" if @target != @book.default_target
|
149
162
|
epub_name += "-#{configuration_suffix}" unless configuration_suffix.nil?
|
150
|
-
epub_name
|
163
|
+
"#{epub_name}.epub"
|
151
164
|
end
|
152
165
|
|
153
166
|
|
@@ -158,21 +171,25 @@ module Epuber
|
|
158
171
|
def remove_empty_folders
|
159
172
|
Dir.chdir(@file_resolver.destination_path) do
|
160
173
|
Dir.glob('**/*')
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
174
|
+
.select { |d| File.directory?(d) }
|
175
|
+
.select { |d| (Dir.entries(d) - %w[. ..]).empty? }
|
176
|
+
.each do |d|
|
177
|
+
UI.puts "DEBUG: removing empty folder `#{d}`" if compilation_context.verbose?
|
178
|
+
Dir.rmdir(d)
|
179
|
+
end
|
167
180
|
end
|
168
181
|
end
|
169
182
|
|
170
183
|
# @return nil
|
171
184
|
#
|
172
185
|
def remove_unnecessary_files
|
173
|
-
unnecessary_paths = @file_resolver.unneeded_files_in_destination.map
|
186
|
+
unnecessary_paths = @file_resolver.unneeded_files_in_destination.map do |path|
|
187
|
+
File.join(@file_resolver.destination_path, path)
|
188
|
+
end
|
174
189
|
unnecessary_paths.each do |path|
|
175
|
-
|
190
|
+
if compilation_context.verbose?
|
191
|
+
UI.puts "DEBUG: removing unnecessary file: `#{Config.instance.pretty_path_from_project(path)}`"
|
192
|
+
end
|
176
193
|
|
177
194
|
File.delete(path)
|
178
195
|
end
|
@@ -201,11 +218,11 @@ module Epuber
|
|
201
218
|
@file_resolver.add_file(container_xml)
|
202
219
|
process_file(container_xml)
|
203
220
|
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
221
|
+
return unless @target.epub_version >= 2.0 && @target.epub_version < 3.0
|
222
|
+
|
223
|
+
ibooks_options = FileTypes::IBooksDisplayOptionsFile.new
|
224
|
+
@file_resolver.add_file(ibooks_options)
|
225
|
+
process_file(ibooks_options)
|
209
226
|
end
|
210
227
|
|
211
228
|
# @return nil
|
@@ -239,6 +256,7 @@ module Epuber
|
|
239
256
|
# add missing files to file_resolver
|
240
257
|
paths.each do |path|
|
241
258
|
next if file_resolver.file_with_source_path(path)
|
259
|
+
|
242
260
|
file_resolver.add_file(FileTypes::SourceFile.new(path))
|
243
261
|
end
|
244
262
|
|
@@ -247,7 +265,7 @@ module Epuber
|
|
247
265
|
|
248
266
|
# add all activated plugin files
|
249
267
|
paths += compilation_context.plugins.map do |plugin|
|
250
|
-
plugin.files.map
|
268
|
+
plugin.files.map(&:source_path)
|
251
269
|
end.flatten
|
252
270
|
|
253
271
|
# add dependencies to databases
|
@@ -272,7 +290,7 @@ module Epuber
|
|
272
290
|
UI.processing_files_done
|
273
291
|
end
|
274
292
|
|
275
|
-
# @param
|
293
|
+
# @param [Epuber::Book::TocItem] toc_item
|
276
294
|
#
|
277
295
|
def parse_toc_item(toc_item)
|
278
296
|
unless toc_item.file_request.nil?
|
@@ -286,7 +304,41 @@ module Epuber
|
|
286
304
|
end
|
287
305
|
end
|
288
306
|
|
289
|
-
|
307
|
+
def process_global_ids
|
308
|
+
xhtml_files = @file_resolver.files.select { |file| file.is_a?(FileTypes::XHTMLFile) }
|
309
|
+
global_ids = validate_global_ids(xhtml_files)
|
310
|
+
|
311
|
+
xhtml_files.each do |file|
|
312
|
+
file.process_global_ids(compilation_context, global_ids)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
|
316
|
+
# Validates duplicity of global ids in all files + returns map of global ids to files
|
317
|
+
#
|
318
|
+
# @param [Array<FileTypes::XHTMLFile>] xhtml_files
|
319
|
+
# @return [Hash<String, FileTypes::XHTMLFile>]
|
320
|
+
#
|
321
|
+
def validate_global_ids(xhtml_files)
|
322
|
+
map = {}
|
323
|
+
xhtml_files.each do |file|
|
324
|
+
file.global_ids.each do |id|
|
325
|
+
if map.key?(id)
|
326
|
+
message = "Duplicate global id `#{id}` in file `#{file.source_path}`."
|
327
|
+
if compilation_context.release_build?
|
328
|
+
UI.error!(message)
|
329
|
+
else
|
330
|
+
UI.warning("#{message} Will fail during release build.")
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
map[id] = file
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
map
|
339
|
+
end
|
340
|
+
|
341
|
+
# @param [String] cmd
|
290
342
|
#
|
291
343
|
# @return [void]
|
292
344
|
#
|
data/lib/epuber/config.rb
CHANGED
@@ -14,7 +14,7 @@ module Epuber
|
|
14
14
|
@project_path ||= Dir.pwd.unicode_normalize
|
15
15
|
end
|
16
16
|
|
17
|
-
# @param
|
17
|
+
# @param [String] of_file absolute path to file
|
18
18
|
#
|
19
19
|
# @return [String] relative path to file from root of project
|
20
20
|
#
|
@@ -60,9 +60,11 @@ module Epuber
|
|
60
60
|
#
|
61
61
|
# @return [Epuber::Book]
|
62
62
|
#
|
63
|
-
|
64
|
-
|
65
|
-
|
63
|
+
attr_writer :bookspec
|
64
|
+
|
65
|
+
# @return [Boolean]
|
66
|
+
#
|
67
|
+
attr_accessor :release_build
|
66
68
|
|
67
69
|
# @return [Epuber::Lockfile]
|
68
70
|
#
|
@@ -82,7 +84,7 @@ module Epuber
|
|
82
84
|
bookspec_lockfile.write_to_file
|
83
85
|
end
|
84
86
|
|
85
|
-
# @param
|
87
|
+
# @param [Epuber::Book::Target] target
|
86
88
|
#
|
87
89
|
# @return [String]
|
88
90
|
#
|
@@ -90,7 +92,7 @@ module Epuber
|
|
90
92
|
File.join(working_path, 'build', target.name.to_s)
|
91
93
|
end
|
92
94
|
|
93
|
-
# @param
|
95
|
+
# @param [Epuber::Book::Target] target
|
94
96
|
#
|
95
97
|
# @return [String]
|
96
98
|
#
|
@@ -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(
|
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
|
-
|
129
|
-
|
130
|
-
|
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?
|
data/lib/epuber/dsl/attribute.rb
CHANGED
@@ -17,7 +17,7 @@ module Epuber
|
|
17
17
|
|
18
18
|
# Returns a new attribute initialized with the given options.
|
19
19
|
#
|
20
|
-
# @param
|
20
|
+
# @param [Symbol] name
|
21
21
|
#
|
22
22
|
# @see #name
|
23
23
|
#
|
@@ -109,25 +109,25 @@ module Epuber
|
|
109
109
|
# is not specified.
|
110
110
|
#
|
111
111
|
attr_reader :required
|
112
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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,
|
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
|
@@ -10,8 +9,8 @@ module Epuber
|
|
10
9
|
# attribute :name
|
11
10
|
# attribute :title, required: true, inherited: true
|
12
11
|
#
|
13
|
-
# @param
|
14
|
-
# @param
|
12
|
+
# @param [Symbol] name attribute name
|
13
|
+
# @param [Hash] options
|
15
14
|
#
|
16
15
|
# @see Attribute
|
17
16
|
#
|
@@ -45,8 +44,8 @@ module Epuber
|
|
45
44
|
end
|
46
45
|
end
|
47
46
|
|
48
|
-
# @param
|
49
|
-
# @param
|
47
|
+
# @param [Symbol] name
|
48
|
+
# @param [Epuber::DSL::Attribute] attr
|
50
49
|
#
|
51
50
|
# @return nil
|
52
51
|
#
|
@@ -86,8 +85,9 @@ module Epuber
|
|
86
85
|
else
|
87
86
|
begin
|
88
87
|
@attributes_values[key] = attr.converted_value(value)
|
89
|
-
rescue
|
90
|
-
UI.warning("Invalid value `#{value}` for attribute `#{name}`, original error `#{e}`",
|
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
|
data/lib/epuber/dsl/object.rb
CHANGED
@@ -49,17 +49,16 @@ module Epuber
|
|
49
49
|
|
50
50
|
next unless attr.required? && value.nil?
|
51
51
|
|
52
|
-
if attr.singularize?
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
end
|
52
|
+
raise ValidationError, "missing required attribute `#{key.to_s.singularize}|#{key}`" if attr.singularize?
|
53
|
+
|
54
|
+
|
55
|
+
raise ValidationError, "missing required attribute `#{key}`"
|
57
56
|
end
|
58
57
|
end
|
59
58
|
|
60
59
|
# Creates new instance by parsing ruby code from file
|
61
60
|
#
|
62
|
-
# @param
|
61
|
+
# @param [String] file_path
|
63
62
|
#
|
64
63
|
# @return [Self]
|
65
64
|
#
|
@@ -69,26 +68,21 @@ module Epuber
|
|
69
68
|
|
70
69
|
# Creates new instance by parsing ruby code from string
|
71
70
|
#
|
72
|
-
# @param
|
71
|
+
# @param [String] string
|
73
72
|
#
|
74
73
|
# @return [Self]
|
75
74
|
#
|
76
75
|
def self.from_string(string, file_path = nil)
|
77
|
-
# rubocop:disable Lint/Eval
|
78
76
|
obj = if file_path
|
79
|
-
eval(string, nil, file_path)
|
77
|
+
eval(string, nil, file_path) # rubocop:disable Security/Eval
|
80
78
|
else
|
81
|
-
eval(string)
|
79
|
+
eval(string) # rubocop:disable Security/Eval
|
82
80
|
end
|
83
|
-
# rubocop:enable Lint/Eval
|
84
81
|
|
85
82
|
unless obj.is_a?(self)
|
86
83
|
msg = "Invalid object #{obj.class}, expected object of class #{self}"
|
87
84
|
|
88
|
-
if file_path
|
89
|
-
msg += ", loaded from file #{file_path}"
|
90
|
-
|
91
|
-
end
|
85
|
+
msg += ", loaded from file #{file_path}" if file_path
|
92
86
|
|
93
87
|
raise StandardError, msg
|
94
88
|
end
|
@@ -121,6 +115,10 @@ module Epuber
|
|
121
115
|
#
|
122
116
|
attr_accessor :attributes_values
|
123
117
|
|
118
|
+
def respond_to_missing?(name, include_private = false)
|
119
|
+
@attributes_values.key?(name) || super
|
120
|
+
end
|
121
|
+
|
124
122
|
# Raise exception when there is used some unknown method or attribute
|
125
123
|
#
|
126
124
|
# This is just for creating better message in raised exception
|
@@ -129,9 +127,13 @@ module Epuber
|
|
129
127
|
#
|
130
128
|
def method_missing(name, *args)
|
131
129
|
if /([^=]+)=?/ =~ name
|
132
|
-
attr_name =
|
130
|
+
attr_name = ::Regexp.last_match(1)
|
133
131
|
location = caller_locations.first
|
134
|
-
|
132
|
+
message = <<~MSG
|
133
|
+
Unknown attribute or method `#{attr_name}` for class `#{self.class}` in file `#{location.path}:#{location.lineno}`
|
134
|
+
MSG
|
135
|
+
|
136
|
+
raise NameError, message
|
135
137
|
else
|
136
138
|
super
|
137
139
|
end
|