review 5.3.0 → 5.4.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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby-tex.yml +1 -1
  3. data/.github/workflows/ruby.yml +1 -1
  4. data/.rubocop.yml +1 -322
  5. data/NEWS.ja.md +48 -0
  6. data/NEWS.md +48 -0
  7. data/README.md +9 -8
  8. data/bin/review +1 -1
  9. data/bin/review-catalog-converter +15 -15
  10. data/bin/review-check +7 -7
  11. data/bin/review-compile +6 -8
  12. data/bin/review-index +1 -1
  13. data/bin/review-preproc +1 -1
  14. data/bin/review-validate +2 -2
  15. data/doc/config.yml.sample +7 -1
  16. data/doc/config.yml.sample-simple +1 -1
  17. data/lib/review/book/base.rb +3 -3
  18. data/lib/review/book/book_unit.rb +1 -1
  19. data/lib/review/book/chapter.rb +1 -1
  20. data/lib/review/book/index.rb +3 -3
  21. data/lib/review/book/part.rb +12 -13
  22. data/lib/review/book/volume.rb +1 -1
  23. data/lib/review/builder.rb +10 -12
  24. data/lib/review/catalog.rb +5 -5
  25. data/lib/review/compiler.rb +13 -13
  26. data/lib/review/configure.rb +5 -2
  27. data/lib/review/epub2html.rb +12 -12
  28. data/lib/review/epubmaker/content.rb +1 -1
  29. data/lib/review/epubmaker/epubcommon.rb +44 -42
  30. data/lib/review/epubmaker/epubv2.rb +2 -1
  31. data/lib/review/epubmaker/epubv3.rb +5 -4
  32. data/lib/review/epubmaker/producer.rb +3 -3
  33. data/lib/review/epubmaker/reviewheaderlistener.rb +1 -1
  34. data/lib/review/epubmaker.rb +32 -31
  35. data/lib/review/extentions/string.rb +1 -1
  36. data/lib/review/htmlbuilder.rb +16 -15
  37. data/lib/review/htmlutils.rb +17 -17
  38. data/lib/review/i18n.rb +3 -3
  39. data/lib/review/idgxmlbuilder.rb +22 -21
  40. data/lib/review/idgxmlmaker.rb +15 -13
  41. data/lib/review/index_builder.rb +4 -20
  42. data/lib/review/init.rb +4 -4
  43. data/lib/review/latexbuilder.rb +30 -32
  44. data/lib/review/lineinput.rb +3 -3
  45. data/lib/review/location.rb +1 -1
  46. data/lib/review/logger.rb +21 -21
  47. data/lib/review/makerhelper.rb +3 -3
  48. data/lib/review/markdownbuilder.rb +6 -6
  49. data/lib/review/pdfmaker.rb +23 -19
  50. data/lib/review/plaintextbuilder.rb +5 -5
  51. data/lib/review/preprocessor/repository.rb +1 -1
  52. data/lib/review/preprocessor.rb +5 -5
  53. data/lib/review/textmaker.rb +20 -18
  54. data/lib/review/textutils.rb +3 -5
  55. data/lib/review/topbuilder.rb +71 -12
  56. data/lib/review/update.rb +16 -8
  57. data/lib/review/version.rb +1 -1
  58. data/lib/review/webmaker.rb +32 -32
  59. data/lib/review/webtocprinter.rb +10 -10
  60. data/lib/review/yamlloader.rb +35 -2
  61. data/review.gemspec +1 -0
  62. data/samples/sample-book/src/config.yml +0 -1
  63. data/templates/html/_titlepage.html.erb +9 -17
  64. data/templates/opf/opf_manifest_epubv2.opf.erb +1 -1
  65. data/templates/opf/opf_manifest_epubv3.opf.erb +1 -1
  66. data/test/book_test_helper.rb +10 -10
  67. data/test/test_epub3maker.rb +3 -3
  68. data/test/test_epubmaker.rb +14 -29
  69. data/test/test_epubmaker_cmd.rb +2 -2
  70. data/test/test_htmlbuilder.rb +4 -5
  71. data/test/test_idgxmlbuilder.rb +10 -10
  72. data/test/test_idgxmlmaker_cmd.rb +1 -1
  73. data/test/test_img_math.rb +11 -2
  74. data/test/test_latexbuilder.rb +2 -3
  75. data/test/test_pdfmaker_cmd.rb +10 -10
  76. data/test/test_textmaker_cmd.rb +1 -1
  77. data/test/test_topbuilder.rb +151 -11
  78. data/test/test_yamlloader.rb +28 -42
  79. metadata +8 -7
