coradoc 1.1.8 → 2.0.12

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 (225) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +1 -1
  3. data/Rakefile +3 -12
  4. data/exe/coradoc +21 -2
  5. data/lib/coradoc/cli.rb +185 -91
  6. data/lib/coradoc/configurable.rb +527 -0
  7. data/lib/coradoc/coradoc.rb +463 -0
  8. data/lib/coradoc/core_model/annotation_block.rb +57 -0
  9. data/lib/coradoc/core_model/base.rb +172 -0
  10. data/lib/coradoc/core_model/bibliography.rb +41 -0
  11. data/lib/coradoc/core_model/bibliography_entry.rb +48 -0
  12. data/lib/coradoc/core_model/block.rb +63 -0
  13. data/lib/coradoc/core_model/children_content.rb +53 -0
  14. data/lib/coradoc/core_model/comment_block.rb +10 -0
  15. data/lib/coradoc/core_model/definition_item.rb +46 -0
  16. data/lib/coradoc/core_model/definition_list.rb +28 -0
  17. data/lib/coradoc/core_model/element_attribute.rb +26 -0
  18. data/lib/coradoc/core_model/example_block.rb +10 -0
  19. data/lib/coradoc/core_model/footnote.rb +92 -0
  20. data/lib/coradoc/core_model/horizontal_rule_block.rb +10 -0
  21. data/lib/coradoc/core_model/id_generator.rb +16 -0
  22. data/lib/coradoc/core_model/image.rb +66 -0
  23. data/lib/coradoc/core_model/inline_element.rb +140 -0
  24. data/lib/coradoc/core_model/list_block.rb +135 -0
  25. data/lib/coradoc/core_model/list_item.rb +142 -0
  26. data/lib/coradoc/core_model/listing_block.rb +13 -0
  27. data/lib/coradoc/core_model/literal_block.rb +10 -0
  28. data/lib/coradoc/core_model/metadata.rb +79 -0
  29. data/lib/coradoc/core_model/open_block.rb +10 -0
  30. data/lib/coradoc/core_model/paragraph_block.rb +10 -0
  31. data/lib/coradoc/core_model/pass_block.rb +10 -0
  32. data/lib/coradoc/core_model/quote_block.rb +12 -0
  33. data/lib/coradoc/core_model/reviewer_block.rb +10 -0
  34. data/lib/coradoc/core_model/sidebar_block.rb +10 -0
  35. data/lib/coradoc/core_model/source_block.rb +10 -0
  36. data/lib/coradoc/core_model/structural_element.rb +94 -0
  37. data/lib/coradoc/core_model/table.rb +148 -0
  38. data/lib/coradoc/core_model/term.rb +53 -0
  39. data/lib/coradoc/core_model/text_content.rb +22 -0
  40. data/lib/coradoc/core_model/toc.rb +105 -0
  41. data/lib/coradoc/core_model/toc_generator.rb +151 -0
  42. data/lib/coradoc/core_model/verse_block.rb +12 -0
  43. data/lib/coradoc/core_model.rb +77 -0
  44. data/lib/coradoc/document_builder.rb +184 -0
  45. data/lib/coradoc/document_manipulator.rb +203 -0
  46. data/lib/coradoc/errors.rb +312 -0
  47. data/lib/coradoc/format_module.rb +49 -0
  48. data/lib/coradoc/hooks.rb +176 -0
  49. data/lib/coradoc/input.rb +17 -7
  50. data/lib/coradoc/logger.rb +54 -0
  51. data/lib/coradoc/output.rb +17 -6
  52. data/lib/coradoc/performance_regression.rb +109 -0
  53. data/lib/coradoc/processor_registry.rb +50 -0
  54. data/lib/coradoc/query.rb +455 -0
  55. data/lib/coradoc/registry.rb +156 -0
  56. data/lib/coradoc/serializer/registry.rb +150 -0
  57. data/lib/coradoc/transform.rb +11 -0
  58. data/lib/coradoc/validation.rb +646 -0
  59. data/lib/coradoc/version.rb +1 -1
  60. data/lib/coradoc/visitor.rb +283 -0
  61. data/lib/coradoc.rb +40 -19
  62. metadata +67 -277
  63. data/.editorconfig +0 -15
  64. data/.envrc +0 -1
  65. data/.irbrc +0 -1
  66. data/.pryrc.sample +0 -1
  67. data/.rubocop.yml +0 -14
  68. data/.rubocop_todo.yml +0 -179
  69. data/CHANGELOG.md +0 -9
  70. data/CODE_OF_CONDUCT.md +0 -84
  71. data/Dockerfile +0 -19
  72. data/Gemfile +0 -16
  73. data/LICENSE.txt +0 -21
  74. data/Makefile +0 -35
  75. data/README.Docker.adoc +0 -57
  76. data/README.adoc +0 -119
  77. data/coradoc.gemspec +0 -40
  78. data/docker-compose.yml +0 -14
  79. data/exe/reverse_adoc +0 -81
  80. data/exe/w2a +0 -60
  81. data/flake.lock +0 -114
  82. data/flake.nix +0 -135
  83. data/lib/coradoc/converter.rb +0 -144
  84. data/lib/coradoc/document.rb +0 -77
  85. data/lib/coradoc/element/admonition.rb +0 -18
  86. data/lib/coradoc/element/attribute.rb +0 -36
  87. data/lib/coradoc/element/attribute_list.rb +0 -138
  88. data/lib/coradoc/element/audio.rb +0 -33
  89. data/lib/coradoc/element/author.rb +0 -24
  90. data/lib/coradoc/element/base.rb +0 -92
  91. data/lib/coradoc/element/bibliography.rb +0 -24
  92. data/lib/coradoc/element/bibliography_entry.rb +0 -24
  93. data/lib/coradoc/element/block/core.rb +0 -76
  94. data/lib/coradoc/element/block/example.rb +0 -23
  95. data/lib/coradoc/element/block/listing.rb +0 -21
  96. data/lib/coradoc/element/block/literal.rb +0 -21
  97. data/lib/coradoc/element/block/open.rb +0 -22
  98. data/lib/coradoc/element/block/pass.rb +0 -21
  99. data/lib/coradoc/element/block/quote.rb +0 -19
  100. data/lib/coradoc/element/block/reviewer_comment.rb +0 -19
  101. data/lib/coradoc/element/block/side.rb +0 -19
  102. data/lib/coradoc/element/block/sourcecode.rb +0 -21
  103. data/lib/coradoc/element/block.rb +0 -17
  104. data/lib/coradoc/element/break.rb +0 -11
  105. data/lib/coradoc/element/comment_block.rb +0 -22
  106. data/lib/coradoc/element/comment_line.rb +0 -18
  107. data/lib/coradoc/element/document_attributes.rb +0 -33
  108. data/lib/coradoc/element/header.rb +0 -22
  109. data/lib/coradoc/element/image/block_image.rb +0 -32
  110. data/lib/coradoc/element/image/core.rb +0 -58
  111. data/lib/coradoc/element/image/inline_image.rb +0 -12
  112. data/lib/coradoc/element/image.rb +0 -10
  113. data/lib/coradoc/element/include.rb +0 -18
  114. data/lib/coradoc/element/inline/anchor.rb +0 -19
  115. data/lib/coradoc/element/inline/attribute_reference.rb +0 -19
  116. data/lib/coradoc/element/inline/bold.rb +0 -25
  117. data/lib/coradoc/element/inline/cross_reference.rb +0 -46
  118. data/lib/coradoc/element/inline/footnote.rb +0 -24
  119. data/lib/coradoc/element/inline/hard_line_break.rb +0 -11
  120. data/lib/coradoc/element/inline/highlight.rb +0 -25
  121. data/lib/coradoc/element/inline/italic.rb +0 -25
  122. data/lib/coradoc/element/inline/link.rb +0 -42
  123. data/lib/coradoc/element/inline/monospace.rb +0 -25
  124. data/lib/coradoc/element/inline/quotation.rb +0 -20
  125. data/lib/coradoc/element/inline/small.rb +0 -19
  126. data/lib/coradoc/element/inline/span.rb +0 -37
  127. data/lib/coradoc/element/inline/subscript.rb +0 -20
  128. data/lib/coradoc/element/inline/superscript.rb +0 -20
  129. data/lib/coradoc/element/inline/underline.rb +0 -19
  130. data/lib/coradoc/element/inline.rb +0 -23
  131. data/lib/coradoc/element/list/core.rb +0 -51
  132. data/lib/coradoc/element/list/definition.rb +0 -29
  133. data/lib/coradoc/element/list/ordered.rb +0 -17
  134. data/lib/coradoc/element/list/unordered.rb +0 -17
  135. data/lib/coradoc/element/list.rb +0 -13
  136. data/lib/coradoc/element/list_item.rb +0 -98
  137. data/lib/coradoc/element/list_item_definition.rb +0 -32
  138. data/lib/coradoc/element/paragraph.rb +0 -37
  139. data/lib/coradoc/element/revision.rb +0 -27
  140. data/lib/coradoc/element/section.rb +0 -62
  141. data/lib/coradoc/element/table.rb +0 -91
  142. data/lib/coradoc/element/tag.rb +0 -19
  143. data/lib/coradoc/element/term.rb +0 -22
  144. data/lib/coradoc/element/text_element.rb +0 -92
  145. data/lib/coradoc/element/title.rb +0 -62
  146. data/lib/coradoc/element/video.rb +0 -50
  147. data/lib/coradoc/generator.rb +0 -19
  148. data/lib/coradoc/input/adoc.rb +0 -30
  149. data/lib/coradoc/input/docx.rb +0 -64
  150. data/lib/coradoc/input/html/LICENSE.txt +0 -25
  151. data/lib/coradoc/input/html/README.adoc +0 -308
  152. data/lib/coradoc/input/html/cleaner.rb +0 -142
  153. data/lib/coradoc/input/html/config.rb +0 -77
  154. data/lib/coradoc/input/html/converters/a.rb +0 -52
  155. data/lib/coradoc/input/html/converters/aside.rb +0 -16
  156. data/lib/coradoc/input/html/converters/audio.rb +0 -29
  157. data/lib/coradoc/input/html/converters/base.rb +0 -108
  158. data/lib/coradoc/input/html/converters/blockquote.rb +0 -22
  159. data/lib/coradoc/input/html/converters/br.rb +0 -15
  160. data/lib/coradoc/input/html/converters/bypass.rb +0 -81
  161. data/lib/coradoc/input/html/converters/code.rb +0 -23
  162. data/lib/coradoc/input/html/converters/div.rb +0 -19
  163. data/lib/coradoc/input/html/converters/dl.rb +0 -62
  164. data/lib/coradoc/input/html/converters/drop.rb +0 -26
  165. data/lib/coradoc/input/html/converters/em.rb +0 -21
  166. data/lib/coradoc/input/html/converters/figure.rb +0 -25
  167. data/lib/coradoc/input/html/converters/h.rb +0 -42
  168. data/lib/coradoc/input/html/converters/head.rb +0 -23
  169. data/lib/coradoc/input/html/converters/hr.rb +0 -15
  170. data/lib/coradoc/input/html/converters/ignore.rb +0 -20
  171. data/lib/coradoc/input/html/converters/img.rb +0 -110
  172. data/lib/coradoc/input/html/converters/li.rb +0 -17
  173. data/lib/coradoc/input/html/converters/mark.rb +0 -19
  174. data/lib/coradoc/input/html/converters/markup.rb +0 -31
  175. data/lib/coradoc/input/html/converters/math.rb +0 -38
  176. data/lib/coradoc/input/html/converters/ol.rb +0 -65
  177. data/lib/coradoc/input/html/converters/p.rb +0 -23
  178. data/lib/coradoc/input/html/converters/pass_through.rb +0 -17
  179. data/lib/coradoc/input/html/converters/pre.rb +0 -55
  180. data/lib/coradoc/input/html/converters/q.rb +0 -16
  181. data/lib/coradoc/input/html/converters/strong.rb +0 -20
  182. data/lib/coradoc/input/html/converters/sub.rb +0 -22
  183. data/lib/coradoc/input/html/converters/sup.rb +0 -22
  184. data/lib/coradoc/input/html/converters/table.rb +0 -319
  185. data/lib/coradoc/input/html/converters/td.rb +0 -81
  186. data/lib/coradoc/input/html/converters/text.rb +0 -32
  187. data/lib/coradoc/input/html/converters/th.rb +0 -18
  188. data/lib/coradoc/input/html/converters/tr.rb +0 -22
  189. data/lib/coradoc/input/html/converters/video.rb +0 -29
  190. data/lib/coradoc/input/html/converters.rb +0 -59
  191. data/lib/coradoc/input/html/errors.rb +0 -14
  192. data/lib/coradoc/input/html/html_converter.rb +0 -168
  193. data/lib/coradoc/input/html/plugin.rb +0 -131
  194. data/lib/coradoc/input/html/plugins/plateau.rb +0 -213
  195. data/lib/coradoc/input/html/postprocessor.rb +0 -220
  196. data/lib/coradoc/input/html.rb +0 -61
  197. data/lib/coradoc/legacy_parser.rb +0 -200
  198. data/lib/coradoc/oscal.rb +0 -99
  199. data/lib/coradoc/output/adoc.rb +0 -19
  200. data/lib/coradoc/output/coradoc_tree_debug.rb +0 -21
  201. data/lib/coradoc/parser/asciidoc/admonition.rb +0 -24
  202. data/lib/coradoc/parser/asciidoc/attribute_list.rb +0 -89
  203. data/lib/coradoc/parser/asciidoc/base.rb +0 -87
  204. data/lib/coradoc/parser/asciidoc/bibliography.rb +0 -29
  205. data/lib/coradoc/parser/asciidoc/block.rb +0 -94
  206. data/lib/coradoc/parser/asciidoc/citation.rb +0 -30
  207. data/lib/coradoc/parser/asciidoc/content.rb +0 -64
  208. data/lib/coradoc/parser/asciidoc/document_attributes.rb +0 -25
  209. data/lib/coradoc/parser/asciidoc/header.rb +0 -29
  210. data/lib/coradoc/parser/asciidoc/inline.rb +0 -195
  211. data/lib/coradoc/parser/asciidoc/list.rb +0 -115
  212. data/lib/coradoc/parser/asciidoc/paragraph.rb +0 -54
  213. data/lib/coradoc/parser/asciidoc/section.rb +0 -61
  214. data/lib/coradoc/parser/asciidoc/table.rb +0 -32
  215. data/lib/coradoc/parser/asciidoc/term.rb +0 -41
  216. data/lib/coradoc/parser/asciidoc/text.rb +0 -158
  217. data/lib/coradoc/parser/base.rb +0 -40
  218. data/lib/coradoc/parser.rb +0 -11
  219. data/lib/coradoc/reverse_adoc.rb +0 -18
  220. data/lib/coradoc/transformer.rb +0 -476
  221. data/lib/coradoc/util.rb +0 -12
  222. data/lib/reverse_adoc.rb +0 -20
  223. data/utils/inspect_asciidoc.rb +0 -29
  224. data/utils/parser_analyzer.rb +0 -66
  225. data/utils/round_trip.rb +0 -53
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ # Declared interface contract for format modules.
5
+ #
6
+ # Format modules registered via Coradoc.register_format must implement:
7
+ #
8
+ # Required:
9
+ # - parse_to_core(input, options={}) → CoreModel::Base
10
+ # - serialize(model, **options) → String
11
+ #
12
+ # Optional:
13
+ # - parse(input, options={}) → format-specific model
14
+ # - handles_model?(model) → Boolean
15
+ # - to_core(model) → CoreModel::Base
16
+ # - serialize? → Boolean
17
+ #
18
+ module FormatModule
19
+ MINIMUM_PARSE_METHODS = %i[parse_to_core parse].freeze
20
+ REQUIRED_METHODS = %i[serialize].freeze
21
+
22
+ # Default implementations for optional format module methods.
23
+ # Format modules are auto-extended with this during registration.
24
+ module Interface
25
+ def handles_model?(_model)
26
+ false
27
+ end
28
+
29
+ def serialize?
30
+ true
31
+ end
32
+ end
33
+
34
+ # Validate that a format module implements the minimum interface.
35
+ # Warns to $stderr if methods are missing. Returns true if valid.
36
+ def self.validate!(format_module, format_name)
37
+ has_parse = MINIMUM_PARSE_METHODS.any? { |m| format_module.public_methods.include?(m) }
38
+ has_serialize = REQUIRED_METHODS.all? { |m| format_module.public_methods.include?(m) }
39
+
40
+ return true if has_parse && has_serialize
41
+
42
+ missing = []
43
+ missing << 'parse_to_core or parse' unless has_parse
44
+ missing << 'serialize' unless has_serialize
45
+ warn "Coradoc: format :#{format_name} (#{format_module}) missing: #{missing.join(', ')}"
46
+ false
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,176 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securerandom'
4
+
5
+ module Coradoc
6
+ module Hooks
7
+ HOOK_POINTS = {
8
+ before_parse: 'Called before parsing text content. Receives (content, format:, **options). Should return modified content.',
9
+ after_parse: 'Called after parsing. Receives (model, format:, **options). Should return modified model.',
10
+ before_transform: 'Called before transforming model. Receives (model, direction:, **options). Should return modified model.',
11
+ after_transform: 'Called after transforming model. Receives (model, direction:, **options). Should return modified model.',
12
+ before_serialize: 'Called before serializing model. Receives (model, format:, **options). Should return modified model.',
13
+ after_serialize: 'Called after serializing. Receives (output, format:, **options). Should return modified output.',
14
+ on_error: 'Called when an error occurs. Receives (error, context). Can re-raise or return fallback value.'
15
+ }.freeze
16
+
17
+ class << self
18
+ attr_accessor :strict_mode
19
+
20
+ def register(hook_point, priority: 100, name: nil, &block)
21
+ validate_hook_point!(hook_point)
22
+ validate_block!(block, hook_point)
23
+
24
+ hook_id = name || generate_hook_id(hook_point)
25
+ registry[hook_point] ||= []
26
+ registry[hook_point] << {
27
+ id: hook_id,
28
+ priority: priority,
29
+ sequence: next_sequence,
30
+ callback: block
31
+ }
32
+ registry[hook_point].sort_by! { |h| [h[:priority], h[:sequence]] }
33
+ hook_id
34
+ end
35
+
36
+ def remove(hook_point, hook_id)
37
+ validate_hook_point!(hook_point)
38
+ return false unless registry[hook_point]
39
+
40
+ original_size = registry[hook_point].size
41
+ registry[hook_point].reject! { |h| h[:id] == hook_id }
42
+ registry[hook_point].size < original_size
43
+ end
44
+
45
+ def clear(hook_point)
46
+ validate_hook_point!(hook_point)
47
+ removed = registry[hook_point]&.size || 0
48
+ registry.delete(hook_point)
49
+ removed
50
+ end
51
+
52
+ def clear_all
53
+ total = registry.values.sum(&:size)
54
+ @registry = {}
55
+ @sequence_counter = nil
56
+ total
57
+ end
58
+
59
+ def registered?(hook_point)
60
+ validate_hook_point!(hook_point)
61
+ registry[hook_point]&.any? || false
62
+ end
63
+
64
+ def list(hook_point = nil)
65
+ if hook_point
66
+ validate_hook_point!(hook_point)
67
+ registry[hook_point]&.map { |h| h.merge(point: hook_point) } || []
68
+ else
69
+ registry.flat_map do |point, hooks|
70
+ hooks.map { |h| h.merge(point: point) }
71
+ end
72
+ end
73
+ end
74
+
75
+ def invoke(hook_point, *args, **kwargs)
76
+ validate_hook_point!(hook_point)
77
+ return args.first if args.one? && !registry[hook_point]&.any?
78
+
79
+ result = args.first
80
+ registry[hook_point].each do |hook|
81
+ result = invoke_hook(hook[:callback], result, args, kwargs)
82
+ end
83
+ result
84
+ end
85
+
86
+ def with_hooks(hook_point, *args, **kwargs)
87
+ validate_hook_point!(hook_point)
88
+
89
+ modified_args = invoke(hook_point, *args, **kwargs)
90
+ result = yield(*modified_args)
91
+
92
+ after_point = "after_#{hook_point.to_s.sub('before_', '')}".to_sym
93
+ result = invoke(after_point, result, **kwargs) if HOOK_POINTS.key?(after_point)
94
+
95
+ result
96
+ rescue StandardError => e
97
+ invoke_error_hooks(e, hook_point, args, kwargs)
98
+ end
99
+
100
+ def documentation(hook_point = nil)
101
+ if hook_point
102
+ validate_hook_point!(hook_point)
103
+ HOOK_POINTS[hook_point]
104
+ else
105
+ HOOK_POINTS.dup
106
+ end
107
+ end
108
+
109
+ private
110
+
111
+ def registry
112
+ @registry ||= {}
113
+ end
114
+
115
+ def next_sequence
116
+ @sequence_counter = (@sequence_counter || 0) + 1
117
+ end
118
+
119
+ def validate_hook_point!(hook_point)
120
+ return if HOOK_POINTS.key?(hook_point)
121
+
122
+ valid_points = HOOK_POINTS.keys.join(', ')
123
+ raise ArgumentError,
124
+ "Unknown hook point: #{hook_point}. Valid points: #{valid_points}"
125
+ end
126
+
127
+ def validate_block!(block, hook_point)
128
+ return if block
129
+
130
+ raise ArgumentError,
131
+ "Block required for hook registration: #{hook_point}"
132
+ end
133
+
134
+ def generate_hook_id(hook_point)
135
+ "#{hook_point}_#{SecureRandom.hex(8)}"
136
+ end
137
+
138
+ def invoke_hook(callback, result, _args, kwargs)
139
+ arity = callback.arity
140
+
141
+ if kwargs.empty?
142
+ case arity
143
+ when 0
144
+ callback.call
145
+ else
146
+ callback.call(result)
147
+ end
148
+ else
149
+ callback.call(result, **kwargs)
150
+ end
151
+ rescue StandardError => e
152
+ raise if @strict_mode
153
+
154
+ Coradoc::Logger.warn("Hook failed: #{e.message}")
155
+ result
156
+ end
157
+
158
+ def invoke_error_hooks(error, hook_point, args, kwargs)
159
+ context = {
160
+ hook_point: hook_point,
161
+ args: args,
162
+ kwargs: kwargs
163
+ }
164
+
165
+ if registry[:on_error]&.any?
166
+ registry[:on_error].each do |hook|
167
+ result = hook[:callback].call(error, context)
168
+ return result if result
169
+ end
170
+ end
171
+
172
+ raise error
173
+ end
174
+ end
175
+ end
176
+ end
data/lib/coradoc/input.rb CHANGED
@@ -1,12 +1,22 @@
1
- require "coradoc"
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'processor_registry'
2
4
 
