renogen 1.0.0 → 1.3.0

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 +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