milestoner 16.2.1 → 17.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +3 -1
  3. data/README.adoc +333 -57
  4. data/lib/milestoner/builders/ascii_doc.rb +32 -0
  5. data/lib/milestoner/builders/container.rb +17 -0
  6. data/lib/milestoner/builders/import.rb +9 -0
  7. data/lib/milestoner/builders/markdown.rb +32 -0
  8. data/lib/milestoner/builders/stream.rb +29 -0
  9. data/lib/milestoner/builders/web.rb +41 -0
  10. data/lib/milestoner/cli/actions/build/format.rb +24 -0
  11. data/lib/milestoner/cli/actions/build/label.rb +26 -0
  12. data/lib/milestoner/cli/actions/build/layout.rb +36 -0
  13. data/lib/milestoner/cli/actions/build/root.rb +25 -0
  14. data/lib/milestoner/cli/actions/build/version.rb +31 -0
  15. data/lib/milestoner/cli/actions/cache/create.rb +35 -0
  16. data/lib/milestoner/cli/actions/cache/delete.rb +31 -0
  17. data/lib/milestoner/cli/actions/cache/find.rb +31 -0
  18. data/lib/milestoner/cli/actions/cache/info.rb +29 -0
  19. data/lib/milestoner/cli/actions/cache/list.rb +33 -0
  20. data/lib/milestoner/cli/actions/publish.rb +9 -5
  21. data/lib/milestoner/cli/commands/build.rb +60 -0
  22. data/lib/milestoner/cli/commands/cache.rb +27 -0
  23. data/lib/milestoner/cli/shell.rb +3 -2
  24. data/lib/milestoner/commits/categorizer.rb +21 -29
  25. data/lib/milestoner/commits/collector.rb +18 -0
  26. data/lib/milestoner/commits/enricher.rb +51 -0
  27. data/lib/milestoner/commits/enrichers/author.rb +26 -0
  28. data/lib/milestoner/commits/enrichers/body.rb +28 -0
  29. data/lib/milestoner/commits/enrichers/colleague.rb +33 -0
  30. data/lib/milestoner/commits/enrichers/container.rb +25 -0
  31. data/lib/milestoner/commits/enrichers/format.rb +23 -0
  32. data/lib/milestoner/commits/enrichers/import.rb +11 -0
  33. data/lib/milestoner/commits/enrichers/issue.rb +28 -0
  34. data/lib/milestoner/commits/enrichers/milestone.rb +24 -0
  35. data/lib/milestoner/commits/enrichers/note.rb +28 -0
  36. data/lib/milestoner/commits/enrichers/review.rb +23 -0
  37. data/lib/milestoner/commits/enrichers/uri.rb +14 -0
  38. data/lib/milestoner/commits/versioner.rb +48 -0
  39. data/lib/milestoner/configuration/contract.rb +30 -3
  40. data/lib/milestoner/configuration/defaults.yml +31 -8
  41. data/lib/milestoner/configuration/model.rb +26 -1
  42. data/lib/milestoner/configuration/transformers/build/root.rb +21 -0
  43. data/lib/milestoner/configuration/transformers/build/template_paths.rb +32 -0
  44. data/lib/milestoner/configuration/transformers/citations/description.rb +39 -0
  45. data/lib/milestoner/configuration/transformers/citations/label.rb +39 -0
  46. data/lib/milestoner/configuration/transformers/gems/description.rb +34 -0
  47. data/lib/milestoner/configuration/transformers/gems/label.rb +35 -0
  48. data/lib/milestoner/configuration/transformers/gems/name.rb +34 -0
  49. data/lib/milestoner/configuration/transformers/gems/uri.rb +35 -0
  50. data/lib/milestoner/configuration/transformers/generator/label.rb +31 -0
  51. data/lib/milestoner/configuration/transformers/generator/uri.rb +31 -0
  52. data/lib/milestoner/configuration/transformers/project/author.rb +34 -0
  53. data/lib/milestoner/configuration/transformers/project/generator.rb +35 -0
  54. data/lib/milestoner/configuration/transformers/project/label.rb +23 -0
  55. data/lib/milestoner/configuration/transformers/project/name.rb +21 -0
  56. data/lib/milestoner/configuration/transformers/project/version.rb +33 -0
  57. data/lib/milestoner/configuration/transformers/uri/avatar.rb +21 -0
  58. data/lib/milestoner/configuration/transformers/uri/commit.rb +24 -0
  59. data/lib/milestoner/configuration/transformers/uri/profile.rb +21 -0
  60. data/lib/milestoner/configuration/transformers/uri/review.rb +24 -0
  61. data/lib/milestoner/configuration/transformers/uri/tracker.rb +24 -0
  62. data/lib/milestoner/container.rb +52 -9
  63. data/lib/milestoner/models/commit.rb +42 -0
  64. data/lib/milestoner/models/link.rb +12 -0
  65. data/lib/milestoner/models/user.rb +12 -0
  66. data/lib/milestoner/renderers/asciidoc.rb +20 -0
  67. data/lib/milestoner/renderers/markdown.rb +20 -0
  68. data/lib/milestoner/renderers/universal.rb +26 -0
  69. data/lib/milestoner/tags/creator.rb +31 -23
  70. data/lib/milestoner/tags/publisher.rb +5 -5
  71. data/lib/milestoner/tags/pusher.rb +9 -3
  72. data/lib/milestoner/templates/layouts/page.adoc.erb +2 -0
  73. data/lib/milestoner/templates/layouts/page.html.erb +37 -0
  74. data/lib/milestoner/templates/layouts/page.md.erb +2 -0
  75. data/lib/milestoner/templates/layouts/page.stream.erb +2 -0
  76. data/lib/milestoner/templates/milestones/_avatar.html.erb +5 -0
  77. data/lib/milestoner/templates/milestones/_commit.adoc.erb +1 -0
  78. data/lib/milestoner/templates/milestones/_commit.html.erb +104 -0
  79. data/lib/milestoner/templates/milestones/_commit.md.erb +1 -0
  80. data/lib/milestoner/templates/milestones/_commit.stream.erb +1 -0
  81. data/lib/milestoner/templates/milestones/_icon.html.erb +6 -0
  82. data/lib/milestoner/templates/milestones/_profile.html.erb +5 -0
  83. data/lib/milestoner/templates/milestones/show.adoc.erb +9 -0
  84. data/lib/milestoner/templates/milestones/show.html.erb +46 -0
  85. data/lib/milestoner/templates/milestones/show.md.erb +9 -0
  86. data/lib/milestoner/templates/milestones/show.stream.erb +9 -0
  87. data/lib/milestoner/templates/public/page.css.erb +373 -0
  88. data/lib/milestoner/views/context.rb +27 -0
  89. data/lib/milestoner/views/milestones/show.rb +47 -0
  90. data/lib/milestoner/views/parts/commit.rb +85 -0
  91. data/lib/milestoner.rb +1 -1
  92. data/milestoner.gemspec +17 -11
  93. data.tar.gz.sig +0 -0
  94. metadata +182 -26
  95. metadata.gz.sig +1 -1
  96. data/lib/milestoner/cli/actions/status.rb +0 -37
  97. data/lib/milestoner/presenters/commit.rb +0 -36
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "sod"
5
+ require "spek"
6
+
7
+ module Milestoner
8
+ module CLI
9
+ module Actions
10
+ module Build
11
+ # Handles build label.
12
+ class Label < Sod::Action
13
+ include Import[:input]
14
+
15
+ description "Set label."
16
+
17
+ on %w[-l --label], argument: "[TEXT]"
18
+
19
+ default { Container[:configuration].project_label }
20
+
21
+ def call(label = nil) = input.project_label = label || default
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sod"
4
+
5
+ module Milestoner
6
+ module CLI
7
+ module Actions
8
+ module Build
9
+ # Handles build layout.
10
+ class Layout < Sod::Action
11
+ include Import[:input]
12
+
13
+ description "Set layout."
14
+
15
+ ancillary "Use false to disable."
16
+
17
+ on %w[-L --layout], argument: "[LAYOUT]"
18
+
19
+ default { Container[:configuration].build_layout }
20
+
21
+ def call(layout = nil) = input.build_layout = parse(layout)
22
+
23
+ private
24
+
25
+ def parse value
26
+ case value
27
+ in "false" then false
28
+ in String then value
29
+ else default
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "sod"
5
+
6
+ module Milestoner
7
+ module CLI
8
+ module Actions
9
+ module Build
10
+ # Handles build root path.
11
+ class Root < Sod::Action
12
+ include Import[:input]
13
+
14
+ description "Set root output path."
15
+
16
+ on %w[-r --root], argument: "[PATH]"
17
+
18
+ default { Container[:configuration].build_root }
19
+
20
+ def call(path = nil) = input.build_root = (path || default).expand_path
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "pathname"
4
+ require "sod"
5
+ require "spek"
6
+ require "versionaire"
7
+
8
+ module Milestoner
9
+ module CLI
10
+ module Actions
11
+ module Build
12
+ # Handles build version.
13
+ class Version < Sod::Action
14
+ include Import[:input]
15
+
16
+ using Versionaire::Cast
17
+
18
+ description "Set version."
19
+
20
+ ancillary "Calculated from commit trailers when not supplied."
21
+
22
+ on %w[-v --version], argument: "[VERSION]"
23
+
24
+ default { Container[:configuration].project_version }
25
+
26
+ def call(version = nil) = input.project_version = Version(version || default)
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sod"
4
+
5
+ module Milestoner
6
+ module CLI
7
+ module Actions
8
+ module Cache
9
+ # Handles creating or updating a user within the cache.
10
+ class Create < Sod::Action
11
+ include Import[:logger, client: :cache]
12
+
13
+ description "Create user."
14
+
15
+ ancillary %(Example: "1,zoe,Zoë Washburne".)
16
+
17
+ on %w[-c --create], argument: "external_id,handle,name"
18
+
19
+ def call values
20
+ process(values).bind { |user| log_info "Created: #{user.name.inspect}" }
21
+ end
22
+
23
+ private
24
+
25
+ def process values
26
+ external_id, handle, name = values.split ","
27
+ client.commit(:users) { upsert({external_id:, handle:, name:}) }
28
+ end
29
+
30
+ def log_info(message) = logger.info { message }
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sod"
4
+
5
+ module Milestoner
6
+ module CLI
7
+ module Actions
8
+ module Cache
9
+ # Handles deleting a user from the cache.
10
+ class Delete < Sod::Action
11
+ include Import[:kernel, :logger, client: :cache]
12
+
13
+ description "Delete user."
14
+
15
+ on %w[-d --delete], argument: "NAME"
16
+
17
+ def call name
18
+ client.commit(:users) { delete name }
19
+ .either(method(:success), method(:failure))
20
+ end
21
+
22
+ private
23
+
24
+ def success(user) = logger.info { "Deleted: #{user.name.inspect}." }
25
+
26
+ def failure(message) = logger.abort message
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sod"
4
+
5
+ module Milestoner
6
+ module CLI
7
+ module Actions
8
+ module Cache
9
+ # Handles finding a user in the cache.
10
+ class Find < Sod::Action
11
+ include Import[:kernel, :logger, client: :cache]
12
+
13
+ description "Find user."
14
+
15
+ on %w[-f --find], argument: "NAME"
16
+
17
+ def call name
18
+ client.commit(:users) { find name }
19
+ .either(method(:success), method(:failure))
20
+ end
21
+
22
+ private
23
+
24
+ def success(user) = kernel.puts user.to_h.values.join(", ")
25
+
26
+ def failure(message) = logger.abort message
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sod"
4
+
5
+ module Milestoner
6
+ module CLI
7
+ module Actions
8
+ module Cache
9
+ # Handles cache information.
10
+ class Info < Sod::Action
11
+ include Import[:kernel, :logger, client: :cache]
12
+
13
+ description "Show information."
14
+
15
+ on %w[-i --info]
16
+
17
+ def call(*)
18
+ path = client.path
19
+ path.exist? ? log_info("Path: #{path}.") : log_info("No cache found.")
20
+ end
21
+
22
+ private
23
+
24
+ def log_info(message) = logger.info { message }
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "sod"
4
+
5
+ module Milestoner
6
+ module CLI
7
+ module Actions
8
+ module Cache
9
+ # Handles listing users within the cache.
10
+ class List < Sod::Action
11
+ include Import[:kernel, :logger, client: :cache]
12
+
13
+ description "List users."
14
+
15
+ on %w[-l --list]
16
+
17
+ def call(*)
18
+ logger.info { "Listing users..." }
19
+ client.commit(:users, &:all).bind { |users| print users }
20
+ end
21
+
22
+ private
23
+
24
+ def print users
25
+ return logger.info { "No users found." } if users.empty?
26
+
27
+ users.each { |user| kernel.puts user.to_h.values.join ", " }
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "refinements/structs"
3
+ require "refinements/struct"
4
4
  require "sod"
