defmastership 1.0.1

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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +4 -0
  4. data/.rubocop.yml +63 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +140 -0
  7. data/README.rdoc +6 -0
  8. data/Rakefile +53 -0
  9. data/bin/defmastership +99 -0
  10. data/config/devtools.yml +2 -0
  11. data/config/flay.yml +3 -0
  12. data/config/flog.yml +2 -0
  13. data/config/mutant.yml +6 -0
  14. data/config/reek.yml +106 -0
  15. data/config/rubocop.yml +44 -0
  16. data/config/yardstick.yml +2 -0
  17. data/defmastership.gemspec +37 -0
  18. data/defmastership.rdoc +5 -0
  19. data/features/changeref.feature +296 -0
  20. data/features/defmastership.feature +8 -0
  21. data/features/export.feature +275 -0
  22. data/features/step_definitions/defmastership_steps.rb +8 -0
  23. data/features/support/env.rb +18 -0
  24. data/lib/defmastership.rb +15 -0
  25. data/lib/defmastership/batch_changer.rb +40 -0
  26. data/lib/defmastership/comment_filter.rb +42 -0
  27. data/lib/defmastership/constants.rb +77 -0
  28. data/lib/defmastership/csv_formatter.rb +42 -0
  29. data/lib/defmastership/csv_formatter_body.rb +34 -0
  30. data/lib/defmastership/csv_formatter_header.rb +35 -0
  31. data/lib/defmastership/definition.rb +41 -0
  32. data/lib/defmastership/document.rb +153 -0
  33. data/lib/defmastership/project_ref_changer.rb +27 -0
  34. data/lib/defmastership/ref_changer.rb +102 -0
  35. data/lib/defmastership/version.rb +6 -0
  36. data/spec/spec_helper.rb +35 -0
  37. data/spec/unit/defmastership/batch_changer_spec.rb +108 -0
  38. data/spec/unit/defmastership/comment_filter_spec.rb +121 -0
  39. data/spec/unit/defmastership/csv_formatter_body_spec.rb +167 -0
  40. data/spec/unit/defmastership/csv_formatter_header_spec.rb +100 -0
  41. data/spec/unit/defmastership/csv_formatter_spec.rb +171 -0
  42. data/spec/unit/defmastership/definition_spec.rb +110 -0
  43. data/spec/unit/defmastership/document_spec.rb +398 -0
  44. data/spec/unit/defmastership/project_ref_changer_spec.rb +79 -0
  45. data/spec/unit/defmastership/ref_changer_spec.rb +205 -0
  46. data/spec/unit/defmastership_spec.rb +7 -0
  47. metadata +234 -0
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ When(/^I get help for "([^"]*)"$/) do |app_name|
4
+ @app_name = app_name
5
+ step %(I run `#{app_name} help`)
6
+ end
7
+
8
+ # Add more step definitions here
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('aruba/cucumber')
4
+
5
+ # ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}"\
6
+ # "#{File::PATH_SEPARATOR}#{ENV['PATH']}"
7
+ # LIB_DIR = File.join(__dir__, '..', '..', 'lib')
8
+
9
+ # Before do
10
+ # # Using "announce" causes massive warnings on 1.9.2
11
+ # @puts = true
12
+ # @original_rubylib = ENV['RUBYLIB']
13
+ # ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
14
+ # end
15
+
16
+ # After do
17
+ # ENV['RUBYLIB'] = @original_rubylib
18
+ # end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('defmastership/version')
4
+
5
+ # Add requires for other files you add to your project here, so
6
+ # you just need to require this one file in your bin file
7
+ require('defmastership/constants')
8
+ require('defmastership/definition')
9
+ require('defmastership/document')
10
+ require('defmastership/comment_filter')
11
+ require('defmastership/csv_formatter')
12
+
13
+ require('defmastership/ref_changer')
14
+ require('defmastership/batch_changer')
15
+ require('defmastership/project_ref_changer')
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefMastership
4
+ # Change references from temporary to definitive with multiple RefChangers
5
+ class BatchChanger
6
+ attr_reader :refchangers
7
+
8
+ def initialize
9
+ @refchangers = {}
10
+ end
11
+
12
+ def self.from_h(hash)
13
+ new.from_h(hash)
14
+ end
15
+
16
+ def from_h(hash)
17
+ @refchangers =
18
+ hash.transform_values do |value|
19
+ DefMastership::RefChanger.from_h(value)
20
+ end
21
+ self
22
+ end
23
+
24
+ def to_h
25
+ @refchangers.transform_values(&:to_h)
26
+ end
27
+
28
+ def replace(symb, line)
29
+ @refchangers.reduce(line) do |res_line, (_, refchanger)|
30
+ refchanger.public_send("replace_#{symb}".to_sym, res_line)
31
+ end
32
+ end
33
+
34
+ def changes
35
+ @refchangers.reduce([]) do |all_changes, (_, refchanger)|
36
+ all_changes + refchanger.changes
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Add a class to handled asciidoctor comments
4
+ module DefMastership
5
+ attr_reader :comment_filter
6
+
7
+ # Kind of proxy that Filters comments fome Asciidoctor document
8
+ class CommentFilter
9
+ def initialize
10
+ @in_multiline_comment = false
11
+ end
12
+
13
+ def accept?(line)
14
+ case line
15
+ when DMRegexp::SINGLE_LINE_COMMENT then false
16
+ when DMRegexp::MULTI_LINE_COMMENT_DELIM
17
+ @in_multiline_comment ^= true
18
+ false
19
+ else !@in_multiline_comment
20
+ end
21
+ end
22
+ end
23
+
24
+ @comment_filter = CommentFilter.new
25
+
26
+ def self.reset_comments
27
+ @comment_filter = CommentFilter.new
28
+ nil
29
+ end
30
+
31
+ def self.comment_filter
32
+ @comment_filter
33
+ end
34
+ end
35
+
36
+ # adding a method to string to figure out if a string is commented or
37
+ # not.
38
+ class String
39
+ def commented?
40
+ !DefMastership.comment_filter.accept?(self)
41
+ end
42
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefMastership
4
+ # set of regexp of added asciidoctor constructions
5
+ module DMRegexp
6
+ SINGLE_LINE_COMMENT = %r{^//[^/]}.freeze
7
+
8
+ MULTI_LINE_COMMENT_DELIM = %r{^////\s*$}.freeze
9
+
10
+ DEF_BEFORE_REF = <<~'BEF'
11
+ ^\s*
12
+ \[\s*define\s*,
13
+ \s*(?<type>[\w:_-]+)\s*,
14
+ \s*
15
+ BEF
16
+
17
+ DEF_AFTER_REF = <<~'AFT'
18
+ \s*
19
+ (,\s*\[\s*(?<labels>.*?)\s*\])?\s*\]
20
+ AFT
21
+ DEFINITION = Regexp.new(
22
+ "#{DEF_BEFORE_REF}(?<reference>[\\w:_-]+)#{DEF_AFTER_REF}",
23
+ Regexp::EXTENDED
24
+ )
25
+
26
+ VARIABLE_DEF = /^\s*:(?<varname>[\w:_-]+):\s*
27
+ \s*(?<value>\S.*\S)\s*$/x.freeze
28
+
29
+ VARIABLE_USE = /{(?<varname>[\w:_-]+)}/x.freeze
30
+
31
+ EREF_CONFIG = /^\s*:eref-(?<refname>[\w:_-]+)-(?<symb>prefix|url):\s*
32
+ \s*(?<value>\S.*\S)\s*/x.freeze
33
+ EREF_DEF = /^\s*
34
+ defs:eref\[
35
+ \s*(?<refname>[\w:_-]+)\s*,
36
+ \s*\[(?<extrefs>[^\]]+)\]\s*\]/x.freeze
37
+ BLOCK = /^--\s*$/.freeze
38
+ LITERAL_BLOCK = /^\.\.\.\.\s*$/.freeze
39
+
40
+ IREF_DEF_BEF = 'defs:iref\[\s*'
41
+ IREF_DEF_AFT = '\s*\]'
42
+ IREF_DEF = Regexp.new(
43
+ "#{IREF_DEF_BEF}(?<intref>[\\w:_-]+)#{IREF_DEF_AFT}",
44
+ Regexp::EXTENDED
45
+ )
46
+
47
+ ATTR_CONFIG = /\s*:attr-(?<attr>[\w:_-]+)-prefix:
48
+ \s+(?<prefix>.+?)\s*$/x.freeze
49
+ ATTR_SET = /\s*
50
+ defs:attribute\[
51
+ \s*(?<attr>[\w:_-]+)\s*,
52
+ \s*(?<value>.+?)\s*\]/x.freeze
53
+
54
+ EMPTY_LINE = /^\s*$/.freeze
55
+
56
+ WHATEVER = //.freeze
57
+
58
+ public_constant :SINGLE_LINE_COMMENT,
59
+ :MULTI_LINE_COMMENT_DELIM,
60
+ :DEF_BEFORE_REF,
61
+ :DEF_AFTER_REF,
62
+ :DEFINITION,
63
+ :VARIABLE_DEF,
64
+ :VARIABLE_USE,
65
+ :EREF_CONFIG,
66
+ :EREF_DEF,
67
+ :BLOCK,
68
+ :LITERAL_BLOCK,
69
+ :IREF_DEF_BEF,
70
+ :IREF_DEF_AFT,
71
+ :IREF_DEF,
72
+ :ATTR_CONFIG,
73
+ :ATTR_SET,
74
+ :EMPTY_LINE,
75
+ :WHATEVER
76
+ end
77
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('csv')
4
+ require('defmastership/csv_formatter_header')
5
+ require('defmastership/csv_formatter_body')
6
+
7
+ module DefMastership
8
+ # to export a CSV file
9
+ class CSVFormatter
10
+ COLUMN_LIST = %w[fixed labels eref iref attributes].freeze
11
+ private_constant :COLUMN_LIST
12
+
13
+ def initialize(doc)
14
+ @doc = doc
15
+ @header_formatter = CSVFormatterHeader.new(@doc)
16
+ @body_formatter = CSVFormatterBody.new(@doc)
17
+ end
18
+
19
+ def export_to(output_file)
20
+ CSV.open(output_file, 'w:ISO-8859-1') do |csv|
21
+ csv << header
22
+ @doc.definitions.each { |definition| csv << body(definition) }
23
+ end
24
+ end
25
+
26
+ def header
27
+ header_line =
28
+ COLUMN_LIST.map do |part|
29
+ @header_formatter.public_send("#{part}_header".to_sym)
30
+ end
31
+ header_line.reduce(:+)
32
+ end
33
+
34
+ def body(definition)
35
+ body_line =
36
+ COLUMN_LIST.map do |part|
37
+ @body_formatter.public_send("#{part}_body".to_sym, definition)
38
+ end
39
+ body_line.reduce(:+)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('csv')
4
+
5
+ module DefMastership
6
+ # format lines per definition
7
+ class CSVFormatterBody
8
+ def initialize(doc)
9
+ @doc = doc
10
+ end
11
+
12
+ def fixed_body(definition)
13
+ [definition.type, definition.reference, definition.value]
14
+ end
15
+
16
+ def labels_body(definition)
17
+ @doc.labels.empty? ? [] : [definition.labels.to_a.join("\n")]
18
+ end
19
+
20
+ def eref_body(definition)
21
+ @doc.eref.map { |key, _| definition.eref[key].join("\n") }
22
+ end
23
+
24
+ def iref_body(definition)
25
+ @doc.iref ? [definition.iref.join("\n")] : []
26
+ end
27
+
28
+ def attributes_body(definition)
29
+ @doc.attributes.map do |key, _|
30
+ definition.attributes[key]
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('csv')
4
+
5
+ module DefMastership
6
+ # format header for one document
7
+ class CSVFormatterHeader
8
+ def initialize(doc)
9
+ @doc = doc
10
+ end
11
+
12
+ def fixed_header
13
+ %w[Type Reference Value]
14
+ end
15
+
16
+ def labels_header
17
+ @doc.labels.empty? ? [] : %w[Labels]
18
+ end
19
+
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
+ end
26
+
27
+ def iref_header
28
+ @doc.iref ? ['Internal links'] : []
29
+ end
30
+
31
+ def attributes_header
32
+ @doc.attributes.map { |_, value| value }
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DefMastership
4
+ # DefMastership definition: contains all data of a definition
5
+ class Definition
6
+ attr_reader :type, :reference, :lines, :labels, :eref, :iref, :attributes
7
+
8
+ def initialize(match)
9
+ @type = match[:type]
10
+ @reference = match[:reference]
11
+ @lines = []
12
+ @labels = Set.new
13
+ labels = match[:labels]
14
+ @labels.merge(labels.split(/\s*,\s*/).to_set) if labels
15
+ @eref = Hash.new([])
16
+ @iref = []
17
+ @attributes = {}
18
+ end
19
+
20
+ def <<(new_line)
21
+ @lines << new_line
22
+ self
23
+ end
24
+
25
+ def value
26
+ @lines.join("\n")
27
+ end
28
+
29
+ def add_eref(refname, extrefs)
30
+ @eref[refname] = extrefs.strip.split(/\s*,\s*/)
31
+ end
32
+
33
+ def add_iref(ref)
34
+ @iref << ref
35
+ end
36
+
37
+ def set_attribute(key, value)
38
+ @attributes[key] = value
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,153 @@
1
+ # frozen_string_literal: true
2
+
3
+ require('aasm')
4
+
5
+ module DefMastership
6
+ # 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
+
35
+ # Reflects document structure from a definition point of view
36
+ class Document
37
+ include AASM
38
+ attr_reader :definitions, :labels, :eref, :iref, :attributes, :variables
39
+
40
+ def initialize
41
+ @definitions = []
42
+ @labels = Set.new
43
+ @eref = {}
44
+ @iref = false
45
+ @attributes = {}
46
+ @in_literal = true
47
+ @current_line = nil
48
+ @variables = {}
49
+ end
50
+
51
+ aasm do
52
+ state :idle, initial: true
53
+ state :wait_content
54
+ state :in_block
55
+ state :single_para
56
+
57
+ event :new_definition do
58
+ transitions from: :idle, to: :wait_content, after: :add_new_definition
59
+ end
60
+
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
65
+ end
66
+
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
73
+
74
+ event :empty_line do
75
+ transitions from: %i[wait_content single_para idle], to: :idle
76
+ transitions from: :in_block, to: :in_block
77
+ end
78
+ end
79
+
80
+ def code_block_delimiter(_match)
81
+ @in_literal ^= true
82
+ end
83
+
84
+ def add_new_definition(match)
85
+ definition = Definition.new(match)
86
+ @labels.merge(definition.labels)
87
+ @definitions << definition
88
+ end
89
+
90
+ def add_line(_match)
91
+ @definitions.last << @current_line
92
+ end
93
+
94
+ def new_eref_setup(match)
95
+ @eref[match[:refname].to_sym] ||= {}
96
+
97
+ @eref[match[:refname].to_sym][match[:symb].to_sym] =
98
+ match[:value]
99
+ end
100
+
101
+ def new_eref_def(match)
102
+ @definitions.last.add_eref(
103
+ match[:refname].to_sym,
104
+ match[:extrefs]
105
+ )
106
+ end
107
+
108
+ def new_iref_def(_match)
109
+ @iref = true
110
+ @current_line.scan(DMRegexp::IREF_DEF) do |_|
111
+ @definitions.last.add_iref(Regexp.last_match[:intref])
112
+ end
113
+ end
114
+
115
+ def new_attribute_conf(match)
116
+ @attributes[match[:attr].to_sym] = match[:prefix]
117
+ end
118
+
119
+ def new_attribute_value(match)
120
+ @definitions.last.set_attribute(
121
+ match[:attr].to_sym,
122
+ match[:value]
123
+ )
124
+ end
125
+
126
+ def new_variable_def(match)
127
+ @variables[match[:varname].to_sym] = match[:value]
128
+ end
129
+
130
+ def new_variable_use(_match)
131
+ @current_line.scan(DMRegexp::VARIABLE_USE) do |_|
132
+ varname = Regexp.last_match[:varname]
133
+ next if @variables[varname.to_sym].nil?
134
+
135
+ @current_line = @current_line.gsub(
136
+ "{#{varname}}", @variables[varname.to_sym]
137
+ )
138
+ end
139
+ end
140
+
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)
146
+
147
+ public_send(filter.event, Regexp.last_match)
148
+ break if filter.consumed_line
149
+ end
150
+ end
151
+ end
152
+ end
153
+ end