renogen 1.1.1 → 1.4.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 +155 -25
  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 +11 -8
  6. data/lib/renogen/change_log/validator.rb +61 -0
  7. data/lib/renogen/change_log/writer.rb +35 -3
  8. data/lib/renogen/cli.rb +8 -4
  9. data/lib/renogen/cli/param_parser.rb +12 -0
  10. data/lib/renogen/config.rb +16 -15
  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 +42 -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 +1 -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/change_log/writer_spec.rb +199 -2
  32. data/spec/lib/renogen/config_spec.rb +7 -2
  33. data/spec/lib/renogen/exceptions/invalid_item_found_spec.rb +35 -0
  34. data/spec/lib/renogen/exceptions/stratagy_not_found_spec.rb +2 -1
  35. data/spec/lib/renogen/{extraction_stratagies/yaml_file/exceptions → exceptions}/yaml_file_blank_spec.rb +1 -1
  36. data/spec/lib/renogen/exceptions/yaml_file_invalid_spec.rb +12 -0
  37. data/spec/lib/renogen/extraction_stratagies/yaml_file/parser_spec.rb +3 -3
  38. data/spec/lib/renogen/extraction_stratagies/yaml_file/reader_spec.rb +11 -5
  39. data/spec/lib/renogen/formatters/base_spec.rb +14 -4
  40. data/spec/lib/renogen/formatters/csv_spec.rb +49 -0
  41. data/spec/lib/renogen/formatters/html_spec.rb +1 -1
  42. data/spec/lib/renogen/formatters/markdown_spec.rb +2 -2
  43. data/spec/spec_helper.rb +5 -0
  44. data/spec/support/renogen_helper.rb +9 -0
  45. metadata +20 -11
  46. data/lib/renogen/extraction_stratagies/yaml_file/exceptions.rb +0 -12
  47. data/lib/renogen/extraction_stratagies/yaml_file/exceptions/yaml_file_blank.rb +0 -25
@@ -44,6 +44,18 @@ module Renogen
44
44
  args['legacy_version'] = n
45
45
  end
46
46
 
47
+ opts.on("-rDATE", "--release-date=DATE", "Release date") do |n|
48
+ args['release_date'] = n
49
+ end
50
+
51
+ opts.on("-IPATH", "--include=PATH", "Add a path to the load path") do |n|
52
+ $LOAD_PATH << n
53
+ end
54
+
55
+ opts.on("-RFILE", "--require=FILE", "Require a file from the load path") do |n|
56
+ require n
57
+ end
58
+
47
59
  opts.on_tail("-h", "--help", "Show this message") do
48
60
  puts opts
49
61
  exit
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'singleton'
2
4
  require 'yaml'
3
5
 
@@ -5,19 +7,21 @@ module Renogen
5
7
  # Stores configuratin values to be used by the libary
6
8
  class Config
7
9
  include Singleton
8
- attr_accessor :single_line_format, :input_source, :output_format, :supported_keys, :changelog_path, :default_headings
10
+ attr_accessor :single_line_format, :input_source, :output_format, :supported_keys, :changelog_path,
11
+ :default_headings, :validations, :remove_duplicates
9
12
 
10
13
  def initialize
11
14
  config_file = load_yaml_config
12
- self.single_line_format = config_file['single_line_format'] || 'summary (see link)'.freeze
13
- self.supported_keys = config_file['supported_keys'] || ['identifier', 'link', 'summary'].freeze
14
- self.input_source = config_file['input_source'] || 'yaml'.freeze
15
- self.output_format = config_file['output_format'] || 'markdown'.freeze
16
- self.changelog_path = config_file['changelog_path'] || './change_log'.freeze
17
- self.default_headings = config_file['default_headings'] || %w(Summary Detailed Tasks).freeze
15
+ self.single_line_format = config_file['single_line_format'] || 'summary (see link)'
16
+ self.supported_keys = config_file['supported_keys'] || %w(identifier link summary)
17
+ self.input_source = config_file['input_source'] || 'yaml'
18
+ self.output_format = config_file['output_format'] || 'markdown'
19
+ self.changelog_path = config_file['changelog_path'] || './change_log'
20
+ self.default_headings = config_file['default_headings'] || %w(Summary Detailed Tasks)
21
+ self.validations = config_file['allowed_values']
22
+ self.remove_duplicates = config_file['remove_duplicates'] || false
18
23
  end