5
+ require "versionaire"
5
6
 
6
7
  module Milestoner
7
8
  module CLI
@@ -10,20 +11,23 @@ module Milestoner
10
11
  class Publish < Sod::Action
11
12
  include Import[:configuration]
12
13
 
13
- using Refinements::Structs
14
+ using Refinements::Struct
15
+ using Versionaire::Cast
14
16
 
15
17
  description "Publish milestone."
16
18
 
17
- ancillary "(tags and pushes to remote repository)"
19
+ ancillary "Build, commit, tag, and push to remote repository."
18
20
 
19
- on %w[-p --publish], argument: "VERSION"
21
+ on %w[-p --publish], argument: "[VERSION]"
22
+
23
+ default { Container[:configuration].project_version }
20
24
 
21
25
  def initialize(publisher: Tags::Publisher.new, **)
22
26
  super(**)
23
27
  @publisher = publisher
24
28
  end
25
29
 
26
- def call(version) = publisher.call configuration.merge(version:)
30
+ def call(version = nil) = publisher.call Version(version || default)
27
31
 
28
32
  private
29
33
 
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/pathname"
4
+ require "sod"
5
+
6
+ module Milestoner
7
+ module CLI
8
+ module Commands
9
+ # Handles the building of milestone output.
10
+ class Build < Sod::Command
11
+ include Import[:input, :logger, :kernel]
12
+ include Builders::Import[:ascii_doc, :markdown, :stream, :web]
13
+
14
+ using Refinements::Pathname
15
+
16
+ handle "build"
17
+
18
+ description "Build milestone."
19
+
20
+ on Actions::Build::Label
21
+ on Actions::Build::Version
22
+ on Actions::Build::Layout
23
+ on Actions::Build::Format
24
+ on Actions::Build::Root
25
+
26
+ # :reek:TooManyStatements
27
+ def call
28
+ log_info "Building milestone..."
29
+
30
+ format = input.build_format
31
+
32
+ case format
33
+ when "ascii_doc" then build_ascii_doc
34
+ when "markdown" then build_markdown
35
+ when "stream" then build_stream
36
+ when "web" then build_web
37
+ else logger.abort "Invalid build format: #{format}."
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :view, :enricher
44
+
45
+ def build_ascii_doc = log_info("Milestone built: #{ascii_doc.call}.")
46
+
47
+ def build_markdown = log_info("Milestone built: #{markdown.call}.")
48
+
49
+ def build_stream
50
+ kernel.puts
51
+ stream.call
52
+ end
53
+
54
+ def build_web = log_info "Milestone built: #{web.call}."
55
+
56
+ def log_info(message) = logger.info { message }
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "refinements/pathname"
4
+ require "sod"
5
+
6
+ module Milestoner
7
+ module CLI
8
+ module Commands
9
+ # Handles the building of milestone output.
10
+ class Cache < Sod::Command
11
+ include Import[:input, :logger]
12
+
13
+ using Refinements::Pathname
14
+
15
+ handle "cache"
16
+
17
+ description "Manage cache."
18
+
19
+ on Actions::Cache::Info
20
+ on Actions::Cache::List
21
+ on Actions::Cache::Find
22
+ on Actions::Cache::Create
23
+ on Actions::Cache::Delete
24
+ end
25
+ end
26
+ end
27
+ end
@@ -6,7 +6,7 @@ module Milestoner
6
6
  module CLI
