CarrierWaveを使った時にundefined method `validate_integrity'が出て困った話

TL;DR

アプリケーション名とCarrierWaveで生成されるuploaderが同じ名前になるとundefined method `validate_integrity'のエラーが出て困る。

とった解決策

多分何らかのオプションがあってずらせる気がするが、今回はアプリケーション名とuploaderが変わるように変更した。

まとめ

名前空間が衝突すると変なことになるので注意

前提

多分こんなマヌケなことをやらかすのは僕だけだと思ってるのですが、

Rails+CarrierWaveを使った時にうまく動かなくてちょっと諦めかけてたのですが、問題自体がかなり間抜けっぽかったので多分皆引っかからないと思うのですが解決策もよくわかってないのでメモだけは残しておく。

CarrierWaveのせいでもRailsのせいでも無くて使い手の問題な気がします。

つまり俺が悪い。一応バージョンだけ記載しておきます。

使用したソフトウェア バージョン
ruby 2.2.3
rails 4.2.4
carrierwave 0.10.0

他はまぁ適当に使ってます。databaseとかはなんでもいい気がするので書いてません。

何をしたか?

手慰みに画像のアップロード機能をRailsでためしてみることにしたかった。

というわけで安直に名前をImageUploaderとしてrails newをした。(ココが原因)

振り返り

プロジェクト生成

Rails new ImageUploader

生成したプロジェクトにCarrierWaveのGemを追加する。

source 'https://rubygems.org'

gem 'carrierwave'
#~~~~長いんで省略~~~~

で、Gemfileに書き加えたのでいつものごとくbundle installを叩く。

これで準備は完了。

Scaffold

そして画像をアップロードした時のモデルとコントローラーをscaffoldでどかっと作ってしまう。 名前(name)と画像管理用のカラム(image)をもつImageモデルを作る。

管理に使うカラムのクラスはCarrierWaveのREADEMEにかかれている通り、stringを指定している。

bundle exec rails g scaffold Image name:string image:string

で、生成されたmigrationファイルを実行しておく。

bundle exec rake db:create
bundle exec rake db:migrate
== 20151012181358 CreateImages: migrating =====================================
-- create_table(:images)
   -> 0.0016s
== 20151012181358 CreateImages: migrated (0.0017s) ============================

この時点でrailsを起動すると、したのようにいつもの画面が出てくる

f:id:k2170:20151013032341p:plain

uploaderを作る

そして本題のuploaderを作る。この辺りもちゃんとREADMEにかかれているので参照されると良いと思う。

bundle exec rails g uploader Image

これを叩くと app/uploaders/ の配下に image_uploader.rb が生成される。

特に変換とかしたいわけではないので、デフォルトをそのまま使う。

モデルと結びつける

class Image < ActiveRecord::Base
  mount_uploader :image, ImageUploader
end

railsを起動する

f:id:k2170:20151013032643p:plain エラーが出る…

はてさてこの原因は何だろうということでエラーをじっと見てみるとModuleとか出てるわけで、

というわけでconfig/application.rbを見てみると

require File.expand_path('../boot', __FILE__)

require 'rails/all'

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module ImageUploader
  class Application < Rails::Application
    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration should go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded.

    # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
    # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
    # config.time_zone = 'Central Time (US & Canada)'

    # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
    # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
    # config.i18n.default_locale = :de

    # Do not swallow errors in after_commit/after_rollback callbacks.
    config.active_record.raise_in_transactional_callbacks = true
  end
end

うん、多分これだ。

名前空間が衝突しちゃったみたいだ。

というわけで、別の名前でmigrationからやり直してみる。

%s/Image/Gazo/

bundle exec rails g scaffold Gazo name:string gazo:string
bundle exec rails g uploader Gazo

bundle exec rake db:migrate
class Gazo < ActiveRecord::Base
  mount_uploader :gazo, GazoUploader
end

で実行してみるとちゃんと実行される f:id:k2170:20151013034353p:plain

まとめ

めったに無いとは思いますが、application名とuploaderが一緒になるような場合問題になるようです。 ご注意ください。