tapioca 0.16.2 → 0.16.4
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/lib/tapioca/dsl/compiler.rb +35 -2
- data/lib/tapioca/dsl/compilers/aasm.rb +41 -1
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +32 -5
- data/lib/tapioca/dsl/pipeline.rb +1 -0
- data/lib/tapioca/helpers/rbi_helper.rb +2 -26
- data/lib/tapioca/helpers/source_uri.rb +8 -1
- data/lib/tapioca/internal.rb +1 -0
- data/lib/tapioca/runtime/reflection.rb +8 -2
- data/lib/tapioca/version.rb +1 -1
- metadata +3 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 80c01f44b0600c3a471756da73e43322f23a951c8f3ebc15c922173a0ccf4d3c
         | 
| 4 | 
            +
              data.tar.gz: c778cfa78b0494513f5ad329e432c534486c3ba75fcf83969462a874c7dc7654
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: f12a7fbe0d64109a5de03d8a98f564413bee79dc0db1ced8c1eaeae8dae73e68c73e8e76a188a06ddfefa333b11024d606359b7f706d13e15dacea081b0a19ea
         | 
| 7 | 
            +
              data.tar.gz: 99ef8194c8dab5e776b591992aefa8b59b0b8e2d8927caecfc68d63bf7bb4c7687a92af772c6f8ac5fcfbdd8ebe1f9c1b748a2334b8207d233939d35d02d2b26
         | 
    
        data/lib/tapioca/dsl/compiler.rb
    CHANGED
    
    | @@ -25,6 +25,8 @@ module Tapioca | |
| 25 25 | 
             
                  sig { returns(T::Hash[String, T.untyped]) }
         | 
| 26 26 | 
             
                  attr_reader :options
         | 
| 27 27 |  | 
| 28 | 
            +
                  @@requested_constants = T.let([], T::Array[Module]) # rubocop:disable Style/ClassVars
         | 
| 29 | 
            +
             | 
| 28 30 | 
             
                  class << self
         | 
| 29 31 | 
             
                    extend T::Sig
         | 
| 30 32 |  | 
| @@ -44,12 +46,39 @@ module Tapioca | |
| 44 46 | 
             
                      )
         | 
| 45 47 | 
             
                    end
         | 
| 46 48 |  | 
| 49 | 
            +
                    sig { params(constants: T::Array[Module]).void }
         | 
| 50 | 
            +
                    def requested_constants=(constants)
         | 
| 51 | 
            +
                      @@requested_constants = constants # rubocop:disable Style/ClassVars
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
             | 
| 47 54 | 
             
                    private
         | 
| 48 55 |  | 
| 56 | 
            +
                    sig do
         | 
| 57 | 
            +
                      type_parameters(:U)
         | 
| 58 | 
            +
                        .params(klass: T.all(T::Class[T.anything], T.type_parameter(:U)))
         | 
| 59 | 
            +
                        .returns(T::Array[T.type_parameter(:U)])
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
                    def descendants_of(klass)
         | 
| 62 | 
            +
                      if @@requested_constants.any?
         | 
| 63 | 
            +
                        T.cast(
         | 
| 64 | 
            +
                          @@requested_constants.select do |k|
         | 
| 65 | 
            +
                            k < klass && !k.singleton_class?
         | 
| 66 | 
            +
                          end,
         | 
| 67 | 
            +
                          T::Array[T.type_parameter(:U)],
         | 
| 68 | 
            +
                        )
         | 
| 69 | 
            +
                      else
         | 
| 70 | 
            +
                        super
         | 
| 71 | 
            +
                      end
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 49 74 | 
             
                    sig { returns(T::Enumerable[T::Class[T.anything]]) }
         | 
| 50 75 | 
             
                    def all_classes
         | 
| 51 76 | 
             
                      @all_classes ||= T.let(
         | 
| 52 | 
            -
                         | 
| 77 | 
            +
                        if @@requested_constants.any?
         | 
| 78 | 
            +
                          @@requested_constants.grep(Class)
         | 
| 79 | 
            +
                        else
         | 
| 80 | 
            +
                          ObjectSpace.each_object(Class)
         | 
| 81 | 
            +
                        end,
         | 
| 53 82 | 
             
                        T.nilable(T::Enumerable[T::Class[T.anything]]),
         | 
| 54 83 | 
             
                      )
         | 