7
7
  # The main Command Line Interface (CLI) object.
8
8
  class Shell
9
- include Import[:defaults_path, :xdg_config, :specification]
9
+ include Import[:defaults_path, :specification, xdg_config: "xdg.config"]
10
10
 
11
11
  def initialize(context: Sod::Context, dsl: Sod, **)
12
12
  super(**)
@@ -25,8 +25,9 @@ module Milestoner
25
25
 
26
26
  dsl.new :milestoner, banner: specification.banner do
27
27
  on(Sod::Prefabs::Commands::Config, context:)
28
+ on Commands::Cache
29
+ on Commands::Build
28
30
  on Actions::Publish
29
- on Actions::Status
30
31
  on(Sod::Prefabs::Actions::Version, context:)
31
32
  on Sod::Prefabs::Actions::Help, self
32
33
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "refinements/array"
3
4
  require "versionaire"
4
5
 
5
6
  module Milestoner
@@ -8,49 +9,40 @@ module Milestoner
8
9
  class Categorizer
9
10
  include Import[:git]
10
11
 
11
- def initialize(expression: Regexp, **)
12
- super(**)
12
+ using Refinements::Array
13
+
14
+ def initialize(collector: Collector.new, expression: Regexp, **)
15
+ @collector = collector
13
16
  @expression = expression
