From d7f63babc8206a92f509af8483767230d3be932f Mon Sep 17 00:00:00 2001 From: Erik Berlin Date: Wed, 30 Aug 2023 08:59:41 -0400 Subject: [PATCH] Define RBS type signatures --- Gemfile | 2 + Rakefile | 10 +++- Steepfile | 9 ++++ sig/x.rbs | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 160 insertions(+), 3 deletions(-) create mode 100644 Steepfile diff --git a/Gemfile b/Gemfile index f6d6dc6..9ad928a 100644 --- a/Gemfile +++ b/Gemfile @@ -6,11 +6,13 @@ gemspec gem "hashie", ">= 5" gem "minitest", ">= 5.19" gem "rake", ">= 13.0.6" +gem "rbs", ">= 3.2.1" gem "rubocop", ">= 1.21" gem "rubocop-minitest", ">= 0.31" gem "rubocop-performance", ">= 1.18" gem "rubocop-rake", ">= 0.6" gem "simplecov", ">= 0.22" gem "standard", ">= 1.30.1" +gem "steep", ">= 1.5.3" gem "timecop", ">= 0.9.6" gem "webmock", ">= 3.18.1" diff --git a/Rakefile b/Rakefile index b252b29..d7c554f 100644 --- a/Rakefile +++ b/Rakefile @@ -12,4 +12,12 @@ require "rubocop/rake_task" RuboCop::RakeTask.new -task default: %i[test rubocop standard] +require "steep" +require "steep/cli" + +desc "Type check with Steep" +task :steep do + Steep::CLI.new(argv: ["check"], stdout: $stdout, stderr: $stderr, stdin: $stdin).run +end + +task default: %i[test rubocop standard steep] diff --git a/Steepfile b/Steepfile new file mode 100644 index 0000000..7b8d8a0 --- /dev/null +++ b/Steepfile @@ -0,0 +1,9 @@ +target :lib do + signature "sig" + check "lib" + library "forwardable" + library "json" + library "net-http" + library "uri" + configure_code_diagnostics(Steep::Diagnostic::Ruby.strict) +end diff --git a/sig/x.rbs b/sig/x.rbs index 6ec4859..c283592 100644 --- a/sig/x.rbs +++ b/sig/x.rbs @@ -1,4 +1,142 @@ +module OAuth + class Consumer + def initialize: (String api_key, String api_key_secret, site: String) -> void + def sign!: (Net::HTTPRequest request, OAuth::Token token) -> void + end + class Token + def initialize: (String access_token, String access_token_secret) -> void + end +end + module X - VERSION: String - # See the writing guide of rbs: https://github.com/ruby/rbs#guides + VERSION: Gem::Version + + class Authenticator + extend Forwardable + @consumer: OAuth::Consumer + @access_token: OAuth::Token + + def initialize: (String api_key, String api_key_secret, String access_token, String access_token_secret) -> void + def sign!: (Net::HTTPRequest request) -> void + end + + module ClientDefaults + DEFAULT_BASE_URL: String + DEFAULT_CONTENT_TYPE: String + DEFAULT_OPEN_TIMEOUT: Integer + DEFAULT_READ_TIMEOUT: Integer + DEFAULT_WRITE_TIMEOUT: Integer + DEFAULT_USER_AGENT: String + DEFAULT_ARRAY_CLASS: Class + DEFAULT_OBJECT_CLASS: Class + end + + class Error < StandardError + include ClientDefaults + + attr_reader object: untyped + def initialize: (String msg, response: Net::HTTPResponse, ?array_class: Class, ?object_class: Class) -> void + + private + def json_response?: (Net::HTTPResponse response) -> bool + end + + class ClientError < Error + end + + class BadRequestError < ClientError + end + + class AuthenticationError < ClientError + end + + class ForbiddenError < ClientError + end + + class NotFoundError < ClientError + end + + class TooManyRequestsError < ClientError + include ClientDefaults + @response: Net::HTTPResponse + + def initialize: (String msg, response: Net::HTTPResponse, ?array_class: Class, ?object_class: Class) -> void + def limit: -> Integer + def remaining: -> Integer + def reset_at: -> Time + def reset_in: -> Integer? + end + + class ServerError < Error + end + + class ServiceUnavailableError < ServerError + end + + module Errors + ERROR_CLASSES: Hash[Integer, singleton(AuthenticationError) | singleton(BadRequestError) | singleton(ForbiddenError) | singleton(NotFoundError) | singleton(ServerError) | singleton(ServiceUnavailableError) | singleton(TooManyRequestsError)] + NETWORK_ERRORS: Array[(singleton(::Errno::ECONNREFUSED) | singleton(::Net::OpenTimeout) | singleton(::Net::ReadTimeout))] + end + + class NetworkError < Error + end + + class Connection + extend Forwardable + include Errors + @http_client: Net::HTTP + + attr_reader base_url: URI::Generic + def initialize: (URI::Generic | String url, Float | Integer open_timeout, Float | Integer read_timeout, Float | Integer write_timeout, ?debug_output: IO?) -> void + def send_request: (Net::HTTPRequest request) -> Net::HTTPResponse + def base_url=: (URI::Generic | String new_base_url) -> URI::Generic + def debug_output: -> IO? + end + + class RequestBuilder + HTTP_METHODS: Hash[::Symbol, (singleton(::Net::HTTP::Get) | singleton(::Net::HTTP::Post) | singleton(::Net::HTTP::Put) | singleton(::Net::HTTP::Delete))] + + attr_accessor content_type: String + attr_accessor user_agent: String + def initialize: (String content_type, String user_agent) -> void + def build: (Authenticator authenticator, :delete | :get | :post | :put http_method, URI::Generic base_url, String endpoint, ?body: nil) -> (Net::HTTPRequest) + + private + def create_request: (:delete | :get | :post | :put http_method, URI::Generic url, nil body) -> (Net::HTTPRequest) + def add_authorization: (Net::HTTPRequest request, Authenticator authenticator) -> void + def add_content_type: (Net::HTTPRequest request) -> void + def add_user_agent: (Net::HTTPRequest request) -> void + end + + class ResponseHandler + include Errors + include ClientDefaults + + attr_accessor array_class: Class + attr_accessor object_class: Class + def initialize: (Class array_class, Class object_class) -> void + def handle: (Net::HTTPResponse response) -> untyped + + private + def successful_json_response?: (Net::HTTPResponse response) -> bool + end + + class Client + extend Forwardable + include ClientDefaults + @authenticator: Authenticator + @connection: Connection + @request_builder: RequestBuilder + @response_handler: ResponseHandler + + attr_reader base_url: URI::Generic + def initialize: (api_key: String, api_key_secret: String, access_token: String, access_token_secret: String, ?base_url: URI::Generic | String, ?content_type: String, ?user_agent: String, ?open_timeout: Float | Integer, ?read_timeout: Float | Integer, ?write_timeout: Float | Integer, ?debug_output: IO?, ?array_class: Class, ?object_class: Class) -> void + def get: (String endpoint) -> untyped + def post: (String endpoint, ?nil body) -> untyped + def put: (String endpoint, ?nil body) -> untyped + def delete: (String endpoint) -> untyped + + private + def send_request: (:delete | :get | :post | :put http_method, String endpoint, ?nil body) -> untyped + end end