tapioca 0.4.25 → 0.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +14 -14
- data/README.md +2 -2
- data/Rakefile +5 -7
- data/exe/tapioca +2 -2
- data/lib/tapioca/cli.rb +256 -2
- data/lib/tapioca/compilers/dsl/aasm.rb +122 -0
- data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +52 -12
- data/lib/tapioca/compilers/dsl/action_mailer.rb +6 -9
- data/lib/tapioca/compilers/dsl/active_job.rb +8 -12
- data/lib/tapioca/compilers/dsl/active_model_attributes.rb +131 -0
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +33 -54
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +10 -105
- data/lib/tapioca/compilers/dsl/active_record_enum.rb +8 -10
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +7 -10
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +5 -8
- data/lib/tapioca/compilers/dsl/active_resource.rb +9 -37
- data/lib/tapioca/compilers/dsl/active_storage.rb +98 -0
- data/lib/tapioca/compilers/dsl/active_support_concern.rb +108 -0
- data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +13 -8
- data/lib/tapioca/compilers/dsl/base.rb +96 -82
- data/lib/tapioca/compilers/dsl/config.rb +111 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +5 -7
- data/lib/tapioca/compilers/dsl/identity_cache.rb +66 -29
- data/lib/tapioca/compilers/dsl/protobuf.rb +19 -69
- data/lib/tapioca/compilers/dsl/sidekiq_worker.rb +25 -12
- data/lib/tapioca/compilers/dsl/smart_properties.rb +21 -33
- data/lib/tapioca/compilers/dsl/state_machines.rb +56 -78
- data/lib/tapioca/compilers/dsl/url_helpers.rb +7 -10
- data/lib/tapioca/compilers/dsl_compiler.rb +22 -38
- data/lib/tapioca/compilers/requires_compiler.rb +2 -2
- data/lib/tapioca/compilers/sorbet.rb +26 -5
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +139 -154
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
- data/lib/tapioca/compilers/symbol_table_compiler.rb +1 -1
- data/lib/tapioca/compilers/todos_compiler.rb +1 -1
- data/lib/tapioca/config.rb +2 -0
- data/lib/tapioca/config_builder.rb +4 -2
- data/lib/tapioca/constant_locator.rb +6 -8
- data/lib/tapioca/gemfile.rb +10 -12
- data/lib/tapioca/generator.rb +129 -45
- data/lib/tapioca/generic_type_registry.rb +25 -98
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
- data/lib/tapioca/internal.rb +1 -10
- data/lib/tapioca/loader.rb +14 -48
- data/lib/tapioca/rbi_ext/model.rb +122 -0
- data/lib/tapioca/reflection.rb +131 -0
- data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
- data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +3 -0
- metadata +34 -22
- data/lib/tapioca/cli/main.rb +0 -146
- data/lib/tapioca/core_ext/class.rb +0 -28
- data/lib/tapioca/core_ext/string.rb +0 -18
- data/lib/tapioca/rbi/model.rb +0 -405
- data/lib/tapioca/rbi/printer.rb +0 -410
- data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
- data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
- data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
- data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -86
- data/lib/tapioca/rbi/visitor.rb +0 -21
| @@ -0,0 +1,108 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require "tapioca/compilers/sorbet"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            begin
         | 
| 7 | 
            +
              require "active_support"
         | 
| 8 | 
            +
            rescue LoadError
         | 
| 9 | 
            +
              return
         | 
| 10 | 
            +
            end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            return unless Tapioca::Compilers::Sorbet.supports?(:mixes_in_class_methods_multiple_args)
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            module Tapioca
         | 
| 15 | 
            +
              module Compilers
         | 
| 16 | 
            +
                module Dsl
         | 
| 17 | 
            +
                  # `Tapioca::Compilers::Dsl::ActiveSupportConcern` generates RBI files for classes that both `extend`
         | 
| 18 | 
            +
                  # `ActiveSupport::Concern` and `include` another class that extends `ActiveSupport::Concern`
         | 
| 19 | 
            +
                  #
         | 
| 20 | 
            +
                  # For example for the following hierarchy:
         | 
| 21 | 
            +
                  #
         | 
| 22 | 
            +
                  # ~~~rb
         | 
| 23 | 
            +
                  # # concern.rb
         | 