17
+ super(**)
14
18
  end
15
19
 
16
20
  def call configuration = Container[:configuration]
17
- prefixes = configuration.prefixes
18
-
19
- prefixes.reduce({}) { |group, prefix| group.merge prefix => [] }
20
- .merge("Unknown" => [])
21
- .then { |groups| group_by_prefix prefixes, groups }
22
- .each_value { |commits| commits.sort_by!(&:subject) }
23
- .values
24
- .flatten
25
- .uniq(&:subject)
21
+ categories = configuration.commit_categories.pluck :label
22
+
23
+ categories.reduce({}) { |group, prefix| group.merge prefix => [] }
24
+ .merge("Unknown" => [])
25
+ .then { |groups| group_by_category categories, groups }
26
+ .each_value { |commits| commits.sort_by!(&:subject) }
27
+ .values
28
+ .flatten
26
29
  end
27
30
 
28
31
  private
29
32
 
30
- attr_reader :expression
33
+ attr_reader :collector, :expression
31
34
 
32
- def group_by_prefix prefixes, groups
33
- computed_commits.each.with_object groups do |commit, collection|
34
- prefix = commit.subject[subject_pattern(prefixes)]
35
- key = collection.key?(prefix) ? prefix : "Unknown"
35
+ def group_by_category categories, groups
36
+ collector.call.value_or([]).each.with_object groups do |commit, collection|
37
+ category = commit.subject[subject_pattern(categories)]
38
+ key = collection.key?(category) ? category : "Unknown"
36
39
  collection[key] << commit