| 55 84 | 
             
                    end
         | 
| @@ -57,7 +86,11 @@ module Tapioca | |
| 57 86 | 
             
                    sig { returns(T::Enumerable[Module]) }
         | 
| 58 87 | 
             
                    def all_modules
         | 
| 59 88 | 
             
                      @all_modules ||= T.let(
         | 
| 60 | 
            -
                         | 
| 89 | 
            +
                        if @@requested_constants.any?
         | 
| 90 | 
            +
                          @@requested_constants.select { |k| k.is_a?(Module) }
         | 
| 91 | 
            +
                        else
         | 
| 92 | 
            +
                          ObjectSpace.each_object(Module)
         | 
| 93 | 
            +
                        end,
         | 
| 61 94 | 
             
                        T.nilable(T::Enumerable[Module]),
         | 
| 62 95 | 
             
                      )
         | 
| 63 96 | 
             
                    end
         | 
| @@ -69,6 +69,17 @@ module Tapioca | |
| 69 69 | 
             
                        T::Array[String],
         | 
| 70 70 | 
             
                      )
         | 
| 71 71 |  | 
| 72 | 
            +
                    TRANSITION_CALLBACKS =
         | 
| 73 | 
            +
                      T.let(
         | 
| 74 | 
            +
                        [
         | 
| 75 | 
            +
                          "on_transition",
         | 
| 76 | 
            +
                          "guard",
         | 
| 77 | 
            +
                          "after",
         | 
| 78 | 
            +
                          "success",
         | 
| 79 | 
            +
                        ].freeze,
         | 
| 80 | 
            +
                        T::Array[String],
         | 
| 81 | 
            +
                      )
         | 
| 82 | 
            +
             | 
| 72 83 | 
             
                    ConstantType = type_member { { fixed: T.all(T::Class[::AASM], ::AASM::ClassMethods) } }
         | 
| 73 84 |  | 
| 74 85 | 
             
                    sig { override.void }
         | 
| @@ -162,8 +173,37 @@ module Tapioca | |
| 162 173 | 
             
                                method,
         | 
| 163 174 | 
             
                                parameters: [
         | 
| 164 175 | 
             
                                  create_opt_param("symbol", type: "T.nilable(Symbol)", default: "nil"),
         | 
| 165 | 
            -
                                  create_block_param( | 
| 176 | 
            +
                                  create_block_param(
         | 
| 177 | 
            +
                                    "block",
         | 
| 178 | 
            +
                                    type: "T.nilable(T.proc.bind(#{constant_name}).params(opts: T.untyped).void)",
         | 
| 179 | 
            +
                                  ),
         | 
| 180 | 
            +
                                ],
         | 
| 181 | 
            +
                              )
         | 
| 182 | 
            +
                            end
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                            event.create_method(
         | 
| 185 | 
            +
                              "transitions",
         | 
| 186 | 
            +
                              parameters: [
         | 
| 187 | 
            +
                                create_opt_param("definitions", default: "nil", type: "T.untyped"),
         | 
| 188 | 
            +
                                create_block_param("block", type: "T.nilable(T.proc.bind(PrivateAASMTransition).void)"),
         | 
| 189 | 
            +
                              ],
         | 
| 190 | 
            +
                            )
         | 
| 191 | 
            +
                          end
         | 
| 192 | 
            +
             | 
| 193 | 
            +
                          machine.create_class("PrivateAASMTransition", superclass_name: "AASM::Core::Transition") do |transition|
         | 
| 194 | 
            +
                            TRANSITION_CALLBACKS.each do |method|
         | 
| 195 | 
            +
                              return_type = "T.untyped"
         | 
| 196 | 
            +
                              return_type = "T::Boolean" if method == "guard"
         | 
| 197 | 
            +
             | 
| 198 | 
            +
                              transition.create_method(
         | 
| 199 | 
            +
                                method,
         | 
| 200 | 
            +
                                parameters: [
         | 
| 201 | 
            +
                                  create_block_param(
         | 
| 202 | 
            +
                                    "block",
         | 
| 203 | 
            +
                                    type: "T.nilable(T.proc.bind(#{constant_name}).params(opts: T.untyped).void)",
         | 
| 204 | 
            +
                                  ),
         | 
| 166 205 | 
             
                                ],
         | 
| 206 | 
            +
                                return_type: return_type,
         | 
| 167 207 | 
             
                              )
         | 
