refactor: improve the Range class

This commit is contained in:
Glenn Y. Rolland 2023-07-12 11:27:21 +02:00
parent 90e9ecf509
commit 878c2cb95c
6 changed files with 151 additions and 50 deletions

View file

@ -12,9 +12,10 @@ describe TimeCost::Commit do
) )
commit = TimeCost::Commit.new( commit = TimeCost::Commit.new(
commit_hash: "53c01d0db42ac662ed1aff3799d2a92a04e03908", commit_hash: Random::Secure.base64(40),
datetime: Time.utc, datetime: Time.utc,
author: author, author: author,
message: "First commit"
) )
commit.should be_a(TimeCost::Commit) commit.should be_a(TimeCost::Commit)
end end

View file

@ -14,30 +14,35 @@ describe TimeCost::Range do
email: "jon.snow@example.com" email: "jon.snow@example.com"
) )
commit_base = TimeCost::Commit.new( commit_base = TimeCost::Commit.new(
commit_hash: Random::Secure.base64(40), commit_hash: Random::Secure.hex(40),
author: author, author: author,
datetime: Time.utc, datetime: Time.utc,
message: "Commit base"
) )
commit_overlap_before = TimeCost::Commit.new( commit_overlap_before = TimeCost::Commit.new(
commit_hash: Random::Secure.base64(40), commit_hash: Random::Secure.hex(40),
author: author, author: author,
datetime: Time.utc - overlap_time, datetime: Time.utc - overlap_time,
message: "Commit with overlap before"
) )
commit_overlap_after = TimeCost::Commit.new( commit_overlap_after = TimeCost::Commit.new(
commit_hash: Random::Secure.base64(40), commit_hash: Random::Secure.hex(40),
author: author, author: author,
datetime: Time.utc + overlap_time, datetime: Time.utc + overlap_time,
message: "Commit with overlap after"
) )
commit_separate_before = TimeCost::Commit.new( commit_separate_before = TimeCost::Commit.new(
commit_hash: Random::Secure.base64(40), commit_hash: Random::Secure.hex(40),
author: author, author: author,
datetime: Time.utc - separate_time, datetime: Time.utc - separate_time,
message: "Commit separate before"
) )
commit_separate_after = TimeCost::Commit.new( commit_separate_after = TimeCost::Commit.new(
commit_hash: Random::Secure.base64(40), commit_hash: Random::Secure.hex(40),
author: author, author: author,
datetime: Time.utc + separate_time, datetime: Time.utc + separate_time,
message: "Commit separate after"
) )
@ -58,6 +63,16 @@ describe TimeCost::Range do
end end
end end
describe ".to_s" do
it "must display something" do
range_base = TimeCost::Range.new(
commit: commit_base
)
str = range_base.to_s
puts str
end
end
describe ".overlap?" do describe ".overlap?" do
it "must return true when range overlaps range before" do it "must return true when range overlaps range before" do
range_base = TimeCost::Range.new( range_base = TimeCost::Range.new(
@ -115,5 +130,81 @@ describe TimeCost::Range do
overlap2.should be_false overlap2.should be_false
end end
end end
describe ".add" do
pending "must add the commit to the range"
pending "must not duplicate existing commits"
pending "must change the boundaries"
end
describe ".merge" do
it "must return a merged range with all commits" do
range_base = TimeCost::Range.new(
commit: commit_base
)
range_before = TimeCost::Range.new(
commit: commit_overlap_before
)
range_after = TimeCost::Range.new(
commit: commit_overlap_after
)
range_result1 = range_base.merge(range_before)
range_result1.commits.size.should eq(2)
range_result2 = range_base.merge(range_after)
range_result2.commits.size.should eq(2)
end
pending "must not include a commit twice"
it "must return a merged range with correct boundaries" do
range_base = TimeCost::Range.new(
commit: commit_base
)
range_before = TimeCost::Range.new(
commit: commit_overlap_before
)
range_after = TimeCost::Range.new(
commit: commit_overlap_after
)
range_result1 = range_base.merge(range_before)
range_result1.time_start.should eq(range_before.time_start)
range_result1.time_stop.should eq(range_base.time_stop)
range_result2 = range_base.merge(range_after)
range_result2.time_start.should eq(range_base.time_start)
range_result2.time_stop.should eq(range_after.time_stop)
end
it "must fail with error when separate before" do
range_base = TimeCost::Range.new(
commit: commit_base
)
range_before = TimeCost::Range.new(
commit: commit_separate_before
)
expect_raises(TimeCost::Range::MissingOverlapError) do
range_base.merge(range_before)
end
end
it "must fail with error when separate after" do
range_base = TimeCost::Range.new(
commit: commit_base
)
range_after = TimeCost::Range.new(
commit: commit_separate_after
)
expect_raises(TimeCost::Range::MissingOverlapError) do
range_base.merge(range_after)
end
end
end
end end

View file

@ -12,5 +12,9 @@ module TimeCost
(self.email == other_author.email) (self.email == other_author.email)
) )
end end
def to_s
"#{self.name} <#{self.email}>"
end
end end
end end