@@ -165,7 +165,7 @@ module ReVIEW
165
165
  end
166
166
 
167
167
  def check_spec(spec)
168
- app_error "wrong spec: #{spec.inspect}" unless /\A\w+\z/ =~ spec
168
+ app_error "wrong spec: #{spec.inspect}" unless /\A\w+\z/.match?(spec)
169
169
  spec
170
170
  end
171
171
 
@@ -70,11 +70,11 @@ module ReVIEW
70
70
  direc = parse_directive(line, 1, 'eval')
71
71
  path = expand(direc.arg)
72
72
  @leave_content = File.extname(path) == '.re'
73
- if direc['eval']
74
- ent = evaluate(path, ent)
75
- else
76
- ent = @repository.fetch_file(path)
77
- end
73
+ ent = if direc['eval']
74
+ evaluate(path, ent)
75
+ else
76
+ @repository.fetch_file(path)
77
+ end
78
78
  replace_block(f, line, ent, false) # FIXME: turn off lineno: tmp
79
79
 
80
80
  when /\A\#@map(?:range)?/
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2018-2021 Kenshi Muto
1
+ # Copyright (c) 2018-2022 Kenshi Muto
2
2
  #
3
3
  # This program is free software.
4
4
  # You can distribute or modify this program under the terms of
@@ -74,9 +74,14 @@ module ReVIEW
74
74
  cmd_config, yamlfile = parse_opts(args)
75
75
  error! "#{yamlfile} not found." unless File.exist?(yamlfile)
76
76
 
77
- @config = ReVIEW::Configure.create(maker: 'textmaker',
78
- yamlfile: yamlfile,
79
- config: cmd_config)
77
+ begin
78
+ @config = ReVIEW::Configure.create(maker: 'textmaker',
79
+ yamlfile: yamlfile,
80
+ config: cmd_config)
81
+ rescue ReVIEW::ConfigError => e
82
+ error! e.message
83
+ end
84
+
80
85
  @img_math = ReVIEW::ImgMath.new(@config, path_name: '_review_math_text')
81
86
 
82
87
  I18n.setup(@config['language'])
@@ -111,12 +116,11 @@ module ReVIEW
111
116
 
112
117
  def build_body(basetmpdir, _yamlfile)
113
118
  base_path = Pathname.new(@basedir)
114
- builder = nil
115
- if @plaintext
116
- builder = ReVIEW::PLAINTEXTBuilder.new(img_math: @img_math)
117
- else
118
- builder = ReVIEW::TOPBuilder.new(img_math: @img_math)
119
- end
119
+ builder = if @plaintext
120
+ ReVIEW::PLAINTEXTBuilder.new(img_math: @img_math)
121
+ else
122
+ ReVIEW::TOPBuilder.new(img_math: @img_math)
123
+ end
120
124
  @converter = ReVIEW::Converter.new(@book, builder)
121
125
  @book.parts.each do |part|
122
126
  if part.name.present?
@@ -142,13 +146,11 @@ module ReVIEW
142
146
  end
143
147
 
144
148
  def build_chap(chap, base_path, basetmpdir, ispart)
145
- filename = ''
146
-
147
- if ispart.present?
148
- filename = chap.path
149
- else
150
- filename = Pathname.new(chap.path).relative_path_from(base_path).to_s
151
- end
149
+ filename = if ispart.present?
150
+ chap.path
151
+ else
152
+ Pathname.new(chap.path).relative_path_from(base_path).to_s
153
+ end
152
154
  id = File.basename(filename).sub(/\.re\Z/, '')
153
155
  if @buildonly && !@buildonly.include?(id)
154
156
  warn "skip #{id}.re"
@@ -159,7 +161,7 @@ module ReVIEW
159
161
 
160
162
  begin
161
163
  @converter.convert(filename, File.join(basetmpdir, textfile))
162
- rescue => e
164
+ rescue StandardError => e
163
165
  @compile_errors = true
