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.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.gitlab-ci.yml +27 -0
  4. data/Gemfile +27 -16
  5. data/LICENSE +1 -1
  6. data/README.adoc +24 -0
  7. data/Rakefile +0 -1
  8. data/bin/defmastership +5 -5
  9. data/config/mutant.yml +18 -16
  10. data/config/rubocop.yml +5 -10
  11. data/defmastership.gemspec +3 -3
  12. data/lib/defmastership/batch_modifier.rb +23 -5
  13. data/lib/defmastership/comment_filter.rb +2 -1
  14. data/lib/defmastership/definition.rb +24 -4
  15. data/lib/defmastership/definition_parser.rb +2 -2
  16. data/lib/defmastership/document.rb +39 -19
  17. data/lib/defmastership/export/body_formatter.rb +55 -0
  18. data/lib/defmastership/export/csv/formatter.rb +64 -0
  19. data/lib/defmastership/export/header_formatter.rb +51 -0
  20. data/lib/defmastership/filters.rb +15 -12
  21. data/lib/defmastership/matching_line.rb +3 -2
  22. data/lib/defmastership/modifier/change_ref.rb +117 -0
  23. data/lib/defmastership/modifier/factory.rb +17 -0
  24. data/lib/defmastership/modifier/modifier_common.rb +71 -0
  25. data/lib/defmastership/modifier/rename_included_files.rb +223 -0
  26. data/lib/defmastership/modifier/update_def.rb +72 -0
  27. data/lib/defmastership/modifier/update_def_checksum.rb +17 -0
  28. data/lib/defmastership/modifier/update_def_version.rb +110 -0
  29. data/lib/defmastership/set_join_hack.rb +2 -0
  30. data/lib/defmastership/version.rb +3 -2
  31. data/lib/defmastership.rb +6 -8
  32. data/spec/unit/{def_mastership → defmastership}/batch_modifier_spec.rb +13 -13
  33. data/spec/unit/{def_mastership → defmastership}/definition_parser_spec.rb +1 -1
  34. data/spec/unit/{def_mastership → defmastership}/definition_spec.rb +1 -1
  35. data/spec/unit/{def_mastership → defmastership}/document_spec.rb +57 -57
  36. data/spec/unit/{def_mastership/csv_formatter_body_spec.rb → defmastership/export/body_formatter_spec.rb} +4 -4
  37. data/spec/unit/{def_mastership/csv_formatter_spec.rb → defmastership/export/csv/formatter_spec.rb} +13 -8
  38. data/spec/unit/{def_mastership/csv_formatter_header_spec.rb → defmastership/export/header_formatter_spec.rb} +3 -3
  39. data/spec/unit/{def_mastership → defmastership}/matching_line_spec.rb +1 -1
  40. data/spec/unit/{def_mastership/change_ref_modifier_spec.rb → defmastership/modifier/change_ref_spec.rb} +19 -44
  41. data/spec/unit/defmastership/modifier/factory_spec.rb +45 -0
  42. data/spec/unit/{def_mastership/modifier_spec.rb → defmastership/modifier/modifier_common_spec.rb} +3 -12
  43. data/spec/unit/{def_mastership/rename_included_files_modifier_spec.rb → defmastership/modifier/rename_included_files_spec.rb} +3 -3
  44. data/spec/unit/{def_mastership/update_def_checksum_modifier_spec.rb → defmastership/modifier/update_def_checksum_spec.rb} +10 -10
  45. data/spec/unit/{def_mastership/update_def_modifier_spec.rb → defmastership/modifier/update_def_spec.rb} +22 -20
  46. data/spec/unit/{def_mastership/update_def_version_modifier_spec.rb → defmastership/modifier/update_def_version_spec.rb} +38 -38
  47. data/spec/unit/{def_mastership_spec.rb → defmastership_spec.rb} +2 -2
  48. data/tasks/documentation.rake +19 -0
  49. data/tasks/package.rake +4 -0
  50. metadata +63 -43
  51. data/README.rdoc +0 -6
  52. data/defmastership.rdoc +0 -5
  53. data/lib/defmastership/change_ref_modifier.rb +0 -99
  54. data/lib/defmastership/constants.rb +0 -91
  55. data/lib/defmastership/csv_formatter.rb +0 -53
  56. data/lib/defmastership/csv_formatter_body.rb +0 -46
  57. data/lib/defmastership/csv_formatter_header.rb +0 -41
  58. data/lib/defmastership/modifier.rb +0 -42
  59. data/lib/defmastership/modifier_factory.rb +0 -12
  60. data/lib/defmastership/parsing_state.rb +0 -31
  61. data/lib/defmastership/rename_included_files_modifier.rb +0 -182
  62. data/lib/defmastership/update_def_checksum_modifier.rb +0 -16
  63. data/lib/defmastership/update_def_modifier.rb +0 -49
  64. data/lib/defmastership/update_def_version_modifier.rb +0 -91
  65. data/spec/unit/def_mastership/modifier_factory_spec.rb +0 -38
  66. data/spec/unit/def_mastership/parsing_state_spec.rb +0 -62
  67. data/tasks/package.task +0 -9
  68. /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
- module DefMastership
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 DefMastership
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