19
24
 
20
-
21
25
  # Renogen configuration extension
22
26
  # a block can be provided to programatily setup configuration values
23
27
  def self.configure
@@ -26,13 +30,10 @@ module Renogen
26
30
 
27
31
  private
28
32
 
29
- def load_yaml_config(config_file_path='.renogen')
30
- begin
31
- YAML.load_file(config_file_path)
32
- rescue
33
- {}
34
- end
33
+ def load_yaml_config(config_file_path = '.renogen')
34
+ YAML.load_file(config_file_path)
35
+ rescue StandardError
36
+ {}
35
37
  end
36
38
  end
37
39
  end
38
-
@@ -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
@@ -13,20 +25,31 @@ module Renogen
13
25
  Renogen::Formatters.add(identifier.to_sym, self)
14
26
  end
15
27
 
28
+ # Generate a header for a given changelog
29
+ #
30
+ # @param changelog [Renogen::ChangeLog::Model]
31
+ #
32
+ # return [String]
33
+ def header(changelog)
34
+ "#{changelog.version} (#{changelog.date})"
35
+ end
36
+
16
37
  # Outputs a line or block of text appearing at the top of the change log.
17
38
  #
39
+ # @abstract
40
+ #
18
41
  # @param header [String]
19
- # @return [NotImplementedError]
42
+ # @raise NotImplementedError
20
43
  def write_header(header)
21
- raise NotImplementedError
22
44
  end
23
45
 
24
46
  # Outputs a line or block as a header for a group.
25
47
  #
48
+ # @abstract
49
+ #
26
50
  # @param group [String]
27
- # @return [NotImplementedError]
51
+ # @raise NotImplementedError
28
52
  def write_group(group)
29
- raise NotImplementedError
30
53
  end
31
54
 
32
55
  # Outputs a line or block of text appearing after a group.
@@ -37,8 +60,10 @@ module Renogen
37
60
 
38
61
  # Outputs a line or block as the body for a change.
39
62
  #
63
+ # @abstract
64
+ #
40
65
  # @param change [String]
41
- # @return [NotImplementedError]
66
+ # @raise NotImplementedError
42
67
  def write_change(change)
43
68
  raise NotImplementedError
44
69
  end
@@ -0,0 +1,42 @@
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 = parsed_line.chomp
26
+ parsed_line.gsub!("\n", '\n') if parsed_line.include?("\n")
27
+
28
+ if parsed_line.include?(',')
29
+ "\"#{parsed_line}\""
30
+ else
31
+ parsed_line
32
+ end
33
+ end.join(',')
34
+ end
35
+
36
+ def header(changelog)
37
+ @headings = changelog.groups.keys
38
+ @headings.join(',')
39
+ end
40
+ end
41
+ end
42
+ end
@@ -9,7 +9,7 @@ module Renogen
9
9
  # @param header [String]
10
10
  # @return [String]
11
11
  def write_header(header)
12
- "<html>\n<h1>#{header}</h1>"
12
+ "<html>\n<h1>#{header}</h1>\n"
13
13
  end
14
14
 
15
15
  # Outputs a line or block as a header for a group.
@@ -10,7 +10,7 @@ module Renogen
10
10
  # @param header [String]
11
11
  # @return [String]
12
12
  def write_header(header)
13
- "# #{header}"
13
+ "# #{header}\n\n"
14
14
  end
15
15
 
16
16
  # Outputs a line or block as a header for a group.
@@ -18,7 +18,7 @@ module Renogen
18
18
  # @param group [String]
19
19
  # @return [String]
20
20
  def write_group(group)
21
- "### #{group}"
21
+ "## #{group}\n\n"
22
22
  end
23
23
 
24
24
  # Outputs a line or block as the body for a change.
@@ -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