164
166
  error "compile error in #{filename} (#{e.class})"
165
167
  error e.message
@@ -72,13 +72,11 @@ module ReVIEW
72
72
  end
73
73
 
74
74
  # lazy than rule 3, but it looks better
75
- if lazy
76
- if (%i[F W H].include?(Unicode::Eaw.property(tail)) &&
75
+ if lazy && ((%i[F W H].include?(Unicode::Eaw.property(tail)) &&
77
76
  tail !~ /\p{Hangul}/) ||
78
77
  (%i[F W H].include?(Unicode::Eaw.property(head)) &&
79
- head !~ /\p{Hangul}/)
80
- space = nil
81
- end
78
+ head !~ /\p{Hangul}/))
79
+ space = nil
82
80
  end
83
81
  end
84
82
  space
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2008-2021 Minero Aoki, Kenshi Muto
1
+ # Copyright (c) 2008-2022 Minero Aoki, Kenshi Muto
2
2
  # 2002-2006 Minero Aoki
3
3
  #
4
4
  # This program is free software.
@@ -262,13 +262,61 @@ module ReVIEW
262
262
  blank
263
263
  end
264
264
 
265
+ def table_rows(sepidx, rows)
266
+ if sepidx
267
+ sepidx.times do
268
+ tr(rows.shift.map { |s| th(s) })
269
+ end
270
+ if !@book.config['textmaker'] || !@book.config['textmaker']['th_bold']
271
+ puts '-' * 12
272
+ end
273
+ rows.each do |cols|
274
+ tr(cols.map { |s| td(s) })
275
+ end
276
+ else
277
+ rows.each do |cols|
278
+ h, *cs = *cols
279
+ tr([th(h)] + cs.map { |s| td(s) })
280
+ end
281
+ end
282
+ end
283
+
265
284
  def th(str)
266
- "★#{str}☆"
285
+ if @book.config['textmaker'] && @book.config['textmaker']['th_bold']
286
+ "★#{str}☆"
287
+ else
288
+ str
289
+ end
267
290
  end
268
291
 
269
292
  def table_end
270
293
  end
271
294
 
295
+ def imgtable(lines, id, caption = nil, metric = nil)
296
+ metrics = parse_metric('top', metric)
297
+ metrics = " #{metrics}" if metrics.present?
298
+ blank
299
+ puts "◆→開始:#{@titles['table']}←◆"
300
+ if caption_top?('table') && caption.present?
301
+ table_header(id, caption)
302
+ end
303
+
304
+ if @chapter.image_bound?(id)
305
+ puts "◆→#{@chapter.image(id).path}#{metrics}←◆"
306
+ else
307
+ warn "image not bound: #{id}", location: location
308
+ lines.each do |line|
309
+ puts line
310
+ end
311
+ end
312
+
313
+ if !caption_top?('table') && caption.present?
314
+ table_header(id, caption)
315
+ end
316
+ puts "◆→終了:#{@titles['table']}←◆"
317
+ blank
318
+ end
319
+
272
320
  def comment(lines, comment = nil)
273
321
  return unless @book.config['draft']
274
322
 
@@ -314,8 +362,9 @@ module ReVIEW
314
362
 
315
363
  def compile_kw(word, alt)
316
364
  if alt
317
- then "★#{word}☆(#{alt.strip})"
318
- else "★#{word}☆"
365
+ "★#{word}☆(#{alt.strip})"
366
+ else
367
+ "★#{word}☆"
319
368
  end
320
369
  end
321
370
 
@@ -398,7 +447,7 @@ module ReVIEW
398
447
  def inline_icon(id)
399
448
  begin
400
449
  "◆→画像 #{@chapter.image(id).path.sub(%r{\A\./}, '')}←◆"
401
- rescue
450
+ rescue StandardError
402
451
  warn "image not bound: #{id}", location: location
403
452
  "◆→画像 #{id}←◆"
404
453
  end
@@ -495,27 +544,37 @@ module ReVIEW
495
544
  ), __FILE__, __LINE__ - 11
496
545
  end
497
546
 
498
- def indepimage(_lines, id, caption = nil, metric = nil)
547
+ def indepimage(lines, id, caption = nil, metric = nil)
499
548
  metrics = parse_metric('top', metric)
500
549
  metrics = " #{metrics}" if metrics.present?
