renogen 1.2.1 → 1.3.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 (42) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +112 -27
  3. data/lib/renogen/change_log.rb +1 -0
  4. data/lib/renogen/change_log/item.rb +6 -4
  5. data/lib/renogen/change_log/model.rb +9 -0
  6. data/lib/renogen/change_log/validator.rb +61 -0
  7. data/lib/renogen/change_log/writer.rb +22 -2
  8. data/lib/renogen/cli.rb +6 -4
  9. data/lib/renogen/config.rb +15 -15
  10. data/lib/renogen/exceptions.rb +5 -0
  11. data/lib/renogen/exceptions/invalid_item_found.rb +32 -0
  12. data/lib/renogen/exceptions/yaml_file_blank.rb +21 -0
  13. data/lib/renogen/exceptions/yaml_file_invalid.rb +20 -0
  14. data/lib/renogen/extraction_stratagies.rb +0 -2
  15. data/lib/renogen/extraction_stratagies/yaml_file.rb +0 -1
  16. data/lib/renogen/extraction_stratagies/yaml_file/parser.rb +5 -4
  17. data/lib/renogen/extraction_stratagies/yaml_file/reader.rb +20 -11
  18. data/lib/renogen/formatters.rb +2 -0
  19. data/lib/renogen/formatters/base.rb +14 -4
  20. data/lib/renogen/formatters/csv.rb +41 -0
  21. data/lib/renogen/formatters/markdown_table.rb +58 -0
  22. data/lib/renogen/generator.rb +8 -1
  23. data/lib/renogen/version.rb +1 -1
  24. data/lib/renogen/writers/csv.rb +23 -0
  25. data/spec/lib/renogen/change_log/item_spec.rb +1 -1
  26. data/spec/lib/renogen/change_log/model_spec.rb +1 -1
  27. data/spec/lib/renogen/change_log/validator_spec.rb +38 -0
  28. data/spec/lib/renogen/config_spec.rb +7 -2
  29. data/spec/lib/renogen/exceptions/invalid_item_found_spec.rb +35 -0
  30. data/spec/lib/renogen/exceptions/stratagy_not_found_spec.rb +2 -1
  31. data/spec/lib/renogen/{extraction_stratagies/yaml_file/exceptions → exceptions}/yaml_file_blank_spec.rb +1 -1
  32. data/spec/lib/renogen/exceptions/yaml_file_invalid_spec.rb +12 -0
  33. data/spec/lib/renogen/extraction_stratagies/yaml_file/parser_spec.rb +3 -3
  34. data/spec/lib/renogen/extraction_stratagies/yaml_file/reader_spec.rb +11 -5
  35. data/spec/lib/renogen/formatters/base_spec.rb +8 -8
  36. data/spec/lib/renogen/formatters/csv_spec.rb +49 -0
  37. data/spec/spec_helper.rb +5 -0
  38. data/spec/support/renogen_helper.rb +9 -0
  39. metadata +15 -6
  40. data/lib/renogen/extraction_stratagies/yaml_file/exceptions.rb +0 -12
  41. data/lib/renogen/extraction_stratagies/yaml_file/exceptions/yaml_file_blank.rb +0 -25
  42. data/spec/lib/renogen/change_log/writer_spec.rb +0 -8
@@ -1,7 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Renogen
2
4
  # Custom exceptions throw by the libary
3
5
  module Exceptions
4
6
  require_relative 'exceptions/base'
5
7
  require_relative 'exceptions/stratagy_not_found'
8
+ require_relative 'exceptions/invalid_item_found'
9
+ require_relative 'exceptions/yaml_file_blank'
10
+ require_relative 'exceptions/yaml_file_invalid'
6
11
  end
