defmastership 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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