tapioca 0.4.23 → 0.5.0
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 +19 -31
- 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 +26 -19
- data/lib/tapioca/generator.rb +127 -43
- 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 -9
- 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 +2 -0
- metadata +35 -23
- 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 -82
- data/lib/tapioca/rbi/visitor.rb +0 -21
| @@ -20,14 +20,15 @@ module Tapioca | |
| 20 20 | 
             
              # variable to type variable serializers. This allows us to associate type variables
         | 
| 21 21 | 
             
              # to the constant names that represent them, easily.
         | 
| 22 22 | 
             
              module GenericTypeRegistry
         | 
| 23 | 
            +
                TypeVariable = T.type_alias { T.any(TypeMember, TypeTemplate) }
         | 
| 23 24 | 
             
                @generic_instances = T.let(
         | 
| 24 25 | 
             
                  {},
         | 
| 25 26 | 
             
                  T::Hash[String, Module]
         | 
| 26 27 | 
             
                )
         | 
| 27 28 |  | 
| 28 29 | 
             
                @type_variables = T.let(
         | 
| 29 | 
            -
                  {},
         | 
| 30 | 
            -
                  T::Hash[ | 
| 30 | 
            +
                  {}.compare_by_identity,
         | 
| 31 | 
            +
                  T::Hash[Module, T::Hash[TypeVariable, String]]
         | 
| 31 32 | 
             
                )
         | 
| 32 33 |  | 
| 33 34 | 
             
                class << self
         | 
| @@ -49,7 +50,7 @@ module Tapioca | |
| 49 50 | 
             
                    # Build the name of the instantiated generic type,
         | 
| 50 51 | 
             
                    # something like `"Foo[X, Y, Z]"`
         | 
| 51 52 | 
             
                    type_list = types.map { |type| T::Utils.coerce(type).name }.join(", ")
         | 
| 52 | 
            -
                    name = "#{name_of(constant)}[#{type_list}]"
         | 
| 53 | 
            +
                    name = "#{Reflection.name_of(constant)}[#{type_list}]"
         | 
| 53 54 |  | 
| 54 55 | 
             
                    # Create a generic type with an overridden `name`
         | 
| 55 56 | 
             
                    # method that returns the name we constructed above.
         | 
| @@ -59,35 +60,30 @@ module Tapioca | |
| 59 60 | 
             
                    @generic_instances[name] ||= create_generic_type(constant, name)
         | 
| 60 61 | 
             
                  end
         | 
| 61 62 |  | 
| 62 | 
            -
                  sig  | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
                      type_member: T::Types::TypeVariable,
         | 
| 66 | 
            -
                      fixed: T.untyped,
         | 
| 67 | 
            -
                      lower: T.untyped,
         | 
| 68 | 
            -
                      upper: T.untyped
         | 
| 69 | 
            -
                    ).void
         | 
| 70 | 
            -
                  end
         | 
| 71 | 
            -
                  def register_type_member(constant, type_member, fixed, lower, upper)
         | 
| 72 | 
            -
                    register_type_variable(constant, :type_member, type_member, fixed, lower, upper)
         | 
| 63 | 
            +
                  sig { params(constant: Module).returns(T.nilable(T::Hash[TypeVariable, String])) }
         | 
| 64 | 
            +
                  def lookup_type_variables(constant)
         | 
| 65 | 
            +
                    @type_variables[constant]
         | 
| 73 66 | 
             
                  end
         | 
| 74 67 |  | 
| 68 | 
            +
                  # This method is called from intercepted calls to `type_member` and `type_template`.
         | 
| 69 | 
            +
                  # We get passed all the arguments to those methods, as well as the `T::Types::TypeVariable`
         | 
| 70 | 
            +
                  # instance generated by the Sorbet defined `type_member`/`type_template` call on `T::Generic`.
         | 
| 71 | 
            +
                  #
         | 
| 72 | 
            +
                  # This method creates a `String` with that data and stores it in the
         | 
| 73 | 
            +
                  # `@type_variables` lookup table, keyed by the `constant` and `type_variable`.
         | 
| 74 | 
            +
                  #
         | 