3
5
  module Coradoc
6
+ # Input module for document ingestion
7
+ #
8
+ # Provides a unified interface for document input processing.
9
+ # Format-specific input processors register themselves with this module.
10
+ #
11
+ # @example Registering an input processor
12
+ # Coradoc::Input.define(MyHtmlProcessor)
13
+ #
14
+ # @example Using a registered processor
15
+ # processor = Coradoc::Input.for(:html)
16
+ # document = processor.convert(html_content)
17
+ #
4
18
  module Input
5
- @processors = {}
6
- extend Converter::CommonInputOutputMethods
19
+ extend ProcessorRegistry
20
+ self.error_label = 'input processor'
7
21
  end
8
22
  end
9
-
10
- require "coradoc/input/adoc"
11
- require "coradoc/input/docx"
12
- require "coradoc/input/html"
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ class Logger
5
+ BADGE = 'Coradoc'
6
+
7
+ COLORS = {
8
+ error: "\e[31m", # Red
9
+ info: "\e[34m", # Blue
10
+ reset: "\e[m", # Reset
11
+ success: "\e[32m", # Green
12
+ warn: "\e[33m", # Yellow
13
+ bold: "\e[1m",
14
+ unbold: "\e[22m"
15
+ }.freeze
16
+
17
+ def self.error(message)
18
+ log(message, :error)
19
+ end
20
+
21
+ def self.info(message)
22
+ log(message, :info)
23
+ end
24
+
25
+ def self.success(message)
26
+ log(message, :success)
27
+ end
28
+
29
+ def self.warn(message)
30
+ log(message, :warn)
31
+ end
32
+
33
+ def self.log(message, type)
34
+ Warning.warn format_message(message, type)
35
+ end
36
+
37
+ def self.format_message(message, type)
38
+ colorize(
39
+ "\n[#{BADGE}] #{COLORS[:bold]}#{type.upcase}#{COLORS[:unbold]}" \
40
+ ": #{message}\n",
41
+ type
42
+ )
43
+ end
44
+
45
+ def self.colorize(message, type)
46
+ io = type == :warn ? $stderr : $stdout
47
+ return message unless io.tty?
48
+
49
+ "#{COLORS[type]}#{message}#{COLORS[:reset]}"
50
+ end
51
+
52
+ private_class_method :log, :format_message, :colorize
53
+ end
54
+ end
@@ -1,11 +1,22 @@
1
- require "coradoc"
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'processor_registry'
2
4
 
