defmastership 1.0.2 → 1.0.7

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 (59) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.gitlab-ci.yml +20 -0
  4. data/.rubocop.yml +6 -8
  5. data/Gemfile +1 -0
  6. data/LICENSE +22 -0
  7. data/Rakefile +2 -2
  8. data/bin/defmastership +37 -24
  9. data/cucumber.yml +2 -0
  10. data/defmastership.gemspec +17 -10
  11. data/features/changeref.feature +82 -129
  12. data/features/checksum.feature +244 -0
  13. data/features/export.feature +49 -31
  14. data/features/modify.feature +143 -0
  15. data/features/rename_included_files.feature +121 -0
  16. data/features/step_definitions/defmastership_steps.rb +1 -0
  17. data/features/support/env.rb +1 -0
  18. data/lib/defmastership.rb +15 -3
  19. data/lib/defmastership/batch_modifier.rb +33 -0
  20. data/lib/defmastership/{ref_changer.rb → change_ref_line_modifier.rb} +19 -35
  21. data/lib/defmastership/change_ref_modifier.rb +15 -0
  22. data/lib/defmastership/comment_filter.rb +1 -0
  23. data/lib/defmastership/constants.rb +15 -1
  24. data/lib/defmastership/csv_formatter.rb +19 -18
  25. data/lib/defmastership/csv_formatter_body.rb +12 -6
  26. data/lib/defmastership/csv_formatter_header.rb +12 -10
  27. data/lib/defmastership/definition.rb +12 -0
  28. data/lib/defmastership/definition_parser.rb +46 -0
  29. data/lib/defmastership/document.rb +54 -75
  30. data/lib/defmastership/filters.rb +30 -0
  31. data/lib/defmastership/line_modifier_base.rb +29 -0
  32. data/lib/defmastership/modifier_base.rb +29 -0
  33. data/lib/defmastership/rename_included_files_line_modifier.rb +126 -0
  34. data/lib/defmastership/rename_included_files_modifier.rb +15 -0
  35. data/lib/defmastership/update_def_checksum_line_modifier.rb +39 -0
  36. data/lib/defmastership/update_def_checksum_modifier.rb +21 -0
  37. data/lib/defmastership/version.rb +2 -1
  38. data/spec/spec_helper.rb +2 -0
  39. data/spec/unit/defmastership/batch_modifier_spec.rb +115 -0
  40. data/spec/unit/defmastership/{ref_changer_spec.rb → change_ref_line_modifier_spec.rb} +49 -26
  41. data/spec/unit/defmastership/change_ref_modifier_spec.rb +76 -0
  42. data/spec/unit/defmastership/comment_filter_spec.rb +9 -4
  43. data/spec/unit/defmastership/csv_formatter_body_spec.rb +62 -37
  44. data/spec/unit/defmastership/csv_formatter_header_spec.rb +47 -22
  45. data/spec/unit/defmastership/csv_formatter_spec.rb +67 -105
  46. data/spec/unit/defmastership/definition_parser_spec.rb +63 -0
  47. data/spec/unit/defmastership/definition_spec.rb +31 -4
  48. data/spec/unit/defmastership/document_spec.rb +170 -35
  49. data/spec/unit/defmastership/rename_included_files_line_modifier_spec.rb +203 -0
  50. data/spec/unit/defmastership/rename_included_files_modifier_spec.rb +67 -0
  51. data/spec/unit/defmastership/update_def_checksum_line_modifier_spec.rb +68 -0
  52. data/spec/unit/defmastership/update_def_checksum_modifier_spec.rb +75 -0
  53. data/spec/unit/defmastership_spec.rb +1 -0
  54. metadata +50 -18
  55. data/Gemfile.lock +0 -137
  56. data/lib/defmastership/batch_changer.rb +0 -40
  57. data/lib/defmastership/project_ref_changer.rb +0 -27
  58. data/spec/unit/defmastership/batch_changer_spec.rb +0 -108
  59. data/spec/unit/defmastership/project_ref_changer_spec.rb +0 -79