| 75 | 
            +
                  # Finally, the original `type_variable` is returned from this method, so that the caller
         | 
| 76 | 
            +
                  # can return it from the original methods as well.
         | 
| 75 77 | 
             
                  sig do
         | 
| 76 78 | 
             
                    params(
         | 
| 77 79 | 
             
                      constant: T.untyped,
         | 
| 78 | 
            -
                       | 
| 79 | 
            -
                      fixed: T.untyped,
         | 
| 80 | 
            -
                      lower: T.untyped,
         | 
| 81 | 
            -
                      upper: T.untyped
         | 
| 80 | 
            +
                      type_variable: TypeVariable,
         | 
| 82 81 | 
             
                    ).void
         | 
| 83 82 | 
             
                  end
         | 
| 84 | 
            -
                  def  | 
| 85 | 
            -
                     | 
| 86 | 
            -
                  end
         | 
| 83 | 
            +
                  def register_type_variable(constant, type_variable)
         | 
| 84 | 
            +
                    type_variables = lookup_or_initialize_type_variables(constant)
         | 
| 87 85 |  | 
| 88 | 
            -
             | 
| 89 | 
            -
                  def lookup_type_variables(constant)
         | 
| 90 | 
            -
                    @type_variables[object_id_of(constant)]
         | 
| 86 | 
            +
                    type_variables[type_variable] = type_variable.serialize
         | 
| 91 87 | 
             
                  end
         | 
| 92 88 |  | 
| 93 89 | 
             
                  private
         | 
| @@ -117,39 +113,6 @@ module Tapioca | |
| 117 113 | 
             
                    generic_type
         | 
| 118 114 | 
             
                  end
         | 
| 119 115 |  | 
| 120 | 
            -
                  # This method is called from intercepted calls to `type_member` and `type_template`.
         | 
| 121 | 
            -
                  # We get passed all the arguments to those methods, as well as the `T::Types::TypeVariable`
         | 
| 122 | 
            -
                  # instance generated by the Sorbet defined `type_member`/`type_template` call on `T::Generic`.
         | 
| 123 | 
            -
                  #
         | 
| 124 | 
            -
                  # This method creates a `String` with that data and stores it in the
         | 
| 125 | 
            -
                  # `@type_variables` lookup table, keyed by the `constant` and `type_variable`.
         | 
| 126 | 
            -
                  #
         | 
| 127 | 
            -
                  # Finally, the original `type_variable` is returned from this method, so that the caller
         | 
| 128 | 
            -
                  # can return it from the original methods as well.
         | 
| 129 | 
            -
                  sig do
         | 
| 130 | 
            -
                    params(
         | 
| 131 | 
            -
                      constant: T.untyped,
         | 
| 132 | 
            -
                      type_variable_type: T.enum([:type_member, :type_template]),
         | 
| 133 | 
            -
                      type_variable: T::Types::TypeVariable,
         | 
| 134 | 
            -
                      fixed: T.untyped,
         | 
| 135 | 
            -
                      lower: T.untyped,
         | 
| 136 | 
            -
                      upper: T.untyped
         | 
| 137 | 
            -
                    ).void
         | 
| 138 | 
            -
                  end
         | 
| 139 | 
            -
                  # rubocop:disable Metrics/ParameterLists
         | 
| 140 | 
            -
                  def register_type_variable(constant, type_variable_type, type_variable, fixed, lower, upper)
         | 
| 141 | 
            -
                    # rubocop:enable Metrics/ParameterLists
         | 
| 142 | 
            -
                    type_variables = lookup_or_initialize_type_variables(constant)
         | 
| 143 | 
            -
             | 
| 144 | 
            -
                    type_variables[object_id_of(type_variable)] = serialize_type_variable(
         | 
| 145 | 
            -
                      type_variable_type,
         | 
| 146 | 
            -
                      type_variable.variance,
         | 
| 147 | 
            -
                      fixed,
         | 
| 148 | 
            -
                      lower,
         | 
| 149 | 
            -
                      upper
         | 
| 150 | 
            -
                    )
         | 
