6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

エムスリーAdvent Calendar 2015

Day 11

`NameError: uninitialized constant SchemaMonkey::Middleware::Model`で困っていた昨日の自分へ

Posted at
rm -rf vendor/bundle

これでランチを食べに行ける。

背景

昨日、Railsの開発中にCI環境のJenkinsさんでRspecを回そうとしたところ、タイトルのエラーが出て解決できずはまった。

環境

Ruby On Rails 4.x.x
触ってるRailsアプリが多すぎて正確なバージョンは覚えていない。

エラー発生の状況

呼び出し元はこんなクラス。普通にhas_manyしているだけ。

class Hoge < ActiveRecord::Base
  has_many :fugas
end

それがこのコードを呼んで

schema_plus_core/blob/master/lib/schema_plus/core/active_record/base.rb
def has_many(name, scope = nil, options = {}, &extension)
  SchemaMonkey::Middleware::Model::Association::Declaration.start(model: self, name: name, scope: scope, options: options, extension: extension) do |env|
    env.result = super(env.name, env.scope, env.options, &env.extension)
  end.result
end

from: https://github.com/SchemaPlus/schema_plus_core/blob/master/lib/schema_plus/core/active_record/base.rb#L23

こうなる。

NameError: uninitialized constant SchemaMonkey::Middleware::Model

で、このSchemaMonkey::Middleware::Modelの定義は同じgemの中で行われている。

つまり、あるgemの中で定義されたメソッドを呼び出しているのに、同じgemの中で定義されている定数が呼び出せない、ということが起こっていた。
普通にhas_manyしただけなのにヘビーすぎる。

原因は?

has_manyメソッドが呼ばれている以上、このgemの読み込みには成功している。
gemが読み込めている以上、SchemaMonkey::Middleware::Modelは定義されている。
ここで定義されていない可能性を疑う(= gemのバグと考える)のは早計なので、定義されていて、かつ読み込めない状況を考える。

普通、Rubyでは定数を宣言したら以下のように宣言しなおしたからと言って上書きされることはない。

module Hoge; end

puts Object.const_defined?(:Hoge) #=> true
puts Hoge.const_defined?(:Fuga) #=> false

module Hoge
  module Fuga
  end
end

puts Object.const_defined?(:Hoge) #=> true
puts Hoge.const_defined?(:Fuga) #=> true

module Hoge; end # 宣言しなおしてみる

puts Object.const_defined?(:Hoge) #=> true
puts Hoge.const_defined?(:Fuga) #=> true

ここでSchemaMonkeyの宣言方法を見てみる。

schema_plus_core/lib/schema_plus/core.rb
SchemaMonkey.register(SchemaPlus::Core)

こんなメソッドにたどり着く。

schema_monkey/lib/schema_monkey/monkey.rb
def register(mod)
  @client_map[mod] ||= Client.new(mod).tap { |client|
    client.insert if @inserted
    client.insert(dbm: @inserted_dbm) if @inserted_dbm
  }
end

この先を追求するのは、ちょっと辛いのでここまででわかったことを整理する。
通常、Rubyのmoduleやclassの宣言(定数の宣言)は元の定数を"開く"だけで上書きはしない。
このgemではmoduleの宣言をHashを使ってまとめているので上書きされている可能性がある…?
#=> registerの引数の定数がkeyなので可能性は低そう。

何か、よくわからない力が働いているに違いない…。

結局

bunderを信じるしかない。
困った時はvendor/bundleを消してbundle installしなおす。

もやもやするのでいつかbundlerを調べて続きを書くかもしれません。

6
6
1

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
6
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?