7
12
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renogen
4
+ module Exceptions
5
+ # Raised when change log contains invalid items.
6
+ class InvalidItemFound < Base
7
+ attr_reader :invalid_items
8
+
9
+ def initialize(invalid_items)
10
+ @invalid_items = invalid_items
11
+ super
12
+ end
13
+
14
+ # Friendly error message
15
+ #
16
+ # @return [String]
17
+ def message
18
+ messages = ['Invalid items:']
19
+ invalid_items.each do |item|
20
+ invalid_value = item[:invalid_value]
21
+
22
+ messages << if item[:valid_values].is_a?(Regexp)
23
+ "Group: #{item[:group_name]}, Content: #{invalid_value}, Pattern: #{item[:valid_values].inspect}"
24
+ else
25
+ "Group: #{item[:group_name]}, Content: #{invalid_value}, Valid Values: #{item[:valid_values]}"
26
+ end
27
+ end
28
+ messages.join("\n")
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,21 @@
1
+ module Renogen
2
+ module Exceptions
3
+ # This is raised when a yaml file change is found but has not contents
4
+ class YamlFileBlank < Renogen::Exceptions::Base
5
+ attr_reader :file_path
6
+
7
+ def initialize(file_path)
8
+ @file_path = file_path
9
+ super
10
+ end
11
+
12
+ # Friendly error message
13
+ #
14
+ # @return [String]
15
+ def message
16
+ "Error: File contents blank '#{file_path}'"
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,20 @@
1
+ module Renogen
2
+ module Exceptions
3
+ # This is raised when a yaml file change is found but has invalid contents
4
+ class YamlFileInvalid < Renogen::Exceptions::Base
5
+ attr_reader :file_path
6
+
7
+ def initialize(file_path)
8
+ @file_path = file_path
9
+ super
10
+ end
11
+
12
+ # Friendly error message
13
+ #
14
+ # @return [String]
15
+ def message
16
+ "Error: File contents invalid '#{file_path}'"
17
+ end
18
+ end
19
+ end
20
+ end
@@ -36,7 +36,5 @@ module Renogen
36
36
 
37
37
  require_relative 'extraction_stratagies/base'
38
38
  require_relative 'extraction_stratagies/yaml_file'
39
- # require_relative 'extraction_stratagies/github'
40
- # require_relative 'extraction_stratagies/gitlog'
41
39
  end
42
40
  end
@@ -5,7 +5,6 @@ module Renogen
5
5
  require_relative 'yaml_file/reader'
6
6
  require_relative 'yaml_file/parser'
7
7
  require_relative 'yaml_file/provider'
8
- require_relative 'yaml_file/exceptions'
9
8
  end
10
9
  end
11
10
  end
@@ -13,8 +13,8 @@ module Renogen
13
13
 
14
14
  # @return [ChangeLog::Model]
15
15
  def parse!
16
- yaml_file_reader.each_yaml_file do |file|
17
- parse_file(file)
16
+ yaml_file_reader.each_yaml_file do |file, index|
17
+ parse_file(index, file)
18
18
  end
19
19
  changelog
20
20
  end
@@ -23,9 +23,10 @@ module Renogen
23
23
 
24
24
  attr_reader :yaml_file_reader
25
25
 
26
- def parse_file(file)
26
+ # @param [Hash] data
27
+ def parse_file(id, file)
27
28
  file.each do |group_name, content|
28
- changelog.add_change(ChangeLog::Item.new(group_name, content))
29
+ changelog.add_change(ChangeLog::Item.new(id, group_name, content))
29
30
  end
30
31
  end
31
32
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
 
3
5
  module Renogen
@@ -7,23 +9,28 @@ module Renogen
7
9
  class Reader
8
10
  attr_accessor :directory_path, :legacy_version
9
11
 
10
- def initialize(directory_path, options={})
12
+ def initialize(directory_path, options = {})
11
13
  @legacy_version = options['legacy_version']
12
14
  @directory_path = directory_path
13
15
  @directory_path ||= './change_log/'
14
16
  end
15
17
 
16
18
  # Iterates thorugh each change file and yields the contents.
17
- #
18
- # an exception is thrown if the contents are blank
19
+ #
20
+ # an exception is thrown if the contents are blank or invalid
19
21
  #
20
22
  # @yield [Hash] yaml_file
21
23
  def each_yaml_file
22
- change_directories.each do |file_path|
24
+ path = ''
25
+ change_directories.each_with_index do |file_path, i|
26
+ path = file_path
23
27
  content = ::YAML.load_file(file_path)
24
- raise Exceptions::YamlFileBlank.new(file_path) unless content
25
- yield content
28
+ raise Exceptions::YamlFileBlank, file_path unless content
29
+
30
+ yield content, i
26
31
  end
32
+ rescue Psych::SyntaxError
33
+ raise Exceptions::YamlFileInvalid, path
27
34
  end
28
35
 
29
36
  private
@@ -31,20 +38,22 @@ module Renogen
31
38
  # @return [Array]
32
39
  def change_directories
33
40
  upgrade_versions = legacy_versions.map do |path|
34
- File.join(path, "*.yml")
41
+ File.join(path, '*.yml')
35
42
  end
