この記事は Akatsuki Advent Calendar 2020 6日目の記事です.
ruby-3.0.0が12/25日にリリースされる予定なので、新しく追加される型検査機能について簡単なアプリケーションを作成しながら触れていこうと思います。
Ruby3.0.0-preview1のインストール
まず型検査機能を使うために機能が含まれているRuby3.0.0のpreview版をインストールしていきます。
> brew upgrade ruby-build
> rbenv install 3.0.0-preview1
> rbenv global 3.0.0-preview1
インストールされたrubyにはデフォルトでrbsと言うgemが含まれています。
> gem list | grep rbs
> rbs(0.12.2)
これがruby3.0.0で導入される予定であるgemです。
RBSとは?
RBSとはRubyのプログラム構造を記述するための言語です。
クラスやモジュール、定義されたクラスのメソッドや変数の型の定義を書く事が出来ます。
その他必要なツールのインストール
Railsのアプリケーションを作成するので、その他必要なgemのインストールをしていきます。
Railsのインストールの他に、RailsのRBSファイル作成ツールであるrbs_rails、RBSファイルを元に型チェックを行ってくれる型チェッカーが必要になります。
今回は型チェッカーとしてsteepを導入したいと思います。
まずはRailsのアプリケーションを作成します。
使用したバージョンは6.0.3.4です。
> gem install rails
> rails new rbs_training
次にGemfileに追記を行ってrbs_railsとsteepのインストールを行います。
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
ruby '3.0.0'
gem 'rails', '~> 6.0.3', '>= 6.0.3.4'
gem 'mysql2', '>= 0.4.4'
gem 'puma', '~> 4.1'
gem 'sass-rails', '>= 6'
gem 'webpacker', '~> 4.0'
gem 'turbolinks', '~> 5'
gem 'jbuilder', '~> 2.7'
gem 'bootsnap', '>= 1.4.2', require: false
group :development, :test do
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
gem 'steep'
gem 'rbs_rails'
end
group :development do
gem 'web-console', '>= 3.3.0'
gem 'listen', '~> 3.2'
gem 'spring'
gem 'spring-watcher-listen', '~> 2.0.0'
end
group :test do
gem 'capybara', '>= 2.15'
gem 'selenium-webdriver'
gem 'webdrivers'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
これで準備は完了したので、実際のアプリケーションを書いていきます。
rbs_railsによる型情報の追加
Railsの型情報の追加
簡単な掲示板を作成して、モデルに対して型情報を追加して行きます。
今回作成するのはトピックと投稿がある簡単な掲示板です。
> rails g model Topic title:string
> rails g model Post body:string posts:references
> rails db:migrate
class Topic < ApplicationRecord
validates :title, presence: true
end
class Post < ApplicationRecord
belongs_to :topic
validates :body, presence: true
end
アプリケーションの型情報を追加するには、Railsの型情報を定義する必要があります。
rbs_railsのドキュメントに書いてあるように型情報を追加してくれるrakeタスクを作成します。
task copy_signature_files: :environment do
require 'rbs_rails'
to = Rails.root.join('sig/rbs_rails/')
to.mkpath unless to.exist?
RbsRails.copy_signatures(to: to)
end
このrakeタスクを実行するとアプリケーションのルートディレクトリにsig/rbs_railsというディレクトリが追加され、Railsの型情報が定義されたrbsファイルが自動で生成されます。
モデルに対する型情報の追加
次に作成したモデルに対して型情報を追加していきます。
こちらもrbs_railsを利用して自動で生成する事が出来ます。
次のようなrakeタスクを作成して、実行します。
task generate_rbs_for_model: :environment do
require 'rbs_rails'
out_dir = Rails.root / 'sig'
out_dir.mkdir unless out_dir.exist?
Rails.application.eager_load!
ActiveRecord::Base.descendants.each do |klass|
next if klass.abstract_class?
path = out_dir / "app/models/#{klass.name.underscore}.rbs"
FileUtils.mkdir_p(path.dirname)
sig = RbsRails::ActiveRecord.class_to_rbs(klass)
path.write sig
end
end
task generate_rbs_for_path_helpers: :environment do
require 'rbs_rails'
out_path = Rails.root.join 'sig/path_helpers.rbs'
rbs = RbsRails::PathHelpers.generate
out_path.write rbs
end
> bundle exec rake generate_rbs_for_model
すると'sig/app'以下に'rails generate'コマンドで作成されたモデルの型情報が自動で追加されます。
Rails6でコマンドを実行するとActionText関連のrbsファイルを生成しようとしてしまい、エラーとなってしまいますがモデル自体のrbsファイルは生成されるようです。
また、自分で独自に定義したメソッドに対しては自分で型情報を追加していく必要があります。
class Post < ApplicationRecord
def hoge
"hogehoge"
end
end
class Post < ApplicationRecord
def hoge: () -> String
end
steepによる型チェックの実行
型定義の追加は終わったので、次にsteepを利用して型チェックをしていきたいと思います。
'steep init'コマンドを叩いてsteepの設定ファイルを作成し、以下を追加します。
target :app do
signature "sig"
check "app"
check 'rbs_rails'
library "pathname"
library "logger"
library "mutex_m"
library "date"
end
次に'steep check'コマンドを叩いて型チェックを実行します。
> steep check
W, [2020-12-07T07:39:16.274442 #11364] WARN -- rbs: `overload def` syntax is deprecated. Use `...` syntax instead.
sig/app/models/post.rbs:1:0...70:3 UnknownTypeNameError: name=ApplicationRecord
sig/app/models/topic.rbs:1:0...54:3 UnknownTypeNameError: name=ApplicationRecord
どうやら、ApplicationRecordの定義がうまく生成されていないようなので、追加します。
class ApplicationRecord < ActiveRecord::Base
end
> steep check
W, [2020-12-07T07:33:24.351191 #11004] WARN -- rbs: `overload def` syntax is deprecated. Use `...` syntax instead.
型定義に問題が無ければ上記のようにエラーが出力されないで終了します。
以上で導入は一通り完了です!
今回は簡単に導入をしただけだったのですが、ruby 3.0.0がリリースされたら自分のプロダクトに導入してみて使い方やチップスを共有出来たらなと思います。
まとめ
- ruby3.0.0からrbsが追加される
- Rails本体の型情報を追加するのにはrbs_railsを使う
- rbs_railsでモデルの型情報を自動生成する事が出来る
- steepを使って型チェックをする