coradoc 0.2.0 → 1.0.0

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