| 24 | 
            +
                  # module Foo
         | 
| 25 | 
            +
                  #  extend ActiveSupport::Concern
         | 
| 26 | 
            +
                  #  module ClassMethods; end
         | 
| 27 | 
            +
                  # end
         | 
| 28 | 
            +
                  #
         | 
| 29 | 
            +
                  # module Bar
         | 
| 30 | 
            +
                  #  extend ActiveSupport::Concern
         | 
| 31 | 
            +
                  #  module ClassMethods; end
         | 
| 32 | 
            +
                  #  include Foo
         | 
| 33 | 
            +
                  # end
         | 
| 34 | 
            +
                  #
         | 
| 35 | 
            +
                  # class Baz
         | 
| 36 | 
            +
                  #  include Bar
         | 
| 37 | 
            +
                  # end
         | 
| 38 | 
            +
                  # ~~~
         | 
| 39 | 
            +
                  #
         | 
| 40 | 
            +
                  # this generator will produce the RBI file `concern.rbi` with the following content:
         | 
| 41 | 
            +
                  #
         | 
| 42 | 
            +
                  # ~~~rbi
         | 
| 43 | 
            +
                  # # typed: true
         | 
| 44 | 
            +
                  # module Bar
         | 
| 45 | 
            +
                  #   mixes_in_class_methods(::Foo::ClassMethods)
         | 
| 46 | 
            +
                  # end
         | 
| 47 | 
            +
                  # ~~~
         | 
| 48 | 
            +
                  class ActiveSupportConcern < Base
         | 
| 49 | 
            +
                    extend T::Sig
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    sig { override.params(root: RBI::Tree, constant: Module).void }
         | 
| 52 | 
            +
                    def decorate(root, constant)
         | 
| 53 | 
            +
                      dependencies = linearized_dependencies_of(constant)
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                      mixed_in_class_methods = dependencies
         | 
| 56 | 
            +
                        .uniq # Deduplicate
         | 
| 57 | 
            +
                        .map do |concern| # Map to class methods module name, if exists
         | 
| 58 | 
            +
                          "#{qualified_name_of(concern)}::ClassMethods" if concern.const_defined?(:ClassMethods)
         | 
| 59 | 
            +
                        end
         | 
| 60 | 
            +
                        .compact # Remove non-existent records
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                      return if mixed_in_class_methods.empty?
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                      root.create_path(constant) do |mod|
         | 
| 65 | 
            +
                        mixed_in_class_methods.each do |mix|
         | 
| 66 | 
            +
                          mod.create_mixes_in_class_methods(mix)
         | 
| 67 | 
            +
                        end
         | 
| 68 | 
            +
                      end
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    sig { override.returns(T::Enumerable[Module]) }
         | 
| 72 | 
            +
                    def gather_constants
         | 
| 73 | 
            +
                      # Find all Modules that are:
         | 
| 74 | 
            +
                      all_modules.select do |mod|
         | 
| 75 | 
            +
                        # named (i.e. not anonymous)
         | 
| 76 | 
            +
                        name_of(mod) &&
         | 
| 77 | 
            +
                          # not singleton classes
         | 
| 78 | 
            +
                          !mod.singleton_class? &&
         | 
| 79 | 
            +
                          # extend ActiveSupport::Concern, and
         | 
| 80 | 
            +
                          mod.singleton_class < ActiveSupport::Concern &&
         | 
| 81 | 
            +
                          # have dependencies (i.e. include another concern)
         | 
| 82 | 
            +
                          !dependencies_of(mod).empty?
         | 
| 83 | 
            +
                      end
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
                    private
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    sig { params(concern: Module).returns(T::Array[Module]) }
         | 
| 89 | 
            +
                    def dependencies_of(concern)
         | 
| 90 | 
            +
                      concern.instance_variable_get(:@_dependencies)
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    sig { params(concern: Module).returns(T::Array[Module]) }
         | 
| 94 | 
            +
                    def linearized_dependencies_of(concern)
         | 
| 95 | 
            +
                      # Grab all the dependencies of the concern
         | 
| 96 | 
            +
                      dependencies = dependencies_of(concern)
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                      # Flatten this concern's dependencies and all of their dependencies
         | 
| 99 | 
            +
                      dependencies.flat_map do |dependency|
         | 