501
550
  blank
551
+ puts "◆→開始:#{@titles['image']}←◆"
502
552
  if caption_top?('image') && caption.present?
503
- puts "図 #{compile_inline(caption)}"
553
+ indepimage_header(id, caption)
554
+ blank
504
555
  end
505
- begin
506
- puts "◆→画像 #{@chapter.image(id).path.sub(%r{\A\./}, '')}#{metrics}←◆"
507
- rescue
556
+ if @chapter.image_bound?(id)
557
+ puts "◆→#{@chapter.image(id).path}#{metrics}←◆"
558
+ else
508
559
  warn "image not bound: #{id}", location: location
509
- puts "◆→画像 #{id}←◆"
560
+ lines.each do |line|
561
+ puts line
562
+ end
510
563
  end
511
564
  if !caption_top?('image') && caption.present?
512
- puts "図 #{compile_inline(caption)}"
565
+ blank
566
+ indepimage_header(id, caption)
513
567
  end
568
+ puts "◆→終了:#{@titles['image']}←◆"
514
569
  blank
515
570
  end
516
571
 
517
572
  alias_method :numberlessimage, :indepimage
518
573
 
574
+ def indepimage_header(_id, caption)
575
+ puts "図#{I18n.t('caption_prefix_idgxml')}#{compile_inline(caption)}"
576
+ end
577
+
519
578
  def inline_code(str)
520
579
  "△#{str}☆"
521
580
  end
data/lib/review/update.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  #
2
- # Copyright (c) 2018-2020 Kenshi Muto
2
+ # Copyright (c) 2018-2022 Kenshi Muto
3
3
  #
4
4
  # This program is free software.
5
5
  # You can distribute or modify this program under the terms of
@@ -165,7 +165,7 @@ module ReVIEW
165
165
 
166
166
  Dir.glob(File.join(dir, '*.yml')).sort.each do |yml|
167
167
  begin
168
- config = YAML.load_file(yml)
168
+ config = YAMLLoader.safe_load_file(yml)
169
169
  if config['language'].present?
170
170
  language = config['language']
171
171
  end
@@ -226,9 +226,11 @@ module ReVIEW
226
226
 
227
227
  def check_own_files(dir)
228
228
  if File.exist?(File.join(dir, 'layouts/layout.tex.erb'))
229
+ # rubocop:disable Style/SoleNestedConditional
229
230
  unless confirm('** There is custom layouts/layout.tex.erb file. Updating may break to make PDF until you fix layout.tex.erb. Do you really proceed to update? **', [], nil)
230
231
  raise ApplicationError
231
232
  end
233
+ # rubocop:enable Style/SoleNestedConditional
232
234
  end
233
235
 
234
236
  if File.exist?(File.join(dir, 'review-ext.rb'))
@@ -238,7 +240,7 @@ module ReVIEW
238
240
 
239
241
  def update_version
240
242
  @config_ymls.each do |yml|
241
- config = YAML.load_file(yml)
243
+ config = YAMLLoader.safe_load_file(yml)
242
244
  if config['review_version'].to_f.round(1) == TARGET_VERSION.to_f.round(1)
243
245
  next
244
246
  end
@@ -265,10 +267,12 @@ module ReVIEW
265
267
  target_rakefile = File.join(dir, 'Rakefile')
266
268
  if File.exist?(target_rakefile)
267
269
  if Digest::SHA256.hexdigest(File.read(target_rakefile)) != Digest::SHA256.hexdigest(File.read(master_rakefile))
270
+ # rubocop:disable Style/SoleNestedConditional
268
271
  if confirm('%s will be overridden with Re:VIEW version (%s). Do you really proceed?', ['Rakefile', master_rakefile])
269
272
  FileUtils.mv(target_rakefile, "#{target_rakefile}-old")
270
273
  FileUtils.cp(master_rakefile, target_rakefile)
271
274
  end
275
+ # rubocop:enable Style/SoleNestedConditional
272
276
  end
273
277
  else
274
278
  @logger.info t('new file %s is created.', [target_rakefile]) unless @force
@@ -278,12 +282,14 @@ module ReVIEW
278
282
  master_rakefile = File.join(@review_dir, 'samples/sample-book/src/lib/tasks/review.rake')
