defmastership 1.0.19 → 1.1.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.
- checksums.yaml +4 -4
- data/.gitignore +3 -0
- data/.gitlab-ci.yml +27 -0
- data/Gemfile +27 -16
- data/LICENSE +1 -1
- data/README.adoc +24 -0
- data/Rakefile +0 -1
- data/bin/defmastership +5 -5
- data/config/mutant.yml +18 -16
- data/config/rubocop.yml +5 -10
- data/defmastership.gemspec +3 -3
- data/lib/defmastership/batch_modifier.rb +23 -5
- data/lib/defmastership/comment_filter.rb +2 -1
- data/lib/defmastership/definition.rb +24 -4
- data/lib/defmastership/definition_parser.rb +2 -2
- data/lib/defmastership/document.rb +39 -19
- data/lib/defmastership/export/body_formatter.rb +55 -0
- data/lib/defmastership/export/csv/formatter.rb +64 -0
- data/lib/defmastership/export/header_formatter.rb +51 -0
- data/lib/defmastership/filters.rb +15 -12
- data/lib/defmastership/matching_line.rb +3 -2
- data/lib/defmastership/modifier/change_ref.rb +117 -0
- data/lib/defmastership/modifier/factory.rb +17 -0
- data/lib/defmastership/modifier/modifier_common.rb +71 -0
- data/lib/defmastership/modifier/rename_included_files.rb +223 -0
- data/lib/defmastership/modifier/update_def.rb +72 -0
- data/lib/defmastership/modifier/update_def_checksum.rb +17 -0
- data/lib/defmastership/modifier/update_def_version.rb +110 -0
- data/lib/defmastership/set_join_hack.rb +2 -0
- data/lib/defmastership/version.rb +3 -2
- data/lib/defmastership.rb +6 -8
- data/spec/unit/{def_mastership → defmastership}/batch_modifier_spec.rb +13 -13
- data/spec/unit/{def_mastership → defmastership}/definition_parser_spec.rb +1 -1
- data/spec/unit/{def_mastership → defmastership}/definition_spec.rb +1 -1
- data/spec/unit/{def_mastership → defmastership}/document_spec.rb +57 -57
- data/spec/unit/{def_mastership/csv_formatter_body_spec.rb → defmastership/export/body_formatter_spec.rb} +4 -4
- data/spec/unit/{def_mastership/csv_formatter_spec.rb → defmastership/export/csv/formatter_spec.rb} +13 -8
- data/spec/unit/{def_mastership/csv_formatter_header_spec.rb → defmastership/export/header_formatter_spec.rb} +3 -3
- data/spec/unit/{def_mastership → defmastership}/matching_line_spec.rb +1 -1
- data/spec/unit/{def_mastership/change_ref_modifier_spec.rb → defmastership/modifier/change_ref_spec.rb} +19 -44
- data/spec/unit/defmastership/modifier/factory_spec.rb +45 -0
- data/spec/unit/{def_mastership/modifier_spec.rb → defmastership/modifier/modifier_common_spec.rb} +3 -12
- data/spec/unit/{def_mastership/rename_included_files_modifier_spec.rb → defmastership/modifier/rename_included_files_spec.rb} +3 -3
- data/spec/unit/{def_mastership/update_def_checksum_modifier_spec.rb → defmastership/modifier/update_def_checksum_spec.rb} +10 -10
- data/spec/unit/{def_mastership/update_def_modifier_spec.rb → defmastership/modifier/update_def_spec.rb} +22 -20
- data/spec/unit/{def_mastership/update_def_version_modifier_spec.rb → defmastership/modifier/update_def_version_spec.rb} +38 -38
- data/spec/unit/{def_mastership_spec.rb → defmastership_spec.rb} +2 -2
- data/tasks/documentation.rake +19 -0
- data/tasks/package.rake +4 -0
- metadata +63 -43
- data/README.rdoc +0 -6
- data/defmastership.rdoc +0 -5
- data/lib/defmastership/change_ref_modifier.rb +0 -99
- data/lib/defmastership/constants.rb +0 -91
- data/lib/defmastership/csv_formatter.rb +0 -53
- data/lib/defmastership/csv_formatter_body.rb +0 -46
- data/lib/defmastership/csv_formatter_header.rb +0 -41
- data/lib/defmastership/modifier.rb +0 -42
- data/lib/defmastership/modifier_factory.rb +0 -12
- data/lib/defmastership/parsing_state.rb +0 -31
- data/lib/defmastership/rename_included_files_modifier.rb +0 -182
- data/lib/defmastership/update_def_checksum_modifier.rb +0 -16
- data/lib/defmastership/update_def_modifier.rb +0 -49
- data/lib/defmastership/update_def_version_modifier.rb +0 -91
- data/spec/unit/def_mastership/modifier_factory_spec.rb +0 -38
- data/spec/unit/def_mastership/parsing_state_spec.rb +0 -62
- data/tasks/package.task +0 -9
- /data/spec/unit/{def_mastership → defmastership}/string_spec.rb +0 -0
@@ -0,0 +1,64 @@
|
|
1
|
+
# Copyright (c) 2020 Jerome Arbez-Gindre
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require('csv')
|
5
|
+
require('defmastership/export/body_formatter')
|
6
|
+
require('defmastership/export/header_formatter')
|
7
|
+
|
8
|
+
module Defmastership
|
9
|
+
# Module to host export classes
|
10
|
+
module Export
|
11
|
+
# Module to host CSV export classes
|
12
|
+
module CSV
|
13
|
+
# to export a CSV file
|
14
|
+
class Formatter
|
15
|
+
# @param doc [Document] the document to export
|
16
|
+
# @param sep [String] the CSV separator
|
17
|
+
def initialize(doc, sep)
|
18
|
+
@doc = doc
|
19
|
+
@sep = sep
|
20
|
+
end
|
21
|
+
|
22
|
+
# Export the document to a CSV file
|
23
|
+
#
|
24
|
+
# @param output_file [Strinf] filename for the export
|
25
|
+
def export_to(output_file)
|
26
|
+
column_list = build_column_list
|
27
|
+
::CSV.open(output_file, 'w:ISO-8859-1', col_sep: @sep) do |csv|
|
28
|
+
csv << header(column_list)
|
29
|
+
export_definitions_in(csv, column_list)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def export_definitions_in(csv, column_list)
|
36
|
+
@doc.definitions.each { |definition| csv << body(definition, column_list) }
|
37
|
+
end
|
38
|
+
|
39
|
+
def header(column_list)
|
40
|
+
header_formatter = HeaderFormatter.new(@doc)
|
41
|
+
header_line = column_list.map { |part| header_formatter.public_send(part) }
|
42
|
+
header_line.flatten
|
43
|
+
end
|
44
|
+
|
45
|
+
def body(definition, column_list)
|
46
|
+
body_formatter = BodyFormatter.new(@doc, definition)
|
47
|
+
body_line = column_list.map { |part| body_formatter.public_send(part) }
|
48
|
+
body_line.flatten
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_column_list
|
52
|
+
[
|
53
|
+
[:wrong_explicit_checksum, @doc.wrong_explicit_checksum?],
|
54
|
+
[:explicit_version, @doc.explicit_version?],
|
55
|
+
[:labels, !@doc.labels.empty?],
|
56
|
+
[:eref, !@doc.eref.empty?],
|
57
|
+
[:iref, @doc.iref],
|
58
|
+
[:attributes, !@doc.attributes.empty?]
|
59
|
+
].reduce([:fixed]) { |acc, elem| acc + (elem.fetch(1) ? [elem.first] : []) }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Copyright (c) 2020 Jerome Arbez-Gindre
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require('csv')
|
5
|
+
|
6
|
+
module Defmastership
|
7
|
+
module Export
|
8
|
+
# format CSV header (first line) for one document
|
9
|
+
class HeaderFormatter
|
10
|
+
# @param doc [Document] the document to export
|
11
|
+
def initialize(doc)
|
12
|
+
@doc = doc
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Array<String>] list of always available columns
|
16
|
+
def fixed
|
17
|
+
%w[Type Reference Value Checksum]
|
18
|
+
end
|
19
|
+
|
20
|
+
# @return [Array<String>] Optional wrong_explicit_checksum column caption
|
21
|
+
def wrong_explicit_checksum
|
22
|
+
@doc.wrong_explicit_checksum? ? ['Wrong explicit checksum'] : []
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Array<String>] Optional version column captions
|
26
|
+
def explicit_version
|
27
|
+
@doc.explicit_version? ? ['Version'] : []
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [Array<String>] Optional labels columns captions
|
31
|
+
def labels
|
32
|
+
@doc.labels.empty? ? [] : %w[Labels]
|
33
|
+
end
|
34
|
+
|
35
|
+
# @return [Array<String>] Optional external refs columns captions
|
36
|
+
def eref
|
37
|
+
@doc.eref.map { |_, ref| ref.fetch(:prefix) }
|
38
|
+
end
|
39
|
+
|
40
|
+
# @return [Array<String>] Optional internal refs column caption
|
41
|
+
def iref
|
42
|
+
@doc.iref ? ['Internal links'] : []
|
43
|
+
end
|
44
|
+
|
45
|
+
# @return [Array<String>] Optional attributes columns captions
|
46
|
+
def attributes
|
47
|
+
@doc.attributes.map { |_, value| value }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -1,23 +1,26 @@
|
|
1
1
|
# Copyright (c) 2020 Jerome Arbez-Gindre
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
require('defmastership/core/constants')
|
5
|
+
|
6
|
+
# Main module of the application
|
7
|
+
module Defmastership
|
5
8
|
# Contains regexp / action couples
|
6
9
|
Filter = Struct.new(:regexp, :event, :consumed_line)
|
7
10
|
private_constant :Filter
|
8
11
|
|
9
12
|
FILTERS = [
|
10
|
-
Filter.new(DMRegexp::VARIABLE_DEF, :new_variable_def, false),
|
11
|
-
Filter.new(DMRegexp::VARIABLE_USE, :new_variable_use, false),
|
12
|
-
Filter.new(DMRegexp::DEFINITION, :new_definition, true),
|
13
|
-
Filter.new(DMRegexp::EREF_CONFIG, :new_eref_setup, true),
|
14
|
-
Filter.new(DMRegexp::EREF_DEF, :new_eref_def, false),
|
15
|
-
Filter.new(DMRegexp::IREF_DEF, :new_iref_def, false),
|
16
|
-
Filter.new(DMRegexp::ATTR_CONFIG, :new_attribute_conf, true),
|
17
|
-
Filter.new(DMRegexp::ATTR_SET, :new_attribute_value, false),
|
18
|
-
Filter.new(DMRegexp::BLOCK, :block_delimiter, true),
|
19
|
-
Filter.new(DMRegexp::EMPTY_LINE, :empty_line, false),
|
20
|
-
Filter.new(DMRegexp::WHATEVER, :new_line, true)
|
13
|
+
Filter.new(Core::DMRegexp::VARIABLE_DEF, :new_variable_def, false),
|
14
|
+
Filter.new(Core::DMRegexp::VARIABLE_USE, :new_variable_use, false),
|
15
|
+
Filter.new(Core::DMRegexp::DEFINITION, :new_definition, true),
|
16
|
+
Filter.new(Core::DMRegexp::EREF_CONFIG, :new_eref_setup, true),
|
17
|
+
Filter.new(Core::DMRegexp::EREF_DEF, :new_eref_def, false),
|
18
|
+
Filter.new(Core::DMRegexp::IREF_DEF, :new_iref_def, false),
|
19
|
+
Filter.new(Core::DMRegexp::ATTR_CONFIG, :new_attribute_conf, true),
|
20
|
+
Filter.new(Core::DMRegexp::ATTR_SET, :new_attribute_value, false),
|
21
|
+
Filter.new(Core::DMRegexp::BLOCK, :block_delimiter, true),
|
22
|
+
Filter.new(Core::DMRegexp::EMPTY_LINE, :empty_line, false),
|
23
|
+
Filter.new(Core::DMRegexp::WHATEVER, :new_line, true)
|
21
24
|
].freeze
|
22
25
|
private_constant :FILTERS
|
23
26
|
end
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# Copyright (c) 2020 Jerome Arbez-Gindre
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
module
|
5
|
-
# a composite class
|
4
|
+
module Defmastership
|
5
|
+
# a composite class storing a line and the corresponding match
|
6
6
|
MatchingLine =
|
7
7
|
Struct.new(:match, :line) do
|
8
|
+
# Act as a Hasg and delegate to the Match
|
8
9
|
def [](key)
|
9
10
|
value = match[key]
|
10
11
|
return value if value
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# Copyright (c) 2020 Jerome Arbez-Gindre
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'defmastership/core/constants'
|
5
|
+
require('defmastership/core/parsing_state')
|
6
|
+
require 'defmastership/modifier/modifier_common'
|
7
|
+
|
8
|
+
module Defmastership
|
9
|
+
module Modifier
|
10
|
+
# Change references from temporary to definitive with multiple RefChangers
|
11
|
+
class ChangeRef
|
12
|
+
include ModifierCommon
|
13
|
+
|
14
|
+
REGEXP_FROM = {
|
15
|
+
definition: {
|
16
|
+
before: Core::DMRegexp::DEF_BEFORE_REF,
|
17
|
+
after: Core::DMRegexp::DEF_AFTER_REF
|
18
|
+
},
|
19
|
+
iref: {
|
20
|
+
before: Core::DMRegexp::IREF_DEF_BEF,
|
21
|
+
after: Core::DMRegexp::IREF_DEF_AFT
|
22
|
+
}
|
23
|
+
}.freeze
|
24
|
+
|
25
|
+
private_constant :REGEXP_FROM
|
26
|
+
|
27
|
+
# Methods's symbols will be called in sequence to perform the document modifications
|
28
|
+
#
|
29
|
+
# @return [Array<Symbol>] the two symbols of replacement methods
|
30
|
+
def self.replacement_methods
|
31
|
+
%i[replace_refdef replace_irefs]
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Hash{Symbol => Object}] the default configuration
|
35
|
+
def self.default_config
|
36
|
+
{
|
37
|
+
from_regexp: '',
|
38
|
+
to_template: '',
|
39
|
+
next_ref: 0
|
40
|
+
}
|
41
|
+
end
|
42
|
+
|
43
|
+
# @param config [YAML] the modifier's provided configurations
|
44
|
+
def initialize(config)
|
45
|
+
@parsing_state = Core::ParsingState.new
|
46
|
+
|
47
|
+
setup_modifier_module(config)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Replace the definition's ref in the line if relevant
|
51
|
+
#
|
52
|
+
# @param line [String] the current line
|
53
|
+
# @return [String] the modified line
|
54
|
+
def replace_refdef(line)
|
55
|
+
if @parsing_state.enabled?(line)
|
56
|
+
do_replace_refdef(line)
|
57
|
+
else
|
58
|
+
line
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Replace the definition's refs in intenal refs
|
63
|
+
#
|
64
|
+
# @param line [String] the current line
|
65
|
+
# @return [String] the modified line
|
66
|
+
def replace_irefs(line)
|
67
|
+
changes.reduce(line) do |res_line, (from, to)|
|
68
|
+
res_line.gsub(Helper.regexp_from(:iref, from)) do
|
69
|
+
Helper.text_with(Regexp.last_match, to)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def do_replace_refdef(line)
|
77
|
+
line.sub(Helper.regexp_from(:definition, from_regexp)) do
|
78
|
+
replacement_from(Regexp.last_match)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def replacement_from(match)
|
83
|
+
replacement = to_template % hmerge(match)
|
84
|
+
changes << [match[:from], replacement]
|
85
|
+
config[:next_ref] += 1
|
86
|
+
Helper.text_with(match, replacement)
|
87
|
+
end
|
88
|
+
|
89
|
+
def hmerge(match)
|
90
|
+
config.merge(match.named_captures.transform_keys(&:to_sym))
|
91
|
+
end
|
92
|
+
|
93
|
+
# Helper functions
|
94
|
+
module Helper
|
95
|
+
# @param const [Symbol] the key to retrieve preamble and post-amble
|
96
|
+
# @param from [Symbol] the piece of text to be replaced
|
97
|
+
# @return [Regexp] a built regexp
|
98
|
+
def self.regexp_from(const, from)
|
99
|
+
regexps = REGEXP_FROM.fetch(const)
|
100
|
+
regexp_str =
|
101
|
+
"(?<before>#{regexps[:before]})" \
|
102
|
+
"(?<from>#{from})" \
|
103
|
+
"#{Core::DMRegexp::DEF_VERSION_AND_CHECKSUM}" \
|
104
|
+
"(?<after>#{regexps[:after]})"
|
105
|
+
Regexp.new(regexp_str, Regexp::EXTENDED)
|
106
|
+
end
|
107
|
+
|
108
|
+
# @param match [MatchData] The match form Regepxp match
|
109
|
+
# @param replacement [String] the reference replacement text
|
110
|
+
# @return [String] the overall replaced text
|
111
|
+
def self.text_with(match, replacement)
|
112
|
+
match[:before] + replacement + (match[:version_and_checksum] || '') + match[:after]
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Copyright (c) 2023 Jerome Arbez-Gindre
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Defmastership
|
5
|
+
module Modifier
|
6
|
+
# build modifiers from a piece of configuration
|
7
|
+
module Factory
|
8
|
+
# Build a concrete class from config 'type' field
|
9
|
+
#
|
10
|
+
# @param config [YAML] piece of configuration for this Modifier
|
11
|
+
def self.from_config(config)
|
12
|
+
class_name = config.fetch(:type).split('_').map(&:capitalize).join
|
13
|
+
Modifier.const_get(class_name).new(config.fetch(:config))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# Copyright (c) 2020 Jerome Arbez-Gindre
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module Defmastership
|
5
|
+
module Modifier
|
6
|
+
# Defines common methods and attributes for line modifiers
|
7
|
+
#
|
8
|
+
# @abstract Include and define +self.replacement_methods+ and +self.default_config+
|
9
|
+
module ModifierCommon
|
10
|
+
# Stores the configuration of this modifier
|
11
|
+
# @return [YAML] configuration as eventualy modified
|
12
|
+
attr_reader :config
|
13
|
+
# Provides the list of performed modifications
|
14
|
+
# @return [Array<Array<String>>] List of performed modifications
|
15
|
+
# (each line: [Modifier, Was, Becomes])
|
16
|
+
attr_reader :changes
|
17
|
+
|
18
|
+
# Setup config from class's default config and provided config
|
19
|
+
#
|
20
|
+
# @param config [YAML] the modifier's provided configurations
|
21
|
+
def setup_modifier_module(config)
|
22
|
+
@config = self.class.default_config.merge(config)
|
23
|
+
@changes = []
|
24
|
+
end
|
25
|
+
|
26
|
+
# Allow to view config items as methods
|
27
|
+
#
|
28
|
+
# @param method_name [Symbol] the name of the method
|
29
|
+
# @param args[Array<Object>] the arguments of the method
|
30
|
+
def method_missing(method_name, *args)
|
31
|
+
config_method_name = config[method_name]
|
32
|
+
return config_method_name if config_method_name
|
33
|
+
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
# Allow to check if a config item is available
|
38
|
+
#
|
39
|
+
# @param method_name [Symbol] the name of the method
|
40
|
+
# @param args[Array<Object>] the arguments of the method
|
41
|
+
def respond_to_missing?(method_name, *args)
|
42
|
+
config.key?(method_name) || super
|
43
|
+
end
|
44
|
+
|
45
|
+
# Apply the modifier on all provided asciidoc sources based on modifier's
|
46
|
+
# +replacement_methods+ list
|
47
|
+
#
|
48
|
+
# @param adoc_sources [Hash{String => String}] asciidoc sources
|
49
|
+
# * :key filename
|
50
|
+
# * :value file content
|
51
|
+
def do_modifications(adoc_sources)
|
52
|
+
self.class.replacement_methods.reduce(adoc_sources) do |texts, method|
|
53
|
+
apply_to_all(texts, method)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def apply_to_all(texts, method)
|
60
|
+
texts.transform_values do |text|
|
61
|
+
apply_to_one(text, method)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def apply_to_one(text, method)
|
66
|
+
text.lines.map { |line| public_send(method, line) }
|
67
|
+
.join
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# Copyright (c) 2020 Jerome Arbez-Gindre
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require('defmastership/core/constants')
|
5
|
+
require('defmastership/matching_line')
|
6
|
+
require('defmastership/modifier/modifier_common')
|
7
|
+
|
8
|
+
module Defmastership
|
9
|
+
# defintion of the Rename Included Files Modifier
|
10
|
+
module Modifier
|
11
|
+
LOCAL_FILTERS = [
|
12
|
+
Filter.new(Core::DMRegexp::VARIABLE_DEF, :new_variable_def),
|
13
|
+
Filter.new(Core::DMRegexp::DEFINITION, :new_definition),
|
14
|
+
Filter.new(Core::DMRegexp::BLOCK, :block_delimiter),
|
15
|
+
Filter.new(Core::DMRegexp::EMPTY_LINE, :empty_line),
|
16
|
+
Filter.new(Core::DMRegexp::WHATEVER, :new_line)
|
17
|
+
].freeze
|
18
|
+
private_constant :LOCAL_FILTERS
|
19
|
+
|
20
|
+
# Change included filenames on one line at a time
|
21
|
+
class RenameIncludedFiles
|
22
|
+
include ModifierCommon
|
23
|
+
|
24
|
+
PARSER_ACTIONS = {
|
25
|
+
new_variable_def: lambda { |matching_line|
|
26
|
+
@variables.merge!(Helper.variable_def_hash(matching_line.match))
|
27
|
+
},
|
28
|
+
add_line: proc { |_| },
|
29
|
+
add_new_definition: lambda { |matching_line|
|
30
|
+
config[:reference] = matching_line.match[:reference]
|
31
|
+
}
|
32
|
+
}.freeze
|
33
|
+
|
34
|
+
private_constant :PARSER_ACTIONS
|
35
|
+
|
36
|
+
# @return [Array<Symbol>] the only replacement method symbol
|
37
|
+
def self.replacement_methods
|
38
|
+
%i[replace]
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [Hash{Symbol => Object}] the default configuration
|
42
|
+
def self.default_config
|
43
|
+
{
|
44
|
+
from_regexp: '',
|
45
|
+
to_template: ''
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
# @param config [YAML] the modifier's provided configurations
|
50
|
+
def initialize(config)
|
51
|
+
@variables = {}
|
52
|
+
@definition_parser = DefinitionParser.new(self)
|
53
|
+
|
54
|
+
setup_modifier_module(config)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Allow to add methods from parser actions
|
58
|
+
#
|
59
|
+
# @param method_name [Symbol] the name of the method
|
60
|
+
# @param args[Array<Object>] the arguments of the method
|
61
|
+
def method_missing(method_name, *args)
|
62
|
+
action = PARSER_ACTIONS[method_name]
|
63
|
+
return instance_exec(*args, &action) if action
|
64
|
+
|
65
|
+
super
|
66
|
+
end
|
67
|
+
|
68
|
+
# Allow to check if a parser action is available
|
69
|
+
#
|
70
|
+
# @param method_name [Symbol] the name of the method
|
71
|
+
# @param args[Array<Object>] the arguments of the method
|
72
|
+
def respond_to_missing?(method_name, *args)
|
73
|
+
PARSER_ACTIONS.key?(method_name) || super
|
74
|
+
end
|
75
|
+
|
76
|
+
# Modify line if it match
|
77
|
+
#
|
78
|
+
# @return [String] the modified line
|
79
|
+
def replace(line)
|
80
|
+
match = matched?(line)
|
81
|
+
|
82
|
+
return line unless match
|
83
|
+
|
84
|
+
new_line = build_new_include_line(match, line)
|
85
|
+
|
86
|
+
rename_file(line, new_line)
|
87
|
+
|
88
|
+
new_line
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def build_new_include_line(match, line)
|
94
|
+
line.sub(Helper.complete_regexp_from(from_regexp)) do
|
95
|
+
Helper.text_with(match, to_template % Helper.hmerge(config, match))
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def matched?(line)
|
100
|
+
return false unless concerned_line?(line)
|
101
|
+
return false unless line =~ Helper.complete_regexp_from(from_regexp)
|
102
|
+
|
103
|
+
match = Regexp.last_match
|
104
|
+
|
105
|
+
return false if config.key?(:cancel_if_match) && match[:filename].match?(cancel_if_match)
|
106
|
+
|
107
|
+
match
|
108
|
+
end
|
109
|
+
|
110
|
+
def concerned_line?(line)
|
111
|
+
return false if line.commented?
|
112
|
+
|
113
|
+
parse(line)
|
114
|
+
|
115
|
+
return false if @definition_parser.idle?
|
116
|
+
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
def parse(line)
|
121
|
+
Helper.apply_filters_until_consumed(line) do |event, match|
|
122
|
+
generate_event(event, MatchingLine.new(match))
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
def rename_file(from, to)
|
127
|
+
filename_from = Helper.extract_filename(from, @variables)
|
128
|
+
filename_to = Helper.extract_filename(to, @variables)
|
129
|
+
changes << [filename_from, filename_to]
|
130
|
+
File.rename(filename_from, filename_to)
|
131
|
+
end
|
132
|
+
|
133
|
+
def generate_event(event, matching_line)
|
134
|
+
if PARSER_ACTIONS.key?(event)
|
135
|
+
public_send(event, matching_line)
|
136
|
+
else
|
137
|
+
@definition_parser.public_send(event, matching_line)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# Helper functions
|
142
|
+
module Helper
|
143
|
+
# @param include_statement [String] the overall include statement
|
144
|
+
# @param variables [Hash{Symbol => String}] asciidoc variables for replacement
|
145
|
+
# @return [String] the path+filename from 'include' statement
|
146
|
+
def self.extract_filename(include_statement, variables)
|
147
|
+
filename = filename_from_include_statement(include_statement)
|
148
|
+
|
149
|
+
filename_replace_all_variables(filename, variables).chomp
|
150
|
+
end
|
151
|
+
|
152
|
+
# @param include_statement [String] the overall include statement
|
153
|
+
# @return [String] the path+filename from 'include' statement
|
154
|
+
def self.filename_from_include_statement(include_statement)
|
155
|
+
include_statement
|
156
|
+
.sub(Regexp.new(Core::DMRegexp::INCLUDE_KEYWORD), '')
|
157
|
+
.sub(Regexp.new(Core::DMRegexp::INCLUDE_OPTIONS), '')
|
158
|
+
end
|
159
|
+
|
160
|
+
# @param filename [String] the path+filename from 'include' statement
|
161
|
+
# @param variables [Hash{Symbol => String}] asciidoc variables for replacement
|
162
|
+
# @return [String] the path+filename from 'include' statement with replaced variables
|
163
|
+
def self.filename_replace_all_variables(filename, variables)
|
164
|
+
filename.scan(Core::DMRegexp::VARIABLE_USE) do
|
165
|
+
varname = Regexp.last_match[:varname]
|
166
|
+
value = variables.fetch(varname.to_sym)
|
167
|
+
filename = filename_replace_one_variable(filename, varname, value)
|
168
|
+
end
|
169
|
+
filename
|
170
|
+
end
|
171
|
+
|
172
|
+
# @param filename [String] the path+filename from 'include' statement
|
173
|
+
# @param varname [Symbol] asciidoc variable symbol
|
174
|
+
# @param value [Symbol] asciidoc variable value
|
175
|
+
# @return [String] the path+filename from 'include' statement with one replace variable
|
176
|
+
def self.filename_replace_one_variable(filename, varname, value)
|
177
|
+
filename.sub("{#{varname}}", value)
|
178
|
+
end
|
179
|
+
|
180
|
+
# @param from [String] the piece of regexp to match the filename
|
181
|
+
# @return [Regexp] the regexp to match overal include statement
|
182
|
+
def self.complete_regexp_from(from)
|
183
|
+
Regexp.new(
|
184
|
+
Core::DMRegexp::INCLUDE_KEYWORD + Core::DMRegexp::INCLUDE_PATH +
|
185
|
+
"(?<filename>#{from})" + Core::DMRegexp::INCLUDE_OPTIONS
|
186
|
+
)
|
187
|
+
end
|
188
|
+
|
189
|
+
# @param match [MatchData] from a Regexp match
|
190
|
+
# @param to [String] the replacement text
|
191
|
+
# @return [String] the include statement with replaced filenames
|
192
|
+
def self.text_with(match, to)
|
193
|
+
"include::#{match[:path]}#{to}[#{match[:options]}]"
|
194
|
+
end
|
195
|
+
|
196
|
+
# @param config [Hash{Symbol => Object}] the provided configuration
|
197
|
+
# @param match [MatchData] the match from a regexp with named back refs
|
198
|
+
# @return [Hash{Symbol => String}] config hash merged with match data
|
199
|
+
def self.hmerge(config, match)
|
200
|
+
config.merge(match.names.map(&:to_sym).zip(match.captures).to_h)
|
201
|
+
end
|
202
|
+
|
203
|
+
# @param match [MatchData] the match from a regexp with 'varname' named back refs
|
204
|
+
# @return [Hash{Symbol => String}] a hash with only one key from 'varname'
|
205
|
+
def self.variable_def_hash(match)
|
206
|
+
{ match[:varname].to_sym => match[:value] }
|
207
|
+
end
|
208
|
+
|
209
|
+
# Helper function: apply all filters
|
210
|
+
#
|
211
|
+
# @param line [String] the line
|
212
|
+
def self.apply_filters_until_consumed(line)
|
213
|
+
LOCAL_FILTERS.each do |filter|
|
214
|
+
next unless line.match(filter.regexp)
|
215
|
+
|
216
|
+
yield(filter.event, Regexp.last_match)
|
217
|
+
break
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# Copyright (c) 2024 Jerome Arbez-Gindre
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require('defmastership/core/constants')
|
5
|
+
|
6
|
+
module Defmastership
|
7
|
+
module Modifier
|
8
|
+
# @abstract Subclass and define +reference_replacement+ to implement a
|
9
|
+
# custom reference replacement class
|
10
|
+
class UpdateDef
|
11
|
+
include ModifierCommon
|
12
|
+
|
13
|
+
attr_reader :document
|
14
|
+
|
15
|
+
# @return [Hash{Symbol => Object}] the default configuration
|
16
|
+
def self.default_config
|
17
|
+
{
|
18
|
+
def_type: ''
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Array<Symbol>] the methods's symbols that will be called in sequence for modifications
|
23
|
+
def self.replacement_methods
|
24
|
+
%i[replace_reference]
|
25
|
+
end
|
26
|
+
|
27
|
+
# Builds a Regexp to match a particular reference defintion (with its
|
28
|
+
# optional version and checksum)
|
29
|
+
#
|
30
|
+
# @param reference [String] the reference
|
31
|
+
# @return [Regexp] the built Regexp
|
32
|
+
def self.reference_regexp(reference)
|
33
|
+
Regexp.new("#{reference}#{Core::DMRegexp::DEF_VERSION_AND_CHECKSUM}")
|
34
|
+
end
|
35
|
+
|
36
|
+
# @param config [YAML] the modifier's provided configurations
|
37
|
+
def initialize(config)
|
38
|
+
@document = Document.new
|
39
|
+
|
40
|
+
setup_modifier_module(config)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Apply the modifier on all provided asciidoc sources based on modifier's
|
44
|
+
# +self.replacement_methods+ list
|
45
|
+
#
|
46
|
+
# @param adoc_sources [Hash{String => String}] asciidoc sources
|
47
|
+
# * :key filename
|
48
|
+
# * :value file content
|
49
|
+
def do_modifications(adoc_sources)
|
50
|
+
adoc_sources.each_key do |adoc_file|
|
51
|
+
document.parse_file_with_preprocessor(adoc_file)
|
52
|
+
end
|
53
|
+
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
# Called on each line for an opportunity for text replacement
|
58
|
+
#
|
59
|
+
# @param line [String] line from asciidoc sources files
|
60
|
+
# @return [String] the modified line
|
61
|
+
def replace_reference(line)
|
62
|
+
match = line.match(Core::DMRegexp::DEFINITION)
|
63
|
+
|
64
|
+
return line unless match
|
65
|
+
return line unless match[:type] == def_type
|
66
|
+
|
67
|
+
reference = match[:reference]
|
68
|
+
line.sub(self.class.reference_regexp(reference), reference_replacement(reference, match))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Copyright (c) 2020 Jerome Arbez-Gindre
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'defmastership/modifier/update_def'
|
5
|
+
|
6
|
+
module Defmastership
|
7
|
+
module Modifier
|
8
|
+
# modify one line after another
|
9
|
+
class UpdateDefChecksum < UpdateDef
|
10
|
+
private
|
11
|
+
|
12
|
+
def reference_replacement(reference, match)
|
13
|
+
"#{reference}(#{match[:explicit_version]}#{document.ref_to_def(reference).sha256_short})"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|