| 168 208 | 
             
                            end
         | 
| 169 209 | 
             
                          end
         | 
| @@ -88,17 +88,24 @@ module Tapioca | |
| 88 88 | 
             
                    sig { returns([String, String]) }
         | 
| 89 89 | 
             
                    def id_type
         | 
| 90 90 | 
             
                      if @constant.respond_to?(:composite_primary_key?) && T.unsafe(@constant).composite_primary_key?
         | 
| 91 | 
            -
                        @constant.primary_key | 
| 92 | 
            -
             | 
| 93 | 
            -
                         | 
| 94 | 
            -
             | 
| 91 | 
            +
                        primary_key_columns = @constant.primary_key
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                        getters = []
         | 
| 94 | 
            +
                        setters = []
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                        primary_key_columns.each do |column|
         | 
| 97 | 
            +
                          getter, setter = column_type_for(column)
         | 
| 98 | 
            +
                          getters << getter
         | 
| 99 | 
            +
                          setters << setter
         | 
| 95 100 | 
             
                        end
         | 
| 101 | 
            +
             | 
| 102 | 
            +
                        ["[#{getters.join(", ")}]", "[#{setters.join(", ")}]"]
         | 
| 96 103 | 
             
                      else
         | 
| 97 104 | 
             
                        column_type_for(@constant.primary_key)
         | 
| 98 105 | 
             
                      end
         | 
| 99 106 | 
             
                    end
         | 
| 100 107 |  | 
| 101 | 
            -
                    sig { params(column_name: String).returns([String, String]) }
         | 
| 108 | 
            +
                    sig { params(column_name: T.nilable(String)).returns([String, String]) }
         | 
| 102 109 | 
             
                    def column_type_for(column_name)
         | 
| 103 110 | 
             
                      return ["T.untyped", "T.untyped"] if @column_type_option.untyped?
         | 
| 104 111 |  | 
| @@ -179,11 +186,31 @@ module Tapioca | |
| 179 186 | 
             
                               ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Hstore === type
         | 
| 180 187 | 
             
                           }
         | 
| 181 188 | 
             
                        "T::Hash[::String, ::String]"
         | 
| 189 | 
            +
                      when ->(type) {
         | 
| 190 | 
            +
                             defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Interval) &&
         | 
| 191 | 
            +
                               ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Interval === type
         | 
| 192 | 
            +
                           }
         | 
| 193 | 
            +
                        "::ActiveSupport::Duration"
         | 
| 182 194 | 
             
                      when ->(type) {
         | 
| 183 195 | 
             
                             defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array) &&
         | 
| 184 196 | 
             
                               ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Array === type
         | 
| 185 197 | 
             
                           }
         | 
| 186 198 | 
             
                        "T::Array[#{type_for_activerecord_value(column_type.subtype, column_nullability:)}]"
         | 
| 199 | 
            +
                      when ->(type) {
         | 
| 200 | 
            +
                             defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bit) &&
         | 
| 201 | 
            +
                               ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Bit === type
         | 
| 202 | 
            +
                           }
         | 
| 203 | 
            +
                        "::String"
         | 
| 204 | 
            +
                      when ->(type) {
         | 
| 205 | 
            +
                             defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::BitVarying) &&
         | 
| 206 | 
            +
                               ActiveRecord::ConnectionAdapters::PostgreSQL::OID::BitVarying === type
         | 
| 207 | 
            +
                           }
         | 
| 208 | 
            +
                        "::String"
         | 
| 209 | 
            +
                      when ->(type) {
         | 
| 210 | 
            +
                             defined?(ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range) &&
         | 
| 211 | 
            +
                               ActiveRecord::ConnectionAdapters::PostgreSQL::OID::Range === type
         | 
| 212 | 
            +
                           }
         | 
| 213 | 
            +
                        "T::Range[#{type_for_activerecord_value(column_type.subtype, column_nullability:)}]"
         | 
| 187 214 | 
             
                      else
         | 
| 188 215 | 
             
                        as_non_nilable_if_persisted_and_not_nullable(
         | 
| 189 216 | 
             
                          ActiveModelTypeHelper.type_for(column_type),
         | 
    
        data/lib/tapioca/dsl/pipeline.rb
    CHANGED
    
    | @@ -145,6 +145,7 @@ module Tapioca | |
| 145 145 | 
             
                    ).returns(T::Set[Module])
         | 
