renogen 1.2.1 → 1.3.0

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