LoginSignup
0
0

[Zeitwerk] クラスを定義しようとすると "XXX is not a class" と怒られるパターン

Last updated at Posted at 2024-02-10

概要

foo.rb
foo/
  bar/
    baz.rb

という構造があったときに、"foo.rb内で"クラス Bar を定義しようとした瞬間、Bar is not a class と怒られるという話です。

以下、具体的な再現コードです。

require 'bundler/inline'
require 'tmpdir'

gemfile(true) do
  source 'https://rubygems.org'
  gem 'zeitwerk', '= 2.6.13'
end

Dir.mktmpdir { |dir|
  loadpath_1 = "#{dir}/loadpath_1"
  FileUtils.mkpath "#{loadpath_1}"

  IO.write("#{loadpath_1}/foo.rb", <<~RUBY)
    module Foo
      # 次の行に到達した瞬間エラーが発生
      class Bar
      end
    end
  RUBY


  FileUtils.mkpath "#{loadpath_1}/foo/bar"

  IO.write("#{loadpath_1}/foo/bar/baz.rb", <<~RUBY)
    module Foo
      class Bar
        class Baz
        end
      end
    end
  RUBY

  Zeitwerk::Loader.new.then { |loader|
    loader.push_dir loadpath_1
    loader.setup
  }

  # causes
  # `loadpath_1/foo.rb:2:in `<module:Foo>': Bar is not a class (TypeError)`
  p(Foo::Bar)
}

# 次の行に到達した瞬間エラーが発生 の箇所でエラーとなります。

関連情報

にも関連するセクションがあります。

Zeitwerkは事前定義されてない名前空間は全てmoduleであるとして決め打ちします。

つまり、こういうものを書いていると・・・

app/controllers/admins/items_controller.rb
class Admins # 外側をクラスにする
  class ItemsController # ここはclass・moduleどちらでも構わない
  end
end
> Admins::ItemsController
=> TypeError: Admins is not a class

怒られてしまいます。

これと多少似ている状況です。本記事では、このAdminsに相当する名前空間である Foo::Bar を、きちんと foo.rb の中でクラスとして定義しようとしています。

ところが、定義しようとするとその瞬間、 Foo::Bar は module として autovivify により定義されてしまうのです。何のこっちゃと驚いてしまう現象です。

解決方法

とはいえ、そもそもZeitwerkで名前空間として扱われるようなクラスなら、ファイルとして独立させ、 foo/bar.rb で定義するべきといえます。
Foo::BarFoo 下のクラスなんだからと、 foo.rb の中へ押し込んでしまうのはそもそも分かりづらいです。

Foo::Bar の定義を foo/bar.rb に分離してやれば解決します:

  IO.write("#{loadpath_1}/foo.rb", <<~RUBY)
    module Foo
      # Bar の定義を除去
    end
  RUBY

  # Bar の定義は foo/bar.rb に分離
  FileUtils.mkpath "#{loadpath_1}/foo"
  IO.write("#{loadpath_1}/foo/bar.rb", <<~RUBY)
    module Foo
      class Bar
      end
    end
  RUBY
0
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
0
0