Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.1.0-alpha.46"
".": "0.1.0-alpha.47"
}
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# Changelog

## 0.1.0-alpha.47 (2025-09-29)

Full Changelog: [v0.1.0-alpha.46...v0.1.0-alpha.47](https://github.com/lithic-com/lithic-ruby/compare/v0.1.0-alpha.46...v0.1.0-alpha.47)

### Bug Fixes

* always send `filename=...` for multipart requests where a file is expected ([f52304d](https://github.com/lithic-com/lithic-ruby/commit/f52304dd7ef5d4dc716e173121f9d1f478275ec8))


### Chores

* allow fast-format to use bsd sed as well ([5022d53](https://github.com/lithic-com/lithic-ruby/commit/5022d5368e2e3bdb98b69edc72f0bef7a8da0abf))

## 0.1.0-alpha.46 (2025-09-26)

Full Changelog: [v0.1.0-alpha.45...v0.1.0-alpha.46](https://github.com/lithic-com/lithic-ruby/compare/v0.1.0-alpha.45...v0.1.0-alpha.46)
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ GIT
PATH
remote: .
specs:
lithic (0.1.0.pre.alpha.46)
lithic (0.1.0.pre.alpha.47)
connection_pool

GEM
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ To use this gem, install via Bundler by adding the following to your application
<!-- x-release-please-start-version -->

```ruby
gem "lithic", "~> 0.1.0.pre.alpha.46"
gem "lithic", "~> 0.1.0.pre.alpha.47"
```

<!-- x-release-please-end -->
Expand Down
26 changes: 18 additions & 8 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,14 @@ end
xargs = %w[xargs --no-run-if-empty --null --max-procs=0 --max-args=300 --]
ruby_opt = {"RUBYOPT" => [ENV["RUBYOPT"], "--encoding=UTF-8"].compact.join(" ")}

filtered = ->(ext, dirs) do
if ENV.key?(FILES_ENV)
%w[sed -E -n -e] << "/\\.#{ext}$/p" << "--" << ENV.fetch(FILES_ENV)
else
(%w[find] + dirs + %w[-type f -and -name]) << "*.#{ext}" << "-print0"
end
end

desc("Lint `*.rb(i)`")
multitask(:"lint:rubocop") do
find = %w[find ./lib ./test ./rbi ./examples -type f -and ( -name *.rb -or -name *.rbi ) -print0]
Expand All @@ -54,24 +62,26 @@ multitask(:"lint:rubocop") do
sh("#{find.shelljoin} | #{lint.shelljoin}")
end

norm_lines = %w[tr -- \n \0].shelljoin

desc("Format `*.rb`")
multitask(:"format:rb") do
# while `syntax_tree` is much faster than `rubocop`, `rubocop` is the only formatter with full syntax support
files = ENV.key?(FILES_ENV) ? %w[sed -E -z -n -e /\.rb$/p --] << ENV.fetch(FILES_ENV) : %w[find ./lib ./test ./examples -type f -and -name *.rb -print0]
files = filtered["rb", %w[./lib ./test ./examples]]
fmt = xargs + %w[rubocop --fail-level F --autocorrect --format simple --]
sh("#{files.shelljoin} | #{fmt.shelljoin}")
sh("#{files.shelljoin} | #{norm_lines} | #{fmt.shelljoin}")
end

desc("Format `*.rbi`")
multitask(:"format:rbi") do
files = ENV.key?(FILES_ENV) ? %w[sed -E -z -n -e /\.rbi$/p --] << ENV.fetch(FILES_ENV) : %w[find ./rbi -type f -and -name *.rbi -print0]
files = filtered["rbi", %w[./rbi]]
fmt = xargs + %w[stree write --]
sh(ruby_opt, "#{files.shelljoin} | #{fmt.shelljoin}")
sh(ruby_opt, "#{files.shelljoin} | #{norm_lines} | #{fmt.shelljoin}")
end

desc("Format `*.rbs`")
multitask(:"format:rbs") do
files = ENV.key?(FILES_ENV) ? %w[sed -E -z -n -e /\.rbs$/p --] << ENV.fetch(FILES_ENV) : %w[find ./sig -type f -name *.rbs -print0]
files = filtered["rbs", %w[./sig]]
inplace = /darwin|bsd/ =~ RUBY_PLATFORM ? ["-i", ""] : %w[-i]
uuid = SecureRandom.uuid

Expand Down Expand Up @@ -100,13 +110,13 @@ multitask(:"format:rbs") do
success = false

# transform class aliases to type aliases, which syntax tree has no trouble with
sh("#{files.shelljoin} | #{pre.shelljoin}")
sh("#{files.shelljoin} | #{norm_lines} | #{pre.shelljoin}")
# run syntax tree to format `*.rbs` files
sh(ruby_opt, "#{files.shelljoin} | #{fmt.shelljoin}") do
sh(ruby_opt, "#{files.shelljoin} | #{norm_lines} | #{fmt.shelljoin}") do
success = _1
end
# transform type aliases back to class aliases
sh("#{files.shelljoin} | #{pst.shelljoin}")
sh("#{files.shelljoin} | #{norm_lines} | #{pst.shelljoin}")

# always run post-processing to remove comment marker
fail unless success
Expand Down
17 changes: 10 additions & 7 deletions lib/lithic/file_part.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,21 @@ def to_json(*a) = read.to_json(*a)
def to_yaml(*a) = read.to_yaml(*a)

# @param content [Pathname, StringIO, IO, String]
# @param filename [String, nil]
# @param filename [Pathname, String, nil]
# @param content_type [String, nil]
def initialize(content, filename: nil, content_type: nil)
@content = content
@content_type = content_type
@filename =
case content
in Pathname
filename.nil? ? content.basename.to_path : ::File.basename(filename)
case [filename, (@content = content)]
in [String | Pathname, _]
::File.basename(filename)
in [nil, Pathname]
content.basename.to_path
in [nil, IO]
content.to_path
else
filename.nil? ? nil : ::File.basename(filename)
filename
end
@content_type = content_type
end
end
end
11 changes: 7 additions & 4 deletions lib/lithic/internal/type/file_input.rb
Original file line number Diff line number Diff line change
Expand Up @@ -82,17 +82,20 @@ def coerce(value, state:)
#
# @return [Pathname, StringIO, IO, String, Object]
def dump(value, state:)
# rubocop:disable Lint/DuplicateBranch
case value
in StringIO | String
# https://datatracker.ietf.org/doc/html/rfc7578#section-4.2
# while not required, a filename is recommended, and in practice many servers do expect this
Lithic::FilePart.new(value, filename: "upload")
in IO
state[:can_retry] = false
value.to_path.nil? ? Lithic::FilePart.new(value, filename: "upload") : value
in Lithic::FilePart if value.content.is_a?(IO)
state[:can_retry] = false
value
else
value
end
# rubocop:enable Lint/DuplicateBranch

value
end

# @api private
Expand Down
2 changes: 1 addition & 1 deletion lib/lithic/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module Lithic
VERSION = "0.1.0.pre.alpha.46"
VERSION = "0.1.0.pre.alpha.47"
end
2 changes: 1 addition & 1 deletion rbi/lithic/file_part.rbi
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ module Lithic
sig do
params(
content: T.any(Pathname, StringIO, IO, String),
filename: T.nilable(String),
filename: T.nilable(T.any(Pathname, String)),
content_type: T.nilable(String)
).returns(T.attached_class)
end
Expand Down
5 changes: 1 addition & 4 deletions scripts/fast-format
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,4 @@ if [ $# -eq 0 ]; then
exit 1
fi

FILE="$(mktemp)"
tr -- '\n' '\0' < "$1" > "$FILE"

exec -- bundle exec rake format FORMAT_FILE="$FILE"
exec -- bundle exec rake format FORMAT_FILE="$1"
2 changes: 1 addition & 1 deletion sig/lithic/file_part.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module Lithic

def initialize: (
Pathname | StringIO | IO | String content,
?filename: String?,
?filename: (Pathname | String)?,
?content_type: String?
) -> void
end
Expand Down
29 changes: 20 additions & 9 deletions test/lithic/internal/util_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -227,20 +227,24 @@ def test_encoding_length

def test_file_encode
file = Pathname(__FILE__)
fileinput = Lithic::Internal::Type::Converter.dump(Lithic::Internal::Type::FileInput, "abc")
headers = {"content-type" => "multipart/form-data"}
cases = {
"abc" => "abc",
StringIO.new("abc") => "abc",
Lithic::FilePart.new("abc") => "abc",
Lithic::FilePart.new(StringIO.new("abc")) => "abc",
file => /^class Lithic/,
Lithic::FilePart.new(file) => /^class Lithic/
"abc" => ["", "abc"],
StringIO.new("abc") => ["", "abc"],
fileinput => %w[upload abc],
Lithic::FilePart.new(StringIO.new("abc")) => ["", "abc"],
file => [file.basename.to_path, /^class Lithic/],
Lithic::FilePart.new(file, filename: "d o g") => ["d%20o%20g", /^class Lithic/]
}
cases.each do |body, val|
cases.each do |body, testcase|
filename, val = testcase
encoded = Lithic::Internal::Util.encode_content(headers, body)
cgi = FakeCGI.new(*encoded)
io = cgi[""]
assert_pattern do
cgi[""].read => ^val
io.original_filename => ^filename
io.read => ^val
end
end
end
Expand All @@ -261,7 +265,14 @@ def test_hash_encode
cgi = FakeCGI.new(*encoded)
testcase.each do |key, val|
assert_pattern do
cgi[key] => ^val
parsed =
case (p = cgi[key])
in StringIO
p.read
else
p
end
parsed => ^val
end
end
end
Expand Down