はじめに
DB で利用しないカラムが発生して、プライベート化するということで他の方が対応していた
コードレビュー時に「ほんとにこれでいいの?」と思ったので検証および問題点の対応策を考えてみました
ActiveRecord のカラムについて
ActiveRecordは、Ruby on Railsの一部であり、
オブジェクト指向型データベースマッピングの実装を提供します
ActiveRecordを使用すると、
データベーステーブルの行が Ruby オブジェクトとして扱われます
ActiveRecordモデルのカラムには、
通常publicアクセス権が与えられており、どの部分からでもアクセスすることができます
他の方の対応内容
ActiveRecordでは、モデルクラスの各カラムには通常、
自動的にアクセサメソッドが定義されます
しかし、その中にはDB上で利用しなくなってしまったカラムが含まれる場合があります
このような場合、アクセサメソッドをprivateにすることで、
予期せぬ変更が加えられるのを防止することができます
検索してもこのような内容が多くでてきます
class Sample < ApplicationRecord
private
attr_accessor :unused_column
end
問題点
attr_accessorをprivateにしても、クラス外からはアクセスできませんが、
ハッシュ構造でアクセスする場合はprivate化が効果を発揮しません。
class Sample < ApplicationRecord
private
attr_accessor :unused_column
end
sample = Sample.new()
sample.unused_column = "foo" # => NoMethodError (private method `unused_column=' called for #<Sample:0x00007fd0cc104e10>)
sample[:unused_column] = "foo"
解決策
hash アクセスを禁止する
[]=メソッドをオーバーライドして、ハッシュアクセス自体を禁止することで、
privateなカラムを保護することができます。
class Sample < ApplicationRecord
private
attr_accessor :unused_column
def []=(key, value)
raise 'Hash access is not allowed' if key == :unused_column
super
end
end
影響範囲
テストコードのデータ生成にFactoryBotを使用している場合には影響があります
FactoryBotではcreate時にunused_column=のメソッドが呼び出されるため、
別の方法を使用する必要があります
例えば、以下のようにtraitを定義することで、
create時にunused_columnに値を設定することができます
class Sample < ApplicationRecord
def []=(key, value)
raise 'Hash access is not allowed' if key == :unused_column
super
end
private
attr_accessor :unused_column
end
FactoryBot.define do
factory :sample do
trait 'unused_column_foo' do
before(:build) do |sample|
sample.send('unused_column=', 'foo')
end
end
end
end
以下のようにcreateメソッドを使用してサンプルオブジェクトを作成することができます
sample = FactoryBot.create(:sample, :unused_column_foo)
おわりに
RailsにおけるActiveRecordカラムのプライベート化についてまとめてみました
これによって、モデルのカラムを適切に保護し予期しない変更を防ぐことができます
色々な事情でDBの構造の変更が難しいケースの場合の参考になれば