| 100 | 
            +
                        # Linearize dependencies of the current dependency,
         | 
| 101 | 
            +
                        # which, itself, is a concern
         | 
| 102 | 
            +
                        linearized_dependencies_of(dependency) << dependency
         | 
| 103 | 
            +
                      end
         | 
| 104 | 
            +
                    end
         | 
| 105 | 
            +
                  end
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
              end
         | 
| 108 | 
            +
            end
         | 
| @@ -1,10 +1,10 @@ | |
| 1 1 | 
             
            # typed: strict
         | 
| 2 2 | 
             
            # frozen_string_literal: true
         | 
| 3 3 |  | 
| 4 | 
            -
            require "parlour"
         | 
| 5 | 
            -
             | 
| 6 4 | 
             
            begin
         | 
| 7 5 | 
             
              require "active_support"
         | 
| 6 | 
            +
              # The following is needed due to https://github.com/rails/rails/pull/41610
         | 
| 7 | 
            +
              require "active_support/core_ext/module/delegation"
         | 
| 8 8 | 
             
            rescue LoadError
         | 
| 9 9 | 
             
              return
         | 
| 10 10 | 
             
            end
         | 
| @@ -65,7 +65,7 @@ module Tapioca | |
| 65 65 | 
             
                    sig do
         | 
| 66 66 | 
             
                      override
         | 
| 67 67 | 
             
                        .params(
         | 
| 68 | 
            -
                          root:  | 
| 68 | 
            +
                          root: RBI::Tree,
         | 
| 69 69 | 
             
                          constant: T.class_of(::ActiveSupport::CurrentAttributes)
         | 
| 70 70 | 
             
                        )
         | 
| 71 71 | 
             
                        .void
         | 
| @@ -75,7 +75,7 @@ module Tapioca | |
| 75 75 | 
             
                      instance_methods = instance_methods_for(constant) - dynamic_methods
         | 
| 76 76 | 
             
                      return if dynamic_methods.empty? && instance_methods.empty?
         | 
| 77 77 |  | 
| 78 | 
            -
                      root. | 
| 78 | 
            +
                      root.create_path(constant) do |current_attributes|
         | 
| 79 79 | 
             
                        dynamic_methods.each do |method|
         | 
| 80 80 | 
             
                          method = method.to_s
         | 
| 81 81 | 
             
                          # We want to generate each method both on the class
         | 
| @@ -95,7 +95,7 @@ module Tapioca | |
| 95 95 |  | 
| 96 96 | 
             
                    sig { override.returns(T::Enumerable[Module]) }
         | 
| 97 97 | 
             
                    def gather_constants
         | 
| 98 | 
            -
                      ::ActiveSupport::CurrentAttributes | 
| 98 | 
            +
                      descendants_of(::ActiveSupport::CurrentAttributes)
         | 
| 99 99 | 
             
                    end
         | 
| 100 100 |  | 
| 101 101 | 
             
                    private
         | 
| @@ -110,11 +110,16 @@ module Tapioca | |
| 110 110 | 
             
                      constant.instance_methods(false)
         | 
| 111 111 | 
             
                    end
         | 
