kakakakakku blog

Weekly Tech Blog: Keep on Learning!

"hash is defined by Active Record" って怒られた

先週に引き続き Rails 移行検証をしていて,既存のテーブルに hash カラムが存在していると ActiveRecord で ActiveRecord::DangerousAttributeError が発生する問題を発見した.少し調べてみたので残しておく.正確に言うと hash だけじゃなくて ActiveRecord::Base に実装されているメソッド名は基本的にダメっていう理解で正しいと思う.例えば save もそう.

ActiveRecord::DangerousAttributeError: hash is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.

検証環境

  • Ruby 2.2.3
  • Rails 4.2.4

Rails Issue を読んでみた

結論として hash カラムは ActiveRecord ではサポートしていないということだった.ワークアラウンドとして dangerous_attribute_method? にモンキーパッチを当てる方法もあるけど,何が起きても知らないよと.

there's no supported way around this behavior. You "could" monkey patch this method but there's no guarantee that you'll end up with working code.

safe_attributes

ActiveRecord::DangerousAttributeError の問題を解決してくれそうな Gem として safe_attributes がある.ただし,現在はもう全くメンテナンスされていないし,サポートしてる環境も Ruby 1.9 で止まってるので,今から使うには厳しそうな雰囲気だった.

実際に試してみたけど,単純に ActiveRecord::DangerousAttributeError を回避できるだけで,実際に hash カラムの値が取得できるわけではなく,モデルオブジェクトに対する .hash の値が返ってくるだけだった.

pry(main)> Project.first.hash
-4443704500580638528

参考として実装を見てみたところ ActiveRecord::Base に実装されているメソッドに一致する場合に return したり,ActiveRecord::DangerousAttributeError が発生したら rescue してた.

def define_method_attribute(attr_name)
  return if (bad_attribute_names.include?(attr_name.to_sym))
  super(attr_name)
end
def instance_method_already_implemented?(method_name)
  begin 
    return super(method_name)
  rescue ActiveRecord::DangerousAttributeError
    return true
  end
  return false
end

まとめ

先週書いたエントリーと同じ結論になるけど,Rails に移行するのであれば,変に技術的負債を抱えるのではなくて,事前に解消した方が幸せになれると思う.とは言え,あくまで理想論なので,システムの状況を踏まえて意思決定をしたいなというところ.

関連エントリー