@@ -1,3 +1,4 @@
1
+ # Copyright (c) 2020 Jerome Arbez-Gindre
1
2
  # frozen_string_literal: true
2
3
 
3
4
  require('csv')
@@ -9,26 +10,27 @@ module DefMastership
9
10
  @doc = doc
10
11
  end
11
12
 
12
- def fixed_header
13
- %w[Type Reference Value]
13
+ def fixed
14
+ %w[Type Reference Value sha256]
14
15
  end
15
16
 
16
- def labels_header
17
+ def wrong_explicit_checksum
18
+ @doc.wrong_explicit_checksum? ? ['Wrong explicit checksum'] : []
19
+ end
20
+
21
+ def labels
17
22
  @doc.labels.empty? ? [] : %w[Labels]
18
23
  end
19
24
 
20
- def eref_header
21
- @doc.eref.map do |_, ref|
22
- ref[:prefix] +
23
- (ref[:url].nil? || ref[:url] == 'none' ? '' : " #{ref[:url]}")
24
- end
25
+ def eref
26
+ @doc.eref.map { |_, ref| ref[:prefix] }
25
27
  end
26
28
 
27
- def iref_header
29
+ def iref
28
30
  @doc.iref ? ['Internal links'] : []
29
31
  end
30
32
 
31
- def attributes_header
33
+ def attributes
32
34
  @doc.attributes.map { |_, value| value }
33
35
  end
34
36
  end
@@ -1,5 +1,8 @@
1
+ # Copyright (c) 2020 Jerome Arbez-Gindre
1
2
  # frozen_string_literal: true
2
3
 
4
+ require 'digest'
5
+
3
6
  module DefMastership
4
7
  # DefMastership definition: contains all data of a definition
5
8
  class Definition
@@ -15,6 +18,7 @@ module DefMastership
15
18
  @eref = Hash.new([])
16
19
  @iref = []
17
20
  @attributes = {}
21
+ @explicit_checksum = match[:explicit_checksum]
18
22
  end
19
23
 
20
24
  def <<(new_line)
@@ -26,6 +30,14 @@ module DefMastership
26
30
  @lines.join("\n")
27
31
  end
28
32
 