37
40
  end
38
41
  end
39
42
 
40
- def subject_pattern prefixes
41
- prefixes.empty? ? expression.new(//) : expression.union(prefixes)
43
+ def subject_pattern categories
44
+ categories.empty? ? expression.new(//) : expression.union(categories)
42
45
  end
43
-
44
- def computed_commits = git.tagged? ? tagged_commits : saved_commits
45
-
46
- def tagged_commits
47
- git.tag_last
48
- .value_or(nil)
49
- .then { |tag| git.commits "#{tag}..HEAD" }
50
- .value_or([])
51
- end
52
-
53
- def saved_commits = git.commits.value_or([])
54
46
  end
55
47
  end
56
48
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Milestoner
4
+ module Commits
5
+ # Collects commits since last tag or all commits if untagged.
6
+ class Collector
7
+ include Import[:git]
8
+
9
+ def call = git.tagged? ? latest : all
10
+
11
+ private
12
+
13
+ def latest = git.tag_last.bind { |tag| git.commits "#{tag}..HEAD" }
14
+
15
+ def all = git.commits
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/monads"
4
+
5
+ module Milestoner
6
+ module Commits
7
+ # Enriches commits and associated trailers for final processing.
8
+ class Enricher
9
+ include Import[:input]
10
+
11
+ include Enrichers::Import[
12
+ :author,
13
+ :body,
14
+ :collaborators,
15
+ :format,
16
+ :issue,
17
+ :milestone,
18
+ :notes,
19
+ :review,
20
+ :signers,
21
+ :uri
22
+ ]
23
+
24
+ include Dry::Monads[:result]
25
+
26
+ def initialize(categorizer: Commits::Categorizer.new, model: Models::Commit, **)
27
+ @categorizer = categorizer
28
+ @model = model
29
+ super(**)
30
+ end
31
+
32
+ def call
33
+ categorizer.call
34
+ .map { |commit| record_for commit }
35
+ .then { |commits| Success commits }
36
+ end
37
+
38
+ private
39
+
40
+ attr_reader :categorizer, :model
41
+
42
+ def record_for(commit) = model.for(commit, **build_attributes(commit))
43
+
44
+ def build_attributes commit
45
+ infused_keys.each.with_object({}) do |command, attributes|
46
+ attributes[command] = __send__(command).call commit
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Milestoner
4
+ module Commits
5
+ module Enrichers
6
+ # Enriches a commit author by using cache.
7
+ class Author
8
+ include Milestoner::Import[:cache]
9
+
10
+ def initialize(model: Models::User, **)
11
+ @model = model
12
+ super(**)
13
+ end
14
+
15
+ def call commit
16
+ cache.commit(:users) { |table| table.find commit.author_name }
17
+ .value_or(model.new)
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :model
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Milestoner
4
+ module Commits
5
+ module Enrichers
6
+ # Enriches commit text by rendering as HTML based on trailer information.
7
+ class Body
8
+ include Milestoner::Import[:input]
9
+
10
+ def initialize(key: "Format", renderer: Renderers::Universal.new, **)
11
+ @key = key
12
+ @renderer = renderer
13
+ super(**)
14
+ end
15
+
16
+ def call commit
17
+ commit.trailer_value_for(key)
18
+ .value_or(input.commit_format)
19
+ .then { |format| renderer.call commit.body, for: format.to_sym }
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :key, :renderer
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "gitt"
4
+
5
+ module Milestoner
6
+ module Commits
7
+ module Enrichers
8
+ # Enriches a commit colleague by using cache.
9
+ class Colleague
10
+ include Milestoner::Import[:cache]
11
+
12
+ def initialize(key:, parser: Gitt::Parsers::Person.new, **)
13
+ @key = key
14
+ @parser = parser
15
+ super(**)
16
+ end
17
+
18
+ def call(commit) = commit.find_trailers(key).bind { |trailers| users_for(trailers).compact }
19
+
20
+ private
21
+
22
+ attr_reader :key, :parser
23
+
24
+ def users_for(trailers) = trailers.map { |trailer| user_for parser.call(trailer.value) }
25
+
26
+ def user_for person
27
+ cache.commit(:users) { find person.name }
28
+ .value_or(nil)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end