Ruby の steep を試してみたメモ

雑な覚書

インストール

$ gem install steep
$ steep --version
0.47.0

注意点

$ steep --version
/home/worker/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.1/lib/active_support/tagged_logging.rb:60:in `current_tags': uninitialized constant ActiveSupport::TaggedLogging::Formatter::IsolatedExecutionState (NameError)

        IsolatedExecutionState[thread_key] ||= []
        ^^^^^^^^^^^^^^^^^^^^^^
    from /home/worker/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.1/lib/active_support/tagged_logging.rb:45:in `push_tags'
    from /home/worker/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/activesupport-7.0.1/lib/active_support/tagged_logging.rb:95:in `push_tags'
    from /home/worker/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/steep-0.47.0/lib/steep.rb:146:in `block in new_logger'
    from <internal:kernel>:90:in `tap'
    from /home/worker/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/steep-0.47.0/lib/steep.rb:145:in `new_logger'
    from /home/worker/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/steep-0.47.0/lib/steep.rb:158:in `log_output='
    from /home/worker/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/steep-0.47.0/lib/steep.rb:162:in `<module:Steep>'
    from /home/worker/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/steep-0.47.0/lib/steep.rb:139:in `<top (required)>'
    from <internal:/home/worker/.rbenv/versions/3.1.0/lib/ruby/3.1.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
    from <internal:/home/worker/.rbenv/versions/3.1.0/lib/ruby/3.1.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
    from /home/worker/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/steep-0.47.0/exe/steep:7:in `<top (required)>'
    from /home/worker/.rbenv/versions/3.1.0/bin/steep:25:in `load'
    from /home/worker/.rbenv/versions/3.1.0/bin/steep:25:in `<main>'

lsp-vim + vim-lsp-settings で steep を使う

  • steep をインストール
" インストール
:LspInstallServer steep
  • steep のみを使うように設定
let g:lsp_settings_filetype_ruby = ['steep']
" solargraph と併用する場合はこんな感じ?
" let g:lsp_settings_filetype_ruby = ['solargraph', 'steep']
  • lsp-vim だと『グローバル』にインストールされている steep を使うので注意

サンプル環境

  • steep の設定ファル
    • 参照する Ruby のコードや rbs ファイルのパスなどを設定する
# ./Steepfile
target :app do
  check "lib"
  signature "sig"

  library "set", "pathname"
end
# ./lib/steep_example.rb
homu.name
  attr_reader :contacts

  def initialize(name:)
    @name = name
    @contacts = []
  end

  def guess_country()
    contacts.map do |contact|
      case contact
      when Phone
        contact.country
      end
    end.compact.first
  end
end

class Email
  attr_reader :address

  def initialize(address:)
    @address = address
  end

  def ==(other)
    other.is_a?(self.class) && other.address == address
  end

  def hash
    self.class.hash ^ address.hash
  end
end

class Phone
  attr_reader :country, :number

  def initialize(country:, number:)
    @country = country
    @number = number
  end

  def ==(other)
    if other.is_a?(Phone)
      other.country == country && other.number == number
    end
  end

  def hash
    self.class.hash ^ country.hash ^ number.hash
  end
end
  • Ruby の型情報の rbs ファイル
# ./lib/steep_example.rbs
class Person
  @name: String
  @contacts: Array[Email | Phone]

  def initialize: (name: String) -> untyped
  def name: -> String
  def contacts: -> Array[Email | Phone]
  def guess_country: -> (String | nil)
end

class Email
  @address: String

  def initialize: (address: String) -> untyped
  def address: -> String
end

class Phone
  @country: String
  @number: String

  def initialize: (country: String, number: String) -> untyped
  def country: -> String
  def number: -> String
  def ==: (Phone) -> (bool | nil)

  def self.countries: -> Hash[String, String]
end

動作

所感

  • コード補完やドキュメントの表示ができて便利
  • steep check で型チェックもできた
  • 一方で定義ジャンプで利用したかったが定義ジャンプは rbs ファイルの方を参照しているようで残念
    • これは回避方法あるんだろうか…

参照リンク