33
+ def sha256
34
+ Digest::SHA2.new(256).hexdigest(value).split(//).last(8).join
35
+ end
36
+
37
+ def wrong_explicit_checksum
38
+ @explicit_checksum if @explicit_checksum != sha256
39
+ end
40
+
29
41
  def add_eref(refname, extrefs)
30
42
  @eref[refname] = extrefs.strip.split(/\s*,\s*/)
31
43
  end
@@ -0,0 +1,46 @@
1
+ # Copyright (c) 2020 Jerome Arbez-Gindre
2
+ # frozen_string_literal: true
3
+
4
+ require('aasm')
5
+
6
+ module DefMastership
7
+ # DefMastership definition: contains all data of a definition
8
+ class DefinitionParser
9
+ include AASM
10
+
11
+ def initialize(document)
12
+ @document = document
13
+ end
14
+
15
+ aasm do
16
+ state :idle, initial: true
17
+ state :wait_content
18
+ state :in_block
19
+ state :single_para
20
+
21
+ event :new_definition do
22
+ transitions from: :idle, to: :wait_content, after: ->(*args) { @document.add_new_definition(*args) }
23
+ end
24
+
25
+ event :block_delimiter do
26
+ transitions from: :wait_content, to: :in_block
27
+ transitions from: %i[in_block idle single_para], to: :idle
28
+ end
29
+
30
+ event :new_line do
31
+ transitions from: %i[wait_content single_para],
32
+ to: :single_para,
33
+ after: ->(*args) { @document.add_line(*args) }
34
+ transitions from: :in_block,
35
+ to: :in_block,
36
+ after: ->(*args) { @document.add_line(*args) }
37
+ transitions from: :idle, to: :idle
38
+ end
39
+
40
+ event :empty_line do
41
+ transitions from: %i[wait_content single_para idle], to: :idle
42
+ transitions from: :in_block, to: :in_block
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,40 +1,13 @@
1
+ # Copyright (c) 2020 Jerome Arbez-Gindre
1
2
  # frozen_string_literal: true
2
3
 
3
- require('aasm')
4
+ require('asciidoctor')
4
5
 
5
6
  module DefMastership
6
7
  # Contains the content of a DefMastership document: mainly definitions
7
- # TODO: make this class smaller by defining a separated parser
8
-
9
- # Contains regexp / action couples
10
- Filter = Struct.new(:regexp, :event, :consumed_line)
11
- private_constant :Filter
12
-
13
- FILTERS_IN_LITERAL = [
14
- Filter.new(DMRegexp::LITERAL_BLOCK, :code_block_delimiter, false),
15
- Filter.new(DMRegexp::WHATEVER, :new_line, true)
16
- ].freeze
17
- private_constant :FILTERS_IN_LITERAL
18
-
19
- FILTERS = [
20
- Filter.new(DMRegexp::VARIABLE_DEF, :new_variable_def, false),
21
- Filter.new(DMRegexp::VARIABLE_USE, :new_variable_use, false),
22
- Filter.new(DMRegexp::DEFINITION, :new_definition, true),
23
- Filter.new(DMRegexp::EREF_CONFIG, :new_eref_setup, true),
24
- Filter.new(DMRegexp::EREF_DEF, :new_eref_def, false),
25
- Filter.new(DMRegexp::IREF_DEF, :new_iref_def, false),
26
- Filter.new(DMRegexp::ATTR_CONFIG, :new_attribute_conf, true),
27
- Filter.new(DMRegexp::ATTR_SET, :new_attribute_value, false),
28
- Filter.new(DMRegexp::BLOCK, :block_delimiter, true),
29
- Filter.new(DMRegexp::LITERAL_BLOCK, :code_block_delimiter, true),
30
- Filter.new(DMRegexp::EMPTY_LINE, :empty_line, false),
31
- Filter.new(DMRegexp::WHATEVER, :new_line, true)
32
- ].freeze
33
- private_constant :FILTERS
34
8
 
35
9
  # Reflects document structure from a definition point of view
36
10
  class Document
37
- include AASM
38
11
  attr_reader :definitions, :labels, :eref, :iref, :attributes, :variables
39
12
 
40
13
  def initialize
@@ -44,110 +17,116 @@ module DefMastership
44
17
  @iref = false
45
18
  @attributes = {}
46
19
  @in_literal = true
47
- @current_line = nil
48
20
  @variables = {}
21
+
22
+ @definition_parser = DefinitionParser.new(self)
49
23
  end
50
24
 
51
- aasm do
52
- state :idle, initial: true
53
- state :wait_content
54
- state :in_block
55
- state :single_para
25
+ def parse(lines)
26
+ lines.reject(&:commented?).each do |line|
27
+ (@in_literal ? FILTERS : FILTERS_IN_LITERAL).each do |filter|
28
+ next unless line.match(filter.regexp)
56
29
 
57
- event :new_definition do
58
- transitions from: :idle, to: :wait_content, after: :add_new_definition
59
- end
30
+ line = generate_event(filter.event, Regexp.last_match, line)
60
31
 
61
- event :block_delimiter do
62
- transitions from: :wait_content, to: :in_block
63
- transitions from: %i[in_block idle], to: :idle
64
- transitions from: :single_para, to: :idle
32
+ break if filter.consumed_line
33
+ end
65
34
  end
35
+ end
66
36
 
67
- event :new_line do
68
- transitions from: :wait_content, to: :single_para, after: :add_line
69
- transitions from: :single_para, to: :single_para, after: :add_line
70
- transitions from: :in_block, to: :in_block, after: :add_line
71
- transitions from: :idle, to: :idle
72
- end
37
+ def parse_file_with_preprocessor(adoc_file)
38
+ parse(Asciidoctor.load_file(adoc_file, safe: :unsafe, parse: false).reader.read_lines)
39
+ end
73
40
 
74
- event :empty_line do
75
- transitions from: %i[wait_content single_para idle], to: :idle
76
- transitions from: :in_block, to: :in_block
41
+ def wrong_explicit_checksum?
42
+ @definitions.reduce(false) do |res, definition|
43
+ res || !definition.wrong_explicit_checksum.nil?
77
44
  end
78
45
  end
79
46
 
80
- def code_block_delimiter(_match)
47
+ def code_block_delimiter(_match, line)
81
48
  @in_literal ^= true
49
+ line
82
50
  end
83
51
 
84
- def add_new_definition(match)
52
+ def add_new_definition(match, line)
85
53
  definition = Definition.new(match)
86
54
  @labels.merge(definition.labels)
87
55
  @definitions << definition
56
+ line
57
+ end
58
+
59
+ def ref_to_def(ref)
60
+ @definitions.find { |definition| definition.reference == ref }
88
61
  end
89
62
 
90
- def add_line(_match)
91
- @definitions.last << @current_line
63
+ def add_line(_match, line)
64
+ @definitions.last << line
65
+ line
92
66
  end
93
67
 
94
- def new_eref_setup(match)
68
+ def new_eref_setup(match, line)
95
69
  @eref[match[:refname].to_sym] ||= {}
96
70
 
97
71
  @eref[match[:refname].to_sym][match[:symb].to_sym] =
98
72
  match[:value]
73
+ line
99
74
  end
100
75
 
101
- def new_eref_def(match)
76
+ def new_eref_def(match, line)
102
77
  @definitions.last.add_eref(
103
78
  match[:refname].to_sym,
104
79
  match[:extrefs]
105
80
  )
81
+ line
106
82
  end
107
83
 
108
- def new_iref_def(_match)
84
+ def new_iref_def(_match, line)
109
85
  @iref = true
110
- @current_line.scan(DMRegexp::IREF_DEF) do |_|
86
+ line.scan(DMRegexp::IREF_DEF) do |_|
111
87
  @definitions.last.add_iref(Regexp.last_match[:intref])
112
88
  end
89
+ line
113
90
  end
114
91
 
115
- def new_attribute_conf(match)
92
+ def new_attribute_conf(match, line)
116
93
  @attributes[match[:attr].to_sym] = match[:prefix]
94
+ line
117
95
  end
118
96
 
119
- def new_attribute_value(match)
97
+ def new_attribute_value(match, line)
120
98
  @definitions.last.set_attribute(
121
99
  match[:attr].to_sym,
122
100
  match[:value]
123
101
  )
102
+ line
124
103
  end
125
104
 
126
- def new_variable_def(match)
105
+ def new_variable_def(match, line)
127
106
  @variables[match[:varname].to_sym] = match[:value]
107
+ line
128
108
  end
129
109
 
130
- def new_variable_use(_match)
131
- @current_line.scan(DMRegexp::VARIABLE_USE) do |_|
110
+ def new_variable_use(_match, line)
111
+ new_line = line.dup
112
+ line.scan(DMRegexp::VARIABLE_USE) do |_|
132
113
  varname = Regexp.last_match[:varname]
133
114
  next if @variables[varname.to_sym].nil?
134
115
 
135
- @current_line = @current_line.gsub(
136
- "{#{varname}}", @variables[varname.to_sym]
137
- )
116
+ new_line.gsub!("{#{varname}}", @variables[varname.to_sym])
138
117
  end
118
+ new_line
139
119
  end
140
120
 
141
- def parse(lines)
142
- lines.reject(&:commented?).each do |line|
143
- @current_line = line
144
- (@in_literal ? FILTERS : FILTERS_IN_LITERAL).each do |filter|
145
- next unless line.match(filter.regexp)
121
+ private
146
122
 
147
- public_send(filter.event, Regexp.last_match)
148
- break if filter.consumed_line
149
- end
123
+ def generate_event(event, match, line)
124
+ if respond_to?(event)
125
+ line = public_send(event, match, line)
126
+ else
127
+ @definition_parser.public_send(event, match, line)
150
128
  end
129
+ line
151
130
  end
152
131
  end
153
132
  end
@@ -0,0 +1,30 @@
1
+ # Copyright (c) 2020 Jerome Arbez-Gindre
2
+ # frozen_string_literal: true
3
+
4
+ module DefMastership
5
+ # Contains regexp / action couples
6
+ Filter = Struct.new(:regexp, :event, :consumed_line)
7
+ private_constant :Filter
8
+
9
+ FILTERS_IN_LITERAL = [
10
+ Filter.new(DMRegexp::LITERAL_BLOCK, :code_block_delimiter, false),
11
+ Filter.new(DMRegexp::WHATEVER, :new_line, true)
12
+ ].freeze
13
+ private_constant :FILTERS_IN_LITERAL
14
+
15
+ FILTERS = [
16
+ Filter.new(DMRegexp::VARIABLE_DEF, :new_variable_def, false),
17
+ Filter.new(DMRegexp::VARIABLE_USE, :new_variable_use, false),
18
+ Filter.new(DMRegexp::DEFINITION, :new_definition, true),
19
+ Filter.new(DMRegexp::EREF_CONFIG, :new_eref_setup, true),
20
+ Filter.new(DMRegexp::EREF_DEF, :new_eref_def, false),
21
+ Filter.new(DMRegexp::IREF_DEF, :new_iref_def, false),
22
+ Filter.new(DMRegexp::ATTR_CONFIG, :new_attribute_conf, true),
23
+ Filter.new(DMRegexp::ATTR_SET, :new_attribute_value, false),
24
+ Filter.new(DMRegexp::BLOCK, :block_delimiter, true),
25
+ Filter.new(DMRegexp::LITERAL_BLOCK, :code_block_delimiter, true),
26
+ Filter.new(DMRegexp::EMPTY_LINE, :empty_line, false),
27
+ Filter.new(DMRegexp::WHATEVER, :new_line, true)
28
+ ].freeze
29
+ private_constant :FILTERS
30
+ end
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2020 Jerome Arbez-Gindre
2
+ # frozen_string_literal: true
3
+
4
+ module DefMastership
5
+ # Change references from temporary to definitive with multiple RefChangers
6
+ class LineModifierBase
7
+ attr_reader :changes, :config
8
+
9
+ def initialize
10
+ @config = {}
11
+ @changes = []
12
+ end
13
+
14
+ def method_missing(method_name, *args, &block)
15
+ return @config[method_name] if @config[method_name]
16
+
17
+ super
18
+ end
19
+
20
+ def respond_to_missing?(method_name, *args)
21
+ @config[method_name] || super
22
+ end
23
+
24
+ def from_config(config)
25
+ @config.merge!(config)
26
+ self
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ # Copyright (c) 2020 Jerome Arbez-Gindre
2
+ # frozen_string_literal: true
3
+
4
+ module DefMastership
5
+ # Change references from temporary to definitive with multiple RefChangers
6
+ class ModifierBase
7
+ attr_reader :config, :changes
8
+
9
+ def initialize(config)
10
+ @config = config
11
+ @changes = []
12
+ end
13
+
14
+ def do_modifications(adoc_texts)
15
+ line_modifier = new_line_modifier(@config, adoc_texts)
16
+
17
+ adoc_texts =
18
+ replacements.reduce(adoc_texts) do |texts, method|
19
+ texts.transform_values do |text|
20
+ text.lines.map { |line| line_modifier.public_send(method, line) }.join
21
+ end
22
+ end
23
+
24
+ @config = line_modifier.config
25
+ @changes = line_modifier.changes
26
+ adoc_texts
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,126 @@
1
+ # Copyright (c) 2020 Jerome Arbez-Gindre
2
+ # frozen_string_literal: true
3
+
4
+ module DefMastership
5
+ # Change included filenames on one line at a time
6
+ class RenameIncludedFilesLineModifier < LineModifierBase
7
+ LOCAL_FILTERS = [
8
+ Filter.new(DMRegexp::VARIABLE_DEF, :new_variable_def, false),
9
+ Filter.new(DMRegexp::DEFINITION, :new_definition, true),
10
+ Filter.new(DMRegexp::BLOCK, :block_delimiter, true),
11
+ Filter.new(DMRegexp::EMPTY_LINE, :empty_line, false),
12
+ Filter.new(DMRegexp::WHATEVER, :new_line, true)
13
+ ].freeze
14
+ private_constant :LOCAL_FILTERS
15
+
16
+ def self.from_config(config)
17
+ new.from_config(config)
18
+ end
19
+
20
+ def initialize
21
+ super
22
+ @config = {
23
+ from_regexp: '',
24
+ to_template: ''
25
+ }
26
+ @variables = {}
27
+ @definition_parser = DefinitionParser.new(self)
28
+ end
29
+
30
+ def replace(line)
31
+ match = matched?(line)
32
+
33
+ return line unless match
34
+
35
+ new_line =
36
+ line.gsub(complete_regexp_from(from_regexp)) do
37
+ text_with(match, to_template % hmerge(match))
38
+ end
39
+
40
+ rename_file(line, new_line)
41
+
42
+ new_line
43
+ end
44
+
45
+ def add_new_definition(match, _line)
46
+ @config[:reference] = match[:reference]
47
+ end
48
+
49
+ def add_line(_match, _line) end
50
+
51
+ def new_variable_def(match, line)
52
+ @variables[match[:varname].to_sym] = match[:value]
53
+ line
54
+ end
55
+
56
+ private
57
+
58
+ def matched?(line)
59
+ return if line.commented?
60
+
61
+ parse(line)
62
+
63
+ return if @definition_parser.idle?
64
+ return unless line =~ complete_regexp_from(from_regexp)
65
+
66
+ match = Regexp.last_match
67
+
68
+ return if @config[:cancel_if_match] && match[:filename] =~ Regexp.new(cancel_if_match)
69
+
70
+ match
71
+ end
72
+
73
+ def parse(line)
74
+ LOCAL_FILTERS.each do |filter|
75
+ next unless line.match(filter.regexp)
76
+
77
+ line = generate_event(filter.event, Regexp.last_match, line)
78
+
79
+ break if filter.consumed_line
80
+ end
81
+ end
82
+
83
+ def rename_file(from, to)
84
+ @changes << [extract_filename(from), extract_filename(to)]
85
+ File.rename(extract_filename(from), extract_filename(to))
86
+ end
87
+
88
+ def extract_filename(include_statement)
89
+ filename = include_statement
90
+ .gsub(Regexp.new(DMRegexp::INCLUDE_KEYWORD), '')
91
+ .gsub(Regexp.new(DMRegexp::INCLUDE_OPTIONS), '')
92
+
93
+ filename.dup.scan(DMRegexp::VARIABLE_USE) do |_|
94
+ varname = Regexp.last_match[:varname]
95
+ next if @variables[varname.to_sym].nil?
96
+
97
+ filename.gsub!("{#{varname}}", @variables[varname.to_sym])
98
+ end
99
+ filename
100
+ end
101
+
102
+ def complete_regexp_from(from)
103
+ Regexp.new(
104
+ DMRegexp::INCLUDE_KEYWORD + DMRegexp::INCLUDE_PATH +
105
+ "(?<filename>#{from})" + DMRegexp::INCLUDE_OPTIONS
106
+ )
107
+ end
108
+
109
+ def text_with(match, to)
110
+ "include::#{match[:path] || ''}#{to}[#{match[:options]}]"
111
+ end
112
+
113
+ def hmerge(match)
114
+ @config.merge(match.names.map(&:to_sym).zip(match.captures).to_h)
115
+ end
116
+
117
+ def generate_event(event, match, line)
118
+ if respond_to?(event)
119
+ public_send(event, match, line)
120
+ else
121
+ @definition_parser.public_send(event, match, line)
122
+ end
123
+ line
124
+ end
125
+ end
126
+ end