279
283
  target_rakefile = File.join(taskdir, 'review.rake')
280
284
  if File.exist?(target_rakefile)
285
+ # rubocop:disable Style/SoleNestedConditional
281
286
  if Digest::SHA256.hexdigest(File.read(target_rakefile)) != Digest::SHA256.hexdigest(File.read(master_rakefile))
282
287
  if confirm('%s will be overridden with Re:VIEW version (%s). Do you really proceed?', ['lib/tasks/review.rake', master_rakefile])
283
288
  FileUtils.mv(target_rakefile, "#{target_rakefile}-old")
284
289
  FileUtils.cp(master_rakefile, target_rakefile)
285
290
  end
286
291
  end
292
+ # rubocop:enable Style/SoleNestedConditional
287
293
  else
288
294
  @logger.info t('new file %s is created.', [target_rakefile]) unless @force
289
295
  FileUtils.cp(master_rakefile, target_rakefile)
@@ -292,11 +298,13 @@ module ReVIEW
292
298
 
293
299
  def update_epub_version
294
300
  @epub_ymls.each do |yml|
295
- config = YAML.load_file(yml)
301
+ config = YAMLLoader.safe_load_file(yml)
296
302
  if config['epubversion'].present? && config['epubversion'].to_f < EPUB_VERSION.to_f
303
+ # rubocop:disable Style/SoleNestedConditional
297
304
  if confirm("%s: Update '%s' to '%s' from '%s'?", [File.basename(yml), 'epubversion', EPUB_VERSION, config['epubversion']])
298
305
  rewrite_yml(yml, 'epubversion', EPUB_VERSION)
299
306
  end
307
+ # rubocop:enable Style/SoleNestedConditional
300
308
  end
301
309
  if !config['htmlversion'].present? || config['htmlversion'].to_f >= HTML_VERSION.to_f
302
310
  next
@@ -310,7 +318,7 @@ module ReVIEW
310
318
 
311
319
  def update_locale
312
320
  @locale_ymls.each do |yml|
313
- config = YAML.load_file(yml)
321
+ config = YAMLLoader.safe_load_file(yml)
314
322
  if !config['chapter_quote'].present? || config['chapter_quote'].scan('%s').size != 1
315
323
  next
316
324
  end
@@ -324,7 +332,7 @@ module ReVIEW
324
332
 
325
333
  def update_tex_parameters
326
334
  @tex_ymls.each do |yml|
327
- config = YAML.load_file(yml)
335
+ config = YAMLLoader.safe_load_file(yml)
328
336
  unless config['texdocumentclass']
329
337
  next
330
338
  end
@@ -510,7 +518,7 @@ module ReVIEW
510
518
 
511
519
  def update_tex_command
512
520
  @tex_ymls.each do |yml|
513
- config = YAML.load_file(yml)
521
+ config = YAMLLoader.safe_load_file(yml)
514
522
  if !config['texcommand'] || config['texcommand'] !~ /\s+-/
515
523
  next
516
524
  end
@@ -535,7 +543,7 @@ module ReVIEW
535
543
 
536
544
  def update_dvi_command
537
545
  @tex_ymls.each do |yml|
538
- config = YAML.load_file(yml)
546
+ config = YAMLLoader.safe_load_file(yml)
539
547
  if !config['dvicommand'] || config['dvicommand'] !~ /\s+-/
540
548
  next
541
549
  end
@@ -1,3 +1,3 @@
1
1
  module ReVIEW
2
- VERSION = '5.3.0'.freeze
2
+ VERSION = '5.4.0'.freeze
3
3
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2016-2021 Masayoshi Takahashi, Masanori Kado, Kenshi Muto
1
+ # Copyright (c) 2016-2022 Masayoshi Takahashi, Masanori Kado, Kenshi Muto
2
2
  #
3
3
  # This program is free software.
4
4
  # You can distribute or modify this program under the terms of
@@ -78,9 +78,13 @@ module ReVIEW
78
78
  cmd_config, yamlfile = parse_opts(args)
79
79
  error! "#{yamlfile} not found." unless File.exist?(yamlfile)
80
80
 
