coradoc 0.2.0 → 1.0.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/.docker/Dockerfile +1 -1
  3. data/.docker/docker-compose.yml +2 -2
  4. data/.editorconfig +15 -0
  5. data/CHANGELOG.md +4 -0
  6. data/README.md +4 -0
  7. data/Rakefile +10 -0
  8. data/coradoc.gemspec +11 -2
  9. data/exe/reverse_adoc +91 -0
  10. data/exe/w2a +72 -0
  11. data/lib/coradoc/document.rb +6 -6
  12. data/lib/coradoc/element/admonition.rb +8 -6
  13. data/lib/coradoc/element/attribute.rb +2 -2
  14. data/lib/coradoc/element/attribute_list.rb +94 -15
  15. data/lib/coradoc/element/audio.rb +14 -3
  16. data/lib/coradoc/element/author.rb +18 -14
  17. data/lib/coradoc/element/base.rb +69 -8
  18. data/lib/coradoc/element/block/core.rb +10 -6
  19. data/lib/coradoc/element/block/literal.rb +1 -1
  20. data/lib/coradoc/element/block/quote.rb +1 -1
  21. data/lib/coradoc/element/block/sourcecode.rb +2 -2
  22. data/lib/coradoc/element/break.rb +1 -1
  23. data/lib/coradoc/element/document_attributes.rb +6 -6
  24. data/lib/coradoc/element/header.rb +4 -2
  25. data/lib/coradoc/element/image/block_image.rb +13 -2
  26. data/lib/coradoc/element/image/core.rb +35 -5
  27. data/lib/coradoc/element/image/inline_image.rb +2 -2
  28. data/lib/coradoc/element/image.rb +0 -1
  29. data/lib/coradoc/element/inline/anchor.rb +4 -2
  30. data/lib/coradoc/element/inline/bold.rb +10 -4
  31. data/lib/coradoc/element/inline/cross_reference.rb +4 -2
  32. data/lib/coradoc/element/inline/hard_line_break.rb +1 -1
  33. data/lib/coradoc/element/inline/highlight.rb +12 -6
  34. data/lib/coradoc/element/inline/italic.rb +10 -4
  35. data/lib/coradoc/element/inline/link.rb +26 -10
  36. data/lib/coradoc/element/inline/monospace.rb +10 -4
  37. data/lib/coradoc/element/inline/quotation.rb +4 -1
  38. data/lib/coradoc/element/inline/subscript.rb +5 -2
  39. data/lib/coradoc/element/inline/superscript.rb +5 -2
  40. data/lib/coradoc/element/inline.rb +0 -1
  41. data/lib/coradoc/element/list/core.rb +10 -8
  42. data/lib/coradoc/element/list/definition.rb +19 -0
  43. data/lib/coradoc/element/list/ordered.rb +1 -1
  44. data/lib/coradoc/element/list/unordered.rb +1 -1
  45. data/lib/coradoc/element/list.rb +1 -1
  46. data/lib/coradoc/element/list_item.rb +9 -4
  47. data/lib/coradoc/element/list_item_definition.rb +32 -0
  48. data/lib/coradoc/element/paragraph.rb +5 -3
  49. data/lib/coradoc/element/revision.rb +20 -16
  50. data/lib/coradoc/element/section.rb +21 -4
  51. data/lib/coradoc/element/table.rb +36 -19
  52. data/lib/coradoc/element/text_element.rb +63 -17
  53. data/lib/coradoc/element/title.rb +27 -7
  54. data/lib/coradoc/element/video.rb +33 -6
  55. data/lib/coradoc/generator.rb +2 -2
  56. data/lib/coradoc/legacy_parser.rb +41 -41
  57. data/lib/coradoc/oscal.rb +2 -4
  58. data/lib/coradoc/parser/asciidoc/content.rb +15 -15
  59. data/lib/coradoc/parser/asciidoc/document_attributes.rb +1 -1
  60. data/lib/coradoc/parser/asciidoc/header.rb +6 -6
  61. data/lib/coradoc/parser/asciidoc/section.rb +1 -1
  62. data/lib/coradoc/reverse_adoc/LICENSE.txt +25 -0
  63. data/lib/coradoc/reverse_adoc/README.adoc +308 -0
  64. data/lib/coradoc/reverse_adoc/cleaner.rb +125 -0
  65. data/lib/coradoc/reverse_adoc/config.rb +73 -0
  66. data/lib/coradoc/reverse_adoc/converters/a.rb +47 -0
  67. data/lib/coradoc/reverse_adoc/converters/aside.rb +12 -0
  68. data/lib/coradoc/reverse_adoc/converters/audio.rb +25 -0
  69. data/lib/coradoc/reverse_adoc/converters/base.rb +104 -0
  70. data/lib/coradoc/reverse_adoc/converters/blockquote.rb +18 -0
  71. data/lib/coradoc/reverse_adoc/converters/br.rb +11 -0
  72. data/lib/coradoc/reverse_adoc/converters/bypass.rb +77 -0
  73. data/lib/coradoc/reverse_adoc/converters/code.rb +19 -0
  74. data/lib/coradoc/reverse_adoc/converters/div.rb +14 -0
  75. data/lib/coradoc/reverse_adoc/converters/dl.rb +55 -0
  76. data/lib/coradoc/reverse_adoc/converters/drop.rb +22 -0
  77. data/lib/coradoc/reverse_adoc/converters/em.rb +17 -0
  78. data/lib/coradoc/reverse_adoc/converters/figure.rb +21 -0
  79. data/lib/coradoc/reverse_adoc/converters/h.rb +38 -0
  80. data/lib/coradoc/reverse_adoc/converters/head.rb +19 -0
  81. data/lib/coradoc/reverse_adoc/converters/hr.rb +11 -0
  82. data/lib/coradoc/reverse_adoc/converters/ignore.rb +16 -0
  83. data/lib/coradoc/reverse_adoc/converters/img.rb +98 -0
  84. data/lib/coradoc/reverse_adoc/converters/li.rb +13 -0
  85. data/lib/coradoc/reverse_adoc/converters/mark.rb +15 -0
  86. data/lib/coradoc/reverse_adoc/converters/markup.rb +27 -0
  87. data/lib/coradoc/reverse_adoc/converters/math.rb +31 -0
  88. data/lib/coradoc/reverse_adoc/converters/ol.rb +60 -0
  89. data/lib/coradoc/reverse_adoc/converters/p.rb +19 -0
  90. data/lib/coradoc/reverse_adoc/converters/pass_through.rb +13 -0
  91. data/lib/coradoc/reverse_adoc/converters/pre.rb +51 -0
  92. data/lib/coradoc/reverse_adoc/converters/q.rb +12 -0
  93. data/lib/coradoc/reverse_adoc/converters/strong.rb +16 -0
  94. data/lib/coradoc/reverse_adoc/converters/sub.rb +18 -0
  95. data/lib/coradoc/reverse_adoc/converters/sup.rb +18 -0
  96. data/lib/coradoc/reverse_adoc/converters/table.rb +280 -0
  97. data/lib/coradoc/reverse_adoc/converters/td.rb +77 -0
  98. data/lib/coradoc/reverse_adoc/converters/text.rb +28 -0
  99. data/lib/coradoc/reverse_adoc/converters/th.rb +14 -0
  100. data/lib/coradoc/reverse_adoc/converters/tr.rb +18 -0
  101. data/lib/coradoc/reverse_adoc/converters/video.rb +25 -0
  102. data/lib/coradoc/reverse_adoc/converters.rb +53 -0
  103. data/lib/coradoc/reverse_adoc/errors.rb +10 -0
  104. data/lib/coradoc/reverse_adoc/html_converter.rb +150 -0
  105. data/lib/coradoc/reverse_adoc/plugin.rb +131 -0
  106. data/lib/coradoc/reverse_adoc/plugins/plateau.rb +174 -0
  107. data/lib/coradoc/reverse_adoc/postprocessor.rb +148 -0
  108. data/lib/coradoc/reverse_adoc.rb +30 -0
  109. data/lib/coradoc/transformer.rb +24 -14
  110. data/lib/coradoc/version.rb +1 -1
  111. data/lib/reverse_adoc.rb +20 -0
  112. metadata +184 -5
  113. data/lib/coradoc/element/inline/image.rb +0 -25
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09284ec8117d3052ad45275f82328e45e600fabc4f86379093af0dbe4ee9ebc4'
4
- data.tar.gz: 8c9e2cef678a473459b091d05c303110a0caca9179d5ea89e1f4a07608d7bb9c
3
+ metadata.gz: 71bfd2ac723d58ca75b591bceabcdf834abd9cbbf32e54b96352da21b8f15095
4
+ data.tar.gz: 1a7ea6bf80d64ab4c1349c948aa2aee9ed704dc36ddd07fb7cce78293dca86cd
5
5
  SHA512:
