LoginSignup
2
0

More than 1 year has passed since last update.

Railsの自動読み込みにオリジナルの変換処理を追加する

Last updated at Posted at 2022-12-07

この記事は CAMPFIRE Advent Calendar 2022 8日目の記事です。

アドベントカレンダー参加という良い機会に、ふと気になったRailsの自動読み込み(autoload)について調べてみました。

前提

以下の環境で検証しています

  • OS: macOS
  • Rubyバージョン: 2.7.6
  • Railsバージョン: 7.0.4
  • Railsオートローダ: Zeitwerk

また、Railsガイド の表現に基づきautoloadを「自動読み込み」として称しています

Railsの自動読み込み(オートロード)

Railsで利用されているオートローダはRails6以降のバージョンで変更され現在はZeitwerkというgemに任されています

Rails5以前のオートローダは「classic」と称され、Rails6.x の場合のみ、classicとzeitwerkモードの切り替えが可能です
参考: 4 zeitwerkモードを有効にする

バージョン オートローダ モードの変更
Rails 5以前 Active Support で実装 (classic) classicのみ
Rails 6.x Zeitwerk (zeitwerkモード) classicに変更可能
Rails 7 Zeitwerk (zeitwerkモード) zeitwerkモードのみ

classic と zeitwerk モード の違い

2つの違いについて詳細は以下の記事で詳しく記載されています
参考:Zeitwerkの壊し方

大きな違いはファイル名とクラス(やモジュール)の紐付け方法で以下のような違いがあります

  • classic: 定数名からファイルを探す
  • Zeitwerk: ファイル名から定数を推測する(ファイル名をキャメルケースに変換する)

このため、classicから Zeitwerkに以降すると 新ルールに則っていないクラスは場合にcamelizeに失敗します。
例) html_parser.rbHTMLParserで認識できずHtmlParserでないと動かなくなる

Zeitwerk

これからのRailsで使われていくZeitwerk
これを知れば、Railsの自動読み込みを理解できる…!
ということでZeitwerkでできることを紹介しようと思っていたのですが
ちょうど4日前にとてもわかりやすい日本語記事が公開されていました。
Zeitwerk を使いこなしたい

とても丁寧にかかれていて、 上記の記事とZeitwerkのReadme を合わせて読めばかなり理解が進むと思います!

Railsでのカスタマイズ

自動読み込みの対象となる ディレクトリを追加したい場合は configで設定が可能です
参考: Railsガイド 定数の自動読み込みと再読み込み (Zeitwerk)

詳細な設定

また、config/initializers/にファイルを追加し、
Zeitwerk の設定を記載することもできます
参考: 定数の自動読み込みと再読み込み (Zeitwerk) 10 活用形をカスタマイズする

例1) 特定のディレクトリを除外する

# config/initializers/zeitwerk.rb
Rails.autoloaders.each do |autoloader|
  autoloader.inflector = Zeitwerk::Inflector.new

  # app/settings を除外
  autoloader.ignore "app/settings"
end

例2) html_parser.rb のクラスを HTMLParser として利用する(通常はHtmlParser)

# config/initializers/zeitwerk.rb

Rails.autoloaders.each do |autoloader|
  autoloader.inflector = Zeitwerk::Inflector.new
  
  #案1) 
  # autoloader.inflect.acronym "HTML"
  # 案2)
  autoloader.inflector.inflect(
    "html_parser" => "HTMLParser",
  )
end

Railsの自動読み込みにオリジナルの変換処理を追加する

本題です。
更に自由な変更を加えたい場合、インフレクタ(inflector)をオリジナルのインフレクタに変更することでより自由に修正を入れることができます

※基本的にはデフォルトのインフレクタの利用したほうが後々混乱も誘発せず望ましいと思います。

手順

MyInflectorを作成して上記の変換の処理を実装する場合

1. オリジナルのインフレクタ作成

Zeitwerk::Inflector を継承してオリジナルのインフレクタを作成します

#lib/inflector/my_inflector.rb

class MyInflector < Zeitwerk::Inflector
  def camelize(basename, abspath)
    if basename =~ /\Ahtml_(.*)/
      "HTML" + super($1, abspath)
    else
      super
    end
  end
end

2. 作成したインフレクタを読み込み、オートローダに設定

autoloader.inflector = MyInflector.new
で自動読み込みで利用するインフレクタを設定します

# config/initializers/zeitwerk.rb

require 'inflector/my_inflector'
Rails.autoloaders.each do |autoloader|
autoloader.inflector = MyInflector.new
end

解説: def camelize について

パラメータ

MyInflectorのcamelizeメソッドには以下のパラメータが渡され、これらを元にクラス名やモジュール名の変換が可能になります

  • basename: 拡張子を含まないファイル名
  • abspath: ファイルやディレクトリの絶対パス
    例) {PATH}/app/controllers/application_controller.rb
    
    basename: application_controller
    abspath: {PATH}/app/controllers/application_controller.rb
    

変換の処理

if basename =~ /\Ahtml_(.*)/
      "HTML" + super($1, abspath)

「html_」から始まるファイルの「html_」をHTMLにし、残りはそのままキャメルケースに変換している

例えば「HTML」を「YML」のように変更した場合は「html_perser.rb」というファイルのクラスを「YMLParser」として利用できます

    if basename =~ /\Ahtml_(.*)/
      "YML" + super($1, abspath)

最後に

これらを応用すればより柔軟なクラスやモジュール名の変更が可能です。
どうしてもconfigだけでの解決ができない場合検討してはいかがでしょうか?

参考:

2
0
0

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
  3. You can use dark theme
What you can do with signing up
2
0