renogen 1.0.0 → 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 (47) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +162 -23
  3. data/lib/renogen/change_log.rb +1 -0
  4. data/lib/renogen/change_log/item.rb +10 -7
  5. data/lib/renogen/change_log/model.rb +11 -8
  6. data/lib/renogen/change_log/validator.rb +61 -0
  7. data/lib/renogen/change_log/writer.rb +23 -3
  8. data/lib/renogen/cli.rb +28 -7
  9. data/lib/renogen/cli/param_parser.rb +19 -3
  10. data/lib/renogen/config.rb +15 -14
  11. data/lib/renogen/exceptions.rb +5 -0
  12. data/lib/renogen/exceptions/invalid_item_found.rb +32 -0
  13. data/lib/renogen/exceptions/yaml_file_blank.rb +21 -0
  14. data/lib/renogen/exceptions/yaml_file_invalid.rb +20 -0
  15. data/lib/renogen/extraction_stratagies.rb +0 -2
  16. data/lib/renogen/extraction_stratagies/yaml_file.rb +0 -1
  17. data/lib/renogen/extraction_stratagies/yaml_file/parser.rb +5 -4
  18. data/lib/renogen/extraction_stratagies/yaml_file/reader.rb +20 -11
  19. data/lib/renogen/formatters.rb +2 -0
  20. data/lib/renogen/formatters/base.rb +32 -7
  21. data/lib/renogen/formatters/csv.rb +41 -0
  22. data/lib/renogen/formatters/html.rb +1 -1
  23. data/lib/renogen/formatters/markdown.rb +2 -2
  24. data/lib/renogen/formatters/markdown_table.rb +58 -0
  25. data/lib/renogen/generator.rb +9 -1
  26. data/lib/renogen/version.rb +1 -1
  27. data/lib/renogen/writers/csv.rb +23 -0
  28. data/spec/lib/renogen/change_log/item_spec.rb +8 -1
  29. data/spec/lib/renogen/change_log/model_spec.rb +22 -8
  30. data/spec/lib/renogen/change_log/validator_spec.rb +38 -0
  31. data/spec/lib/renogen/config_spec.rb +7 -2
  32. data/spec/lib/renogen/exceptions/invalid_item_found_spec.rb +35 -0
  33. data/spec/lib/renogen/exceptions/stratagy_not_found_spec.rb +2 -1
  34. data/spec/lib/renogen/{extraction_stratagies/yaml_file/exceptions → exceptions}/yaml_file_blank_spec.rb +1 -1
  35. data/spec/lib/renogen/exceptions/yaml_file_invalid_spec.rb +12 -0
  36. data/spec/lib/renogen/extraction_stratagies/yaml_file/parser_spec.rb +3 -3
  37. data/spec/lib/renogen/extraction_stratagies/yaml_file/reader_spec.rb +11 -5
  38. data/spec/lib/renogen/formatters/base_spec.rb +14 -4
  39. data/spec/lib/renogen/formatters/csv_spec.rb +49 -0
  40. data/spec/lib/renogen/formatters/html_spec.rb +1 -1
  41. data/spec/lib/renogen/formatters/markdown_spec.rb +2 -2
  42. data/spec/spec_helper.rb +5 -0
  43. data/spec/support/renogen_helper.rb +9 -0
  44. metadata +17 -8
  45. data/lib/renogen/extraction_stratagies/yaml_file/exceptions.rb +0 -12
  46. data/lib/renogen/extraction_stratagies/yaml_file/exceptions/yaml_file_blank.rb +0 -25
  47. data/spec/lib/renogen/change_log/writer_spec.rb +0 -8
@@ -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
@@ -14,7 +16,9 @@ module Renogen
14
16
  def generate!
15
17
  changelog = extraction_stratagy.extract
16
18
  changelog.version = version
19
+ changelog.date = options['release_date']
17
20
 
21
+ validator.validate!(changelog) if options['allowed_values']&.any?
18
22
  writer.write!(changelog)
19
23
  end
20
24
 
@@ -31,5 +35,9 @@ module Renogen
31
35
  def formatter
32
36
  Renogen::Formatters.obtain(output_format, options)
33
37
  end
38
+
39
+ def validator
40
+ Renogen::ChangeLog::Validator.new(formatter)
41
+ end
34
42
  end
35
43
  end
@@ -1,3 +1,3 @@
1
1
  module Renogen
2
- VERSION='1.0.0'.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,9 +2,16 @@ 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
+ context "when change type is nil" do
9
+ let(:change) { nil }
10
+ it 'returns a blank string ' do
11
+ expect(subject.to_s).to eql ""
12
+ end
13
+ end
14
+
8
15
  context "when change type is a 'String'" do
9
16
  let(:change) { "foo\nbar\n" }
10
17
  it 'returns string with newline at end' 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
@@ -14,13 +14,6 @@ describe Renogen::ChangeLog::Model do
14
14
  end
15
15
  end
16
16
 