36
- upgrade_versions << File.join(directory_path, 'next', "*.yml")
43
+ upgrade_versions << File.join(directory_path, 'next', '*.yml')
37
44
 
38
45
  Dir.glob(upgrade_versions)
39
46
  end
40
47
 
41
- # @return [Array]
48
+ # @return [Array]
42
49
  def legacy_versions
43
50
  return [] unless legacy_version
44
- legacy_version.gsub!('v','')
51
+
52
+ legacy_version.gsub!('v', '')
45
53
  Dir.glob(File.join(directory_path, '*')).select do |dir|
46
- dir = dir.split('/').last.gsub('v','').gsub('_','.')
54
+ dir = dir.split('/').last.gsub('v', '').gsub('_', '.')
47
55
  next if dir == 'next'
56
+
48
57
  Gem::Version.new(dir) > Gem::Version.new(legacy_version)
49
58
  end
50
59
  end
@@ -37,7 +37,9 @@ module Renogen
37
37
  end
38
38
 
39
39
  require_relative 'formatters/base'
40
+ require_relative 'formatters/csv'
40
41
  require_relative 'formatters/plain_text'
41
42
  require_relative 'formatters/markdown'
43
+ require_relative 'formatters/markdown_table'
42
44
  require_relative 'formatters/html'
43
45
  end
@@ -1,9 +1,21 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Renogen
2
4
  module Formatters
3
- # Implements a template pattern that forces the implemention of required
5
+ # Implements a template pattern that forces the implemention of required
4
6
  # methods in sub classes
5
7
  class Base
6
- def initialize(options={})
8
+ attr_reader :options
9
+
10
+ def initialize(options = {})
11
+ @options = options
12
+ end
13
+
14
+ # Switch to determine if the formatter is in table format(instead of group format)
15
+ #
16
+ # return [Boolean] (default: false)
17
+ def table_formatter?
18
+ false # for backward compatibility
7
19
  end
8
20
 
9
21
  # Adds class with identifier to formatters
@@ -29,7 +41,6 @@ module Renogen
29
41
  # @param header [String]
30
42
  # @raise NotImplementedError
31
43
  def write_header(header)
32
- raise NotImplementedError
33
44
  end
34
45
 
35
46
  # Outputs a line or block as a header for a group.
@@ -39,7 +50,6 @@ module Renogen
39
50
  # @param group [String]
40
51
  # @raise NotImplementedError
41
52
  def write_group(group)
42
- raise NotImplementedError
43
53
  end
44
54
 
45
55
  # Outputs a line or block of text appearing after a group.
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renogen
4
+ module Formatters
5
+ # For formatting a change into CSV output
6
+ class Csv < Base
7
+ register :csv
8
+
9
+ attr_reader :headings
10
+
11
+ def table_formatter?
12
+ true
13
+ end
14
+
15
+ def write_header(header)
16
+ "#{header}\n"
17
+ end
18
+
19
+ def write_change(ticket)
20
+ headings.map do |header|
21
+ raw_line = ticket[header]
22
+ next if raw_line.nil?
23
+
24
+ parsed_line = raw_line.is_a?(Array) ? raw_line.join(',') : raw_line
25
+ parsed_line.gsub!("\n", '\n') if parsed_line.include?("\n")
26
+
27
+ if parsed_line.include?(',')
28
+ "\"#{parsed_line}\""
29
+ else
30
+ parsed_line
31
+ end
32
+ end.join(',')
33
+ end
34
+
35
+ def header(changelog)
36
+ @headings = changelog.groups.keys
37
+ @headings.join(',')
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,58 @@
1
+ module Renogen
2
+ module Formatters
3
+ # For formatting a change into markdown format
4
+ class MarkdownTable < Markdown
5
+ register :markdown_table
6
+ register :md_table
7
+
8
+ attr_reader :headings
9
+
10
+ def table_formatter?
11
+ true
12
+ end
13
+
14
+ # Generate header
15
+ #
16
+ # @param changelog [Renogen::ChangeLog::Model]
17
+ #
18
+ # return [String]
19
+ def header(changelog)
20
+ @headings = changelog.groups.keys
21
+ [
22
+ "# #{changelog.version} (#{changelog.date})",
23
+ "",
24
+ "| #{headings.join(' | ')} |",
25
+ "| #{headings.map{|_| ' - |' }.join()}"
26
+ ].join("\n")
27
+ end
28
+
29
+ # Outputs a line or block of text appearing at the top of the change log.
30
+ #
31
+ # @param header [String]
32
+ # @return [String]
33
+ def write_header(header)
34
+ "#{header}\n"
35
+ end
36
+
37
+ # Outputs a line or block as a header for a group.
38
+ #
39
+ # @param group [String]
40
+ # @return [String]
41
+ def write_group(group)
42
+ "## #{group}\n\n"
43
+ end
44
+
45
+ # Outputs a line or block as the body for a change in a group.
46
+ #
47
+ # @param ticket [Hash<group: string>]
48
+ # @return [String]
49
+ def write_change(ticket)
50
+ "| "+ @headings.map do |heading|
51
+ value = ticket.fetch(heading, '-')
52
+ value = value.join("\n") if value.is_a?(Array)
53
+ value.gsub("\n", "<br>")
54
+ end.join(' | ') + " |"
55
+ end
56
+ end
57
+ end
58
+ end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Renogen
2
4
  # This is the conductor of the application