| 151 | 
            -
                  end
         | 
| 152 | 
            -
             | 
| 153 116 | 
             
                  sig { params(constant: Class).returns(Class) }
         | 
| 154 117 | 
             
                  def create_safe_subclass(constant)
         | 
| 155 118 | 
             
                    # Lookup the "inherited" class method
         | 
| @@ -164,11 +127,9 @@ module Tapioca | |
| 164 127 | 
             
                      # Otherwise, some inherited method could be preventing us
         | 
| 165 128 | 
             
                      # from creating subclasses, so let's override it and rescue
         | 
| 166 129 | 
             
                      owner.send(:define_method, :inherited) do |s|
         | 
| 167 | 
            -
                         | 
| 168 | 
            -
             | 
| 169 | 
            -
                         | 
| 170 | 
            -
                          # Ignoring errors
         | 
| 171 | 
            -
                        end
         | 
| 130 | 
            +
                        inherited_method.call(s)
         | 
| 131 | 
            +
                      rescue
         | 
| 132 | 
            +
                        # Ignoring errors
         | 
| 172 133 | 
             
                      end
         | 
| 173 134 |  | 
| 174 135 | 
             
                      # return a subclass
         | 
| @@ -179,43 +140,9 @@ module Tapioca | |
| 179 140 | 
             
                    end
         | 
| 180 141 | 
             
                  end
         | 