17
- describe '#header' do
18
- it 'returns version and date' do
19
- subject.version = '123'
20
- expect(subject.header).to eql "123 (#{Date.today})"
21
- end
22
- end
23
-
24
17
  describe '#add_change' do
25
18
  it 'adds change to changes store' do
26
19
  subject.add_change(change_item)
@@ -33,4 +26,25 @@ describe Renogen::ChangeLog::Model do
33
26
  end
34
27
  end
35
28
 
29
+ describe '#date' do
30
+ it 'defaults to Date.today' do
31
+ expect(subject.date).to eql Date.today
32
+ end
33
+
34
+ it 'can be set using an option' do
35
+ date = Date.parse('2015-12-25')
36
+ model_instance = described_class.new(date: date)
37
+
38
+ expect(model_instance.date).to eql date
39
+ end
40
+
41
+ it 'has a setter' do
42
+ date = Date.parse('1981-03-20')
43
+
44
+ subject.date = date
45
+
46
+ expect(subject.date).to eql date
47
+ end
48
+ end
49
+
36
50
  end
@@ -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
@@ -1,19 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Renogen::Config do
4
-
5
6
  subject { described_class.instance }
6
7
 
7
8
  describe '#configure' do
8
9
  before :each do
9
10
  described_class.configure do |config|
10
11
  config.single_line_format = 'bar'
12
+ config.validations = ['test']
11
13
  end
12
14
  end
13
15
 
14
16
  it 'can set value' do
15
17
  expect(subject.single_line_format).to eql 'bar'
16
18
  end
17
- end
18
19
 
20
+ it 'sets the validations value' do
21
+ expect(subject.validations).to eql ['test']
22
+ end
23
+ end
19
24
  end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Renogen::Exceptions::InvalidItemFound do
6
+ subject { described_class.new(invalid_items) }
7
+
8
+ describe '#message' do
9
+ context 'valid_values is an array' do
10
+ let(:invalid_items) do
11
+ [
12
+ { group_name: 'Product', invalid_value: 'Foo', valid_values: %w(Bar Baz) },
13
+ { group_name: 'Country', invalid_value: 'LL', valid_values: %w(UK IE) }
14
+ ]
15
+ end
16
+ let(:expected_error) do
17
+ "Invalid items:\nGroup: Product, Content: Foo, Valid Values: [\"Bar\", \"Baz\"]" \
18
+ "\nGroup: Country, Content: LL, Valid Values: [\"UK\", \"IE\"]"
19
+ end
20
+
21
+ it 'returns a user friendly error message' do
22
+ expect(subject.message).to eql expected_error
23
+ end
24
+ end
25
+
26
+ context 'valid_values is a RegExp' do
27
+ let(:invalid_items) { [{ group_name: 'Product', invalid_value: 'Foo', valid_values: /\b(Bar|Baz)\b/ }] }
28
+ let(:expected_error) { "Invalid items:\nGroup: Product, Content: Foo, Pattern: #{/\b(Bar|Baz)\b/.inspect}" }
29
+
30
+ it 'returns a user friendly error message' do
31
+ expect(subject.message).to eql expected_error
32
+ end
33
+ end
34
+ end
35
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
 
3
5
  describe Renogen::Exceptions::StratagyNotFound do
@@ -8,6 +10,5 @@ describe Renogen::Exceptions::StratagyNotFound do
8
10
  it 'returns friendly error message' do
9
11
  expect(subject.message).to eql "Error: Stratagy type '#{name}' not found"
10
12
  end
11
-
12
13
  end
13
14
  end
@@ -1,6 +1,6 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe Renogen::ExtractionStratagies::YamlFile::Exceptions::YamlFileBlank do
3
+ describe Renogen::Exceptions::YamlFileBlank do
4
4
  let(:name) { 'foobar' }
5
5
  subject { described_class.new(name) }
6
6
 
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe Renogen::Exceptions::YamlFileInvalid do
4
+ let(:name) { 'foobar' }
5
+ subject { described_class.new(name) }
6
+
7
+ describe '#message' do
8
+ it 'returns friendly error message' do
9
+ expect(subject.message).to eql "Error: File contents invalid '#{name}'"
10
+ end
11
+ end
12
+ end
@@ -6,13 +6,13 @@ describe Renogen::ExtractionStratagies::YamlFile::Parser do
6
6
  describe '#parse!' do
7
7
  before :each do
8
8
  yaml_file_reader = double(Renogen::ExtractionStratagies::YamlFile::Reader)
9
- allow(yaml_file_reader).to receive(:each_yaml_file).and_yield(file_contents)
9
+ allow(yaml_file_reader).to receive(:each_yaml_file).and_yield(file_contents, 1)
10
10
  allow(Renogen::ExtractionStratagies::YamlFile::Reader).to receive(:new).and_return(yaml_file_reader)
11
11
  end
12
12
 
13
13
  it 'extracts contents from file' do
