Skip to content

Commit

Permalink
feat: tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
name committed Dec 3, 2024
1 parent 61015ff commit d845227
Show file tree
Hide file tree
Showing 15 changed files with 127 additions and 58 deletions.
4 changes: 2 additions & 2 deletions lib/degem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
require_relative "degem/matcher"
require_relative "degem/find_unused"
require_relative "degem/multi_delegator"
require_relative "degem/decorated"
require_relative "degem/decorate"
require_relative "degem/rubygem"
require_relative "degem/decorate_rubygems"
require_relative "degem/git_adapter"
require_relative "degem/report"
require_relative "degem/cli"
Expand Down
25 changes: 19 additions & 6 deletions lib/degem/cli.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module Degem
class Cli
GEMFILE = "Gemfile"
Expand All @@ -16,18 +18,29 @@ def call
return 1
end

rubygems = FindUnused
.new(gemfile_path: GEMFILE, gem_specification: Gem::Specification, grep: Grep.new(@stderr))
.call
decorated = Decorate
.new(gem_specification: Gem::Specification)
.call(rubygems:, git_adapter: GitAdapter.new)
unused = find_unused.call
decorated = decorate_rubygems.call(unused)
Report.new(@stderr).call(decorated)
0
end

private

def find_unused
FindUnused.new(
gemfile_path: GEMFILE,
gem_specification: Gem::Specification,
grep: Grep.new(@stderr)
)
end

def decorate_rubygems
DecorateRubygems.new(
gem_specification: Gem::Specification,
git_adapter: GitAdapter.new
)
end

def gemfile_exists?
File.file?(GEMFILE)
end
Expand Down
15 changes: 0 additions & 15 deletions lib/degem/decorate.rb

This file was deleted.

18 changes: 18 additions & 0 deletions lib/degem/decorate_rubygems.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module Degem
class DecorateRubygems
def initialize(gem_specification:, git_adapter:)
@gem_specification = gem_specification
@git_adapter = git_adapter
end

def call(rubygems)
rubygems.map do |rubygem|
gemspec = @gem_specification.find_by_name(rubygem.name)
git = @git_adapter.call(rubygem.name)
Rubygem.new(rubygem, gemspec, git)
end
end
end
end
4 changes: 3 additions & 1 deletion lib/degem/decorated.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

module Degem
class Decorated < MultiDelegator
class Rubygem < MultiDelegator
attr_reader :commits

def initialize(_, _, commits)
Expand Down
6 changes: 4 additions & 2 deletions lib/degem/find_unused.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module Degem
class FindUnused
def initialize(gemfile_path:, gem_specification:, grep: Grep.new, bundle_paths: GitLsFiles.new)
Expand All @@ -22,7 +24,7 @@ def reject_railties(rubygems)
.reject { _1.name == "rails" }
.reject do |rubygem|
gem_path = @gem_specification.find_by_name(rubygem.name).full_gem_path
@grep.inverse?(/(Rails::Railtie|Rails::Engine)/, gem_path)
@grep.match?(/(Rails::Railtie|Rails::Engine)/, gem_path)
end
end

Expand All @@ -44,7 +46,7 @@ def matchers
end

def gemfile
@gemfile = ParseGemfile.new.call(gemfile_path)
@gemfile ||= ParseGemfile.new.call(gemfile_path)
end

def rails?
Expand Down
24 changes: 18 additions & 6 deletions lib/degem/git_adapter.rb
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
# frozen_string_literal: true

require "ostruct"
require "open3"

module Degem
class GitAdapter
require "ostruct"
require "open3"

def call(gem_name)
out, _err, status = git_log(gem_name)
return [] unless status.zero?

out.split("\n").map do |raw_commit|
hash, date, title = raw_commit.split("\t")
out.split("\n").map do |commit|
hash, date, title = commit.split("\t")
OpenStruct.new(hash:, date:, title:, url: to_commit_url(hash))
end
end
Expand All @@ -21,7 +23,17 @@ def git_remote_origin_url
end

def git_log(gem_name)
out, err, status = Open3.capture3("git log --pretty=format:'%H%x09%cs%x09%s' --pickaxe-regex -S '#{gem_name}' -- Gemfile | cat")
out, err, status = Open3.capture3([
"git log",
"--pretty=format:'%H%x09%cs%x09%s'",
"--pickaxe-regex",
"-S '#{gem_name}'",
"--",
"Gemfile",
"|",
"cat"
].join(" "))

[out, err, status.exitstatus]
end

Expand Down
8 changes: 5 additions & 3 deletions lib/degem/git_ls_files.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# frozen_string_literal: true

require "open3"

module Degem
class GitLsFiles
require "open3"

def call(fallback)
out, _err, status = git_ls
return fallback unless status.zero?