3
5
  module Coradoc
6
+ # Output module for document serialization
7
+ #
8
+ # Provides a unified interface for document output processing.
9
+ # Format-specific output processors register themselves with this module.
10
+ #
11
+ # @example Registering an output processor
12
+ # Coradoc::Output.define(MyHtmlProcessor)
13
+ #
14
+ # @example Using a registered processor
15
+ # processor = Coradoc::Output.get(:html_static)
16
+ # html = processor.processor_execute(document, {})
17
+ #
4
18
  module Output
5
- @processors = {}
6
- extend Converter::CommonInputOutputMethods
19
+ extend ProcessorRegistry
20
+ self.error_label = 'output processor'
7
21
  end
8
22
  end
9
-
10
- require "coradoc/output/adoc"
11
- require "coradoc/output/coradoc_tree_debug"
@@ -0,0 +1,109 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'benchmark'
4
+ require 'json'
5
+
6
+ module Coradoc
7
+ module PerformanceRegression
8
+ THRESHOLDS = {
9
+ markdown_parse_small: 2.0,
10
+ markdown_parse_medium: 3.0,
11
+ asciidoc_parse: 5.0,
12
+ html_serialize: 5.0,
13
+ md_to_html: 10.0,
14
+ adoc_to_html: 10.0,
15
+ core_model_creation: 1.0
16
+ }.freeze
17
+
18
+ BenchmarkResult = Struct.new(:name, :duration, :iterations, :threshold, keyword_init: true) do
19
+ def to_h
20
+ { name:, duration:, iterations:, threshold:, passed: passed? }
21
+ end
22
+
23
+ def passed?
24
+ duration < threshold
25
+ end
26
+ end
27
+
28
+ ComparisonResult = Struct.new(:name, :duration, :baseline, keyword_init: true) do
29
+ def regressed?(pct = 0.2)
30
+ return false if baseline.nil? || baseline.zero?
31
+
32
+ (duration - baseline).abs / baseline > pct
33
+ end
34
+ end
35
+
36
+ class << self
37
+ def run_all(iterations: 3)
38
+ benchmarks = build_benchmarks
39
+ benchmarks.map do |name, threshold, block|
40
+ times = []
41
+ iterations.times { times << Benchmark.realtime { block.call } }
42
+ avg = times.sum / times.size
43
+ BenchmarkResult.new(name:, duration: avg, iterations:, threshold:)
44
+ end
45
+ end
46
+
47
+ def run_all_with_summary(iterations: 3)
48
+ results = run_all(iterations:)
49
+ failed = results.count { |r| !r.passed? }
50
+ { results:, failed_count: failed, total: results.size }
51
+ end
52
+
53
+ def print_results(summary)
54
+ results = summary[:results]
55
+ results.each do |r|
56
+ status = r.passed? ? 'PASS' : 'FAIL'
57
+ puts " #{status} #{r.name}: #{r.duration.round(4)}s (threshold: #{r.threshold}s)"
58
+ end
59
+ puts
60
+ puts "#{summary[:total] - summary[:failed_count]}/#{summary[:total]} passed"
61
+ end
62
+
63
+ def compare_with_baseline(baseline_path, iterations: 3)
64
+ return [] unless File.exist?(baseline_path)
65
+
66
+ baseline_data = JSON.parse(File.read(baseline_path))
67
+ baseline_map = baseline_data.each_with_object({}) { |d, m| m[d['name']] = d['duration'] }
68
+
69
+ results = run_all(iterations:)
70
+ results.map do |r|
71
+ ComparisonResult.new(name: r.name, duration: r.duration, baseline: baseline_map[r.name])
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def build_benchmarks
78
+ sample = {
79
+ markdown_small: "# Title\n\n#{'Paragraph ' * 10}",
80
+ markdown_medium: "# Title\n\n#{'Content. ' * 100}\n\n## Section\n\n#{'More. ' * 100}",
81
+ asciidoc: "= Title\nAuthor\n\n== Section\n\n#{'Paragraph content. ' * 50}",
82
+ html_doc: Coradoc::CoreModel::DocumentElement.new(
83
+ title: 'Bench',
84
+ children: [
85
+ Coradoc::CoreModel::Block.new(element_type: 'paragraph', content: 'Test')
86
+ ]
87
+ )
88
+ }
89
+
90
+ [
91
+ [:markdown_parse_small, THRESHOLDS[:markdown_parse_small],
92
+ -> { Coradoc::Markdown.parse(sample[:markdown_small]) }],
93
+ [:markdown_parse_medium, THRESHOLDS[:markdown_parse_medium],
94
+ -> { Coradoc::Markdown.parse(sample[:markdown_medium]) }],
95
+ [:asciidoc_parse, THRESHOLDS[:asciidoc_parse],
96
+ -> { Coradoc.parse(sample[:asciidoc], format: :asciidoc) }],
97
+ [:html_serialize, THRESHOLDS[:html_serialize],
98
+ -> { Coradoc::Html.serialize_static(sample[:html_doc]) }],
99
+ [:md_to_html, THRESHOLDS[:md_to_html],
100
+ -> { Coradoc.convert(sample[:markdown_small], from: :markdown, to: :html) }],
101
+ [:adoc_to_html, THRESHOLDS[:adoc_to_html],
102
+ -> { Coradoc.convert(sample[:asciidoc], from: :asciidoc, to: :html) }],
103
+ [:core_model_creation, THRESHOLDS[:core_model_creation],
104
+ -> { 1000.times { Coradoc::CoreModel::Block.new(element_type: 'paragraph', content: 'x') } }]
105
+ ]
106
+ end
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Coradoc
4
+ # Module adapter that gives a module Registry-backed processor methods.
5
+ #
6
+ # Both Input and Output extend this module to get processor registration,
7
+ # lookup, and dispatch methods. All state is stored in a Registry instance.
8
+ #
9
+ # @api private
10
+ #
11
+ # @example
12
+ # module Input
13
+ # extend ProcessorRegistry
14
+ # self.error_label = "input processor"
15
+ # end
16
+ module ProcessorRegistry
17
+ def error_label=(label)
18
+ @error_label = label
19
+ end
20
+
21
+ def registry
22
+ @registry ||= Registry.new(error_label: @error_label)
23
+ end
24
+
25
+ def define(processor, **options)
26
+ registry.define(processor, **options)
27
+ end
28
+
29
+ def get(id)
30
+ registry.get(id)
31
+ end
32
+ alias [] get
33
+
34
+ def registered?(id)
35
+ registry.registered?(id)
36
+ end
37
+
38
+ def processors
39
+ registry.items
40
+ end
41
+
42
+ def for_file(filename)
43
+ registry.for_file(filename)
44
+ end
45
+
46
+ def process(content, options = {})
47
+ registry.process(content, options)
48
+ end
49
+ end
50
+ end