14
- changelog_item = Renogen::ChangeLog::Item.new('Foo', 'Bar')
15
- allow(Renogen::ChangeLog::Item).to receive(:new).with('Foo', 'Bar').and_return changelog_item
14
+ changelog_item = Renogen::ChangeLog::Item.new(1, 'Foo', 'Bar')
15
+ allow(Renogen::ChangeLog::Item).to receive(:new).with(1, 'Foo', 'Bar').and_return changelog_item
16
16
 
17
17
  changelog = subject.parse!
18
18
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'spec_helper'
2
4
  require 'json'
3
5
 
@@ -6,15 +8,19 @@ describe Renogen::ExtractionStratagies::YamlFile::Reader do
6
8
  subject { described_class.new(directory_path) }
7
9
 
8
10
  describe '#each_yaml_file' do
9
- let(:file_contents) { { 'Foo' => 'Bar' }.to_json }
11
+ let(:file_contents) { { 'Foo' => 'bar' }.to_json }
10
12
 
11
- before :each do
12
- allow(Dir).to receive(:glob).with([File.join(directory_path, 'next', "*.yml")]).and_return(['foo_file'])
13
+ it 'yields each yaml file within given directory' do
14
+ allow(Dir).to receive(:glob).with([File.join(directory_path, 'next', '*.yml')]).and_return(['foo_file'])
13
15
  allow(YAML).to receive(:load_file).with('foo_file').and_return(file_contents)
16
+ expect { |b| subject.each_yaml_file(&b) }.to yield_with_args(file_contents, 0)
14
17
  end
15
18
 
16
- it 'yields each yaml file within given directory' do
17
- expect{ |b| subject.each_yaml_file(&b) }.to yield_with_args(file_contents)
19
+ it 'throws invalid yaml file when missing quotes' do
20
+ allow(subject).to receive(:change_directories).and_return(%w(foo bar))
21
+ foo = Psych::SyntaxError.new('a', 1, 2, 'd', 'e', 'f')
22
+ allow(YAML).to receive(:load_file).and_raise(foo)
23
+ expect { subject.each_yaml_file }.to raise_error(Renogen::Exceptions::YamlFileInvalid)
18
24
  end
19
25
  end
20
26
  end
@@ -1,15 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Renogen::Formatters::Base do
4
+ let(:version) { '1.2.34' }
5
+ let(:date) { Date.today }
6
+ let(:changelog) { double(Renogen::ChangeLog::Model, version: version, date: date) }
7
+
8
+ describe '#header' do
9
+ subject { described_class.new.header(changelog) }
10
+
11
+ it { is_expected.to eql "#{version} (#{date})" }
12
+ end
13
+
4
14
  describe '#write_header' do
5
- it 'raises an NotImplementedError' do
6
- expect{subject.write_header('header')}.to raise_error NotImplementedError
15
+ it 'does nothing' do
16
+ expect(subject.write_header(changelog)).to be_nil
7
17
  end
8
18
  end
9
19
 
10
20
  describe '#write_group' do
11
- it 'raises an NotImplementedError' do
12
- expect{subject.write_group('group_name')}.to raise_error NotImplementedError
21
+ it 'does nothing' do
22
+ expect(subject.write_group(changelog)).to be_nil
13
23
  end
14
24
  end
15
25
 
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Renogen::Formatters::Csv do
6
+ describe 'write_methods' do
7
+ let(:change_1) { renogen_change(1, 'Group 1', 'Change 1') }
8
+ let(:change_2) { renogen_change(1, 'Group 2', 'Change 2') }
9
+ let(:change_3) { renogen_change(2, 'Group 1', 'Change 3') }
10
+ let(:changelog) { Renogen::ChangeLog::Model.new }
11
+
12
+ before do
13
+ [change_1, change_2, change_3].each { |c| changelog.add_change(c) }
14
+ subject.header(changelog)
15
+ end
16
+
17
+ it { is_expected.to be_table_formatter }
18
+ describe '#write_header' do
19
+ it 'returns a comma separated list of group names' do
20
+ expect(subject.write_header('Group 1,Group 2')).to eq("Group 1,Group 2\n")
21
+ end
22
+ end
23
+
24
+ describe '#write_change' do
25
+ let(:ticket) do
26
+ {
27
+ 'Group 1' => 'This is a Group 1 change',
28
+ 'Group 2' => 'This is a Group 2 change, with a comma'
29
+ }
30
+ end
31
+
32
+ context 'when ticket contains same keys as changelog groups' do
33
+ it 'outputs all values' do
34
+ expect(subject.write_change(ticket)).to eq(
35
+ 'This is a Group 1 change,"This is a Group 2 change, with a comma"'
36
+ )
37
+ end
38
+ end
39
+
40
+ context 'when changelog contains keys missing in ticket' do
41
+ it 'outputs empty values for missing keys' do
42
+ expect(subject.write_change(ticket)).to eq(
43
+ 'This is a Group 1 change,"This is a Group 2 change, with a comma"'
44
+ )
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end