|
| 1 | +# Inline RBS Type Declaration |
| 2 | + |
| 3 | +Inline RBS type declarations allow you to write type annotations directly in your Ruby source files using comments. Instead of maintaining separate `.rbs` files, you can keep your type information alongside your Ruby code, making it easier to keep types and implementation in sync. |
| 4 | + |
| 5 | +The following example defines `Calculator` class and `add` instance method. The `@rbs` comment gives the type of the `add` method with the RBS method type syntax. |
| 6 | + |
| 7 | +```ruby |
| 8 | +class Calculator |
| 9 | + # @rbs (Integer, Integer) -> Integer |
| 10 | + def add(a, b) |
| 11 | + a + b |
| 12 | + end |
| 13 | +end |
| 14 | +``` |
| 15 | + |
| 16 | +## Classes |
| 17 | + |
| 18 | +Inline RBS supports class definitions from your Ruby code. When you define a class in Ruby, the library recognizes it and the corresponding class definition is generated in RBS. |
| 19 | + |
| 20 | +```ruby |
| 21 | +class App |
| 22 | +end |
| 23 | +``` |
| 24 | + |
| 25 | +The `::App` class is defined in RBS and you can use it as a type. |
| 26 | + |
| 27 | +### Non-constant class paths |
| 28 | + |
| 29 | +Only classes with constant names are imported. Dynamic or non-constant class definitions are ignored: |
| 30 | + |
| 31 | +```ruby |
| 32 | +# This class is imported |
| 33 | +class MyClass |
| 34 | +end |
| 35 | + |
| 36 | +# This is ignored - dynamic class definition |
| 37 | +MyClass = Class.new do |
| 38 | +end |
| 39 | + |
| 40 | +# This is also ignored - non-constant class name |
| 41 | +object = Object |
| 42 | +class object::MyClass |
| 43 | +end |
| 44 | +``` |
| 45 | + |
| 46 | +### Class Nesting |
| 47 | + |
| 48 | +Nested classes work as expected: |
| 49 | + |
| 50 | +```ruby |
| 51 | +class Client |
| 52 | + class Error |
| 53 | + end |
| 54 | +end |
| 55 | +``` |
| 56 | + |
| 57 | +This creates the types `::Client` and `::Client::Error`. |
| 58 | + |
| 59 | +### Current Limitations |
| 60 | + |
| 61 | +- Inheritance is not supported |
| 62 | +- Generic class definitions are not supported |
| 63 | + |
| 64 | +## Modules |
| 65 | + |
| 66 | +Inline RBS supports module definitions from your Ruby code. When you define a module in Ruby, the library recognizes it and the corresponding module definition is generated in RBS. |
| 67 | + |
| 68 | +```ruby |
| 69 | +module Helper |
| 70 | +end |
| 71 | +``` |
| 72 | + |
| 73 | +The `::Helper` module is defined in RBS and you can use it as a type. |
| 74 | + |
| 75 | +### Non-constant module paths |
| 76 | + |
| 77 | +Only modules with constant names are imported. Dynamic or non-constant module definitions are ignored: |
| 78 | + |
| 79 | +```ruby |
| 80 | +# This module is imported |
| 81 | +module MyModule |
| 82 | +end |
| 83 | + |
| 84 | +# This is ignored - dynamic module definition |
| 85 | +MyModule = Module.new do |
| 86 | +end |
| 87 | + |
| 88 | +# This is also ignored - non-constant module name |
| 89 | +object = Object |
| 90 | +module object::MyModule |
| 91 | +end |
| 92 | +``` |
| 93 | + |
| 94 | +### Module Nesting |
| 95 | + |
| 96 | +Nested modules work as expected: |
| 97 | + |
| 98 | +```ruby |
| 99 | +module API |
| 100 | + module V1 |
| 101 | + module Resources |
| 102 | + end |
| 103 | + end |
| 104 | +end |
| 105 | +``` |
| 106 | + |
| 107 | +This creates the types `::API`, `::API::V1`, and `::API::V1::Resources`. |
| 108 | + |
| 109 | +### Current Limitations |
| 110 | + |
| 111 | +- Generic module definitions are not supported |
| 112 | +- Module self-type constraints are not supported |
| 113 | + |
| 114 | +## Method Definitions |
| 115 | + |
| 116 | +Inline RBS supports methods defined using the `def` syntax in Ruby. |
| 117 | + |
| 118 | +```ruby |
| 119 | +class Calculator |
| 120 | + def add(x, y) = x+y |
| 121 | +end |
| 122 | +``` |
| 123 | +
|
| 124 | +It detects method definitions and allows you to add annotation comments to describe their types. |
| 125 | +
|
| 126 | +### Unannotated method definition |
| 127 | +
|
| 128 | +Methods defined with `def` syntax are detected, but they are untyped. |
| 129 | +
|
| 130 | +```ruby |
| 131 | +class Calculator |
| 132 | + def add(x, y) = x+y |
| 133 | +end |
| 134 | +``` |
| 135 | +
|
| 136 | +The type of the `Calculator#add` method is `(?) -> untyped` -- it accepts any arguments without type checking and returns an `untyped` object. |
| 137 | + |
| 138 | +### Method type annotation syntax |
| 139 | + |
| 140 | +You can define the type of the method using `@rbs` and `:` syntax. |
| 141 | + |
| 142 | +```ruby |
| 143 | +class Calculator |
| 144 | + # @rbs (Integer, Integer) -> Integer |
| 145 | + def add(x, y) = x + y |
| 146 | +
|
| 147 | + #: (Integer, Integer) -> Integer |
| 148 | + def subtract(x, y) = x - y |
| 149 | +end |
| 150 | +``` |
| 151 | + |
| 152 | +The type of both methods is `(Integer, Integer) -> Integer` -- they take two `Integer` objects and return an `Integer` object. |
| 153 | + |
| 154 | +Both syntaxes support method overloading: |
| 155 | + |
| 156 | +```ruby |
| 157 | +class Calculator |
| 158 | + # @rbs (Integer, Integer) -> Integer |
| 159 | + # | (Float, Float) -> Float |
| 160 | + def add(x, y) = x + y |
| 161 | +
|
| 162 | + #: (Integer, Integer) -> Integer |
| 163 | + #: (Float, Float) -> Float |
| 164 | + def subtract(x, y) = x - y |
| 165 | +end |
| 166 | +``` |
| 167 | + |
| 168 | +The type of both methods is `(Integer, Integer) -> Integer | (Float, Float) -> Float`. |
| 169 | + |
| 170 | +> [!NOTE] |
| 171 | +> The `@rbs METHOD-TYPE` syntax allows overloads with the `|` operator, just like in RBS files. |
| 172 | +> Multiple `: METHOD-TYPE` declarations are required for overloads. |
| 173 | + |
| 174 | +#### Doc-style syntax |
| 175 | + |
| 176 | +The `@rbs return: T` syntax declares the return type of a method: |
| 177 | + |
| 178 | +```ruby |
| 179 | +class Calculator |
| 180 | + # @rbs return: String |
| 181 | + def to_s |
| 182 | + "Calculator" |
| 183 | + end |
| 184 | +end |
| 185 | +``` |
| 186 | + |
| 187 | +### Current Limitations |
| 188 | + |
| 189 | +- Class methods and singleton methods are not supported |
| 190 | +- Parameter types are not supported with doc-style syntax |
| 191 | +- Method visibility declaration is not supported yet |
| 192 | + |
| 193 | +## Attributes |
| 194 | + |
| 195 | +Inline RBS supports Ruby's attribute methods: `attr_reader`, `attr_writer`, and `attr_accessor`. |
| 196 | +
|
| 197 | +```ruby |
| 198 | +class Person |
| 199 | + attr_reader :name #: String |
| 200 | + attr_writer :age #: Integer |
| 201 | + attr_accessor :email #: String? |
| 202 | +end |
| 203 | +``` |
| 204 | +
|
| 205 | +It detects these attribute declarations and generates the corresponding getter and setter methods. |
| 206 | +
|
| 207 | +The accessor methods and instance variables are defined. |
| 208 | +
|
| 209 | +### Unannotated attributes |
| 210 | +
|
| 211 | +Attributes defined without type annotations are treated as `untyped`: |
| 212 | +
|
| 213 | +```ruby |
| 214 | +class Person |
| 215 | + attr_reader :name |
| 216 | + attr_writer :age |
| 217 | + attr_accessor :email |
| 218 | +end |
| 219 | +``` |
| 220 | +
|
| 221 | +### Type annotations for attributes |
| 222 | +
|
| 223 | +You can add type annotations to attributes using the `#:` syntax in trailing comments: |
| 224 | +
|
| 225 | +```ruby |
| 226 | +class Person |
| 227 | + attr_reader :name #: String |
| 228 | + attr_writer :age #: Integer |
| 229 | + attr_accessor :email #: String? |
| 230 | +end |
| 231 | +``` |
| 232 | +
|
| 233 | +This generates the following typed methods: |
| 234 | +- `name: () -> String` |
| 235 | +- `age=: (Integer) -> Integer` |
| 236 | +- `email: () -> String?` and `email=: (String?) -> String?` |
| 237 | +
|
| 238 | +### Multiple attributes |
| 239 | +
|
| 240 | +When declaring multiple attributes in one line, the type annotation applies to all attributes: |
| 241 | +
|
| 242 | +```ruby |
| 243 | +class Person |
| 244 | + attr_reader :first_name, :last_name #: String |
| 245 | + attr_accessor :age, :height #: Integer |
| 246 | +end |
| 247 | +``` |
| 248 | +
|
| 249 | +All attributes in each declaration share the same type. |
| 250 | +
|
| 251 | +### Non-symbol attribute names |
| 252 | +
|
| 253 | +Attribute names must be symbol literals. |
| 254 | +
|
| 255 | +```ruby |
| 256 | +class Person |
| 257 | + attr_reader "name" #: String |
| 258 | +
|
| 259 | + age = :age |
| 260 | + attr_writer age #: Integer |
| 261 | +end |
| 262 | +``` |
| 263 | +
|
| 264 | +The attribute definitions are ignored because the names are given by string literals and local variables. |
| 265 | +
|
| 266 | +### Current Limitations |
| 267 | +
|
| 268 | +- Attribute visibility is not supported yet. All attributes are _public_ |
| 269 | +
|
| 270 | +## Mixin |
| 271 | +
|
| 272 | +Inline RBS supports Ruby's mixin methods: `include`, `extend`, and `prepend`. |
| 273 | + |
| 274 | +```ruby |
| 275 | +module Printable |
| 276 | + # @rbs () -> String |
| 277 | + def to_print |
| 278 | + to_s |
| 279 | + end |
| 280 | +end |
| 281 | +
|
| 282 | +class Document |
| 283 | + include Printable |
| 284 | + extend Enumerable #[String] |
| 285 | + prepend Trackable |
| 286 | +end |
| 287 | +``` |
| 288 | + |
| 289 | +It detects these mixin declarations and adds them to the class or module definition. |
| 290 | + |
| 291 | +### Basic mixin usage |
| 292 | + |
| 293 | +Mixins work just like in regular RBS files: |
| 294 | + |
| 295 | +```ruby |
| 296 | +module Helper |
| 297 | +end |
| 298 | +
|
| 299 | +class MyClass |
| 300 | + include Helper |
| 301 | + extend Helper |
| 302 | + prepend Helper |
| 303 | +end |
| 304 | +``` |
| 305 | + |
| 306 | +### Type arguments for generic modules |
| 307 | + |
| 308 | +You can specify type arguments for generic modules using the `#[...]` syntax: |
| 309 | + |
| 310 | +```ruby |
| 311 | +class TodoList |
| 312 | + include Enumerable #[String] |
| 313 | +
|
| 314 | + # @rbs () { (String) -> void } -> void |
| 315 | + def each(&block) |
| 316 | + @items.each(&block) |
| 317 | + end |
| 318 | +end |
| 319 | +``` |
| 320 | + |
| 321 | +### Module name requirements |
| 322 | + |
| 323 | +Only constant module names are supported. Dynamic module references are not allowed: |
| 324 | + |
| 325 | +```ruby |
| 326 | +class MyClass |
| 327 | + include Helper # ✓ Works - constant name |
| 328 | +
|
| 329 | + mod = Helper |
| 330 | + include mod # ✗ Ignored - non-constant module reference |
| 331 | +
|
| 332 | + include Helper.new # ✗ Ignored - not a simple constant |
| 333 | +end |
| 334 | +``` |
| 335 | + |
| 336 | +### Module name resolution |
| 337 | + |
| 338 | +The module name resolution is based on the nesting of the class/module definitions, unlike Ruby. |
| 339 | + |
| 340 | +Modules accessible through ancestors (super-class/included modules) are not supported. |
| 341 | + |
| 342 | +### Current Limitations |
| 343 | + |
| 344 | +- Only single module arguments are supported (no `include A, B` syntax) |
| 345 | +- Module names must be constants |
0 commit comments