Skip to content

Refactor process spawning with Process.capture#701

Draft
straight-shoota wants to merge 9 commits intocrystal-lang:masterfrom
straight-shoota:refactor/process.capture
Draft

Refactor process spawning with Process.capture#701
straight-shoota wants to merge 9 commits intocrystal-lang:masterfrom
straight-shoota:refactor/process.capture

Conversation

@straight-shoota
Copy link
Member

This is a PoC for refactoring app and spec code to simplify the code for spawning subprocesses and capturing their results as part of the discussions crystal-lang/crystal#16657 and crystal-lang/crystal#7171.

The monkey-patched Process.capture and .capture_result methods are a draft variant.
They differ from the proposal in crystal-lang/crystal#16630

Some of the key changes:

  • Command strings are transformed into percent array literals. This keeps the diff simple and the code readable (cf. Representation of Process arguments crystal#14773 (comment)).
  • Specs test the success status of process results directly, instead of expecting an exception.
  • Output and error streams are captured separately.
  • shards-specific capture code is shared between resolvers.

# Change the origin in the cache repo to https://foss.heptapod.net/foo/bar
Dir.cd(library.local_path) do
run "fossil remote-url -R #{library.name}.fossil https://foss.heptapod.net/foo/bar"
capture %w[fossil remote-url -R] << "#{library.name}.fossil" << "https://foss.heptapod.net/foo/bar"
Copy link
Contributor

@ysbaddaden ysbaddaden Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Until we support %W[] then I think being explicit is more readable:

capture ["fossil", "remote-url", "-R", "#{library.name}.fossil", "..."]

The feeling is confirmed for every other cases. An explicit array of strings is leagues better.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall a single array for [cmd, *args] is fine, but Process.new and Process.run shall support the syntax, too.

Copy link
Member Author

@straight-shoota straight-shoota Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose this is a pretty subjective preference. IMO array literal + << reads nicer because there's less noise with quotation marks.

capture %w[fossil remote-url -R] << "#{library.name}.fossil" << "https://foss.heptapod.net/foo/bar"
capture ["fossil", "remote-url", "-R", "#{library.name}.fossil", "https://foss.heptapod.net/foo/bar"]

Also, the diff is smaller, and the future diff to %W will also be smaller. So if nothing else, it has patch ergonomics going for it.

end
end

struct Result
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure a nilable out/err is very significant over empty strings, so this Result type doesn't bring anything valuable over {status, output, error} tuple.

Copy link
Member Author

@straight-shoota straight-shoota Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main reason for a result type over a tuple is that I find it easier to handle. For example, result = Process.capture(...) feels more convenient than status, output, error = Process.capture(...). It's less fragile, can be well documented, and wrapping it in FailedCommandError seems quite useful.

The nilable values are just a convenience feature on top. I believe it might be helpful sometimes to differentiate between whether the output is empty or we didn't even capture any.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments