Merge pull request #20566 from Homebrew/extract-pathname-refinement

Extract `Pathname` refinement from `Formulary`
This commit is contained in:
Rylan Polster 2025-08-25 10:02:06 +00:00 committed by GitHub
commit dd7e3e0142
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 101 additions and 19 deletions

View File

@ -17,6 +17,7 @@ require "utils/socket"
require "cmd/install" require "cmd/install"
require "json/add/exception" require "json/add/exception"
require "utils/output" require "utils/output"
require "extend/pathname/write_mkpath_extension"
# A formula build. # A formula build.
class Build class Build
@ -245,6 +246,9 @@ begin
formula = args.named.to_formulae.first formula = args.named.to_formulae.first
options = Options.create(args.flags_only) options = Options.create(args.flags_only)
build = Build.new(formula, options, args:) build = Build.new(formula, options, args:)
Pathname.prepend WriteMkpathExtension
build.install build.install
# Any exception means the build did not complete. # Any exception means the build did not complete.
# The `case` for what to do per-exception class is further down. # The `case` for what to do per-exception class is further down.

View File

@ -0,0 +1,34 @@
# typed: strict
# frozen_string_literal: true
module WriteMkpathExtension
extend T::Helpers
requires_ancestor { Pathname }
# Source for `sig`: https://github.com/sorbet/sorbet/blob/b4092efe0a4489c28aff7e1ead6ee8a0179dc8b3/rbi/stdlib/pathname.rbi#L1392-L1411
sig {
params(
content: Object,
offset: Integer,
external_encoding: T.any(String, Encoding),
internal_encoding: T.any(String, Encoding),
encoding: T.any(String, Encoding),
textmode: BasicObject,
binmode: BasicObject,
autoclose: BasicObject,
mode: String,
perm: Integer,
).returns(Integer)
}
def write(content, offset = T.unsafe(nil), external_encoding: T.unsafe(nil), internal_encoding: T.unsafe(nil),
encoding: T.unsafe(nil), textmode: T.unsafe(nil), binmode: T.unsafe(nil), autoclose: T.unsafe(nil),
mode: T.unsafe(nil), perm: T.unsafe(nil))
T.bind(self, Pathname)
raise "Will not overwrite #{self}" if exist? && !offset && !mode&.match?(/^a\+?$/)
dirname.mkpath
super
end
end

View File

@ -1,4 +1,4 @@
# typed: true # rubocop:todo Sorbet/StrictSigil # typed: strict
# frozen_string_literal: true # frozen_string_literal: true
require "digest/sha2" require "digest/sha2"
@ -112,24 +112,6 @@ module Formulary
super super
end end
module PathnameWriteMkpath
# TODO: migrate away from refinements here, they don't play nicely with Sorbet
# rubocop:todo Sorbet/BlockMethodDefinition
refine Pathname do
def write(content, offset = nil, **open_args)
T.bind(self, Pathname)
raise "Will not overwrite #{self}" if exist? && !offset && !open_args[:mode]&.match?(/^a\+?$/)
dirname.mkpath
super
end
end
# rubocop:enable Sorbet/BlockMethodDefinition
end
using PathnameWriteMkpath
sig { sig {
params( params(
name: String, name: String,

View File

@ -0,0 +1,62 @@
# frozen_string_literal: true
require "extend/pathname/write_mkpath_extension"
RSpec.describe WriteMkpathExtension do
let(:file_content) { "sample contents" }
before do
Pathname.prepend described_class
end
it "creates parent directories if they do not exist" do
mktmpdir do |tmpdir|
file = tmpdir/"foo/bar/baz.txt"
expect(file.dirname).not_to exist
file.write(file_content)
expect(file).to exist
expect(file.read).to eq(file_content)
end
end
it "raises if file exists and not in append mode or with offset" do
mktmpdir do |tmpdir|
file = tmpdir/"file.txt"
file.write(file_content)
expect { file.write("new content") }.to raise_error(RuntimeError, /Will not overwrite/)
end
end
it "allows overwrite if offset is provided" do
mktmpdir do |tmpdir|
file = tmpdir/"file.txt"
file.write(file_content)
expect do
file.write("change", 0)
end.not_to raise_error
expect(file.read).to eq("change contents")
end
end
it "allows append mode ('a')" do
mktmpdir do |tmpdir|
file = tmpdir/"file.txt"
file.write(file_content)
expect do
file.write(" appended", mode: "a")
end.not_to raise_error
expect(file.read).to eq("#{file_content} appended")
end
end
it "allows append mode ('a+')" do
mktmpdir do |tmpdir|
file = tmpdir/"file.txt"
file.write(file_content)
expect do
file.write(" again", mode: "a+")
end.not_to raise_error
expect(file.read).to include("again")
end
end
end