最近、簡単な移行スクリプトを書きたいと思いました。私が書いた:
@entries = Entries.all(:text => /test/)
@entries.each do |entry|
entry.update(:text => entry.text.gsub!(/test/, "no-test"))
end
updateステートメントがtrueを返したにもかかわらず、レコードは保存されませんでした。私は何を取りこぼしたか?
最近、簡単な移行スクリプトを書きたいと思いました。私が書いた:
@entries = Entries.all(:text => /test/)
@entries.each do |entry|
entry.update(:text => entry.text.gsub!(/test/, "no-test"))
end
updateステートメントがtrueを返したにもかかわらず、レコードは保存されませんでした。私は何を取りこぼしたか?
1.x シリーズの datamapper では#==
、新旧の属性値を呼び出してダーティを検出することにより、ダーティ トラッキングが行われます。オブジェクトがインプレースで変更された場合 (たとえば、String bang メソッドを使用)、「元の」状態も変更されるため、変更を検出できません。
基本的に、次のことが内部で発生します。
a = "foo"
b = a.gsub!("foo", "bar")
a == b # => true both a and b refer to the same mutated object
a.equal?(b) # => true
あなたの例では、元の変更された属性をオブジェクトに割り当てます。ID の変更はありません => 更新は検出されません。
String#gsub
元の属性値を変更する代わりに新しいオブジェクトを作成した場合String#gsub!
、検出可能な変更が発生します。
別の値を持つ新しいオブジェクトを割り当てると、次のことが起こります。
a = "foo"
b = a.gsub("foo", "bar")
a == b # => false, loaded state does not equal resource state so change is detected
a.equal?(b) # => false
そして、すべてのケースをカバーするために、同じ値を持つ新しいオブジェクトを割り当てます:
a = "foo"
b = "foo"
a == b # => true, no dirtyness detected.
a.equal?(b) # => false
うまくいけば、これはすべての同様のケースを説明するのに十分なセマンティックの違いを説明します。
ところで、datamapper 2.0 では、インプレース ミューテーションもキャッチする異なるメカニズムがあります。免責事項、私はdm-sessionと呼ばれるこのコンポーネントの作成者です。
感嘆符を削除します。
entry.update(:text => entry.text.gsub(/test/, "no-test"))
文字列の内容を置き換えても、レコードは汚れません。再割り当てする必要があります。