| 181 142 |  | 
| 182 | 
            -
                  sig { params(constant: Module).returns(T::Hash[ | 
| 143 | 
            +
                  sig { params(constant: Module).returns(T::Hash[TypeVariable, String]) }
         | 
| 183 144 | 
             
                  def lookup_or_initialize_type_variables(constant)
         | 
| 184 | 
            -
                    @type_variables[ | 
| 185 | 
            -
                  end
         | 
| 186 | 
            -
             | 
| 187 | 
            -
                  sig do
         | 
| 188 | 
            -
                    params(
         | 
| 189 | 
            -
                      type_variable_type: Symbol,
         | 
| 190 | 
            -
                      variance: Symbol,
         | 
| 191 | 
            -
                      fixed: T.untyped,
         | 
| 192 | 
            -
                      lower: T.untyped,
         | 
| 193 | 
            -
                      upper: T.untyped
         | 
| 194 | 
            -
                    ).returns(String)
         | 
| 195 | 
            -
                  end
         | 
| 196 | 
            -
                  def serialize_type_variable(type_variable_type, variance, fixed, lower, upper)
         | 
| 197 | 
            -
                    parts = []
         | 
| 198 | 
            -
                    parts << ":#{variance}" unless variance == :invariant
         | 
| 199 | 
            -
                    parts << "fixed: #{fixed}" if fixed
         | 
| 200 | 
            -
                    parts << "lower: #{lower}" unless lower == T.untyped
         | 
| 201 | 
            -
                    parts << "upper: #{upper}" unless upper == BasicObject
         | 
| 202 | 
            -
             | 
| 203 | 
            -
                    parameters = parts.join(", ")
         | 
| 204 | 
            -
             | 
| 205 | 
            -
                    serialized = T.let(type_variable_type.to_s, String)
         | 
| 206 | 
            -
                    serialized += "(#{parameters})" unless parameters.empty?
         | 
| 207 | 
            -
             | 
| 208 | 
            -
                    serialized
         | 
| 209 | 
            -
                  end
         | 
| 210 | 
            -
             | 
| 211 | 
            -
                  sig { params(constant: Module).returns(T.nilable(String)) }
         | 
| 212 | 
            -
                  def name_of(constant)
         | 
| 213 | 
            -
                    Module.instance_method(:name).bind(constant).call
         | 
| 214 | 
            -
                  end
         | 
| 215 | 
            -
             | 
| 216 | 
            -
                  sig { params(object: BasicObject).returns(Integer) }
         | 
| 217 | 
            -
                  def object_id_of(object)
         | 
| 218 | 
            -
                    Object.instance_method(:object_id).bind(object).call
         | 
| 145 | 
            +
                    @type_variables[constant] ||= {}.compare_by_identity
         | 
| 219 146 | 
             
                  end
         | 
| 220 147 | 
             
                end
         | 
| 221 148 | 
             
              end
         | 
| @@ -0,0 +1,98 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            class ActiveRecordColumnTypeHelper
         | 
| 5 | 
            +
              extend T::Sig
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              sig { params(constant: T.class_of(ActiveRecord::Base)).void }
         | 
| 8 | 
            +
              def initialize(constant)
         | 
| 9 | 
            +
                @constant = constant
         | 
| 10 | 
            +
              end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
              sig { params(column_name: String).returns([String, String]) }
         | 
| 13 | 
            +
              def type_for(column_name)
         | 
| 14 | 
            +
                return ["T.untyped", "T.untyped"] if do_not_generate_strong_types?(@constant)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                column_type = @constant.attribute_types[column_name]
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                getter_type =
         | 
| 19 | 
            +
                  case column_type
         | 
| 20 | 
            +
                  when defined?(MoneyColumn) && MoneyColumn::ActiveRecordType
         | 
| 21 | 
            +
                    "::Money"
         | 
| 22 | 
            +
                  when ActiveRecord::Type::Integer
         | 
| 23 | 
            +
                    "::Integer"
         | 
| 24 | 
            +
                  when ActiveRecord::Type::String
         | 
| 25 | 
            +
                    "::String"
         | 
| 26 | 
            +
                  when ActiveRecord::Type::Date
         | 
| 27 | 
            +
                    "::Date"
         | 
| 28 | 
            +
                  when ActiveRecord::Type::Decimal
         | 
| 29 | 
            +
                    "::BigDecimal"
         | 
| 30 | 
            +
                  when ActiveRecord::Type::Float
         | 
| 31 | 
            +
                    "::Float"
         | 
| 32 | 
            +
                  when ActiveRecord::Type::Boolean
         | 
| 33 | 
            +
                    "T::Boolean"
         | 
| 34 | 
            +
                  when ActiveRecord::Type::DateTime, ActiveRecord::Type::Time
         | 
| 35 | 
            +
                    "::DateTime"
         | 
| 36 | 
            +
                  when ActiveRecord::AttributeMethods::TimeZoneConversion::TimeZoneConverter
         | 
| 37 | 
            +
                    "::ActiveSupport::TimeWithZone"
         | 
| 38 | 
            +
                  else
         | 
| 39 | 
            +
                    handle_unknown_type(column_type)
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                column = @constant.columns_hash[column_name]
         | 
| 43 | 
            +
                setter_type = getter_type
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                if column&.null
         | 
| 46 | 
            +
                  return ["T.nilable(#{getter_type})", "T.nilable(#{setter_type})"]
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                if column_name == @constant.primary_key ||
         | 
| 50 | 
            +
                    column_name == "created_at" ||
         | 
| 51 | 
            +
                    column_name == "updated_at"
         | 
| 52 | 
            +
                  getter_type = "T.nilable(#{getter_type})"
         | 
| 53 | 
            +
                end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                [getter_type, setter_type]
         | 
| 56 | 
            +
              end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
              private
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              sig { params(constant: Module).returns(T::Boolean) }
         | 
| 61 | 
            +
              def do_not_generate_strong_types?(constant)
         | 
| 62 | 
            +
                Object.const_defined?(:StrongTypeGeneration) &&
         | 
| 63 | 
            +
                    !(constant.singleton_class < Object.const_get(:StrongTypeGeneration))
         | 
| 64 | 
            +
              end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
              sig { params(column_type: Object).returns(String) }
         | 
| 67 | 
            +
              def handle_unknown_type(column_type)
         | 
| 68 | 
            +
                return "T.untyped" unless ActiveModel::Type::Value === column_type
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                lookup_return_type_of_method(column_type, :deserialize) ||
         | 
| 71 | 
            +
                  lookup_return_type_of_method(column_type, :cast) ||
         | 
| 72 | 
            +
                  lookup_arg_type_of_method(column_type, :serialize) ||
         | 
| 73 | 
            +
                  "T.untyped"
         | 
| 74 | 
            +
              end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
              sig { params(column_type: ActiveModel::Type::Value, method: Symbol).returns(T.nilable(String)) }
         | 
| 77 | 
            +
              def lookup_return_type_of_method(column_type, method)
         | 
| 78 | 
            +
                signature = T::Private::Methods.signature_for_method(column_type.method(method))
         | 
| 79 | 
            +
                return unless signature
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                return_type = signature.return_type
         | 
| 82 | 
            +
                return if return_type == T::Private::Types::Void || return_type == T::Private::Types::NotTyped
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                return_type.to_s
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              sig { params(column_type: ActiveModel::Type::Value, method: Symbol).returns(T.nilable(String)) }
         | 
| 88 | 
            +
              def lookup_arg_type_of_method(column_type, method)
         | 
| 89 | 
            +
                signature = T::Private::Methods.signature_for_method(column_type.method(method))
         | 
| 90 | 
            +
                return unless signature
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                # Arg types is an array [name, type] entries, so we desctructure the type of
         | 
| 93 | 
            +
                # first argument to get the first argument type
         | 
| 94 | 
            +
                _, first_argument_type = signature.arg_types.first
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                first_argument_type.to_s
         | 
| 97 | 
            +
              end
         | 
| 98 | 
            +
            end
         | 
    
        data/lib/tapioca/internal.rb
    CHANGED
    
    | @@ -4,22 +4,14 @@ | |
| 4 4 | 
             
            require "tapioca"
         | 
| 5 5 | 
             
            require "tapioca/loader"
         | 
| 6 6 | 
             
            require "tapioca/constant_locator"
         | 
| 7 | 
            -
            require "tapioca/generic_type_registry"
         | 
| 8 7 | 
             
            require "tapioca/sorbet_ext/generic_name_patch"
         | 
| 9 8 | 
             
            require "tapioca/sorbet_ext/fixed_hash_patch"
         | 
| 9 | 
            +
            require "tapioca/generic_type_registry"
         | 
| 10 10 | 
             
            require "tapioca/config"
         | 
| 11 11 | 
             
            require "tapioca/config_builder"
         | 
| 12 12 | 
             
            require "tapioca/generator"
         | 
| 13 13 | 
             
            require "tapioca/cli"
         | 
| 14 | 
            -
            require "tapioca/cli/main"
         | 
| 15 14 | 
             
            require "tapioca/gemfile"
         | 
| 16 | 
            -
            require "tapioca/rbi/model"
         | 
| 17 | 
            -
            require "tapioca/rbi/visitor"
         | 
| 18 | 
            -
            require "tapioca/rbi/rewriters/nest_singleton_methods"
         | 
| 19 | 
            -
            require "tapioca/rbi/rewriters/nest_non_public_methods"
         | 
| 20 | 
            -
            require "tapioca/rbi/rewriters/group_nodes"
         | 
| 21 | 
            -
            require "tapioca/rbi/rewriters/sort_nodes"
         | 
| 22 | 
            -
            require "tapioca/rbi/printer"
         | 
| 23 15 | 
             
            require "tapioca/compilers/sorbet"
         | 
| 24 16 | 
             
            require "tapioca/compilers/requires_compiler"
         | 
| 25 17 | 
             
            require "tapioca/compilers/symbol_table_compiler"
         | 
    
        data/lib/tapioca/loader.rb
    CHANGED
    
    | @@ -5,19 +5,13 @@ module Tapioca | |
| 5 5 | 
             
              class Loader
         | 
| 6 6 | 
             
                extend(T::Sig)
         | 
| 7 7 |  | 
| 8 | 
            -
                sig { params(gemfile: Tapioca::Gemfile).void }
         | 
| 9 | 
            -
                def  | 
| 10 | 
            -
                  @gemfile = T.let(gemfile, Tapioca::Gemfile)
         | 
| 11 | 
            -
                end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                sig { params(initialize_file: T.nilable(String), require_file: T.nilable(String)).void }
         | 
| 14 | 
            -
                def load_bundle(initialize_file, require_file)
         | 
| 8 | 
            +
                sig { params(gemfile: Tapioca::Gemfile, initialize_file: T.nilable(String), require_file: T.nilable(String)).void }
         | 
| 9 | 
            +
                def load_bundle(gemfile, initialize_file, require_file)
         | 
| 15 10 | 
             
                  require_helper(initialize_file)
         | 
| 16 11 |  | 
| 17 | 
            -
                   | 
| 18 | 
            -
                  load_rake
         | 
| 12 | 
            +
                  gemfile.require_bundle
         | 
| 19 13 |  | 
| 20 | 
            -
                   | 
| 14 | 
            +
                  load_rails_application
         | 
| 21 15 |  | 
| 22 16 | 
             
                  require_helper(require_file)
         | 
| 23 17 |  | 
| @@ -25,14 +19,11 @@ module Tapioca | |
| 25 19 | 
             
                end
         | 
| 26 20 |  | 
| 27 21 | 
             
                sig { params(environment_load: T::Boolean, eager_load: T::Boolean).void }
         | 
| 28 | 
            -
                def  | 
| 22 | 
            +
                def load_rails_application(environment_load: false, eager_load: false)
         | 
| 29 23 | 
             
                  return unless File.exist?("config/application.rb")
         | 
| 30 24 |  | 
| 31 | 
            -
                  safe_require("rails")
         | 
| 32 | 
            -
             | 
| 33 25 | 
             
                  silence_deprecations
         | 
| 34 26 |  | 
| 35 | 
            -
                  safe_require("rails/generators/test_case")
         | 
| 36 27 | 
             
                  if environment_load
         | 
| 37 28 | 
             
                    safe_require("./config/environment")
         | 
| 38 29 | 
             
                  else
         | 
| @@ -44,9 +35,6 @@ module Tapioca | |
| 44 35 |  | 
| 45 36 | 
             
                private
         | 
| 46 37 |  | 
| 47 | 
            -
                sig { returns(Tapioca::Gemfile) }
         | 
| 48 | 
            -
                attr_reader :gemfile
         | 
| 49 | 
            -
             | 
| 50 38 | 
             
                sig { params(file: T.nilable(String)).void }
         | 
| 51 39 | 
             
                def require_helper(file)
         | 
| 52 40 | 
             
                  return unless file
         | 
| @@ -56,25 +44,12 @@ module Tapioca | |
| 56 44 | 
             
                  require(file)
         | 
| 57 45 | 
             
                end
         | 
| 58 46 |  | 
| 59 | 
            -
                sig { void }
         | 
| 60 | 
            -
                def require_bundle
         | 
| 61 | 
            -
                  gemfile.require
         | 
| 62 | 
            -
                end
         | 
| 63 | 
            -
             | 
| 64 47 | 
             
                sig { returns(T::Array[T.untyped]) }
         | 
| 65 48 | 
             
                def rails_engines
         | 
| 66 | 
            -
                   | 
| 67 | 
            -
             | 
| 68 | 
            -
                  return engines unless Object.const_defined?("Rails::Engine")
         | 
| 69 | 
            -
             | 
| 70 | 
            -
                  base = Object.const_get("Rails::Engine")
         | 
| 71 | 
            -
                  ObjectSpace.each_object(base.singleton_class) do |k|
         | 
| 72 | 
            -
                    k = T.cast(k, Class)
         | 
| 73 | 
            -
                    next if k.singleton_class?
         | 
| 74 | 
            -
                    engines.unshift(k) unless k == base
         | 
| 75 | 
            -
                  end
         | 
| 49 | 
            +
                  return [] unless Object.const_defined?("Rails::Engine")
         | 
| 76 50 |  | 
| 77 | 
            -
                   | 
| 51 | 
            +
                  # We can use `Class#descendants` here, since we know Rails is loaded
         | 
| 52 | 
            +
                  Object.const_get("Rails::Engine").descendants.reject(&:abstract_railtie?)
         | 
| 78 53 | 
             
                end
         | 
| 79 54 |  | 
| 80 55 | 
             
                sig { params(path: String).void }
         | 
| @@ -84,11 +59,6 @@ module Tapioca | |
| 84 59 | 
             
                  nil
         | 
| 85 60 | 
             
                end
         | 
| 86 61 |  | 
| 87 | 
            -
                sig { void }
         | 
| 88 | 
            -
                def load_rake
         | 
| 89 | 
            -
                  safe_require("rake")
         | 
| 90 | 
            -
                end
         | 
| 91 | 
            -
             | 
| 92 62 | 
             
                sig { void }
         | 
| 93 63 | 
             
                def silence_deprecations
         | 
| 94 64 | 
             
                  # Stop any ActiveSupport Deprecations from being reported
         | 
| @@ -130,22 +100,18 @@ module Tapioca | |
| 130 100 |  | 
| 131 101 | 
             
                    engine.config.eager_load_paths.each do |load_path|
         | 
| 132 102 | 
             
                      Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
         | 
| 133 | 
            -
                         | 
| 134 | 
            -
             | 
| 135 | 
            -
                         | 
| 136 | 
            -
                          errored_files << file
         | 
| 137 | 
            -
                        end
         | 
| 103 | 
            +
                        require(file)
         | 
| 104 | 
            +
                      rescue LoadError, StandardError
         | 
| 105 | 
            +
                        errored_files << file
         | 
| 138 106 | 
             
                      end
         | 
| 139 107 | 
             
                    end
         | 
| 140 108 |  | 
| 141 109 | 
             
                    # Try files that have errored one more time
         | 
| 142 110 | 
             
                    # It might have been a load order problem
         | 
| 143 111 | 
             
                    errored_files.each do |file|
         | 
| 144 | 
            -
                       | 
| 145 | 
            -
             | 
| 146 | 
            -
                       | 
| 147 | 
            -
                        nil
         | 
| 148 | 
            -
                      end
         | 
| 112 | 
            +
                      require(file)
         | 
| 113 | 
            +
                    rescue LoadError, StandardError
         | 
| 114 | 
            +
                      nil
         | 
| 149 115 | 
             
                    end
         | 
| 150 116 | 
             
                  end
         | 
| 151 117 | 
             
                end
         | 
| @@ -0,0 +1,122 @@ | |
| 1 | 
            +
            # typed: strict
         | 
| 2 | 
            +
            # frozen_string_literal: true
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            require "rbi"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module RBI
         | 
| 7 | 
            +
              class Tree
         | 
| 8 | 
            +
                extend T::Sig
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                sig { params(constant: ::Module, block: T.nilable(T.proc.params(scope: Scope).void)).void }
         | 
| 11 | 
            +
                def create_path(constant, &block)
         | 
| 12 | 
            +
                  constant_name = Tapioca::Reflection.name_of(constant)
         | 
| 13 | 
            +
                  raise "given constant does not have a name" unless constant_name
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  instance = ::Module.const_get(constant_name)
         | 
| 16 | 
            +
                  case instance
         | 
| 17 | 
            +
                  when ::Class
         | 
| 18 | 
            +
                    create_class(constant.to_s, &block)
         | 
| 19 | 
            +
                  when ::Module
         | 
| 20 | 
            +
                    create_module(constant.to_s, &block)
         | 
| 21 | 
            +
                  else
         | 
| 22 | 
            +
                    raise "unexpected type: #{constant_name} is a #{instance.class}"
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                sig { params(name: String, block: T.nilable(T.proc.params(scope: Scope).void)).void }
         | 
| 27 | 
            +
                def create_module(name, &block)
         | 
| 28 | 
            +
                  node = create_node(RBI::Module.new(name))
         | 
| 29 | 
            +
                  block&.call(T.cast(node, RBI::Scope))
         | 
| 30 | 
            +
                end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                sig do
         | 
| 33 | 
            +
                  params(
         | 
| 34 | 
            +
                    name: String,
         | 
| 35 | 
            +
                    superclass_name: T.nilable(String),
         | 
| 36 | 
            +
                    block: T.nilable(T.proc.params(scope: RBI::Scope).void)
         | 
| 37 | 
            +
                  ).void
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
                def create_class(name, superclass_name: nil, &block)
         | 
| 40 | 
            +
                  node = create_node(RBI::Class.new(name, superclass_name: superclass_name))
         | 
| 41 | 
            +
                  block&.call(T.cast(node, RBI::Scope))
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                sig { params(name: String, value: String).void }
         | 
| 45 | 
            +
                def create_constant(name, value:)
         | 
| 46 | 
            +
                  create_node(RBI::Const.new(name, value))
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                sig { params(name: String).void }
         | 
| 50 | 
            +
                def create_include(name)
         | 
| 51 | 
            +
                  create_node(RBI::Include.new(name))
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                sig { params(name: String).void }
         | 
| 55 | 
            +
                def create_extend(name)
         | 
| 56 | 
            +
                  create_node(RBI::Extend.new(name))
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                sig { params(name: String).void }
         | 
| 60 | 
            +
                def create_mixes_in_class_methods(name)
         | 
| 61 | 
            +
                  create_node(RBI::MixesInClassMethods.new(name))
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                sig { params(name: String, value: String).void }
         | 
| 65 | 
            +
                def create_type_member(name, value: "type_member")
         | 
| 66 | 
            +
                  create_node(RBI::TypeMember.new(name, value))
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                sig do
         | 
| 70 | 
            +
                  params(
         | 
| 71 | 
            +
                    name: String,
         | 
| 72 | 
            +
                    parameters: T::Array[TypedParam],
         | 
| 73 | 
            +
                    return_type: String,
         | 
| 74 | 
            +
                    class_method: T::Boolean
         | 
| 75 | 
            +
                  ).void
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
                def create_method(name, parameters: [], return_type: "T.untyped", class_method: false)
         | 
| 78 | 
            +
                  return unless valid_method_name?(name)
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                  sig = RBI::Sig.new(return_type: return_type)
         | 
| 81 | 
            +
                  method = RBI::Method.new(name, sigs: [sig], is_singleton: class_method)
         | 
| 82 | 
            +
                  parameters.each do |param|
         | 
| 83 | 
            +
                    method << param.param
         | 
| 84 | 
            +
                    sig << RBI::SigParam.new(param.param.name, param.type)
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
                  self << method
         | 
| 87 | 
            +
                end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                private
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                SPECIAL_METHOD_NAMES = T.let(
         | 
| 92 | 
            +
                  ["!", "~", "+@", "**", "-@", "*", "/", "%", "+", "-", "<<", ">>", "&", "|", "^", "<", "<=", "=>", ">", ">=",
         | 
| 93 | 
            +
                   "==", "===", "!=", "=~", "!~", "<=>", "[]", "[]=", "`"].freeze,
         | 
| 94 | 
            +
                  T::Array[String]
         | 
| 95 | 
            +
                )
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                sig { params(name: String).returns(T::Boolean) }
         | 
| 98 | 
            +
                def valid_method_name?(name)
         | 
| 99 | 
            +
                  return true if SPECIAL_METHOD_NAMES.include?(name)
         | 
| 100 | 
            +
                  !!name.match(/^[a-zA-Z_][[:word:]]*[?!=]?$/)
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                sig { returns(T::Hash[String, RBI::Node]) }
         | 
| 104 | 
            +
                def nodes_cache
         | 
| 105 | 
            +
                  T.must(@nodes_cache ||= T.let({}, T.nilable(T::Hash[String, Node])))
         | 
| 106 | 
            +
                end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                sig { params(node: RBI::Node).returns(RBI::Node) }
         | 
| 109 | 
            +
                def create_node(node)
         | 
| 110 | 
            +
                  cached = nodes_cache[node.to_s]
         | 
| 111 | 
            +
                  return cached if cached
         | 
| 112 | 
            +
                  nodes_cache[node.to_s] = node
         | 
| 113 | 
            +
                  self << node
         | 
| 114 | 
            +
                  node
         | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
              end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
              class TypedParam < T::Struct
         | 
| 119 | 
            +
                const :param, RBI::Param
         | 
| 120 | 
            +
                const :type, String
         | 
| 121 | 
            +
              end
         | 
| 122 | 
            +
            end
         |