6
- metadata.gz: 35510139cf9cd1cf0eedaaf12b0bc80709628660d88e0178f3651d7f213c870abf68eb04de6f23a8056af4681875f7ba59804228937bc65bfb6550753a4fb769
7
- data.tar.gz: 714c0f79bdd9bb05d58de71c6ab5bcdedddcd48a784fad4b019e168078c221ee6e477c573822a6a8f2db328fa02da5a0b9ef60542f239adb0b109c40ff309a66
6
+ metadata.gz: 555800621a06ffafc07e03a5c2cdca1210de6369d524a606de51df78b1ee9b19aaba30d32d8a448347237e488362a59677781a02502a1d8891fb89aa1cc267f5
7
+ data.tar.gz: 22707b18c6ed99fb4c6127885d871875ed1fed2906d2d10dcdd5f4ed9bdd1b1bfe3f19fd092768ccc4d488bdb7e705aba9ba7ab948ceace9cc679f1ee733eddc
data/.docker/Dockerfile CHANGED
@@ -3,7 +3,7 @@ ARG RUBY_IMAGE=ruby:3.1.2-slim
3
3
  FROM ${RUBY_IMAGE}
4
4
 
5
5
  RUN apt-get update \
6
- && apt-get install -y build-essential git \
6
+ && apt-get install -y build-essential git libreoffice \
7
7
  && apt-get clean && rm -rf /var/lib/apt/lists/*
8
8
 
9
9
  # install latest bundler
@@ -7,8 +7,8 @@ services:
7
7
  dockerfile: ./.docker/Dockerfile
8
8
 
9
9
  volumes:
10
- - .:/workspace
11
- - bundle:/bundle
10
+ - .:/workspace:z
11
+ - bundle:/bundle:z
12
12
 
13
13
  volumes:
14
14
  bundle:
data/.editorconfig ADDED
@@ -0,0 +1,15 @@
1
+ # EditorConfig is awesome: http://EditorConfig.org
2
+
3
+ # top-most EditorConfig file
4
+ root = true
5
+
6
+ # Unix-style newlines with a newline ending every file
7
+ [*]
8
+ charset = utf-8
9
+ end_of_line = lf
10
+
11
+ [{*.adoc,*.html,*.js,*.json,*.rake,*.rb,*.rf,*.yaml,*.yml,Rakefile,rakefile}]
12
+ indent_style = space
13
+ indent_size = 2
14
+ insert_final_newline = true
15
+ trim_trailing_whitespace = true
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.3.0] - 2024-05-21
4
+
5
+ - Merge reverse_adoc into coradoc
6
+
3
7
  ## [0.1.0] - 2023-01-08
4
8
 
5
9
  - Initial release
data/README.md CHANGED
@@ -66,4 +66,8 @@ Coradoc::Parser.parse(sample_asciidoc)
66
66
 
67
67
  This interface will return the abstract syntax tree.
68
68
 
69
+ ### Converting from HTML to AsciiDoc (reverse_adoc)
70
+
71
+ See: [reverse_adoc README](https://github.com/metanorma/coradoc/blob/main/lib/coradoc/reverse_adoc/README.adoc)
72
+
69
73
  [sandi-metz]: http://robots.thoughtbot.com/post/50655960596/sandi-metz-rules-for-developers
data/Rakefile CHANGED
@@ -1,7 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
+
5
+ if File.exist?(".codeclimate")
6
+ ENV["CODECLIMATE_REPO_TOKEN"] = File.read(".codeclimate").strip
7
+ end
8
+
4
9
  require "rspec/core/rake_task"
5
10
 
6
11
  RSpec::Core::RakeTask.new(:spec)
7
12
  task default: :spec
13
+
14
+ desc "Open an irb session preloaded with this library"
15
+ task :console do
16
+ sh "irb -Ilib -rcoradoc -rcoradoc/reverse_adoc"
17
+ end
data/coradoc.gemspec CHANGED
@@ -28,11 +28,20 @@ Gem::Specification.new do |spec|
28
28
  spec.require_paths = ["lib"]
29
29
  spec.required_ruby_version = ">= 2.7.0"
30
30
 
31
- spec.add_dependency "parslet"
31
+ spec.add_dependency "marcel", "~> 1.0.0"
32
+ spec.add_dependency "mathml2asciimath"
33
+ spec.add_dependency "nokogiri", "~> 1.13"
32
34
  spec.add_dependency "oscal", "~> 0.1.1"
33
-
35
+ spec.add_dependency "parslet"
36
+ spec.add_dependency "premailer", "~> 1.11.0"
37
+ spec.add_dependency "word-to-markdown"
38
+ spec.add_development_dependency "codeclimate-test-reporter"
34
39
  spec.add_development_dependency "pry"
35
40
  spec.add_development_dependency "rake"
41
+ spec.add_development_dependency "redcarpet"
36
42
  spec.add_development_dependency "rspec"
37
43
  spec.add_development_dependency "rubocop"
44
+ spec.add_development_dependency "rubocop-performance"
45
+ spec.add_development_dependency "simplecov"
46
+ # spec.add_runtime_dependency "thor"
38
47
  end
data/exe/reverse_adoc ADDED
@@ -0,0 +1,91 @@
1
+ #!/usr/bin/env ruby
2
+ # Usage: reverse_adoc [FILE]...
3
+ # Usage: cat FILE | reverse_adoc
4
+ require "rubygems"
5
+ require "bundler/setup"
6
+
7
+ require "coradoc/reverse_adoc"
8
+ require "optparse"
9
+ require "fileutils"
10
+
11
+ OptionParser.new do |opts|
12
+ opts.banner = "Usage: reverse_adoc [options] <file>"
13
+ opts.on("-m", "--mathml2asciimath", "Convert MathML to AsciiMath") do |_v|
14
+ Coradoc::ReverseAdoc.config.mathml2asciimath = true
15
+ end
16
+
17
+ opts.on("-oFILENAME", "--output=FILENAME", "Output file to write to") do |v|
18
+ Coradoc::ReverseAdoc.config.destination = File.expand_path(v)
19
+ # puts "output goes to #{Coradoc::ReverseAdoc.config.destination}"
20
+ end
21
+
22
+ opts.on("-e", "--external-images", "Export images if data URI") do |_v|
23
+ Coradoc::ReverseAdoc.config.external_images = true
24
+ end
25
+
26
+ opts.on("-u", "--unknown_tags [pass_through, drop, bypass, raise]",
27
+ "Unknown tag handling (default: pass_through)") do |v|
28
+ Coradoc::ReverseAdoc.config.unknown_tags = v
29
+ end
30
+
31
+ opts.on("-r", "--require RUBYMODULE", "Require additional Ruby file") do |v|
32
+ require v
33
+ end
34
+
35
+ opts.on("--track-time", "Track time spent on each step") do
36
+ Coradoc::ReverseAdoc.config.track_time = true
37
+ end
38
+
39
+ opts.on("--split-sections LEVEL", "Split sections up to LEVEL") do |i|
40
+ Coradoc::ReverseAdoc.config.split_sections = i.to_i
41
+ end
42
+
43
+ opts.on("-v", "--version", "Version information") do |_v|
44
+ puts "reverse_adoc: v#{Coradoc::ReverseAdoc::VERSION}"
45
+ exit
46
+ end
47
+
48
+ opts.on("-h", "--help", "Prints this help") do
49
+ puts opts
50
+ exit
51
+ end
52
+ end.parse!
53
+
54
+ if filename = ARGV.pop
55
+ input_content = IO.read(filename)
56
+ Coradoc::ReverseAdoc.config.sourcedir = File.dirname(File.expand_path(filename))
57
+ else
58
+ if Coradoc::ReverseAdoc.config.external_images
59
+ raise "The -e | --external-images feature cannot be used with STDIN input. Exiting."
60
+ end
61
+
62
+ input_content = ARGF.read
63
+ end
64
+
65
+ if Coradoc::ReverseAdoc.config.external_images && Coradoc::ReverseAdoc.config.destination.nil?
66
+ raise "The -e | --external-images feature must be used with -o | --output. Exiting."
67
+ end
68
+
69
+ if Coradoc::ReverseAdoc.config.split_sections && Coradoc::ReverseAdoc.config.destination.nil?
70
+ raise "The --split_sections feature must be used with -o | --output. Exiting."
71
+ end
72
+
73
+ # Read from STDIN
74
+ adoc_content = Coradoc::ReverseAdoc.convert(input_content)
75
+
76
+ # Print to STDOUT
77
+ unless Coradoc::ReverseAdoc.config.destination
78
+ puts adoc_content
79
+ exit
80
+ end
81
+
82
+ # Write output to Coradoc::ReverseAdoc.config.destination
83
+ adoc_content = {nil => adoc_content} unless adoc_content.is_a? Hash
84
+
85
+ adoc_content.each do |file, content|
86
+ destination = Coradoc::ReverseAdoc.config.destination
87
+ destdir = File.dirname(destination)
88
+ filename = file ? "#{destdir}/#{file}" : destination
89
+ FileUtils.mkdir_p(File.dirname(filename))
90
+ File.write(filename, content)
91
+ end
data/exe/w2a ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "rubygems"
5
+ require "bundler/setup"
6
+
7
+ require "word-to-markdown"
8
+ require "optparse"
9
+ require "coradoc/reverse_adoc"
10
+
11
+ ARGV.push("-h") if ARGV.empty?
12
+
13
+ OptionParser.new do |opts|
14
+ opts.banner = "Usage: w2a [options] <file>"
15
+ opts.on("-m", "--mathml2asciimath", "Convert MathML to AsciiMath") do |_v|
16
+ Coradoc::ReverseAdoc.config.mathml2asciimath = true
17
+ end
18
+
19
+ opts.on("-oFILENAME", "--output=FILENAME", "Output file to write to") do |v|
20
+ Coradoc::ReverseAdoc.config.destination = File.expand_path(v)
21
+ # puts "output goes to #{Coradoc::ReverseAdoc.config.destination}"
22
+ end
23
+
24
+ opts.on("-e", "--external-images", "Export images if data URI") do |_v|
25
+ Coradoc::ReverseAdoc.config.external_images = true
26
+ end
27
+
28
+ opts.on("-v", "--version", "Version information") do |_v|
29
+ puts "reverse_adoc: v#{Coradoc::ReverseAdoc::VERSION}"
30
+ puts "[dependency] WordToMarkdown: v#{WordToMarkdown::VERSION}"
31
+ if Gem.win_platform?
32
+ puts "[dependency] LibreOffice: version not available on Windows"
33
+ else
34
+ puts "[dependency] LibreOffice: v#{WordToMarkdown.soffice.version}"
35
+ end
36
+ exit
37
+ end
38
+
39
+ opts.on("-h", "--help", "Prints this help") do
40
+ puts opts
41
+ exit
42
+ end
43
+ end.parse!
44
+
45
+ filename = ARGV.pop
46
+ raise "Please provide an input file to process. Exiting." unless filename
47
+
48
+ if Coradoc::ReverseAdoc.config.external_images && Coradoc::ReverseAdoc.config.destination.nil?
49
+ raise "The -e | --external-images feature must be used with -o | --output. Exiting."
50
+ end
51
+
52
+ Coradoc::ReverseAdoc.config.sourcedir = Dir.mktmpdir
53
+
54
+ doc = WordToMarkdown.new(filename, Coradoc::ReverseAdoc.config.sourcedir)
55
+ # File.open("test.html", "w:UTF-8") { |f| f.write doc.document.html }
56
+ adoc_content = Coradoc::ReverseAdoc.convert(
57
+ Coradoc::ReverseAdoc.cleaner.preprocess_word_html(doc.document.html),
58
+ WordToMarkdown::REVERSE_MARKDOWN_OPTIONS,
59
+ )
60
+ # puts scrub_whitespace(doc.document.html)
61
+
62
+ # Print to STDOUT
63
+ unless Coradoc::ReverseAdoc.config.destination
64
+ puts adoc_content
65
+ exit
66
+ end
67
+
68
+ # Write output to Coradoc::ReverseAdoc.config.destination
69
+ FileUtils.mkdir_p(File.dirname(Coradoc::ReverseAdoc.config.destination))
70
+ File.open(Coradoc::ReverseAdoc.config.destination, "w") do |file|
71
+ file.write(adoc_content)
72
+ end
@@ -1,3 +1,4 @@
1
+ require_relative "element/base"
1
2
  require_relative "element/title"
2
3
  require_relative "element/block"
3
4
  require_relative "element/section"
@@ -20,7 +21,6 @@ require_relative "element/break"
20
21
 
21
22
  module Coradoc
22
23
  class Document
23
-
24
24
  class << self
25
25
  def from_adoc(filename)
26
26
  ast = Coradoc::Parser.parse(filename)
@@ -43,22 +43,22 @@ module Coradoc
43
43
  end
44
44
  end
45
45
 
46
- self.new(
46
+ new(
47
47
  document_attributes: @document_attributes,
48
48
  header: @header,
49
- sections: @sections
49
+ sections: @sections,
50
50
  )
51
51
  end
52
52
  end
53
53
 
54
54
  attr_accessor :header, :document_attributes, :sections
55
55
 
56
- def initialize(options={})
57
- @document_attributes = options.fetch(:document_attributes, Coradoc::Element::DocumentAttributes.new)
56
+ def initialize(options = {})
57
+ @document_attributes = options.fetch(:document_attributes,
58
+ Coradoc::Element::DocumentAttributes.new)
58
59
  @header = options.fetch(:header, Coradoc::Element::Header.new(""))
59
60
  @sections = options.fetch(:sections, [])
60
61
  self
61
62
  end
62
-
63
63
  end
64
64
  end
@@ -1,11 +1,13 @@
1
1
  module Coradoc
2
- class Element::Admonition
3
- attr_reader :type, :content, :line_break
2
+ module Element
3
+ class Admonition < Base
4
+ attr_accessor :type, :content, :line_break
4
5
 
5
- def initialize(content, type, options = {})
6
- @content = content
7
- @type = type.downcase.to_sym
8
- @line_break = options.fetch(:line_break, "")
6
+ def initialize(content, type, options = {})
7
+ @content = content
8
+ @type = type.downcase.to_sym
9
+ @line_break = options.fetch(:line_break, "")
10
+ end
9
11
  end
10
12
  end
11
13
  end
@@ -1,7 +1,7 @@
1
1
  module Coradoc
2
2
  module Element
3
- class Attribute
4
- attr_reader :key, :value
3
+ class Attribute < Base
4
+ attr_accessor :key, :value
5
5
 
6
6
  def initialize(key, value, _options = {})
7
7
  @key = key.to_s
@@ -1,45 +1,124 @@
1
1
  module Coradoc
2
2
  module Element
3
- class AttributeList
4
- attr_reader :positional, :named
3
+ class AttributeList < Base
4
+ attr_accessor :positional, :named, :rejected_positional, :rejected_named
5
+
6
+ declare_children :positional, :named
5
7
 
6
8
  def initialize(*positional, **named)
7
9
  @positional = positional || []
8
10
  @named = named || {}
11
+ @rejected_positional = []
12
+ @rejected_named = []
9
13
  end
10
14
 
11
- def add_positional(attr)
12
- @positional << attr
15
+ def add_positional(*attr)
16
+ @positional += attr
13
17
  end
14
18
 
15
19
  def add_named(name, value)
16
20
  @named[name] = value
17
21
  end
18
22
 
23
+ def any?
24
+ !empty?
25
+ end
26
+
19
27
  def empty?
20
28
  @positional.empty? && @named.empty?
21
29
  end
22
30
 
23
- def to_adoc
31
+ def validate_attr(attr, matcher)
32
+ matcher === attr
33
+ end
34
+
35
+ def validate_positional(validators)
36
+ @positional.each_with_index do |value, i|
37
+ # TODO: Decide what to do with this value
38
+ _positional_name = validators[i][0]
39
+
40
+ validator = validators[i][1]
41
+
42
+ unless validator && validate_attr(value, validator)
43
+ @positional[i] = nil
44
+ @rejected_positional << [i, value]
45
+ end
46
+ end
47
+
48
+ @positional.pop while !@positional.empty? && @positional.last.nil?
49
+ end
50
+
51
+ def validate_named(validators)
52
+ @named.each_with_index do |(name, value), i|
53
+ name = name.to_sym
54
+ validator = validators[name]
55
+
56
+ unless validator && validate_attr(value, validator)
57
+ @named.delete(name)
58
+ @rejected_named << [name, value]
59
+ end
60
+ end
61
+ end
62
+
63
+ def to_adoc(show_empty = true)
24
64
  return "[]" if [@positional, @named].all?(:empty?)
25
65
 
26
- adoc = ""
27
- adoc << @positional.join(",") if @positional.any?
66
+ adoc = +""
67
+ if !@positional.empty?
68
+ adoc << @positional.map { |p| [nil, ""].include?(p) ? '""' : p }.join(",")
69
+ end
28
70
  adoc << "," if @positional.any? && @named.any?
29
71
  adoc << @named.map do |k, v|
30
72
  if v.is_a?(String)
31
- v2 = v.to_s
32
- v2 = v2.include?("\"") ? v2.gsub("\"","\\\"") : v2
33
- if v2.include?(" ") || v2.include?(",") || v2.include?("\"")
34
- v2 = "\"#{v2}\""
73
+ v = v.gsub("\"", "\\\"")
74
+ if v.include?(" ") || v.include?(",") || v.include?('"')
75
+ v = "\"#{v}\""
35
76
  end
36
77
  elsif v.is_a?(Array)
37
- v2 = "\"#{v.join(",")}\""
78
+ v = "\"#{v.join(',')}\""
38
79
  end
39
- [k.to_s, "=", v2].join
80
+ [k.to_s, "=", v].join
40
81
  end.join(",")
41
- adoc = "[#{adoc}]" if @positional.any? || @named.any?
42
- adoc
82
+
83
+ if !empty? || (empty? && show_empty)
84
+ "[#{adoc}]"
85
+ elsif empty? && !show_empty
86
+ adoc
87
+ end
88
+ end
89
+
90
+ module Matchers
91
+ def one(*args)
92
+ One.new(*args)
93
+ end
94
+
95
+ class One
96
+ def initialize(*possibilities)
97
+ @possibilities = possibilities
98
+ end
99
+
100
+ def ===(other)
101
+ @possibilities.any? { |i| i === other }
102
+ end
103
+ end
104
+
105
+ def many(*args)
106
+ Many.new(*args)
107
+ end
108
+
109
+ # TODO: Find a way to only reject some values but not all?
110
+ class Many
111
+ def initialize(*possibilities)
112
+ @possibilities = possibilities
113
+ end
114
+
115
+ def ===(other)
116
+ other = other.split(",") if other.is_a?(String)
117
+
118
+ other.is_a?(Array) &&
119
+ other.all? { |i| @possibilities.any? { |p| p === i } }
120
+ end
121
+ end
43
122
  end
44
123
  end
45
124
  end
@@ -1,13 +1,15 @@
1
1
  module Coradoc
2
2
  module Element
3
- class Audio
4
- attr_reader :id, :title, :src, :options, :anchor
3
+ class Audio < Base
4
+ attr_accessor :id, :title, :src, :options, :anchor, :attributes
5
+
6
+ declare_children :id, :title, :anchor, :attributes
5
7
 
6
8
  def initialize(title, options = {})
7
9
  @title = title
8
10
  @id = options.fetch(:id, nil)
9
11
  @anchor = Inline::Anchor.new(@id) if @id
10
- @src = options.fetch(:src, '')
12
+ @src = options.fetch(:src, "")
11
13
  @attributes = options.fetch(:attributes, [])
12
14
  end
13
15
 
@@ -17,6 +19,15 @@ module Coradoc
17
19
  attrs = @attributes.empty? ? "\[\]" : @attributes.to_adoc
18
20
  [anchor, title, "audio::", @src, attrs].join("")
19
21
  end
22
+
23
+ extend AttributeList::Matchers
24
+ VALIDATORS_NAMED = {
25
+ title: String,
26
+ start: Integer,
27
+ end: Integer,
28
+ options: many("nofollow", "noopener", "inline", "interactive"),
29
+ opts: many("nofollow", "noopener", "inline", "interactive"),
30
+ }
20
31
  end
21
32
  end
22
33
  end
@@ -1,20 +1,24 @@
1
1
  module Coradoc
2
- class Element::Author
3
- attr_reader :email, :last_name, :first_name
2
+ module Element
3
+ class Author < Base
4
+ attr_accessor :email, :last_name, :first_name
4
5
 
5
- def initialize(first_name, last_name, email, middle_name = nil)
6
- @first_name = first_name
7
- @last_name = last_name
8
- @email = email
9
- @middle_name = middle_name
10
- end
6
+ declare_children :email, :last_name, :first_name
7
+
8
+ def initialize(first_name, last_name, email, middle_name = nil)
9
+ @first_name = first_name
10
+ @last_name = last_name
11
+ @email = email
12
+ @middle_name = middle_name
13
+ end
11
14
 
12
- def to_adoc
13
- adoc = "#{@first_name} "
14
- adoc << "#{@middle_name} " if @middle_name
15
- adoc << "#{@last_name}"
16
- adoc << " <#{@email}>" if @email
17
- adoc
15
+ def to_adoc
16
+ adoc = "#{@first_name} "
17
+ adoc << "#{@middle_name} " if @middle_name
18
+ adoc << @last_name.to_s
19
+ adoc << " <#{@email}>" if @email
20
+ adoc
21
+ end
18
22
  end
19
23
  end
20
24
  end
@@ -1,17 +1,78 @@
1
1
  module Coradoc
2
2
  module Element
3
3
  class Base
4
- # attr_reader :document_attributes
4
+ # The idea here, is that HTML content generators may often introduce
5
+ # a lot of unnecessary markup, that only makes sense in the HTML+CSS
6
+ # context. The idea is that certain cases can be simplified, making it
7
+ # so that the result is equivalent, but much simpler, allowing us to
8
+ # generate a nicer AsciiDoc syntax for those cases.
9
+ def simplify_block_content(content)
10
+ content = Array(content)
11
+ collected_content = []
12
+ content.each do |i|
13
+ case i
14
+ when Coradoc::Element::Section
15
+ return content unless i.safe_to_collapse?
5
16
 
6
- # def initialize(asciidoc)
7
- # @document_attributes = extract_document_attributes(asciidoc)
8
- # end
17
+ simplified = simplify_block_content(i.contents)
9
18
 
10
- private
19
+ if simplified && !simplified.empty?
20
+ collected_content << simplified
21
+ end
22
+ else
23
+ collected_content << i
24
+ end
25
+ end
11
26
 
12
- # def extract_document_attributes(asciidoc)
13
- # @document_attributes ||= DocumentAttributes.new(asciidoc.attributes)
14
- # end
27
+ collected_content = collected_content.compact
28
+
29
+ # We can safely do this optimization only if there's just one other
30
+ # element inside this structure.
31
+ if collected_content.length <= 1
32
+ collected_content
33
+ else
34
+ content
35
+ end
36
+ end
37
+
38
+ def self.declare_children(*children)
39
+ @children = (@children || []).dup + children
40
+ end
41
+
42
+ def self.visit(element, &block)
43
+ element = yield element, :pre
44
+ element = if element.respond_to? :visit
45
+ element.visit(&block)
46
+ elsif element.is_a? Array
47
+ element.map { |child| visit(child, &block) }.flatten.compact
48
+ elsif element.is_a? Hash
49
+ element.to_h do |k, v|
50
+ [visit(k, &block), visit(v, &block)]
51
+ end
52
+ else
53
+ element
54
+ end
55
+ yield element, :post
56
+ end
57
+
58
+ def self.children_accessors
59
+ @children || []
60
+ end
61
+
62
+ def children_accessors
63
+ self.class.children_accessors
64
+ end
65
+
66
+ def visit(&block)
67
+ children_accessors.each do |accessor|
68
+ child = public_send(accessor)
69
+ result = self.class.visit(child, &block)
70
+ if result != child
71
+ public_send(:"#{accessor}=", result)
72
+ end
73
+ end
74
+ self
75
+ end
15
76
  end
16
77
  end
17
78
  end