View file

@ -4,13 +4,14 @@ require "./author"
module TimeCost module TimeCost
class Commit class Commit
property author : Author getter author : Author
property commit_hash : String getter commit_hash : String
property datetime : Time getter datetime : Time
property message : String getter message : String
getter notes : String
def initialize(@commit_hash, @datetime=Time, @author=Author, @message="") def initialize(@commit_hash, @datetime, @author, @message, @notes="")
# @note = nil # @note = nil
end end
end end

View file

@ -40,6 +40,7 @@ module TimeCost
datetime: Time.parse!(json["date"].to_s, "%F %T %z"), datetime: Time.parse!(json["date"].to_s, "%F %T %z"),
author: author, author: author,
message: json["message"].to_s, message: json["message"].to_s,
notes: json["notes"].to_s
) )
end end

View file

@ -1,7 +1,10 @@
require "./commit" require "./commit"
module TimeCost module TimeCost
class Range class Range
class MissingOverlapError < RuntimeError ; end
property time_start property time_start
property time_stop property time_stop
property commits : Array(Commit) property commits : Array(Commit)
@ -24,34 +27,35 @@ module TimeCost
@commits.first.try &.author @commits.first.try &.author
end end
def merge(range) def add(commit : Commit) : Range
# B -----[----]---- end
# A --[----]------
# = ---[------]----
# minimum of both def merge!(other_range) : Range
new_start = ( raise MissingOverlapError.new if @time_stop < other_range.time_start
if range.time_start < @time_start raise MissingOverlapError.new if @time_start > other_range.time_stop
range.time_start
else
@time_start
end
)
new_end = ( # B ------[----]----
if range.time_stop >= @time_stop # A --[----]-------
range.time_stop # = ---[-------]----
else
@time_stop # boundaries
end new_start = [@time_start, other_range.time_start].min
) new_end = [@time_stop, other_range.time_stop].max
@time_start = new_start @time_start = new_start
@time_stop = new_end @time_stop = new_end
@commits.concat range.commits @commits.concat other_range.commits
self
end end
def overlap?(range) def merge(other_range) : Range
copy = self.dup
copy.commits = self.commits.dup
copy.merge!(other_range)
end
def overlap?(range) : Bool
result = false result = false
# return early result if ranges come from different authors # return early result if ranges come from different authors
@ -104,27 +108,26 @@ module TimeCost
return result return result
end end
def fixed_start def diff_hours : Float
return @time_start + (@epsilon/24.0) return ((@time_stop - @time_start) / (60* 60)).to_f
end end
def diff : Float def to_s(show_authors = true) : String
return ("%.2f" % ((@time_stop - fixed_start).to_f * 24)).to_f result = String.build do |val|
end val << "(%.2f)\t%s - %s\n" % [diff_hours, @time_start, @time_stop]
if show_authors
def to_s(show_authors = true) val << "\tby %s\n" % @commits.first.author.to_s
val = "(%s)\t%s - %s\n" % [diff, fixed_start, @time_stop] end
if show_authors @commits.each do |commit|
val += "\tby %s\n" % @commits.first.author lines = commit.message.split(/\n/)
r = lines.map_with_index do |line,i|
x = (i == 0) ? "[#{commit.commit_hash[0..7]}]" : " " * 9
"\t#{x} #{line}"
end.join("\n")
val << r + "\n"
end
end end
@commits.each do |commit| result
lines = [] of String
lines.concat commit.note.split(/\n/)
r = lines.map { |s| "\t %s" % s }.join "\n"
r[1] = '*'
val += r + "\n"
end
return val
end end
end end
end end