Skip to content

Commit 18f1b46

Browse files
committed
Add RBS type signatures and Steep type checking
1 parent c78696a commit 18f1b46

17 files changed

Lines changed: 278 additions & 6 deletions

File tree

Gemfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ group :test do
1919
gem "yardstick"
2020
end
2121

22+
group :sig do
23+
gem "steep"
24+
end
25+
2226
group :doc do
2327
gem "kramdown"
2428
gem "yard"

Rakefile

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,16 @@ Yardstick::Rake::Verify.new do |verify|
2323
verify.threshold = 100
2424
end
2525

26+
desc "Type check with Steep"
27+
task :steep do
28+
require "steep"
29+
require "steep/cli"
30+
exit Steep::CLI.new(argv: ["check", "--log-level=fatal"], stdout: $stdout, stderr: $stderr, stdin: $stdin).run
31+
end
32+
2633
desc "Run mutation testing with Mutant"
2734
task :mutant do
2835
system("bundle exec mutant run") || abort("Mutant failed!")
2936
end
3037

31-
task default: %i[test rubocop verify_measurements]
38+
task default: %i[test rubocop verify_measurements steep]

Steepfile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# frozen_string_literal: true
2+
3+
target :lib do
4+
signature "sig"
5+
check "lib"
6+
7+
library "pathname"
8+
library "securerandom"
9+
library "stringio"
10+
library "uri"
11+
end

lib/http/form_data/composite_io.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ def read_chunks(length)
8383

8484
length -= chunk.bytesize
8585

86-
break if length.zero?
86+
break if length == 0 # rubocop:disable Style/NumericPredicate
8787
end
8888
end
8989

lib/http/form_data/multipart.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def tail
8989
# @api private
9090
# @return [Array<Param>]
9191
def parts(data)
92-
params = []
92+
params = [] #: Array[Param]
9393

9494
FormData.ensure_data(data).each do |name, values|
9595
Array(values).each do |value|

lib/http/form_data/multipart/param.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,9 @@ def header
6060
# @api private
6161
# @return [String]
6262
def parameters
63+
fname = filename
6364
parameters = { name: @name }
64-
parameters[:filename] = filename if filename
65+
parameters[:filename] = fname if fname
6566
parameters.map { |k, v| "#{k}=#{v.inspect}" }.join("; ")
6667
end
6768

lib/http/form_data/readable.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module Readable
1313
# @return [String]
1414
def to_s
1515
rewind
16-
content = read
16+
content = read || ""
1717
rewind
1818
content
1919
end
@@ -29,7 +29,7 @@ def to_s
2929
# @param [String] outbuf String to be replaced with retrieved data
3030
# @return [String, nil]
3131
def read(length = nil, outbuf = nil)
32-
@io.read(length, outbuf)
32+
outbuf ? @io.read(length, outbuf) : @io.read(length)
3333
end
3434

3535
# Returns IO size in bytes

sig/deps.rbs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Overrides for stdlib RBS signatures that are missing keyword arguments
2+
3+
class ::File
4+
# Override: Ruby's File.new accepts keyword args like binmode:
5+
def initialize: (string | _ToPath | int file_name, ?string | int mode, ?int perm, **untyped) -> void
6+
end
7+
8+
class ::Pathname
9+
# Override: Ruby's Pathname#open accepts keyword args like binmode:
10+
def open: (?string | int mode, ?int perm, **untyped) -> ::File
11+
| [T] (?string | int mode, ?int perm, **untyped) { (::File) -> T } -> T
12+
end

sig/http/form_data.rbs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Type signatures for the http-form_data gem
2+
3+
module HTTP
4+
module FormData
5+
CRLF: String
6+
7+
class Error < StandardError
8+
end
9+
10+
type formdata = Multipart | Urlencoded
11+
12+
# Selects encoder type based on given data
13+
def self.create: (untyped data, ?encoder: _Encoder?) -> formdata
14+
15+
# Coerces obj to Hash
16+
def self.ensure_hash: (untyped obj) -> Hash[untyped, untyped]
17+
18+
# Coerces obj to an Enumerable of key-value pairs
19+
def self.ensure_data: (untyped obj) -> untyped
20+
21+
private
22+
23+
# Checks if data contains multipart data
24+
def self.multipart?: (untyped data) -> bool
25+
26+
interface _Encoder
27+
def call: (untyped data) -> String
28+
end
29+
end
30+
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
module HTTP
2+
module FormData
3+
class CompositeIO
4+
@index: Integer
5+
@ios: Array[untyped]
6+
@size: Integer?
7+
8+
# Creates a new CompositeIO from an array of IO-like objects
9+
def initialize: (Array[untyped] ios) -> void
10+
11+
# Reads and returns content across multiple IO objects
12+
def read: (?Integer? length, ?String? outbuf) -> String?
13+
14+
# Returns sum of all IO sizes
15+
def size: () -> Integer
16+
17+
# Rewinds all IO objects and resets cursor
18+
def rewind: () -> void
19+
20+
private
21+
22+
# Yields chunks with total length up to `length`
23+
def read_chunks: (Integer? length) { (String chunk) -> void } -> void
24+
25+
# Reads chunk from current IO with length up to `max_length`
26+
def readpartial: (Integer? max_length) -> String?
27+
28+
# Advances cursor to the next IO object
29+
def advance_io: () -> void
30+
end
31+
end
32+
end

0 commit comments

Comments
 (0)