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.
- checksums.yaml +4 -4
- data/.rspec +1 -1
- data/Rakefile +3 -12
- data/exe/coradoc +21 -2
- data/lib/coradoc/cli.rb +185 -91
- data/lib/coradoc/configurable.rb +527 -0
- data/lib/coradoc/coradoc.rb +463 -0
- data/lib/coradoc/core_model/annotation_block.rb +57 -0
- data/lib/coradoc/core_model/base.rb +172 -0
- data/lib/coradoc/core_model/bibliography.rb +41 -0
- data/lib/coradoc/core_model/bibliography_entry.rb +48 -0
- data/lib/coradoc/core_model/block.rb +63 -0
- data/lib/coradoc/core_model/children_content.rb +53 -0
- data/lib/coradoc/core_model/comment_block.rb +10 -0
- data/lib/coradoc/core_model/definition_item.rb +46 -0
- data/lib/coradoc/core_model/definition_list.rb +28 -0
- data/lib/coradoc/core_model/element_attribute.rb +26 -0
- data/lib/coradoc/core_model/example_block.rb +10 -0
- data/lib/coradoc/core_model/footnote.rb +92 -0
- data/lib/coradoc/core_model/horizontal_rule_block.rb +10 -0
- data/lib/coradoc/core_model/id_generator.rb +16 -0
- data/lib/coradoc/core_model/image.rb +66 -0
- data/lib/coradoc/core_model/inline_element.rb +140 -0
- data/lib/coradoc/core_model/list_block.rb +135 -0
- data/lib/coradoc/core_model/list_item.rb +142 -0
- data/lib/coradoc/core_model/listing_block.rb +13 -0
- data/lib/coradoc/core_model/literal_block.rb +10 -0
- data/lib/coradoc/core_model/metadata.rb +79 -0
- data/lib/coradoc/core_model/open_block.rb +10 -0
- data/lib/coradoc/core_model/paragraph_block.rb +10 -0
- data/lib/coradoc/core_model/pass_block.rb +10 -0
- data/lib/coradoc/core_model/quote_block.rb +12 -0
- data/lib/coradoc/core_model/reviewer_block.rb +10 -0
- data/lib/coradoc/core_model/sidebar_block.rb +10 -0
- data/lib/coradoc/core_model/source_block.rb +10 -0
- data/lib/coradoc/core_model/structural_element.rb +94 -0
- data/lib/coradoc/core_model/table.rb +148 -0
- data/lib/coradoc/core_model/term.rb +53 -0
- data/lib/coradoc/core_model/text_content.rb +22 -0
- data/lib/coradoc/core_model/toc.rb +105 -0
- data/lib/coradoc/core_model/toc_generator.rb +151 -0
- data/lib/coradoc/core_model/verse_block.rb +12 -0
- data/lib/coradoc/core_model.rb +77 -0
- data/lib/coradoc/document_builder.rb +184 -0
- data/lib/coradoc/document_manipulator.rb +203 -0
- data/lib/coradoc/errors.rb +312 -0
- data/lib/coradoc/format_module.rb +49 -0
- data/lib/coradoc/hooks.rb +176 -0
- data/lib/coradoc/input.rb +17 -7
- data/lib/coradoc/logger.rb +54 -0
- data/lib/coradoc/output.rb +17 -6
- data/lib/coradoc/performance_regression.rb +109 -0
- data/lib/coradoc/processor_registry.rb +50 -0
- data/lib/coradoc/query.rb +455 -0
- data/lib/coradoc/registry.rb +156 -0
- data/lib/coradoc/serializer/registry.rb +150 -0
- data/lib/coradoc/transform.rb +11 -0
- data/lib/coradoc/validation.rb +646 -0
- data/lib/coradoc/version.rb +1 -1
- data/lib/coradoc/visitor.rb +283 -0
- data/lib/coradoc.rb +40 -19
- metadata +67 -277
- data/.editorconfig +0 -15
- data/.envrc +0 -1
- data/.irbrc +0 -1
- data/.pryrc.sample +0 -1
- data/.rubocop.yml +0 -14
- data/.rubocop_todo.yml +0 -179
- data/CHANGELOG.md +0 -9
- data/CODE_OF_CONDUCT.md +0 -84
- data/Dockerfile +0 -19
- data/Gemfile +0 -16
- data/LICENSE.txt +0 -21
- data/Makefile +0 -35
- data/README.Docker.adoc +0 -57
- data/README.adoc +0 -119
- data/coradoc.gemspec +0 -40
- data/docker-compose.yml +0 -14
- data/exe/reverse_adoc +0 -81
- data/exe/w2a +0 -60
- data/flake.lock +0 -114
- data/flake.nix +0 -135
- data/lib/coradoc/converter.rb +0 -144
- data/lib/coradoc/document.rb +0 -77
- data/lib/coradoc/element/admonition.rb +0 -18
- data/lib/coradoc/element/attribute.rb +0 -36
- data/lib/coradoc/element/attribute_list.rb +0 -138
- data/lib/coradoc/element/audio.rb +0 -33
- data/lib/coradoc/element/author.rb +0 -24
- data/lib/coradoc/element/base.rb +0 -92
- data/lib/coradoc/element/bibliography.rb +0 -24
- data/lib/coradoc/element/bibliography_entry.rb +0 -24
- data/lib/coradoc/element/block/core.rb +0 -76
- data/lib/coradoc/element/block/example.rb +0 -23
- data/lib/coradoc/element/block/listing.rb +0 -21
- data/lib/coradoc/element/block/literal.rb +0 -21
- data/lib/coradoc/element/block/open.rb +0 -22
- data/lib/coradoc/element/block/pass.rb +0 -21
- data/lib/coradoc/element/block/quote.rb +0 -19
- data/lib/coradoc/element/block/reviewer_comment.rb +0 -19
- data/lib/coradoc/element/block/side.rb +0 -19
- data/lib/coradoc/element/block/sourcecode.rb +0 -21
- data/lib/coradoc/element/block.rb +0 -17
- data/lib/coradoc/element/break.rb +0 -11
- data/lib/coradoc/element/comment_block.rb +0 -22
- data/lib/coradoc/element/comment_line.rb +0 -18
- data/lib/coradoc/element/document_attributes.rb +0 -33
- data/lib/coradoc/element/header.rb +0 -22
- data/lib/coradoc/element/image/block_image.rb +0 -32
- data/lib/coradoc/element/image/core.rb +0 -58
- data/lib/coradoc/element/image/inline_image.rb +0 -12
- data/lib/coradoc/element/image.rb +0 -10
- data/lib/coradoc/element/include.rb +0 -18
- data/lib/coradoc/element/inline/anchor.rb +0 -19
- data/lib/coradoc/element/inline/attribute_reference.rb +0 -19
- data/lib/coradoc/element/inline/bold.rb +0 -25
- data/lib/coradoc/element/inline/cross_reference.rb +0 -46
- data/lib/coradoc/element/inline/footnote.rb +0 -24
- data/lib/coradoc/element/inline/hard_line_break.rb +0 -11
- data/lib/coradoc/element/inline/highlight.rb +0 -25
- data/lib/coradoc/element/inline/italic.rb +0 -25
- data/lib/coradoc/element/inline/link.rb +0 -42
- data/lib/coradoc/element/inline/monospace.rb +0 -25
- data/lib/coradoc/element/inline/quotation.rb +0 -20
- data/lib/coradoc/element/inline/small.rb +0 -19
- data/lib/coradoc/element/inline/span.rb +0 -37
- data/lib/coradoc/element/inline/subscript.rb +0 -20
- data/lib/coradoc/element/inline/superscript.rb +0 -20
- data/lib/coradoc/element/inline/underline.rb +0 -19
- data/lib/coradoc/element/inline.rb +0 -23
- data/lib/coradoc/element/list/core.rb +0 -51
- data/lib/coradoc/element/list/definition.rb +0 -29
- data/lib/coradoc/element/list/ordered.rb +0 -17
- data/lib/coradoc/element/list/unordered.rb +0 -17
- data/lib/coradoc/element/list.rb +0 -13
- data/lib/coradoc/element/list_item.rb +0 -98
- data/lib/coradoc/element/list_item_definition.rb +0 -32
- data/lib/coradoc/element/paragraph.rb +0 -37
- data/lib/coradoc/element/revision.rb +0 -27
- data/lib/coradoc/element/section.rb +0 -62
- data/lib/coradoc/element/table.rb +0 -91
- data/lib/coradoc/element/tag.rb +0 -19
- data/lib/coradoc/element/term.rb +0 -22
- data/lib/coradoc/element/text_element.rb +0 -92
- data/lib/coradoc/element/title.rb +0 -62
- data/lib/coradoc/element/video.rb +0 -50
- data/lib/coradoc/generator.rb +0 -19
- data/lib/coradoc/input/adoc.rb +0 -30
- data/lib/coradoc/input/docx.rb +0 -64
- data/lib/coradoc/input/html/LICENSE.txt +0 -25
- data/lib/coradoc/input/html/README.adoc +0 -308
- data/lib/coradoc/input/html/cleaner.rb +0 -142
- data/lib/coradoc/input/html/config.rb +0 -77
- data/lib/coradoc/input/html/converters/a.rb +0 -52
- data/lib/coradoc/input/html/converters/aside.rb +0 -16
- data/lib/coradoc/input/html/converters/audio.rb +0 -29
- data/lib/coradoc/input/html/converters/base.rb +0 -108
- data/lib/coradoc/input/html/converters/blockquote.rb +0 -22
- data/lib/coradoc/input/html/converters/br.rb +0 -15
- data/lib/coradoc/input/html/converters/bypass.rb +0 -81
- data/lib/coradoc/input/html/converters/code.rb +0 -23
- data/lib/coradoc/input/html/converters/div.rb +0 -19
- data/lib/coradoc/input/html/converters/dl.rb +0 -62
- data/lib/coradoc/input/html/converters/drop.rb +0 -26
- data/lib/coradoc/input/html/converters/em.rb +0 -21
- data/lib/coradoc/input/html/converters/figure.rb +0 -25
- data/lib/coradoc/input/html/converters/h.rb +0 -42
- data/lib/coradoc/input/html/converters/head.rb +0 -23
- data/lib/coradoc/input/html/converters/hr.rb +0 -15
- data/lib/coradoc/input/html/converters/ignore.rb +0 -20
- data/lib/coradoc/input/html/converters/img.rb +0 -110
- data/lib/coradoc/input/html/converters/li.rb +0 -17
- data/lib/coradoc/input/html/converters/mark.rb +0 -19
- data/lib/coradoc/input/html/converters/markup.rb +0 -31
- data/lib/coradoc/input/html/converters/math.rb +0 -38
- data/lib/coradoc/input/html/converters/ol.rb +0 -65
- data/lib/coradoc/input/html/converters/p.rb +0 -23
- data/lib/coradoc/input/html/converters/pass_through.rb +0 -17
- data/lib/coradoc/input/html/converters/pre.rb +0 -55
- data/lib/coradoc/input/html/converters/q.rb +0 -16
- data/lib/coradoc/input/html/converters/strong.rb +0 -20
- data/lib/coradoc/input/html/converters/sub.rb +0 -22
- data/lib/coradoc/input/html/converters/sup.rb +0 -22
- data/lib/coradoc/input/html/converters/table.rb +0 -319
- data/lib/coradoc/input/html/converters/td.rb +0 -81
- data/lib/coradoc/input/html/converters/text.rb +0 -32
- data/lib/coradoc/input/html/converters/th.rb +0 -18
- data/lib/coradoc/input/html/converters/tr.rb +0 -22
- data/lib/coradoc/input/html/converters/video.rb +0 -29
- data/lib/coradoc/input/html/converters.rb +0 -59
- data/lib/coradoc/input/html/errors.rb +0 -14
- data/lib/coradoc/input/html/html_converter.rb +0 -168
- data/lib/coradoc/input/html/plugin.rb +0 -131
- data/lib/coradoc/input/html/plugins/plateau.rb +0 -213
- data/lib/coradoc/input/html/postprocessor.rb +0 -220
- data/lib/coradoc/input/html.rb +0 -61
- data/lib/coradoc/legacy_parser.rb +0 -200
- data/lib/coradoc/oscal.rb +0 -99
- data/lib/coradoc/output/adoc.rb +0 -19
- data/lib/coradoc/output/coradoc_tree_debug.rb +0 -21
- data/lib/coradoc/parser/asciidoc/admonition.rb +0 -24
- data/lib/coradoc/parser/asciidoc/attribute_list.rb +0 -89
- data/lib/coradoc/parser/asciidoc/base.rb +0 -87
- data/lib/coradoc/parser/asciidoc/bibliography.rb +0 -29
- data/lib/coradoc/parser/asciidoc/block.rb +0 -94
- data/lib/coradoc/parser/asciidoc/citation.rb +0 -30
- data/lib/coradoc/parser/asciidoc/content.rb +0 -64
- data/lib/coradoc/parser/asciidoc/document_attributes.rb +0 -25
- data/lib/coradoc/parser/asciidoc/header.rb +0 -29
- data/lib/coradoc/parser/asciidoc/inline.rb +0 -195
- data/lib/coradoc/parser/asciidoc/list.rb +0 -115
- data/lib/coradoc/parser/asciidoc/paragraph.rb +0 -54
- data/lib/coradoc/parser/asciidoc/section.rb +0 -61
- data/lib/coradoc/parser/asciidoc/table.rb +0 -32
- data/lib/coradoc/parser/asciidoc/term.rb +0 -41
- data/lib/coradoc/parser/asciidoc/text.rb +0 -158
- data/lib/coradoc/parser/base.rb +0 -40
- data/lib/coradoc/parser.rb +0 -11
- data/lib/coradoc/reverse_adoc.rb +0 -18
- data/lib/coradoc/transformer.rb +0 -476
- data/lib/coradoc/util.rb +0 -12
- data/lib/reverse_adoc.rb +0 -20
- data/utils/inspect_asciidoc.rb +0 -29
- data/utils/parser_analyzer.rb +0 -66
- data/utils/round_trip.rb +0 -53
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Coradoc
|
|
4
|
+
# Global configuration system for Coradoc.
|
|
5
|
+
#
|
|
6
|
+
# Provides centralized configuration management with support for:
|
|
7
|
+
# - Configuration files (.coradoc.yml)
|
|
8
|
+
# - Environment-specific settings
|
|
9
|
+
# - Per-module configuration merging
|
|
10
|
+
# - Validation of configuration values
|
|
11
|
+
#
|
|
12
|
+
# @example Basic configuration
|
|
13
|
+
# Coradoc.configure do |config|
|
|
14
|
+
# config.default_format = :asciidoc
|
|
15
|
+
# config.cache.enabled = true
|
|
16
|
+
# config.cache.max_size = 1000
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# @example Environment-specific configuration
|
|
20
|
+
# Coradoc.configure do |config|
|
|
21
|
+
# config.environment = ENV.fetch("RACK_ENV", "development")
|
|
22
|
+
# config.cache.enabled = config.production?
|
|
23
|
+
# end
|
|
24
|
+
#
|
|
25
|
+
# @example Loading from file
|
|
26
|
+
# Coradoc::Configuration.load_file(".coradoc.yml")
|
|
27
|
+
#
|
|
28
|
+
module Configurable
|
|
29
|
+
# Configuration error
|
|
30
|
+
class ConfigurationError < Coradoc::Error; end
|
|
31
|
+
|
|
32
|
+
# Base class for configuration sections
|
|
33
|
+
class ConfigSection
|
|
34
|
+
# @return [Hash] Raw configuration values
|
|
35
|
+
attr_reader :options
|
|
36
|
+
|
|
37
|
+
def self.symbolize_keys(hash)
|
|
38
|
+
return {} unless hash.is_a?(Hash)
|
|
39
|
+
|
|
40
|
+
hash.transform_keys(&:to_sym)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Create a configuration section
|
|
44
|
+
#
|
|
45
|
+
# @param options [Hash] Configuration options
|
|
46
|
+
def initialize(options = {})
|
|
47
|
+
@options = symbolize_keys(options)
|
|
48
|
+
after_initialize
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Hook for subclass initialization
|
|
52
|
+
def after_initialize; end
|
|
53
|
+
|
|
54
|
+
# Get a configuration value
|
|
55
|
+
#
|
|
56
|
+
# @param key [Symbol] Configuration key
|
|
57
|
+
# @return [Object] Configuration value
|
|
58
|
+
def [](key)
|
|
59
|
+
@options[key]
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Set a configuration value
|
|
63
|
+
#
|
|
64
|
+
# @param key [Symbol] Configuration key
|
|
65
|
+
# @param value [Object] Configuration value
|
|
66
|
+
def []=(key, value)
|
|
67
|
+
@options[key] = value
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Merge options into this section
|
|
71
|
+
#
|
|
72
|
+
# @param other [Hash, ConfigSection] Options to merge
|
|
73
|
+
# @return [void]
|
|
74
|
+
def merge!(other)
|
|
75
|
+
other_options = other.is_a?(ConfigSection) ? other.options : other
|
|
76
|
+
other_options = symbolize_keys(other_options)
|
|
77
|
+
@options.merge!(other_options)
|
|
78
|
+
# Apply merged options to accessors
|
|
79
|
+
apply_options(other_options)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Convert to hash
|
|
83
|
+
#
|
|
84
|
+
# @return [Hash]
|
|
85
|
+
def to_h
|
|
86
|
+
@options.transform_values do |v|
|
|
87
|
+
v.is_a?(ConfigSection) ? v.to_h : v
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def symbolize_keys(hash)
|
|
94
|
+
self.class.symbolize_keys(hash)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Apply options to instance variables
|
|
98
|
+
# Override in subclasses for custom handling
|
|
99
|
+
def apply_options(options)
|
|
100
|
+
options.each do |key, value|
|
|
101
|
+
setter = "#{key}="
|
|
102
|
+
instance_variable_set("@#{key}", value) if public_methods.include?(setter.to_sym)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Cache configuration section
|
|
108
|
+
class CacheConfig < ConfigSection
|
|
109
|
+
# @return [Boolean] Whether caching is enabled
|
|
110
|
+
attr_accessor :enabled
|
|
111
|
+
|
|
112
|
+
# @return [Integer] Maximum cache size (number of entries)
|
|
113
|
+
attr_accessor :max_size
|
|
114
|
+
|
|
115
|
+
# @return [Integer] Cache TTL in seconds (0 = no expiry)
|
|
116
|
+
attr_accessor :ttl
|
|
117
|
+
|
|
118
|
+
# @return [Symbol] Cache storage backend (:memory, :file, :redis)
|
|
119
|
+
attr_accessor :backend
|
|
120
|
+
|
|
121
|
+
# @return [String, nil] Cache directory for file backend
|
|
122
|
+
attr_accessor :cache_dir
|
|
123
|
+
|
|
124
|
+
def initialize(options = {})
|
|
125
|
+
super
|
|
126
|
+
@enabled = @options.fetch(:enabled, true)
|
|
127
|
+
@max_size = @options.fetch(:max_size, 1000)
|
|
128
|
+
@ttl = @options.fetch(:ttl, 0)
|
|
129
|
+
@backend = @options.fetch(:backend, :memory)
|
|
130
|
+
@cache_dir = @options.fetch(:cache_dir, nil)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Parser configuration section
|
|
135
|
+
class ParserConfig < ConfigSection
|
|
136
|
+
# @return [Boolean] Enable parser caching
|
|
137
|
+
attr_accessor :cache_enabled
|
|
138
|
+
|
|
139
|
+
# @return [Boolean] Strict parsing mode
|
|
140
|
+
attr_accessor :strict_mode
|
|
141
|
+
|
|
142
|
+
# @return [Boolean] Include source location in AST
|
|
143
|
+
attr_accessor :include_source_loc
|
|
144
|
+
|
|
145
|
+
# @return [Integer] Maximum nesting depth
|
|
146
|
+
attr_accessor :max_nesting_depth
|
|
147
|
+
|
|
148
|
+
def initialize(options = {})
|
|
149
|
+
super
|
|
150
|
+
@cache_enabled = @options.fetch(:cache_enabled, true)
|
|
151
|
+
@strict_mode = @options.fetch(:strict_mode, false)
|
|
152
|
+
@include_source_loc = @options.fetch(:include_source_loc, true)
|
|
153
|
+
@max_nesting_depth = @options.fetch(:max_nesting_depth, 100)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Transformer configuration section
|
|
158
|
+
class TransformerConfig < ConfigSection
|
|
159
|
+
# @return [Boolean] Enable transformation caching
|
|
160
|
+
attr_accessor :cache_enabled
|
|
161
|
+
|
|
162
|
+
# @return [Boolean] Preserve unknown elements
|
|
163
|
+
attr_accessor :preserve_unknown
|
|
164
|
+
|
|
165
|
+
# @return [Boolean] Validate after transformation
|
|
166
|
+
attr_accessor :validate_output
|
|
167
|
+
|
|
168
|
+
# @return [Array<Symbol>] Enabled transformers
|
|
169
|
+
attr_accessor :enabled_transformers
|
|
170
|
+
|
|
171
|
+
def initialize(options = {})
|
|
172
|
+
super
|
|
173
|
+
@cache_enabled = @options.fetch(:cache_enabled, true)
|
|
174
|
+
@preserve_unknown = @options.fetch(:preserve_unknown, true)
|
|
175
|
+
@validate_output = @options.fetch(:validate_output, false)
|
|
176
|
+
@enabled_transformers = @options.fetch(:enabled_transformers, [])
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Output configuration section
|
|
181
|
+
class OutputConfig < ConfigSection
|
|
182
|
+
# @return [Symbol] Default output format
|
|
183
|
+
attr_accessor :default_format
|
|
184
|
+
|
|
185
|
+
# @return [Boolean] Pretty print output
|
|
186
|
+
attr_accessor :pretty_print
|
|
187
|
+
|
|
188
|
+
# @return [Integer] Line width for text output
|
|
189
|
+
attr_accessor :line_width
|
|
190
|
+
|
|
191
|
+
# @return [String] Indentation string
|
|
192
|
+
attr_accessor :indent
|
|
193
|
+
|
|
194
|
+
# @return [Boolean] Include metadata in output
|
|
195
|
+
attr_accessor :include_metadata
|
|
196
|
+
|
|
197
|
+
def initialize(options = {})
|
|
198
|
+
super
|
|
199
|
+
@default_format = @options.fetch(:default_format, :html)
|
|
200
|
+
@pretty_print = @options.fetch(:pretty_print, true)
|
|
201
|
+
@line_width = @options.fetch(:line_width, 80)
|
|
202
|
+
@indent = @options.fetch(:indent, ' ')
|
|
203
|
+
@include_metadata = @options.fetch(:include_metadata, false)
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Logging configuration section
|
|
208
|
+
class LoggingConfig < ConfigSection
|
|
209
|
+
# @return [Symbol] Log level (:debug, :info, :warn, :error)
|
|
210
|
+
attr_accessor :level
|
|
211
|
+
|
|
212
|
+
# @return [Boolean] Include timestamps
|
|
213
|
+
attr_accessor :timestamps
|
|
214
|
+
|
|
215
|
+
# @return [IO, nil] Log output destination
|
|
216
|
+
attr_accessor :output
|
|
217
|
+
|
|
218
|
+
# @return [Boolean] Colorize output
|
|
219
|
+
attr_accessor :colorize
|
|
220
|
+
|
|
221
|
+
def initialize(options = {})
|
|
222
|
+
super
|
|
223
|
+
@level = @options.fetch(:level, :info)
|
|
224
|
+
@timestamps = @options.fetch(:timestamps, true)
|
|
225
|
+
@output = @options.fetch(:output, $stderr)
|
|
226
|
+
@colorize = @options.fetch(:colorize, true)
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# Main configuration class
|
|
231
|
+
class Configuration
|
|
232
|
+
# @return [String] Current environment
|
|
233
|
+
attr_accessor :environment
|
|
234
|
+
|
|
235
|
+
# @return [CacheConfig] Cache configuration
|
|
236
|
+
attr_reader :cache
|
|
237
|
+
|
|
238
|
+
# @return [ParserConfig] Parser configuration
|
|
239
|
+
attr_reader :parser
|
|
240
|
+
|
|
241
|
+
# @return [TransformerConfig] Transformer configuration
|
|
242
|
+
attr_reader :transformer
|
|
243
|
+
|
|
244
|
+
# @return [OutputConfig] Output configuration
|
|
245
|
+
attr_reader :output
|
|
246
|
+
|
|
247
|
+
# @return [LoggingConfig] Logging configuration
|
|
248
|
+
attr_reader :logging
|
|
249
|
+
|
|
250
|
+
# @return [Hash] Custom configuration values
|
|
251
|
+
attr_reader :custom
|
|
252
|
+
|
|
253
|
+
# Create a new configuration
|
|
254
|
+
#
|
|
255
|
+
# @param options [Hash] Configuration options
|
|
256
|
+
def initialize(options = {})
|
|
257
|
+
options = symbolize_keys(options)
|
|
258
|
+
@environment = options.fetch(:environment, detect_environment)
|
|
259
|
+
@cache = CacheConfig.new(options[:cache] || {})
|
|
260
|
+
@parser = ParserConfig.new(options[:parser] || {})
|
|
261
|
+
@transformer = TransformerConfig.new(options[:transformer] || {})
|
|
262
|
+
@output = OutputConfig.new(options[:output] || {})
|
|
263
|
+
@logging = LoggingConfig.new(options[:logging] || {})
|
|
264
|
+
@custom = options.fetch(:custom, {})
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Check if running in development environment
|
|
268
|
+
#
|
|
269
|
+
# @return [Boolean]
|
|
270
|
+
def development?
|
|
271
|
+
@environment == 'development'
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Check if running in production environment
|
|
275
|
+
#
|
|
276
|
+
# @return [Boolean]
|
|
277
|
+
def production?
|
|
278
|
+
@environment == 'production'
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# Check if running in test environment
|
|
282
|
+
#
|
|
283
|
+
# @return [Boolean]
|
|
284
|
+
def test?
|
|
285
|
+
@environment == 'test'
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
# Get a custom configuration value
|
|
289
|
+
#
|
|
290
|
+
# @param key [Symbol] Configuration key
|
|
291
|
+
# @return [Object] Configuration value
|
|
292
|
+
def [](key)
|
|
293
|
+
@custom[key.to_sym]
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Set a custom configuration value
|
|
297
|
+
#
|
|
298
|
+
# @param key [Symbol] Configuration key
|
|
299
|
+
# @param value [Object] Configuration value
|
|
300
|
+
def []=(key, value)
|
|
301
|
+
@custom[key.to_sym] = value
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# Merge another configuration into this one
|
|
305
|
+
#
|
|
306
|
+
# @param other [Configuration, Hash] Configuration to merge
|
|
307
|
+
# @return [void]
|
|
308
|
+
def merge!(other)
|
|
309
|
+
case other
|
|
310
|
+
when Configuration
|
|
311
|
+
@environment = other.environment if other.environment != detect_environment
|
|
312
|
+
@cache.merge!(other.cache)
|
|
313
|
+
@parser.merge!(other.parser)
|
|
314
|
+
@transformer.merge!(other.transformer)
|
|
315
|
+
@output.merge!(other.output)
|
|
316
|
+
@logging.merge!(other.logging)
|
|
317
|
+
@custom.merge!(other.custom)
|
|
318
|
+
when Hash
|
|
319
|
+
merge_hash(other)
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
# Create a copy of this configuration
|
|
324
|
+
#
|
|
325
|
+
# @return [Configuration]
|
|
326
|
+
def dup
|
|
327
|
+
self.class.new(to_h)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
# Convert configuration to hash
|
|
331
|
+
#
|
|
332
|
+
# @return [Hash]
|
|
333
|
+
def to_h
|
|
334
|
+
{
|
|
335
|
+
environment: @environment,
|
|
336
|
+
cache: @cache.to_h,
|
|
337
|
+
parser: @parser.to_h,
|
|
338
|
+
transformer: @transformer.to_h,
|
|
339
|
+
output: @output.to_h,
|
|
340
|
+
logging: @logging.to_h,
|
|
341
|
+
custom: @custom.dup
|
|
342
|
+
}
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Load configuration from a YAML file
|
|
346
|
+
#
|
|
347
|
+
# @param path [String] Path to configuration file
|
|
348
|
+
# @return [Configuration] Loaded configuration
|
|
349
|
+
# @raise [ConfigurationError] If file cannot be loaded
|
|
350
|
+
def self.load_file(path)
|
|
351
|
+
require 'yaml'
|
|
352
|
+
|
|
353
|
+
raise ConfigurationError, "Configuration file not found: #{path}" unless File.exist?(path)
|
|
354
|
+
|
|
355
|
+
begin
|
|
356
|
+
yaml_content = YAML.load_file(path)
|
|
357
|
+
new(yaml_content || {})
|
|
358
|
+
rescue Psych::SyntaxError => e
|
|
359
|
+
raise ConfigurationError, "Invalid YAML in #{path}: #{e.message}"
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
|
|
363
|
+
# Load configuration from environment variables
|
|
364
|
+
#
|
|
365
|
+
# @param prefix [String] Environment variable prefix
|
|
366
|
+
# @return [Configuration] Configuration from environment
|
|
367
|
+
def self.load_environment(prefix = 'CORADOC')
|
|
368
|
+
options = {}
|
|
369
|
+
|
|
370
|
+
# Parse CORADOC_CACHE_ENABLED=true style variables
|
|
371
|
+
ENV.each do |key, value|
|
|
372
|
+
next unless key.start_with?("#{prefix}_")
|
|
373
|
+
|
|
374
|
+
# Convert CORADOC_CACHE_ENABLED to [:cache, :enabled]
|
|
375
|
+
parts = key.sub("#{prefix}_", '').downcase.split('_')
|
|
376
|
+
next if parts.length < 2
|
|
377
|
+
|
|
378
|
+
section = parts.first.to_sym
|
|
379
|
+
setting = parts[1..].join('_').to_sym
|
|
380
|
+
|
|
381
|
+
options[section] ||= {}
|
|
382
|
+
options[section][setting] = parse_env_value(value)
|
|
383
|
+
end
|
|
384
|
+
|
|
385
|
+
# Special handling for CORADOC_ENV
|
|
386
|
+
options[:environment] = ENV["#{prefix}_ENV"] if ENV["#{prefix}_ENV"]
|
|
387
|
+
|
|
388
|
+
new(options)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# Reset configuration to defaults
|
|
392
|
+
#
|
|
393
|
+
# @return [void]
|
|
394
|
+
def reset!
|
|
395
|
+
@environment = detect_environment
|
|
396
|
+
@cache = CacheConfig.new
|
|
397
|
+
@parser = ParserConfig.new
|
|
398
|
+
@transformer = TransformerConfig.new
|
|
399
|
+
@output = OutputConfig.new
|
|
400
|
+
@logging = LoggingConfig.new
|
|
401
|
+
@custom = {}
|
|
402
|
+
end
|
|
403
|
+
|
|
404
|
+
# Validate configuration
|
|
405
|
+
#
|
|
406
|
+
# @return [Array<String>] List of validation errors
|
|
407
|
+
def validate
|
|
408
|
+
errors = []
|
|
409
|
+
|
|
410
|
+
errors << 'cache.max_size must be positive' if @cache.max_size <= 0
|
|
411
|
+
errors << 'cache.ttl must be non-negative' if @cache.ttl.negative?
|
|
412
|
+
errors << 'parser.max_nesting_depth must be positive' if @parser.max_nesting_depth <= 0
|
|
413
|
+
errors << 'output.line_width must be positive' if @output.line_width <= 0
|
|
414
|
+
|
|
415
|
+
errors << 'cache.backend must be :memory, :file, or :redis' unless %i[memory file
|
|
416
|
+
redis].include?(@cache.backend)
|
|
417
|
+
|
|
418
|
+
errors << 'logging.level must be :debug, :info, :warn, or :error' unless %i[debug info warn
|
|
419
|
+
error].include?(@logging.level)
|
|
420
|
+
|
|
421
|
+
errors
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
# Check if configuration is valid
|
|
425
|
+
#
|
|
426
|
+
# @return [Boolean]
|
|
427
|
+
def valid?
|
|
428
|
+
validate.empty?
|
|
429
|
+
end
|
|
430
|
+
|
|
431
|
+
private
|
|
432
|
+
|
|
433
|
+
def detect_environment
|
|
434
|
+
ENV.fetch('RACK_ENV', ENV.fetch('RAILS_ENV', 'development'))
|
|
435
|
+
end
|
|
436
|
+
|
|
437
|
+
def symbolize_keys(hash)
|
|
438
|
+
ConfigSection.symbolize_keys(hash)
|
|
439
|
+
end
|
|
440
|
+
|
|
441
|
+
def merge_hash(hash)
|
|
442
|
+
hash = symbolize_keys(hash)
|
|
443
|
+
|
|
444
|
+
@environment = hash[:environment] if hash.key?(:environment)
|
|
445
|
+
@cache.merge!(hash[:cache]) if hash.key?(:cache)
|
|
446
|
+
@parser.merge!(hash[:parser]) if hash.key?(:parser)
|
|
447
|
+
@transformer.merge!(hash[:transformer]) if hash.key?(:transformer)
|
|
448
|
+
@output.merge!(hash[:output]) if hash.key?(:output)
|
|
449
|
+
@logging.merge!(hash[:logging]) if hash.key?(:logging)
|
|
450
|
+
@custom.merge!(symbolize_keys(hash[:custom] || {}))
|
|
451
|
+
end
|
|
452
|
+
|
|
453
|
+
def self.parse_env_value(value)
|
|
454
|
+
case value.downcase
|
|
455
|
+
when 'true', 'yes', '1'
|
|
456
|
+
true
|
|
457
|
+
when 'false', 'no', '0'
|
|
458
|
+
false
|
|
459
|
+
when /^\d+$/
|
|
460
|
+
value.to_i
|
|
461
|
+
when /^\d+\.\d+$/
|
|
462
|
+
value.to_f
|
|
463
|
+
else
|
|
464
|
+
value
|
|
465
|
+
end
|
|
466
|
+
end
|
|
467
|
+
private_class_method :parse_env_value
|
|
468
|
+
end
|
|
469
|
+
|
|
470
|
+
class << self
|
|
471
|
+
# Get current global configuration
|
|
472
|
+
#
|
|
473
|
+
# @return [Configuration]
|
|
474
|
+
def configuration
|
|
475
|
+
@configuration ||= Configuration.new
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
# Set global configuration
|
|
479
|
+
#
|
|
480
|
+
# @param config [Configuration] Configuration to set
|
|
481
|
+
# @return [void]
|
|
482
|
+
attr_writer :configuration
|
|
483
|
+
|
|
484
|
+
# Configure Coradoc
|
|
485
|
+
#
|
|
486
|
+
# @yield [Configuration] Block receives configuration object
|
|
487
|
+
# @return [void]
|
|
488
|
+
def configure
|
|
489
|
+
yield configuration if block_given?
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
# Reset configuration to defaults
|
|
493
|
+
#
|
|
494
|
+
# @return [void]
|
|
495
|
+
def reset_configuration!
|
|
496
|
+
@configuration = Configuration.new
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Load configuration from file
|
|
500
|
+
#
|
|
501
|
+
# @param path [String] Path to configuration file
|
|
502
|
+
# @return [void]
|
|
503
|
+
def load_configuration(path)
|
|
504
|
+
config = Configuration.load_file(path)
|
|
505
|
+
configuration.merge!(config)
|
|
506
|
+
end
|
|
507
|
+
end
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# Include Configurable in main module for easy access
|
|
511
|
+
extend Configurable
|
|
512
|
+
|
|
513
|
+
# Shortcut to configuration
|
|
514
|
+
#
|
|
515
|
+
# @return [Configuration]
|
|
516
|
+
def self.config
|
|
517
|
+
Configurable.configuration
|
|
518
|
+
end
|
|
519
|
+
|
|
520
|
+
# Shortcut to configure
|
|
521
|
+
#
|
|
522
|
+
# @yield [Configuration]
|
|
523
|
+
# @return [void]
|
|
524
|
+
def self.configure(&block)
|
|
525
|
+
Configurable.configure(&block) if block_given?
|
|
526
|
+
end
|
|
527
|
+
end
|