| 112 112 |  | 
| 113 | 
            -
                    sig { params(klass:  | 
| 113 | 
            +
                    sig { params(klass: RBI::Scope, method: String, class_method: T::Boolean).void }
         | 
| 114 114 | 
             
                    def generate_method(klass, method, class_method:)
         | 
| 115 115 | 
             
                      if method.end_with?("=")
         | 
| 116 | 
            -
                        parameter =  | 
| 117 | 
            -
                        klass.create_method( | 
| 116 | 
            +
                        parameter = create_param("value", type: "T.untyped")
         | 
| 117 | 
            +
                        klass.create_method(
         | 
| 118 | 
            +
                          method,
         | 
| 119 | 
            +
                          class_method: class_method,
         | 
| 120 | 
            +
                          parameters: [parameter],
         | 
| 121 | 
            +
                          return_type: "T.untyped"
         | 
| 122 | 
            +
                        )
         | 
| 118 123 | 
             
                      else
         | 
| 119 124 | 
             
                        klass.create_method(method, class_method: class_method, return_type: "T.untyped")
         | 
| 120 125 | 
             
                      end
         | 
| @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            # typed: strict
         | 
| 2 2 | 
             
            # frozen_string_literal: true
         | 
| 3 3 |  | 
| 4 | 
            -
            require " | 
| 4 | 
            +
            require "tapioca/rbi_ext/model"
         | 
| 5 5 |  | 
| 6 6 | 
             
            module Tapioca
         | 
| 7 7 | 
             
              module Compilers
         | 
| @@ -10,6 +10,8 @@ module Tapioca | |
| 10 10 | 
             
                    extend T::Sig
         | 
| 11 11 | 
             
                    extend T::Helpers
         | 
| 12 12 |  | 
| 13 | 
            +
                    include Reflection
         | 
| 14 | 
            +
             | 
| 13 15 | 
             
                    abstract!
         | 
| 14 16 |  | 
| 15 17 | 
             
                    sig { returns(T::Set[Module]) }
         | 
| @@ -29,65 +31,111 @@ module Tapioca | |
| 29 31 | 
             
                      abstract
         | 
| 30 32 | 
             
                        .type_parameters(:T)
         | 
| 31 33 | 
             
                        .params(
         | 
| 32 | 
            -
                           | 
| 34 | 
            +
                          tree: RBI::Tree,
         | 
| 33 35 | 
             
                          constant: T.type_parameter(:T)
         | 
| 34 36 | 
             
                        )
         | 
| 35 37 | 
             
                        .void
         | 
| 36 38 | 
             
                    end
         | 
| 37 | 
            -
                    def decorate( | 
| 39 | 
            +
                    def decorate(tree, constant); end
         | 
| 38 40 |  | 
| 39 41 | 
             
                    sig { abstract.returns(T::Enumerable[Module]) }
         | 
| 40 42 | 
             
                    def gather_constants; end
         | 
| 41 43 |  | 
| 42 44 | 
             
                    private
         | 
| 43 45 |  | 
| 44 | 
            -
                     | 
| 45 | 
            -
             | 
| 46 | 
            -
                      T:: | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
                    sig { params(name: String).returns(T::Boolean) }
         | 
| 50 | 
            -
                    def valid_method_name?(name)
         | 
| 51 | 
            -
                      return true if SPECIAL_METHOD_NAMES.include?(name)
         | 
| 52 | 
            -
                      !!name.match(/^[a-zA-Z_][[:word:]]*[?!=]?$/)
         | 
| 46 | 
            +
                    sig { returns(T::Enumerable[Class]) }
         | 
| 47 | 
            +
                    def all_classes
         | 
| 48 | 
            +
                      @all_classes = T.let(@all_classes, T.nilable(T::Enumerable[Class]))
         | 
| 49 | 
            +
                      @all_classes ||= T.cast(ObjectSpace.each_object(Class), T::Enumerable[Class]).each
         | 
| 53 50 | 
             
                    end
         | 
| 54 51 |  | 
| 55 | 
            -
                    sig  | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
                        options: T::Hash[T.untyped, T.untyped]
         | 
| 60 | 
            -
                      ).void
         | 
| 61 | 
            -
                    end
         | 
| 62 | 
            -
                    def create_method(namespace, name, options = {})
         | 
| 63 | 
            -
                      return unless valid_method_name?(name)
         | 
| 64 | 
            -
                      T.unsafe(namespace).create_method(name, **options)
         | 
| 52 | 
            +
                    sig { returns(T::Enumerable[Module]) }
         | 
| 53 | 
            +
                    def all_modules
         | 
| 54 | 
            +
                      @all_modules = T.let(@all_modules, T.nilable(T::Enumerable[Module]))
         | 
| 55 | 
            +
                      @all_modules ||= T.cast(ObjectSpace.each_object(Module), T::Enumerable[Module]).each
         | 
| 65 56 | 
             
                    end
         | 
| 66 57 |  | 
| 67 | 
            -
                    #  | 
| 58 | 
            +
                    # Get the types of each parameter from a method signature
         | 
| 68 59 | 
             
                    sig do
         | 
| 69 60 | 
             
                      params(
         | 
| 70 | 
            -
                        namespace: Parlour::RbiGenerator::Namespace,
         | 
| 71 61 | 
             
                        method_def: T.any(Method, UnboundMethod),
         | 
| 72 | 
            -
                         | 
| 73 | 
            -
                      ). | 
| 62 | 
            +
                        signature: T.untyped # as `T::Private::Methods::Signature` is private
         | 
| 63 | 
            +
                      ).returns(T::Array[String])
         | 
| 74 64 | 
             
                    end
         | 
| 75 | 
            -
                    def  | 
| 76 | 
            -
                       | 
| 77 | 
            -
             | 
| 65 | 
            +
                    def parameters_types_from_signature(method_def, signature)
         | 
| 66 | 
            +
                      params = T.let([], T::Array[String])
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                      return method_def.parameters.map { "T.untyped" } unless signature
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                      # parameters types
         | 
| 71 | 
            +
                      signature.arg_types.each { |arg_type| params << arg_type[1].to_s }
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                      # keyword parameters types
         | 
| 74 | 
            +
                      signature.kwarg_types.each { |_, kwarg_type| params << kwarg_type.to_s }
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                      # rest parameter type
         | 
| 77 | 
            +
                      params << signature.rest_type.to_s if signature.has_rest
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                      # special case `.void` in a proc
         | 
| 80 | 
            +
                      unless signature.block_name.nil?
         | 
| 81 | 
            +
                        params << signature.block_type.to_s.gsub("returns(<VOID>)", "void")
         | 
| 82 | 
            +
                      end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                      params
         | 
| 85 | 
            +
                    end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                    sig { params(scope: RBI::Scope, method_def: T.any(Method, UnboundMethod), class_method: T::Boolean).void }
         | 
| 88 | 
            +
                    def create_method_from_def(scope, method_def, class_method: false)
         | 
| 89 | 
            +
                      scope.create_method(
         | 
| 78 90 | 
             
                        method_def.name.to_s,
         | 
| 79 | 
            -
                        parameters:  | 
| 80 | 
            -
                        return_type:  | 
| 91 | 
            +
                        parameters: compile_method_parameters_to_rbi(method_def),
         | 
| 92 | 
            +
                        return_type: compile_method_return_type_to_rbi(method_def),
         | 
| 81 93 | 
             
                        class_method: class_method
         | 
| 82 94 | 
             
                      )
         | 
| 83 95 | 
             
                    end
         | 
| 84 96 |  | 
| 85 | 
            -
                     | 
| 86 | 
            -
                     | 
| 87 | 
            -
                       | 
| 88 | 
            -
             | 
| 97 | 
            +
                    sig { params(name: String, type: String).returns(RBI::TypedParam) }
         | 
| 98 | 
            +
                    def create_param(name, type:)
         | 
| 99 | 
            +
                      create_typed_param(RBI::Param.new(name), type)
         | 
| 100 | 
            +
                    end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                    sig { params(name: String, type: String, default: String).returns(RBI::TypedParam) }
         | 
| 103 | 
            +
                    def create_opt_param(name, type:, default:)
         | 
| 104 | 
            +
                      create_typed_param(RBI::OptParam.new(name, default), type)
         | 
| 105 | 
            +
                    end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                    sig { params(name: String, type: String).returns(RBI::TypedParam) }
         | 
| 108 | 
            +
                    def create_rest_param(name, type:)
         | 
| 109 | 
            +
                      create_typed_param(RBI::RestParam.new(name), type)
         | 
| 110 | 
            +
                    end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                    sig { params(name: String, type: String).returns(RBI::TypedParam) }
         | 
| 113 | 
            +
                    def create_kw_param(name, type:)
         | 
| 114 | 
            +
                      create_typed_param(RBI::KwParam.new(name), type)
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                    sig { params(name: String, type: String, default: String).returns(RBI::TypedParam) }
         | 
| 118 | 
            +
                    def create_kw_opt_param(name, type:, default:)
         | 
| 119 | 
            +
                      create_typed_param(RBI::KwOptParam.new(name, default), type)
         | 
| 89 120 | 
             
                    end
         | 
| 90 | 
            -
             | 
| 121 | 
            +
             | 
| 122 | 
            +
                    sig { params(name: String, type: String).returns(RBI::TypedParam) }
         | 
| 123 | 
            +
                    def create_kw_rest_param(name, type:)
         | 
| 124 | 
            +
                      create_typed_param(RBI::KwRestParam.new(name), type)
         | 
| 125 | 
            +
                    end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                    sig { params(name: String, type: String).returns(RBI::TypedParam) }
         | 
| 128 | 
            +
                    def create_block_param(name, type:)
         | 
| 129 | 
            +
                      create_typed_param(RBI::BlockParam.new(name), type)
         | 
| 130 | 
            +
                    end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                    sig { params(param: RBI::Param, type: String).returns(RBI::TypedParam) }
         | 
| 133 | 
            +
                    def create_typed_param(param, type)
         | 
| 134 | 
            +
                      RBI::TypedParam.new(param: param, type: type)
         | 
| 135 | 
            +
                    end
         | 
| 136 | 
            +
             | 
| 137 | 
            +
                    sig { params(method_def: T.any(Method, UnboundMethod)).returns(T::Array[RBI::TypedParam]) }
         | 
| 138 | 
            +
                    def compile_method_parameters_to_rbi(method_def)
         | 
| 91 139 | 
             
                      signature = T::Private::Methods.signature_for_method(method_def)
         | 
| 92 140 | 
             
                      method_def = signature.nil? ? method_def : signature.method
         | 
| 93 141 | 
             
                      method_types = parameters_types_from_signature(method_def, signature)
         | 
| @@ -97,72 +145,38 @@ module Tapioca | |
| 97 145 |  | 
| 98 146 | 
             
                        name ||= fallback_arg_name
         | 
| 99 147 | 
             
                        name = name.to_s.gsub(/&|\*/, fallback_arg_name) # avoid incorrect names from `delegate`
         | 
| 100 | 
            -
                        method_type = method_types[index]
         | 
| 148 | 
            +
                        method_type = T.must(method_types[index])
         | 
| 101 149 |  | 
| 102 150 | 
             
                        case type
         | 
| 103 151 | 
             
                        when :req
         | 
| 104 | 
            -
                           | 
| 152 | 
            +
                          create_param(name, type: method_type)
         | 
| 105 153 | 
             
                        when :opt
         | 
| 106 | 
            -
                           | 
| 154 | 
            +
                          create_opt_param(name, type: method_type, default: "T.unsafe(nil)")
         | 
| 107 155 | 
             
                        when :rest
         | 
| 108 | 
            -
                           | 
| 156 | 
            +
                          create_rest_param(name, type: method_type)
         | 
| 109 157 | 
             
                        when :keyreq
         | 
| 110 | 
            -
                           | 
| 158 | 
            +
                          create_kw_param(name, type: method_type)
         | 
| 111 159 | 
             
                        when :key
         | 
| 112 | 
            -
                           | 
| 160 | 
            +
                          create_kw_opt_param(name, type: method_type, default: "T.unsafe(nil)")
         | 
| 113 161 | 
             
                        when :keyrest
         | 
| 114 | 
            -
                           | 
| 162 | 
            +
                          create_kw_rest_param(name, type: method_type)
         | 
| 115 163 | 
             
                        when :block
         | 
| 116 | 
            -
                           | 
| 164 | 
            +
                          create_block_param(name, type: method_type)
         | 
| 117 165 | 
             
                        else
         | 
| 118 166 | 
             
                          raise "Unknown type `#{type}`."
         | 
| 119 167 | 
             
                        end
         | 
| 120 168 | 
             
                      end
         | 
| 121 169 | 
             
                    end
         | 
| 122 170 |  | 
| 123 | 
            -
                     | 
| 124 | 
            -
                     | 
| 125 | 
            -
                      params(method_def: T.any(Method, UnboundMethod))
         | 
| 126 | 
            -
                        .returns(T.nilable(String))
         | 
| 127 | 
            -
                    end
         | 
| 128 | 
            -
                    def compile_method_return_type_to_parlour(method_def)
         | 
| 171 | 
            +
                    sig { params(method_def: T.any(Method, UnboundMethod)).returns(String) }
         | 
| 172 | 
            +
                    def compile_method_return_type_to_rbi(method_def)
         | 
| 129 173 | 
             
                      signature = T::Private::Methods.signature_for_method(method_def)
         | 
| 130 | 
            -
                      return_type = signature.nil? ?  | 
| 131 | 
            -
                       | 
| 132 | 
            -
                      return_type = nil if return_type == "<VOID>"
         | 
| 174 | 
            +
                      return_type = signature.nil? ? "T.untyped" : name_of_type(signature.return_type)
         | 
| 175 | 
            +
                      return_type = "void" if return_type == "<VOID>"
         | 
| 133 176 | 
             
                      # Map <NOT-TYPED> to `T.untyped`
         | 
| 134 177 | 
             
                      return_type = "T.untyped" if return_type == "<NOT-TYPED>"
         | 
| 135 178 | 
             
                      return_type
         | 
| 136 179 | 
             
                    end
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                    # Get the types of each parameter from a method signature
         | 
| 139 | 
            -
                    sig do
         | 
| 140 | 
            -
                      params(
         | 
| 141 | 
            -
                        method_def: T.any(Method, UnboundMethod),
         | 
| 142 | 
            -
                        signature: T.untyped # as `T::Private::Methods::Signature` is private
         | 
| 143 | 
            -
                      ).returns(T::Array[String])
         | 
| 144 | 
            -
                    end
         | 
| 145 | 
            -
                    def parameters_types_from_signature(method_def, signature)
         | 
| 146 | 
            -
                      params = T.let([], T::Array[String])
         | 
| 147 | 
            -
             | 
| 148 | 
            -
                      return method_def.parameters.map { 'T.untyped' } unless signature
         | 
| 149 | 
            -
             | 
| 150 | 
            -
                      # parameters types
         | 
| 151 | 
            -
                      signature.arg_types.each { |arg_type| params << arg_type[1].to_s }
         | 
| 152 | 
            -
             | 
| 153 | 
            -
                      # keyword parameters types
         | 
| 154 | 
            -
                      signature.kwarg_types.each { |_, kwarg_type| params << kwarg_type.to_s }
         | 
| 155 | 
            -
             | 
| 156 | 
            -
                      # rest parameter type
         | 
| 157 | 
            -
                      params << signature.rest_type.to_s if signature.has_rest
         | 
| 158 | 
            -
             | 
| 159 | 
            -
                      # special case `.void` in a proc
         | 
| 160 | 
            -
                      unless signature.block_name.nil?
         | 
| 161 | 
            -
                        params << signature.block_type.to_s.gsub('returns(<VOID>)', 'void')
         | 
| 162 | 
            -
                      end
         | 
| 163 | 
            -
             | 
| 164 | 
            -
                      params
         | 
| 165 | 
            -
                    end
         | 
| 166 180 | 
             
                  end
         | 
| 167 181 | 
             
                end
         | 
| 168 182 | 
             
              end
         | 
| @@ -0,0 +1,111 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            begin
         | 
| 5 | 
            +
              require "config"
         | 
| 6 | 
            +
            rescue LoadError
         | 
| 7 | 
            +
              return
         | 
| 8 | 
            +
            end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            module Tapioca
         | 
| 11 | 
            +
              module Compilers
         | 
| 12 | 
            +
                module Dsl
         | 
| 13 | 
            +
                  # `Tapioca::Compilers::Dsl::Config` generates RBI files for classes generated by the
         | 
| 14 | 
            +
                  # [`config`](https://github.com/rubyconfig/config) gem.
         | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  # The gem creates a `Config::Options` instance based on the settings files and/or
         | 
| 17 | 
            +
                  # env variables. It then assigns this instance to a constant with a configurable name,
         | 
| 18 | 
            +
                  # by default `Settings`. Application code uses methods on this constant to read off
         | 
| 19 | 
            +
                  # config values.
         | 
| 20 | 
            +
                  #
         | 
| 21 | 
            +
                  # For a setting file like the following:
         | 
| 22 | 
            +
                  # ```yaml
         | 
| 23 | 
            +
                  # ---
         | 
| 24 | 
            +
                  # github:
         | 
| 25 | 
            +
                  #   token: 12345
         | 
| 26 | 
            +
                  #   client_id: 54321
         | 
| 27 | 
            +
                  #   client_secret: super_secret
         | 
| 28 | 
            +
                  # ```
         | 
| 29 | 
            +
                  # and a `Config` setup like:
         | 
| 30 | 
            +
                  # ```ruby
         | 
| 31 | 
            +
                  # Config.setup do |config|
         | 
| 32 | 
            +
                  #   config.const_name = "AppSettings"
         | 
| 33 | 
            +
                  # end
         | 
| 34 | 
            +
                  # ```
         | 
| 35 | 
            +
                  # this generator will produce the following RBI file:
         | 
| 36 | 
            +
                  # ```rbi
         | 
| 37 | 
            +
                  # AppSettings = T.let(T.unsafe(nil), AppSettingsConfigOptions)
         | 
| 38 | 
            +
                  #
         | 
| 39 | 
            +
                  # class AppSettingsConfigOptions < ::Config::Options
         | 
| 40 | 
            +
                  #   sig { returns(T.untyped) }
         | 
| 41 | 
            +
                  #   def github; end
         | 
| 42 | 
            +
                  #
         | 
| 43 | 
            +
                  #   sig { params(value: T.untyped).returns(T.untyped) }
         | 
| 44 | 
            +
                  #   def github=(value); end
         | 
| 45 | 
            +
                  # end
         | 
| 46 | 
            +
                  # ```
         | 
| 47 | 
            +
                  class Config < Base
         | 
| 48 | 
            +
                    extend T::Sig
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    CONFIG_OPTIONS_SUFFIX = "ConfigOptions"
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    sig { override.params(root: RBI::Tree, constant: Module).void }
         | 
| 53 | 
            +
                    def decorate(root, constant)
         | 
| 54 | 
            +
                      # The constant we are given is the specialized config options type
         | 
| 55 | 
            +
                      option_class_name = constant.name
         | 
| 56 | 
            +
                      return unless option_class_name
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                      # Grab the config constant name and the actual config constant
         | 
| 59 | 
            +
                      config_constant_name = option_class_name
         | 
| 60 | 
            +
                        .gsub(/#{CONFIG_OPTIONS_SUFFIX}$/, "")
         | 
| 61 | 
            +
                      config_constant = Object.const_get(config_constant_name)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                      # Look up method names from the keys of the config constant
         | 
| 64 | 
            +
                      method_names = config_constant.keys
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                      return if method_names.empty?
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                      root.create_constant(config_constant_name, value: "T.let(T.unsafe(nil), #{option_class_name})")
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                      root.create_class(option_class_name, superclass_name: "::Config::Options") do |mod|
         | 
| 71 | 
            +
                        # We need this to be generic only becuase `Config::Options` is an
         | 
| 72 | 
            +
                        # enumerable and, thus, needs to redeclare the `Elem` type member.
         | 
| 73 | 
            +
                        #
         | 
| 74 | 
            +
                        # We declare it as a fixed member of `T.untyped` so that if anyone
         | 
| 75 | 
            +
                        # enumerates the entries, we don't make any assumptions about their
         | 
| 76 | 
            +
                        # types.
         | 
| 77 | 
            +
                        mod.create_extend("T::Generic")
         | 
| 78 | 
            +
                        mod.create_type_member("Elem", value: "type_member(fixed: T.untyped)")
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                        method_names.each do |method_name|
         | 
| 81 | 
            +
                          # Create getter method
         | 
| 82 | 
            +
                          mod.create_method(
         | 
| 83 | 
            +
                            method_name.to_s,
         | 
| 84 | 
            +
                            return_type: "T.untyped"
         | 
| 85 | 
            +
                          )
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                          # Create setter method
         | 
| 88 | 
            +
                          mod.create_method(
         | 
| 89 | 
            +
                            "#{method_name}=",
         | 
| 90 | 
            +
                            parameters: [create_param("value", type: "T.untyped")],
         | 
| 91 | 
            +
                            return_type: "T.untyped"
         | 
| 92 | 
            +
                          )
         | 
| 93 | 
            +
                        end
         | 
| 94 | 
            +
                      end
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                    sig { override.returns(T::Enumerable[Module]) }
         | 
| 98 | 
            +
                    def gather_constants
         | 
| 99 | 
            +
                      name = ::Config.const_name
         | 
| 100 | 
            +
                      return [] unless Object.const_defined?(name)
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                      config_object = Object.const_get(name)
         | 
| 103 | 
            +
                      options_class_name = "#{name}#{CONFIG_OPTIONS_SUFFIX}"
         | 
| 104 | 
            +
                      Object.const_set(options_class_name, config_object.singleton_class)
         | 
| 105 | 
            +
             | 
| 106 | 
            +
                      Array(config_object.singleton_class)
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
                end
         | 
| 110 | 
            +
              end
         | 
| 111 | 
            +
            end
         |