rspecのfixturesに指定する名前でひっかかった話
rails3のrspecを書いて動かしていたときの話。
- 環境
- rails (3.2.13)
- rspec (2.13.0)
- activerecord (3.2.13)
- activesupport (3.2.13)
rspecのfixturesに指定する名前によっては、例外がログに出る事があるみたい。
fixtureに :xxx_meta という名前を指定した事が引き金だった。
rspecでfixturesを使ってた
とあるrails3なアプリのrspecでfixtureを使ってみたところ、実行する度にログにstack traceが出てくるようになってた。
# spec/models/blog_meta_spec.rb require 'spec_helper' describe BlogMeta do fixtures :blog_meta # ... spec... end
spec/fixtures/blog_meta.yml にはちゃんとfixtureなデータファイルは置いてて、期待する感じで動いてくれる。
動いてくれるのだけど、なぜか実行の度にstack traceがログに出てくる。
気持ち悪い!
rspecの実行の度にstack traceがログに出る
Unable to load blog_metum, underlying cause No such file to load -- blog_metum (snip)/vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/dependencies.rb:317:in `rescue in depend_on' (snip)/vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/dependencies.rb:312:in `depend_on' (snip)/vendor/bundle/ruby/1.9.1/gems/activesupport-3.2.13/lib/active_support/dependencies.rb:225:in `require_dependency' (snip)/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb:767:in `try_to_load_dependency' (snip)/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb:782:in `block in require_fixture_classes' (snip)/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb:779:in `each'(snip)/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb:779:in `require_fixture_classes' (snip)/vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb:762:in `fixtures' (snip)/spec/models/blog_meta_spec.rb:4:in `block in <top (required)>' (snip)/vendor/bundle/ruby/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:242:in `module_eval' (snip)/vendor/bundle/ruby/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:242:in `subclass'(snip)/vendor/bundle/ruby/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/example_group.rb:228:in `describe' (snip)/vendor/bundle/ruby/1.9.1/gems/rspec-core-2.13.1/lib/rspec/core/dsl.rb:18:in `describe' (snip)/spec/models/blog_meta_spec.rb:3:in `<top (required)>' (...snip...)
(一部省略)
原因
結論からいうと、
ActiveSupport(ここでは3.2.13) - String.singularize
文字列の単数形を作ってくれる便利なメソッドの挙動が原因。
$ bundle exec ./script/rails console Loading development environment (Rails 3.2.13) 1.9.1 :001 > "blog_meta".singularize => "blog_metum" 1.9.1 :002 >
meta の 単数形 が metum になってる。
もう少し細かい話
stack traceを追いかけてみると、少しずつ見えてきた。
activerecordでは、fixturesをロードする前に、指定された名前から必要となりそうなファイル名を推測して先にロードしようとがんばる。
そのときに指定した名前を単数形にして、そのファイルをロードしようとする様子。
まさにこのfixtures.rb:781
# vendor/bundle/ruby/1.9.1/gems/activerecord-3.2.13/lib/active_record/fixtures.rb 778 def require_fixture_classes(fixture_names = nil) 779 (fixture_names || fixture_table_names).each do |fixture_name| 780 file_name = fixture_name.to_s 781 file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names 782 try_to_load_dependency(file_name)
rspecの "fixtures :blog_meta"という記述の引数にあたる部分が fixture_namesになってわたっていく。
そして、 "blog_meta".singularize => "blog_metum" となり、その"blog_metum"をloadしようとして、そんなファイルないわーと例外を投げる結果になる。
ちなみにここではActiveRecord::Base.pluralize_table_names はdefaultのまま trueでした。
対策
一つの解決方法として、設定で回避する事に。
Railsで単数形をsingularizeすると不可解な変化をする場合の対応
rails3でも同様な対処ができる様子。
# config/initializers/inflections.rb ActiveSupport::Inflector.inflections do |inflect| inflect.singular 'blog_meta', 'blog_meta' end
こんな風に、「"blog_meta"の複数形は "blog_meta"にしてください」とお願いすると回避できた。
$ bundle exec ./script/rails console Loading development environment (Rails 3.2.13) 1.9.1 :001 > "blog_meta".singularize => "blog_meta" 1.9.1 :002 >
すっきりした。