epuber 0.7.4 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|