RBS::Inline

repository·main·Indexed 43 days ago

https://github.com/soutaro/rbs-inline

A transpiler that allows developers to embed RBS type declarations directly into Ruby source code as comments. It converts inline annotations—such as the # : shorthand or @rbs parameter and return types—into standard RBS files for use with type checkers like Steep.

Tokens
3.2K
Snippets
13
Records
19
Agent score
52%

What's inside RBS::Inline

  1. How to use RBS::Inline syntax in Ruby

    main

    RBS::Inline allows you to embed RBS type declarations directly into Ruby code using comments. To enable the feature in a file, you must include the magic comment # rbs_inline: enabled at the top.

    Supported Annotation Styles

    1. Attribute/Method shorthand (#: syntax): The shortest way to declare types for attributes or methods.

      attr_reader :name #: String
      def hash #: () -> String
    2. Explicit @rbs parameter and return types: Useful for complex method signatures.

      # @rbs name: String
      # @rbs addresses: Array[String]
      # @rbs return: void
      def initialize(name:, addresses:)
    3. Method signature after @rbs:

      # @rbs () -> String
      def to_s
    4. Block annotations:

      # @rbs &block: (String) -> void
      def each_address(&block) #: void

    Full Example

    # rbs_inline: enabled
    
    class Person
      attr_reader :name #: String
      attr_reader :addresses #: Array[String]
    
      # @rbs name: String
      # @rbs addresses: Array[String]
      # @rbs return: void
      def initialize(name:, addresses:)
        @name = name
        @addresses = addresses
      end
    
      # @rbs () -> String
      def to_s
        "Person(name = #{name}, addresses = #{addresses.join(', ')})"
      end
    
      #: () -> String
      def hash
        [name, addresses].hash
      end
    
      # @rbs &block: (String) -> void
      def each_address(&block) #: void
        addresses.each(&block)
      end
    end
    # rbs_inline: enabled
    
    class Person
      attr_reader :name #: String
    
      # @rbs name: String
      # @rbs addresses: Array[String]
      # @rbs return: void
      def initialize(name:, addresses:)
        @name = name
        @addresses = addresses
      end
    end
  2. Install RBS::Inline

    main

    To use RBS::Inline, add it to your application's Gemfile. It is recommended to use require: false to prevent the gem from becoming a runtime dependency for your type definitions.

    If using Bundler:

    $ bundle add rbs-inline --require=false

    Or manually add to Gemfile:

    gem 'rbs-inline', require: false

    If not using Bundler:

    $ gem install rbs-inline

    Note: Use Steep >= 1.8.0.dev to avoid conflicts with the #: syntax.

    gem 'rbs-inline', require: false
  3. Control RBS generation with inline comments

    main

    You can control whether rbs-inline processes a file by adding a special comment at the top of your Ruby file:

    • To enable generation for a file: # rbs_inline: enabled
    • To disable generation for a file: # rbs_inline: disabled

    If you run the CLI with the --opt-out flag, the requirement for the enabled comment is ignored.

    # rbs_inline: enabled
    
    def hello(name: String)
      puts "Hello, #{name}"
    end
  4. How consecutive comments are defined in RBS::Inline

    main

    The RBS::Inline::AnnotationParser groups comments into ParsingResult objects based on whether they are "consecutive". Consecutive comments are used to form a single block of annotations.

    To be considered consecutive, comments must meet these requirements:

    1. They must follow from the previous line.
    2. They must have the same starting column (no indentation changes between comment lines).
    3. There must be no leading code on the line following the comment block.

    Example of consecutive vs non-consecutive comments:

    # Line 1
    # Line 2           #=> Line 1 and Line 2 are consecutive
    
    # Line 3
     # Line 4           #=> Line 3 and Line 4 are NOT consecutive (different starting columns)
    
    # Line 5
    foo()               #=> Line 5 and Line 6 are NOT consecutive (Line 6 has leading code)
  5. Enable RBS::Inline in Ruby files

    main
    To enable RBS::Inline processing for a Ruby file, include the magic comment # rbs_inline: enabled at the top of the file. This signals to the parser that the file contains inline RBS annotations that should be extracted into RBS files.
    # rbs_inline: enabled
  6. RBS::Inline CLI Reference

    main

    The rbs-inline command is used to transpile Ruby comments into RBS files.

    Argument/FlagDescription
    pathThe directory or file containing the Ruby code to process.
    --output <dir>The directory where the generated RBS files will be saved.
  7. Generate RBS files from annotated Ruby code

    main

    RBS::Inline works as a transpiler. It reads your annotated Ruby files and generates standard .rbs files that can be used by Steep or other RBS-compatible tools.

    Commands

    Print generated RBS to stdout:

    $ bundle exec rbs-inline lib

    Save generated RBS files to a specific directory: Use the --output flag to specify the destination directory (e.g., lib or sig/generated).

    $ bundle exec rbs-inline --output lib

    Automation Tip

    To automatically regenerate RBS files whenever you edit Ruby code, you can use fswatch:

    $ fswatch -0 lib | xargs -0 -n1 bundle exec rbs-inline --output
    $ bundle exec rbs-inline --output lib
  8. Add headers to RBS output

    main

    The header method allows you to prepend comment lines to the generated RBS content. Each string passed to the method will be prefixed with # and followed by a newline.

    writer = RBS::Inline::Writer.new
    writer.header("Copyright 2024", "All rights reserved")
    writer = RBS::Inline::Writer.new
    writer.header("Copyright 2024", "All rights reserved")
  9. Parse Ruby source with RBS::Inline::Parser.parse

    main

    Use RBS::Inline::Parser.parse to extract RBS declarations from a Ruby Prism::Result.

    This method returns a three-tuple containing:

    1. An array of use directives (Array[AST::Annotations::Use])
    2. An array of declarations (Array[AST::Declarations::t])
    3. An array of RBS declarations extracted from top-level @rbs! annotations (Array[RBS::AST::Declarations::t])

    Opt-in vs Opt-out Mode

    • Opt-out mode (opt_in: false): The parser processes the file by default unless it encounters the magic comment # rbs_inline: disabled.
    • Opt-in mode (opt_in: true): The parser only processes the file if it contains the magic comment # rbs_inline: enabled.

    Note: Only RBS declarations are allowed in top-level @rbs! annotations; RBS members are ignored in the third element of the returned tuple.

    # Example usage (conceptual)
    # result is a Prism::Result
    uses, decls, rbs_decls = RBS::Inline::Parser.parse(result, opt_in: false)
  10. Generate RBS content with RBS::Inline::Writer

    main

    The RBS::Inline::Writer class is responsible for converting extracted Ruby declarations into valid RBS file content. You can use the class method write to perform a complete transformation in a single block.

    Usage

    Call RBS::Inline::Writer.write passing the extracted uses, decls, and rbs_decls. You can optionally yield to the writer instance to configure it (e.g., setting a default_type) before the writing process begins.

    # Example of using the class method to generate RBS content
    output = RBS::Inline::Writer.write(uses, decls, rbs_decls) do |writer|
      writer.default_type = Types::Bases::NilClass.new(location: nil)
    end
    RBS::Inline::Writer.write(uses, decls, rbs_decls) do |writer|
      writer.default_type = Types::Bases::NilClass.new(location: nil)
    end