LoginSignup
0

More than 1 year has passed since last update.

Railsで型チェックを導入してみる

Last updated at Posted at 2020-12-06

この記事は 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のプログラム構造を記述するための言語です。

クラスやモジュール、定義されたクラスのメソッドや変数の型の定義を書く事が出来ます。

RBS

その他必要なツールのインストール

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を使って型チェックをする

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
0