| 146 146 | 
             
                  end
         | 
| 147 147 | 
             
                  def gather_constants(requested_constants, requested_paths, skipped_constants)
         | 
| 148 | 
            +
                    Compiler.requested_constants = requested_constants
         | 
| 148 149 | 
             
                    constants = Set.new.compare_by_identity
         | 
| 149 150 | 
             
                    active_compilers.each do |compiler|
         | 
| 150 151 | 
             
                      constants.merge(compiler.processable_constants)
         | 
| @@ -107,36 +107,12 @@ module Tapioca | |
| 107 107 |  | 
| 108 108 | 
             
                sig { params(name: String).returns(T::Boolean) }
         | 
| 109 109 | 
             
                def valid_method_name?(name)
         | 
| 110 | 
            -
                   | 
| 111 | 
            -
                  iseq = RubyVM::InstructionSequence.compile("def #{name}; end", nil, nil, 0, false)
         | 
| 112 | 
            -
                  # pull out the first operation in the instruction sequence and its first argument
         | 
| 113 | 
            -
                  op, arg, _data = iseq.to_a.dig(-1, 0)
         | 
| 114 | 
            -
                  # make sure that the operation is a method definition and the method that was
         | 
| 115 | 
            -
                  # defined has the expected name, for example, for `def !foo; end` we don't get
         | 
| 116 | 
            -
                  # a syntax error but instead get a method defined as `"foo"`
         | 
| 117 | 
            -
                  op == :definemethod && arg == name.to_sym
         | 
| 118 | 
            -
                rescue SyntaxError
         | 
| 119 | 
            -
                  false
         | 
| 110 | 
            +
                  Prism.parse_success?("def self.#{name}(a); end")
         | 
| 120 111 | 
             
                end
         | 
| 121 112 |  | 
| 122 113 | 
             
                sig { params(name: String).returns(T::Boolean) }
         | 
| 123 114 | 
             
                def valid_parameter_name?(name)
         | 
| 124 | 
            -
                  sentinel_method_name  | 
| 125 | 
            -
                  # try to parse a method definition with this name as the name of a
         | 
| 126 | 
            -
                  # keyword parameter. If we use a positional parameter, then parameter names
         | 
| 127 | 
            -
                  # like `&` (and maybe others) will be treated like `def foo(&); end` and will
         | 
| 128 | 
            -
                  # thus be considered valid. Using a required keyword parameter prevents that
         | 
| 129 | 
            -
                  # confusion between Ruby syntax and parameter name.
         | 
| 130 | 
            -
                  iseq = RubyVM::InstructionSequence.compile("def #{sentinel_method_name}(#{name}:); end", nil, nil, 0, false)
         | 
| 131 | 
            -
                  # pull out the first operation in the instruction sequence and its first argument and data
         | 
| 132 | 
            -
                  op, arg, data = iseq.to_a.dig(-1, 0)
         | 
| 133 | 
            -
                  # make sure that:
         | 
| 134 | 
            -
                  # 1. a method was defined, and
         | 
| 135 | 
            -
                  # 2. the method has the expected method name, and
         | 
| 136 | 
            -
                  # 3. the method has a keyword parameter with the expected name
         | 
| 137 | 
            -
                  op == :definemethod && arg == sentinel_method_name && data.dig(11, :keyword, 0) == name.to_sym
         | 
| 138 | 
            -
                rescue SyntaxError
         | 
| 139 | 
            -
                  false
         | 
| 115 | 
            +
                  Prism.parse_success?("def sentinel_method_name(#{name}:); end")
         | 
| 140 116 | 
             
                end
         | 
| 141 117 | 
             
              end
         | 
| 142 118 | 
             
            end
         | 
| @@ -18,6 +18,13 @@ module URI | |
| 18 18 | 
             
                  T::Array[Symbol],
         | 
| 19 19 | 
             
                )
         | 
| 20 20 |  | 
| 21 | 
            +
                # `uri` for Ruby 3.4 switched the default parser from RFC2396 to RFC3986. The new parser emits a deprecation
         | 
