📙

Sorceryのコードを読んでみる

2021/07/28に公開

Sorceryの過去のバグ修正を見かけた時にoptionsの定義場所が分からなかったので調べてみた。

def tableized_model_class
  options[:model] ? options[:model].gsub(/::/, '').tableize : 'users'
end

https://github.com/Sorcery/sorcery/pull/274/files

Sorceryにはソースコードのgeneratorがあり、自動でいくつかのファイルを作成してくれるのだが、その際に渡したオプションをテーブル名に変換するメソッドっぽい。

Sorcery内

上のファイル/lib/generators/sorcery/helpers.rb内をチェックしたところ、他にoptionsという変数やメソッドは定義されていなかった。

options[:model]Helpersというmoduleの中に定義されていたので、Helpersを利用している箇所を調べてみる。

唯一/lib/generators/sorcery/install_generator.rbの中でだけincludeされ利用されていた。
https://github.com/Sorcery/sorcery/blob/4f7ea0ba8673e3f49d6347554989bd06c8d337cb/lib/generators/sorcery/install_generator.rb#L8

このファイルの中でも同様にoptions[:migrations]のようにoptionsが利用されているが、定義は存在しない。

moduleをincludeしているクラスはInstallGeneratorであり、このクラスはRails::Generators::Baseを継承しているので、optionsの定義はSorcery内ではなくRails側にありそう

Rails内

Rails::Generators::Baseクラスはここに定義はされている
https://github.com/rails/rails/blob/main/railties/lib/rails/generators/base.rb

が、ここでもoptionsは利用されているだけであり、定義されてないので、さらに継承ツリーを辿る。

class Base < Thor::Groupとある通り、Rails::Generators::BaseThorというgemのGroupクラスを継承していた。
https://github.com/rails/rails/blob/5f22ae4c947be5abadaedf879b609af15be29b66/railties/lib/rails/generators/base.rb#L17

ここでThorについて軽く調べると、CLI作成時に利用できるgemらしいというのがわかった。
https://github.com/rails/thor

Thor内

上でRails::Generators::Baseが継承していたGroupクラスはここにあった
https://github.com/rails/thor/blob/efe79b599eb401d36265ed4a8b48fb60f42a06a4/lib/thor/group.rb#L7

が、この中でもoptionsについて定義している箇所はない。
そしてThor::Groupは他のクラスを継承していない(正確には書かれていないだけでObjectクラスを継承している)

よく見ると、かなり下の方でinclude Thor::Baseという記述があり、Baseモジュールをincludeしていたので確認してみる。
https://github.com/rails/thor/blob/efe79b599eb401d36265ed4a8b48fb60f42a06a4/lib/thor/base.rb#L34

35行目に
attr_accessor :options, :parent_options, :args
という記述があった。おそらくこれ?

attr_accessorって何だ? という人へ
https://bryankawa.hatenablog.com/entry/2017/01/28/150537

https://www.rubydoc.info/github/wycats/thor/Thor/Base#options-instance_method

Thor::Baseのドキュメントにはoptionsインスタンスメソッドについて記載がある
https://www.rubydoc.info/github/wycats/thor/Thor/Base#options-instance_method

まとめ

SorceryはRailsを継承していて、さらにRailsはThorを継承していて、そのThorの中に定義されていた。

1. 一番最初に書いたoptionsがあるモジュールは、SorceryのInstallGeneratorクラスが唯一includeしている
2. SorceryのInstallGeneratorはRailsのRails::Generators::Baseクラスを継承している
3. Rails::Generators::BaseはThorのThor::Groupクラスを継承している
4. Thor::GroupThor::Baseモジュールをincludeしている
5. Thor::Baseの中で、optionsが定義されていた。
https://github.com/rails/thor/blob/efe79b599eb401d36265ed4a8b48fb60f42a06a4/lib/thor/base.rb#L35

optionsメソッドは何をしているのか?

CLIのオプションで設定した値を取得できる。

Railsガイドに詳しく書かれているので以下引用

Railsのジェネレータは、カスタムのコマンドライン引数を与えることで簡単に挙動を変更できます。この機能はThorを利用しています。

class_option :scope, type: :string, default: 'read_products'
これで、ジェネレータを以下のように呼び出せます。
rails generate initializer --scope write_products

このコマンドライン引数は、ジェネレータクラス内ではoptionsメソッドでアクセスできます。
@scope = options['scope']

https://railsguides.jp/generators.html#コマンドライン引数を追加する

thanks

分からない箇所はstack overflowで@nekketsuuuさんがサポートしてくださいました。
https://twitter.com/nekketsuuu

その他

まだ理解しきれておらず、もしかしたら間違えているかもしれないので何かあればコメント頂ければ幸いです

Discussion