81
- @config = ReVIEW::Configure.create(maker: 'webmaker',
82
- yamlfile: yamlfile,
83
- config: cmd_config)
81
+ begin
82
+ @config = ReVIEW::Configure.create(maker: 'webmaker',
83
+ yamlfile: yamlfile,
84
+ config: cmd_config)
85
+ rescue ReVIEW::ConfigError => e
86
+ error! e.message
87
+ end
84
88
 
85
89
  @config['htmlext'] = 'html'
86
90
  @img_math = ReVIEW::ImgMath.new(@config)
@@ -97,7 +101,7 @@ module ReVIEW
97
101
  end
98
102
 
99
103
  def generate_html_files(yamlfile)
100
- @basedir = File.dirname(yamlfile)
104
+ @basedir = File.absolute_path(File.dirname(yamlfile))
101
105
  @path = build_path
102
106
  remove_old_files(@path)
103
107
  Dir.mkdir(@path)
@@ -146,19 +150,27 @@ module ReVIEW
146
150
  def build_part(part, basetmpdir, htmlfile)
147
151
  @title = h("#{ReVIEW::I18n.t('part', part.number)} #{part.name.strip}")
148
152
  File.open("#{basetmpdir}/#{htmlfile}", 'w') do |f|
149
- @body = ''
150
- @body << %Q(<div class="part">\n)
151
- @body << %Q(<h1 class="part-number">#{ReVIEW::I18n.t('part', part.number)}</h1>\n)
152
- @body << %Q(<h2 class="part-title">#{part.name.strip}</h2>\n) if part.name.strip.present?
153
- @body << "</div>\n"
154
-
153
+ @part_number = part.number
154
+ @part_title = part.name.strip
155
+ @body = ReVIEW::Template.generate(path: template_name(localfile: '_part_body.html.erb', systemfile: 'html/_part_body.html.erb'), binding: binding)
155
156
  @language = @config['language']
156
157
  @stylesheets = @config['stylesheet']
157
158
  f.write ReVIEW::Template.generate(path: template_name, binding: binding)
158
159
  end
159
160
  end
160
161
 
161
- def template_name
162
+ def template_name(localfile: 'layout-web.html.erb', systemfile: nil)
163
+ if @basedir
164
+ layoutfile = File.join(@basedir, 'layouts', localfile)
165
+ if File.exist?(layoutfile)
166
+ return layoutfile
167
+ end
168
+ end
169
+
170
+ if systemfile
171
+ return systemfile
172
+ end
173
+
162
174
  if @config['htmlversion'].to_i == 5
163
175
  'web/html/layout-html5.html.erb'
164
176
  else
@@ -167,13 +179,11 @@ module ReVIEW
167
179
  end
168
180
 
169
181
  def build_chap(chap, base_path, basetmpdir, ispart)
170
- filename = ''
171
-
172
- if ispart.present?
173
- filename = chap.path
174
- else
175
- filename = Pathname.new(chap.path).relative_path_from(base_path).to_s
176
- end
182
+ filename = if ispart.present?
183
+ chap.path
184
+ else
185
+ Pathname.new(chap.path).relative_path_from(base_path).to_s
186
+ end
177
187
  id = File.basename(filename).sub(/\.re\Z/, '')
178
188
 
179
189
  if @buildonly && !@buildonly.include?(id)
@@ -219,7 +229,7 @@ module ReVIEW
219
229
 
220
230
  if FileTest.directory?("#{resdir}/#{fname}")
221
231
  recursive_copy_files("#{resdir}/#{fname}", "#{destdir}/#{fname}", allow_exts)
222
- elsif fname =~ /\.(#{allow_exts.join('|')})\Z/i
232
+ elsif /\.(#{allow_exts.join('|')})\Z/i.match?(fname)
223
233
  FileUtils.mkdir_p(destdir)
224
234
  FileUtils.cp("#{resdir}/#{fname}", destdir)
225
235
  end
@@ -274,18 +284,8 @@ module ReVIEW
274
284
 
275
285
  def build_titlepage(basetmpdir, htmlfile)
276
286
  @title = h('titlepage')
277
- File.open("#{basetmpdir}/#{htmlfile}", 'w') do |f|
278
- @body = ''
279
- @body << %Q(<div class="titlepage">)
280
- @body << %Q(<h1 class="tp-title">#{h(@config.name_of('booktitle'))}</h1>)
281
- if @config['aut']
282
- @body << %Q(<h2 class="tp-author">#{join_with_separator(@config.names_of('aut'), ReVIEW::I18n.t('names_splitter'))}</h2>)
283
- end
284
- if @config['pbl']
285
- @body << %Q(<h3 class="tp-publisher">#{join_with_separator(@config.names_of('pbl'), ReVIEW::I18n.t('names_splitter'))}</h3>)
286
- end
287
- @body << '</div>'
288
-
287
+ File.open(File.join(basetmpdir, htmlfile), 'w') do |f|
288
+ @body = ReVIEW::Template.generate(path: template_name(localfile: '_titlepage.html.erb', systemfile: 'html/_titlepage.html.erb'), binding: binding)
289
289
  @language = @config['language']
290
290
  @stylesheets = @config['stylesheet']
291
291
  f.write ReVIEW::Template.generate(path: template_name, binding: binding)
@@ -41,16 +41,16 @@ EOT
41
41
  next
42
42
  end
43
43
 
44
- if path.start_with?('.')
45
- content << "<li>#{escape(result.headline)}"
46
- else
47
- content << %Q(<li><a href="#{path}">#{escape(result.headline)}</a>)
48
- end
49
- if result.level == 0
50
- content << "\n<ul>" # part
51
- else
52
- content << "</li>\n"
53
- end
44
+ content << if path.start_with?('.')
45
+ "<li>#{escape(result.headline)}"
46
+ else
47
+ %Q(<li><a href="#{path}">#{escape(result.headline)}</a>)
48
+ end
49
+ content << if result.level == 0
50
+ "\n<ul>" # part
51
+ else
52
+ "</li>\n"
53
+ end
54
54
  end
55
55
  content << "</ul>\n"
56
56
  end
@@ -2,6 +2,39 @@ require 'yaml'
2
2
 
3
3
  module ReVIEW
4
4
  class YAMLLoader
5
+ def self.safe_load_file(file)
6
+ if YAML.respond_to?(:safe_load_file)
7
+ YAML.safe_load_file(file, aliases: true, permitted_classes: [Date])
8
+ else
9
+ File.open(file, 'rt:bom|utf-8') do |f|
10
+ begin
11
+ # < Ruby 3.1
12
+ YAML.safe_load(f, filename: file, aliases: true, permitted_classes: [Date])
13
+ rescue ArgumentError
14
+ # < Ruby 2.7
15
+ YAML.safe_load(f, [Date])
16
+ rescue Psych::DisallowedClass
17
+ # < Ruby 2.5
18
+ YAML.safe_load(File.read(file), [Date])
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.safe_load(s)
25
+ if YAML.respond_to?(:safe_load_file)
26
+ YAML.safe_load(s, aliases: true, permitted_classes: [Date])
27
+ else
28
+ begin
29
+ # < Ruby 3.1
30
+ YAML.safe_load(s, aliases: true, permitted_classes: [Date])
31
+ rescue ArgumentError, Psych::DisallowedClass
32
+ # < Ruby 2.7
33
+ YAML.safe_load(s, [Date])
34
+ end
35
+ end
36
+ end
37
+
5
38
  def initialize
6
39
  end
7
40
 
@@ -17,8 +50,8 @@ module ReVIEW
17
50
 
18
51
  while file_queue.present?
19
52
  current_file = file_queue.shift
20
- current_yaml = YAML.load_file(current_file)
21
- if current_yaml.instance_of?(FalseClass)
53
+ current_yaml = YAMLLoader.safe_load_file(current_file)
54
+ if current_yaml.instance_of?(FalseClass) || current_yaml.nil?
22
55
  raise "#{File.basename(current_file)} is malformed."
23
56
  end
24
57
 
data/review.gemspec CHANGED
@@ -12,6 +12,7 @@ Gem::Specification.new do |gem|
12
12
  gem.summary = 'Re:VIEW: a easy-to-use digital publishing system'
13
13
  gem.description = 'Re:VIEW is a digital publishing system for books and ebooks. It supports InDesign, EPUB and LaTeX.'
14
14
  gem.required_rubygems_version = Gem::Requirement.new('>= 0') if gem.respond_to?(:required_rubygems_version=)
15
+ gem.metadata = { 'rubygems_mfa_required' => 'true' }
15
16
 
16
17
  gem.files = `git ls-files`.split("\n")
17
18
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
@@ -3,7 +3,6 @@ review_version: 5.0
3
3
  bookname: book
4
4
  language: ja
5
5
  booktitle: Re:VIEWサンプル書籍
6
- urnid: urn:uuid:http://reviewml.com/books/review-sample-book/
7
6
  # isbn: null
8
7
  aut: Re:VIEW Writers
9
8
  pbl: Re:VIEW Publishers
@@ -1,20 +1,12 @@
1
- <h1 class="tp-title"><%= @title_str %></h1>
2
- <% if @subtitle_str %>
3
- <h2 class="tp-subtitle"><%= h(@subtitle_str) %></h2>
1
+ <div class="titlepage">
2
+ <h1 class="tp-title"><%= h(@config.name_of('booktitle')) %></h1>
3
+ <% if @config['subtitle'] %>
4
+ <h2 class="tp-subtitle"><%= h(@config.name_of('subtitle')) %></h2>
4
5
  <% end %>
5
- <% if @author_str %>
6
- <p>
7
- <br />
8
- <br />
9
- </p>
10
- <h2 class="tp-author"><%= h(@author_str) %></h2>
6
+ <% if @config['aut'] %>
7
+ <h2 class="tp-author"><%= h(@config.names_of('aut').join(ReVIEW::I18n.t('names_splitter'))) %></h2>
11
8
  <% end %>
12
- <% if @publisher_str %>
13
- <p>
14
- <br />
15
- <br />
16
- <br />
17
- <br />
18
- </p>
19
- <h3 class="tp-publisher"><%= h(@publisher_str) %></h3>
9
+ <% if @config['pbl'] %>
10
+ <h3 class="tp-publisher"><%= h(@config.names_of('pbl').join(ReVIEW::I18n.t('names_splitter'))) %></h3>
20
11
  <% end %>
12
+ </div>
@@ -4,7 +4,7 @@
4
4
  <% if @config['toc'] && @config['mytoc'] %>
5
5
  <item id="toc" href="<%= @config['bookname'] %>-toc.<%= @config['htmlext'] %>" media-type="application/xhtml+xml"/>
6
6
  <% end %>
7
- <% @items.each do |item| %>
7
+ <% @items.sort_by { |x| x.id }.each do |item| %>
8
8
  <item id="<%= item.id %>" href="<%= item.file %>" media-type="<%= item.media %>"/>
9
9
  <% end %>
10
10
  </manifest>
@@ -4,7 +4,7 @@
4
4
  <% if @coverimage %>
5
5
  <item properties="cover-image" id="cover-<%= @coverimage.id %>" href="<%= @coverimage.file %>" media-type="<%= @coverimage.media %>"/>
6
6
  <% end %>
7
- <% @items.each do |item| %>
7
+ <% @items.sort_by { |x| x.id }.each do |item| %>
8
8
  <item id="<%= item.id %>" href="<%= item.file %>" media-type="<%= item.media %>"<%= item.properties_attribute %>/>
9
9
  <% end %>
10
10
  </manifest>
@@ -20,11 +20,11 @@ module BookTestHelper
20
20
  created_files[filename] = path
21
21
  end
22
22
  conf_path = File.expand_path('config.yml', dir)
23
- if File.exist?(conf_path)
24
- config = ReVIEW::Configure.create(yamlfile: conf_path)
25
- else
26
- config = ReVIEW::Configure.values
27
- end
23
+ config = if File.exist?(conf_path)
24
+ ReVIEW::Configure.create(yamlfile: conf_path)
25
+ else
26
+ ReVIEW::Configure.values
27
+ end
28
28
  book = Book::Base.new(dir, config: config)
29
29
  yield(dir, book, created_files)
30
30
  end
@@ -34,11 +34,11 @@ module BookTestHelper
34
34
  def get_instance_variables(obj)
35
35
  obj.instance_variables.each_with_object({}) do |name, memo|
36
36
  value = obj.instance_variable_get(name)
37
- if value.instance_variables.empty?
38
- memo[name] = value
39
- else
40
- memo[name] = get_instance_variables(value)
41
- end
37
+ memo[name] = if value.instance_variables.empty?
38
+ value
39
+ else
40
+ get_instance_variables(value)
41
+ end
42
42
  end
43
43
  end
44
44
  end