| 22 | 
            +
                # warning on a few methods and delegates them to RFC2396, namely `extract`/`make_regexp`/`escape`/`unescape`.
         | 
| 23 | 
            +
                # On earlier versions of the uri gem, the RFC2396_PARSER constant doesn't exist, so it needs some special
         | 
| 24 | 
            +
                # handling to select a parser that doesn't emit deprecations. While it was backported to Ruby 3.1, users may
         | 
| 25 | 
            +
                # have the uri gem in their own bundle and thus not use a compatible version.
         | 
| 26 | 
            +
                PARSER = T.let(const_defined?(:RFC2396_PARSER) ? RFC2396_PARSER : DEFAULT_PARSER, RFC2396_Parser)
         | 
| 27 | 
            +
             | 
| 21 28 | 
             
                alias_method(:gem_name, :host)
         | 
| 22 29 | 
             
                alias_method(:line_number, :fragment)
         | 
| 23 30 |  | 
| @@ -40,7 +47,7 @@ module URI | |
| 40 47 | 
             
                      {
         | 
| 41 48 | 
             
                        scheme: "source",
         | 
| 42 49 | 
             
                        host: gem_name,
         | 
| 43 | 
            -
                        path:  | 
| 50 | 
            +
                        path: PARSER.escape("/#{gem_version}/#{path}"),
         | 
| 44 51 | 
             
                        fragment: line_number,
         | 
| 45 52 | 
             
                      }
         | 
| 46 53 | 
             
                    )
         | 
    
        data/lib/tapioca/internal.rb
    CHANGED
    
    
| @@ -178,13 +178,19 @@ module Tapioca | |
| 178 178 | 
             
                  end
         | 
| 179 179 |  | 
| 180 180 | 
             
                  # Examines the call stack to identify the closest location where a "require" is performed
         | 
| 181 | 
            -
                  # by searching for the label "<top (required)>" | 
| 181 | 
            +
                  # by searching for the label "<top (required)>" or "block in <class:...>" in the
         | 
| 182 | 
            +
                  # case of an ActiveSupport.on_load hook. If none is found, it returns the location
         | 
| 182 183 | 
             
                  # labeled "<main>", which is the original call site.
         | 
| 183 184 | 
             
                  sig { params(locations: T.nilable(T::Array[Thread::Backtrace::Location])).returns(String) }
         | 
| 184 185 | 
             
                  def resolve_loc(locations)
         | 
| 185 186 | 
             
                    return "" unless locations
         | 
| 186 187 |  | 
| 187 | 
            -
                    resolved_loc = locations.find  | 
| 188 | 
            +
                    resolved_loc = locations.find do |loc|
         | 
| 189 | 
            +
                      label = loc.label
         | 
| 190 | 
            +
                      next unless label
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                      REQUIRED_FROM_LABELS.include?(label) || label.start_with?("block in <class:")
         | 
| 193 | 
            +
                    end
         | 
| 188 194 | 
             
                    return "" unless resolved_loc
         | 
| 189 195 |  | 
| 190 196 | 
             
                    resolved_loc.absolute_path || ""
         | 
    
        data/lib/tapioca/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: tapioca
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.16. | 
| 4 | 
            +
              version: 0.16.4
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Ufuk Kayserilioglu
         | 
| @@ -11,7 +11,7 @@ authors: | |
| 11 11 | 
             
            autorequire:
         | 
| 12 12 | 
             
            bindir: exe
         | 
| 13 13 | 
             
            cert_chain: []
         | 
| 14 | 
            -
            date: 2024- | 
| 14 | 
            +
            date: 2024-11-07 00:00:00.000000000 Z
         | 
| 15 15 | 
             
            dependencies:
         | 
| 16 16 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 17 17 | 
             
              name: bundler
         | 
| @@ -284,7 +284,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 284 284 | 
             
                - !ruby/object:Gem::Version
         | 
| 285 285 | 
             
                  version: '0'
         | 
| 286 286 | 
             
            requirements: []
         | 
| 287 | 
            -
            rubygems_version: 3.5. | 
| 287 | 
            +
            rubygems_version: 3.5.23
         | 
| 288 288 | 
             
            signing_key:
         | 
| 289 289 | 
             
            specification_version: 4
         | 
| 290 290 | 
             
            summary: A Ruby Interface file generator for gems, core types and the Ruby standard
         |