Add a Delegator DSL compiler

This commit is contained in:
Douglas Eichelberger 2024-10-05 18:56:38 -07:00
parent 3c51d0c8d3
commit 6eaf122144
4 changed files with 141 additions and 51 deletions

View File

@ -8,11 +8,23 @@ module Cask
# Class corresponding to the `url` stanza. # Class corresponding to the `url` stanza.
class URL < Delegator class URL < Delegator
class DSL class DSL
attr_reader :uri, :specs, attr_reader :uri, :tag, :branch, :revisions, :revision,
:verified, :using, :trust_cert, :cookies, :header, :data, :only_path
:tag, :branch, :revisions, :revision,
:trust_cert, :cookies, :referer, :header, :user_agent, sig { returns(T.nilable(T.any(URI::Generic, String))) }
:data, :only_path attr_reader :referer
sig { returns(T::Hash[Symbol, T.untyped]) }
attr_reader :specs
sig { returns(T.nilable(T.any(Symbol, String))) }
attr_reader :user_agent
sig { returns(T.any(T::Class[T.anything], Symbol, NilClass)) }
attr_reader :using
sig { returns(T.nilable(String)) }
attr_reader :verified
extend Forwardable extend Forwardable
def_delegators :uri, :path, :scheme, :to_s def_delegators :uri, :path, :scheme, :to_s
@ -137,6 +149,8 @@ module Cask
end end
end end
private
# Allows calling a nested `url` stanza in a `url do` block. # Allows calling a nested `url` stanza in a `url do` block.
# #
# @api public # @api public
@ -150,7 +164,6 @@ module Cask
def url(uri, &block) def url(uri, &block)
self.class.new(uri, dsl: @dsl, &block).call self.class.new(uri, dsl: @dsl, &block).call
end end
private :url
# This allows calling DSL methods from inside a `url` block. # This allows calling DSL methods from inside a `url` block.
# #
@ -162,12 +175,10 @@ module Cask
super super
end end
end end
private :method_missing
def respond_to_missing?(method, include_all) def respond_to_missing?(method, include_all)
@dsl.respond_to?(method, include_all) || super @dsl.respond_to?(method, include_all) || super
end end
private :respond_to_missing?
end end
sig { sig {
@ -244,30 +255,11 @@ module Cask
@caller_location = caller_location @caller_location = caller_location
end end
def __getobj__
@dsl
end
def __setobj__(dsl)
@dsl = dsl
end
sig { returns(Homebrew::SourceLocation) } sig { returns(Homebrew::SourceLocation) }
def location def location
Homebrew::SourceLocation.new(@caller_location.lineno, raw_url_line&.index("url")) Homebrew::SourceLocation.new(@caller_location.lineno, raw_url_line&.index("url"))
end end
sig { returns(T.nilable(String)) }
def raw_url_line
return @raw_url_line if defined?(@raw_url_line)
@raw_url_line = Pathname(T.must(@caller_location.path))
.each_line
.drop(@caller_location.lineno - 1)
.first
end
private :raw_url_line
sig { params(ignore_major_version: T::Boolean).returns(T::Boolean) } sig { params(ignore_major_version: T::Boolean).returns(T::Boolean) }
def unversioned?(ignore_major_version: false) def unversioned?(ignore_major_version: false)
interpolated_url = raw_url_line&.then { |line| line[/url\s+"([^"]+)"/, 1] } interpolated_url = raw_url_line&.then { |line| line[/url\s+"([^"]+)"/, 1] }
@ -283,5 +275,23 @@ module Cask
def from_block? def from_block?
@from_block @from_block
end end
private
def __getobj__ = @dsl
def __setobj__(dsl)
@dsl = dsl
end
sig { returns(T.nilable(String)) }
def raw_url_line
return @raw_url_line if defined?(@raw_url_line)
@raw_url_line = Pathname(T.must(@caller_location.path))
.each_line
.drop(@caller_location.lineno - 1)
.first
end
end end
end end

View File

@ -1,24 +0,0 @@
# typed: strict
module Cask
class URL
include Kernel
# TODO: Generate this
sig { returns(T.any(T::Class[T.anything], Symbol, NilClass)) }
def using; end
sig { returns(T.nilable(T.any(URI::Generic, String))) }
def referer; end
sig { returns(T::Hash[Symbol, T.untyped]) }
def specs; end
sig { returns(T.nilable(T.any(Symbol, String))) }
def user_agent; end
sig { returns(T.nilable(String)) }
def verified; end
end
end

View File

@ -0,0 +1,64 @@
# typed: true
# DO NOT EDIT MANUALLY
# This is an autogenerated file for dynamic methods in `Cask::URL`.
# Please instead update this file by running `bin/tapioca dsl Cask::URL`.
class Cask::URL
include Kernel
sig { returns(T.untyped) }
def branch; end
sig { returns(T.untyped) }
def cookies; end
sig { returns(T.untyped) }
def data; end
sig { returns(T.untyped) }
def header; end
sig { returns(T.untyped) }
def only_path; end
sig { returns(T.untyped) }
def path; end
sig { returns(T.nilable(T.any(::String, ::URI::Generic))) }
def referer; end
sig { returns(T.untyped) }
def revision; end
sig { returns(T.untyped) }
def revisions; end
sig { returns(T.untyped) }
def scheme; end
sig { returns(T::Hash[::Symbol, T.untyped]) }
def specs; end
sig { returns(T.untyped) }
def tag; end
sig { returns(T.untyped) }
def to_s; end
sig { returns(T.untyped) }
def trust_cert; end
sig { returns(T.untyped) }
def uri; end
sig { returns(T.nilable(T.any(::String, ::Symbol))) }
def user_agent; end
sig { returns(T.nilable(T.any(::Symbol, T::Class[T.anything]))) }
def using; end
sig { returns(T.nilable(::String)) }
def verified; end
end

View File

@ -0,0 +1,40 @@
# typed: strict
# frozen_string_literal: true
require_relative "../../../global"
require "cask/url"
module Tapioca
module Compilers
# A compiler for subclasses of Delegator.
# To add a new delegator: require it above add add it to the DELEGATIONS hash below.
class Delegators < Tapioca::Dsl::Compiler
# Mapping of delegator classes to the classes they delegate to (as defined in `__getobj__`).
DELEGATIONS = T.let({
Cask::URL => Cask::URL::DSL,
}.freeze, T::Hash[Module, Module])
ConstantType = type_member { { fixed: Module } }
sig { override.returns(T::Enumerable[Module]) }
def self.gather_constants = DELEGATIONS.keys
sig { override.void }
def decorate
root.create_path(constant) do |klass|
# Note that `Delegtor` does not subclass `Object`:
# https://github.com/ruby/ruby/blob/a6383fb/lib/delegate.rb#L41
# but we assume that we are delegating to a class that does.
klass.create_include("Kernel")
delegated = DELEGATIONS.fetch(constant)
delegated.instance_methods(false).each do |method|
signature = T::Utils.signature_for_method(delegated.instance_method(method))
# TODO: handle methods with parameters
return_type = signature&.return_type&.to_s || "T.untyped"
klass.create_method(method.to_s, return_type:)
end
end
end
end
end
end