2015年5月12日火曜日

undefined method `path2class'

直接の原因

psych.so ファイルが適切な場所に置かれていない。


問題回避方法

私のケースでは:

cp /usr/local/share/ruby/gems/2.0/gems/psych-2.0.13/lib/psych.so \
   /usr/share/ruby/vendor_ruby/2.0/

詳細

path2class メソッドは psych_to_ruby.c 内で定義され、rb_define_private_method() により Psych::ClassLoader クラスのプライベートメソッドとして登録される。下記はそのコード。Init_psych_to_ruby() 関数の最後の行に注目。


static VALUE path2class(VALUE self, VALUE path)
{
#ifdef HAVE_RUBY_ENCODING_H
    return rb_path_to_class(path);
#else
    return rb_path2class(StringValuePtr(path));
#endif
}

void Init_psych_to_ruby(void)
{
    VALUE psych     = rb_define_module("Psych");
    VALUE class_loader  = rb_define_class_under(psych, "ClassLoader", rb_cObject);

    VALUE visitors  = rb_define_module_under(psych, "Visitors");
    VALUE visitor   = rb_define_class_under(visitors, "Visitor", rb_cObject);
    cPsychVisitorsToRuby = rb_define_class_under(visitors, "ToRuby", visitor);

    rb_define_private_method(cPsychVisitorsToRuby, "build_exception", build_exception, 2);
    rb_define_private_method(class_loader, "path2class", path2class, 1);
}

Init_psych_to_ruby() 関数は、psych.c で定義されている Init_psych() 関数から呼ばれている。

void Init_psych(void)
{
    mPsych = rb_define_module("Psych");

    rb_define_singleton_method(mPsych, "libyaml_version", libyaml_version, 0);

    Init_psych_parser();
    Init_psych_emitter();
    Init_psych_to_ruby();
    Init_psych_yaml_tree();
}

Ruby は、ライブラリの共有ライブラリ (.so ファイル) をロードしたあと、「Init_ライブラリ名()」関数を呼ぶ。そのため、Psych の場合、psych.so が見つかりロードされれば、Init_psych() 関数が呼ばれ、それから Init_psych_to_ruby() が呼ばれ、結果 path2class メソッドが登録されることになる。しかし、もしも psych.so が存在しなければ、path2class メソッドは登録されず、"undefined method `path2class'" というエラーメッセージを見ることになる。

おそらく、Ruby もしくは Psych のパッケージングプロセスに何らかの問題があると思われる。


参照