Merge pull request #357 from olleolleolle/feature/spec-allow-file-object-for-parsefile

ParserFile: fail parsing with config file line number; use a File instead of a filename
This commit is contained in:
Olle Jonsson 2016-03-19 16:32:24 +01:00
commit c5a92b71b2
6 changed files with 53 additions and 36 deletions

View File

@ -3,29 +3,48 @@ require "pathname"
module GitHubChangelogGenerator module GitHubChangelogGenerator
ParserError = Class.new(StandardError) ParserError = Class.new(StandardError)
# ParserFile is a configuration file reader which sets options in the
# given Hash.
#
# In your project's root, you can put a file named
# <tt>.github_changelog_generator</tt> to override defaults:
#
# Example:
# header_label=# My Super Changelog
# future-release=5.0.0
# since-tag=1.0.0
#
# The configuration format is <tt>some-key=value</tt> or <tt>some_key=value</tt>.
#
class ParserFile class ParserFile
FILENAME = ".github_changelog_generator" # @param options [Hash] options to be configured from file contents
# @param file [nil,IO] configuration file handle, defaults to opening `.github_changelog_generator`
def initialize(options) def initialize(options, file = open_settings_file)
@options = options @options = options
@file = file
end end
# Destructively change @options using data in configured options file. # Sets options using configuration file content
def parse! def parse!
file.each_line { |line| parse_line!(line) } if file.exist? return unless @file
@file.each_with_index { |line, i| parse_line!(line, i + 1) }
@file.close
end end
private private
def file FILENAME = ".github_changelog_generator"
@file ||= Pathname(File.expand_path(@options[:params_file] || FILENAME))
def open_settings_file
path = Pathname(File.expand_path(FILENAME))
File.open(path) if path.exist?
end end
def parse_line!(line) def parse_line!(line, line_number)
option_name, value = extract_pair(line) option_name, value = extract_pair(line)
@options[option_key_for(option_name)] = convert_value(value, option_name) @options[option_key_for(option_name)] = convert_value(value, option_name)
rescue rescue
raise ParserError, "Config file #{file} is incorrect in line \"#{line.gsub(/[\n\r]+/, '')}\"" raise ParserError, "Failed on line ##{line_number}: \"#{line.gsub(/[\n\r]+/, '')}\""
end end
# Returns a the option name as a symbol and its string value sans newlines. # Returns a the option name as a symbol and its string value sans newlines.

View File

@ -1,2 +0,0 @@
exclude-labels=73a91042-da6f-11e5-9335-1040f38d7f90,7adf83b4-da6f-11e5-ae18-1040f38d7f90
header_label=# My changelog

View File

@ -1,2 +0,0 @@
unreleased_label: staging
unreleased: false

View File

@ -1,3 +0,0 @@
unreleased_label=staging
unreleased=false
header==== Changelog ===

View File

@ -2,35 +2,39 @@ describe GitHubChangelogGenerator::ParserFile do
describe ".github_changelog_generator" do describe ".github_changelog_generator" do
context "when no has file" do context "when no has file" do
let(:options) { {} } let(:options) { {} }
let(:parse) { GitHubChangelogGenerator::ParserFile.new(options) } let(:parser) { GitHubChangelogGenerator::ParserFile.new(options) }
subject { parse.parse! } subject { parser.parse! }
it { is_expected.to be_nil } it { is_expected.to be_nil }
end end
context "when file is empty" do context "when file is empty" do
let(:options) { { params_file: "spec/files/github_changelog_params_empty" } } let(:options) { {} }
let(:parse) { GitHubChangelogGenerator::ParserFile.new(options) } let(:parser) { GitHubChangelogGenerator::ParserFile.new(options, StringIO.new("")) }
it "does not change the options" do it "does not change the options" do
expect { parse.parse! }.to_not change { options } expect { parser.parse! }.to_not change { options }
end end
end end
context "when file is incorrect" do context "when file is incorrect" do
let(:options) { { params_file: "spec/files/github_changelog_params_incorrect" } } let(:options) { {} }
let(:options_before_change) { options.dup } let(:options_before_change) { options.dup }
let(:parse) { GitHubChangelogGenerator::ParserFile.new(options) } let(:file) { StringIO.new("unreleased_label=staging\nunreleased: false") }
it { expect { parse.parse! }.to raise_error(GitHubChangelogGenerator::ParserError) } let(:parser) do
GitHubChangelogGenerator::ParserFile.new(options, file)
end
it { expect { parser.parse! }.to raise_error(/line #2/) }
end end
context "when override default values" do context "when override default values" do
let(:default_options) { GitHubChangelogGenerator::Parser.default_options } let(:default_options) { GitHubChangelogGenerator::Parser.default_options }
let(:options) { { params_file: "spec/files/github_changelog_params_override" }.merge(default_options) } let(:options) { {}.merge(default_options) }
let(:options_before_change) { options.dup } let(:options_before_change) { options.dup }
let(:parse) { GitHubChangelogGenerator::ParserFile.new(options) } let(:file) { StringIO.new("unreleased_label=staging\nunreleased=false\nheader==== Changelog ===") }
let(:parser) { GitHubChangelogGenerator::ParserFile.new(options, file) }
it "changes the options" do it "changes the options" do
expect { parse.parse! }.to change { options } expect { parser.parse! }.to change { options }
.from(options_before_change) .from(options_before_change)
.to(options_before_change.merge(unreleased_label: "staging", .to(options_before_change.merge(unreleased_label: "staging",
unreleased: false, unreleased: false,
@ -38,21 +42,22 @@ describe GitHubChangelogGenerator::ParserFile do
end end
context "turns exclude-labels into an Array", bug: '#327' do context "turns exclude-labels into an Array", bug: '#327' do
let(:options) do let(:file) do
{ StringIO.new(<<EOF
params_file: "spec/files/github_changelog_params_327" exclude-labels=73a91042-da6f-11e5-9335-1040f38d7f90,7adf83b4-da6f-11e5-ae18-1040f38d7f90
} header_label=# My changelog
EOF
)
end end
it "reads exclude_labels into an Array" do it "reads exclude_labels into an Array" do
expect { parse.parse! }.to change { options[:exclude_labels] } expect { parser.parse! }.to change { options[:exclude_labels] }
.from(nil) .from(default_options[:exclude_labels])
.to(["73a91042-da6f-11e5-9335-1040f38d7f90", "7adf83b4-da6f-11e5-ae18-1040f38d7f90"]) .to(["73a91042-da6f-11e5-9335-1040f38d7f90", "7adf83b4-da6f-11e5-ae18-1040f38d7f90"])
end end
it "translates given header_label into the :header option" do it "translates given header_label into the :header option" do
expect { parse.parse! }.to change { options[:header] } expect { parser.parse! }.to change { options[:header] }
.from(nil) .from(default_options[:header])
.to("# My changelog") .to("# My changelog")
end end
end end