3
5
  class Generator
4
6
  attr_accessor :source, :version, :output_format, :options
5
7
 
6
- def initialize(version, source, output_format, options={})
8
+ def initialize(version, source, output_format, options = {})
7
9
  @version = version
8
10
  @source = source
9
11
  @output_format = output_format
@@ -16,6 +18,7 @@ module Renogen
16
18
  changelog.version = version
17
19
  changelog.date = options['release_date']
18
20
 
21
+ validator.validate!(changelog) if options['allowed_values']&.any?
19
22
  writer.write!(changelog)
20
23
  end
21
24
 
@@ -32,5 +35,9 @@ module Renogen
32
35
  def formatter
33
36
  Renogen::Formatters.obtain(output_format, options)
34
37
  end
38
+
39
+ def validator
40
+ Renogen::ChangeLog::Validator.new(formatter)
41
+ end
35
42
  end
36
43
  end
@@ -1,3 +1,3 @@
1
1
  module Renogen
2
- VERSION='1.2.1'.freeze # :nodoc:
2
+ VERSION = '1.3.0'.freeze # :nodoc:
3
3
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Renogen
4
+ module Writers
5
+ # Writes out the change log in CSV format
6
+ class Csv < Base
7
+ register :csv
8
+
9
+ def write!(changelog)
10
+ puts formatter.write_headings(changelog)
11
+ output_files(changelog)
12
+ end
13
+
14
+ private
15
+
16
+ def output_files(changelog)
17
+ changelog.files.each do |file|
18
+ puts formatter.write_file(file, changelog)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Renogen::ChangeLog::Item do
4
4
  let(:change) { Array.new }
5
- subject { described_class.new('foo', change) }
5
+ subject { described_class.new(1, 'foo', change) }
6
6
 
7
7
  describe '#to_s' do
8
8
  context "when change type is nil" do
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Renogen::ChangeLog::Model do
4
- let(:change_item) { Renogen::ChangeLog::Item.new('foo', 'bar') }
4
+ let(:change_item) { Renogen::ChangeLog::Item.new(1, 'foo', 'bar') }
5
5
 
6
6
  describe '#groups' do
7
7
  it 'returns an empty hash when no changes' do
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Renogen::ChangeLog::Validator do
6
+ let(:change_log) { Renogen::ChangeLog::Model.new }
7
+ let(:validations) { { 'foo' => %w(bar baz) } }
8
+
9
+ subject { described_class.new(double('Formatter', options: { 'allowed_values' => validations })) }
10
+
11
+ before(:each) { change_log.add_change(Renogen::ChangeLog::Item.new(1, 'foo', 'bar')) }
12
+
13
+ describe '#validate!' do
14
+ context 'when no validations are available' do
15
+ let(:validations) { nil }
16
+
17
+ it 'successfully passes the validation process' do
18
+ expect { subject.validate!(change_log) }.to_not raise_error
19
+ subject.validate!(change_log)
20
+ end
21
+ end
22
+
23
+ context 'when validation is successful' do
24
+ it 'successfully passes the validation process' do
25
+ expect { subject.validate!(change_log) }.to_not raise_error
26
+ subject.validate!(change_log)
27
+ end
28
+ end
29
+
30
+ context 'when validation has failed' do
31
+ let(:validations) { { 'foo' => ['baz'] } }
32
+
33
+ it 'fails validation' do
34
+ expect { subject.validate!(change_log) }.to raise_error(Renogen::Exceptions::InvalidItemFound)
35
+ end
36
+ end
37
+ end
38
+ end