out.split("\x0").select { _1.end_with?(".rb") }.map { File.expand_path(_1).to_s }
out.split("\x0").select { _1.end_with?(".rb") }.map { File.expand_path(_1) }
end

private
Expand Down
8 changes: 5 additions & 3 deletions lib/degem/grep.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# frozen_string_literal: true

require "find"

module Degem
class Grep
require "find"

def initialize(stderr = StringIO.new)
@stderr = stderr
end

def inverse?(matcher, dir)
def match?(matcher, dir)
Find.find(File.expand_path(dir)) do |path|
next unless File.file?(path)
next if File.extname(path) != ".rb"
Expand Down
2 changes: 2 additions & 0 deletions lib/degem/matcher.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module Degem
class Matcher
attr_reader :rubygem
Expand Down
6 changes: 4 additions & 2 deletions lib/degem/multi_delegator.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
# frozen_string_literal: true

module Degem
class MultiDelegator
def initialize(*delegates)
@delegates = delegates
end

def method_missing(method, *args, &block)
def method_missing(method, *args)
delegate = @delegates.find { _1.respond_to?(method) }
return delegate.public_send(method, *args, &block) if delegate
return delegate.public_send(method, *args) if delegate

super
end
Expand Down
2 changes: 2 additions & 0 deletions lib/degem/parse_gemfile.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

module Degem
class ParseGemfile
def call(gemfile_path)
Expand Down
41 changes: 26 additions & 15 deletions lib/degem/report.rb
Original file line number Diff line number Diff line change
@@ -1,33 +1,44 @@
# frozen_string_literal: true

module Degem
class Report
def initialize(stderr)
@stderr = stderr
end

def call(decorateds)
def call(rubygems)
@stderr.puts
@stderr.puts
@stderr.puts "The following gems may be unused:"
@stderr.puts

decorateds.each do |decorated|
heading =
if decorated.source_code_uri.nil?
decorated.name
else
"#{decorated.name}: #{decorated.source_code_uri}"
end
@stderr.puts(heading)
@stderr.puts("=" * heading.size)
rubygems.each do |rubygem|
gem_name(rubygem)
@stderr.puts
commits(rubygem)
@stderr.puts
end
end

decorated.commits.each.with_index do |commit, i|
@stderr.puts("#{commit.hash[0..6]} (#{commit.date}) #{commit.title}")
@stderr.puts(commit.url)
@stderr.puts if i+1 == decorated.commits.size
private

def gem_name(rubygem)
heading =
if rubygem.source_code_uri.nil?
rubygem.name
else
"#{rubygem.name}: #{rubygem.source_code_uri}"
end

@stderr.puts
@stderr.puts(heading)
@stderr.puts("=" * heading.size)
end

def commits(rubygem)
rubygem.commits.each.with_index do |commit, i|
@stderr.puts("#{commit.hash[0..6]} (#{commit.date}) #{commit.title}")
@stderr.puts(commit.url)
@stderr.puts if i + 1 == rubygem.commits.size
end
end
end
Expand Down
16 changes: 16 additions & 0 deletions lib/degem/rubygem.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

module Degem
class Rubygem < MultiDelegator
attr_reader :commits

def initialize(_, _, commits)
super
@commits = commits
end

def source_code_uri
metadata["source_code_uri"] || homepage
end
end
end
6 changes: 3 additions & 3 deletions test/test_degem.rb
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ def test_it_decorates_the_result_with_git_information

gem_specification = TestableGemSpecification.new(foo_gemspec_path)

decorateds = Degem::Decorate.new(gem_specification:).call(rubygems:, git_adapter:)
decorateds = Degem::DecorateRubygems.new(gem_specification:, git_adapter:).call(rubygems)

assert_equal ["foo"], decorateds.map(&:name)
assert_equal [[true]], decorateds.map(&:autorequire)
Expand Down Expand Up @@ -495,7 +495,7 @@ def test_with_minimal_gemspec_it_decorates_the_result_with_git_information
git_adapter = TestableGitAdapter.new
gem_specification = TestableGemSpecification.new(foo_gemspec_path)

actual = Degem::Decorate.new(gem_specification:).call(rubygems:, git_adapter:)
actual = Degem::DecorateRubygems.new(gem_specification:, git_adapter:).call(rubygems)

assert_equal ["foo"], actual.map(&:name)
assert_equal [nil], actual.map(&:autorequire)
Expand Down Expand Up @@ -548,7 +548,7 @@ def test_it_reports_with_git_information

gem_specification = TestableGemSpecification.new([foo_gemspec_path, bar_gemspec_path])

decorated = Degem::Decorate.new(gem_specification:).call(rubygems:, git_adapter:)
decorated = Degem::DecorateRubygems.new(gem_specification:, git_adapter:).call(rubygems)

stderr = StringIO.new
Degem::Report.new(stderr).call(decorated)
Expand Down

0 comments on commit d845227

Please sign in to comment.