我有一个看起来像这样的模型:
class StopWord < ActiveRecord::Base UPDATE_KEYWORDS_BATCH_SIZE = 1000 before_save :update_keywords def update_keywords offset = 0 max_id = ((max_kw = Keyword.first(:order => 'id DESC')) and max_kw.id) || 0 while offset <= max_id begin conditions = ['id >= ? AND id < ? AND language = ? AND keyword RLIKE ?',offset,offset + UPDATE_KEYWORDS_BATCH_SIZE,language] # Clear keywords that matched the old stop word if @changed_attributes and (old_stop_word = @changed_attributes['stop_word']) and not @new_record Keyword.update_all 'stopword = 0',conditions + [old_stop_word] end Keyword.update_all 'stopword = 1',conditions + [stop_word] rescue Exception => e logger.error "Skipping batch of #{UPDATE_KEYWORDS_BATCH_SIZE} keywords at offset #{offset}" logger.error "#{e.message}: #{e.backtrace.join "\n "}" ensure offset += UPDATE_KEYWORDS_BATCH_SIZE end end end end
这个工作正常,因为单元测试显示:
class KeywordStopWordTest < ActiveSupport::TestCase def test_stop_word_applied_on_create kw = Factory.create :keyword,:keyword => 'foo bar baz',:language => 'en' assert !kw.stopword,'keyword is not a stop word by default' sw = Factory.create :stop_word,:stop_word => kw.keyword.split(' ')[1],:language => kw.language kw.reload assert kw.stopword,'keyword is a stop word' end def test_stop_word_applied_on_save kw = Factory.create :keyword,:language => 'en',:stopword => true sw = Factory.create :keyword_stop_word,:language => kw.language sw.stop_word = 'blah' sw.save kw.reload assert !kw.stopword,'keyword is not a stop word' end end
但是使用@changed_attributes实例变量感觉很糟糕.是否有标准的Rails-y方法来获取正在保存时修改的属性的旧值?
更新:感谢Douglas F Shearer和Simone Carletti(显然更喜欢墨菲的吉尼斯),我有一个更清洁的解决方案:
def update_keywords offset = 0 max_id = ((max_kw = Keyword.first(:order => 'id DESC')) and max_kw.id) || 0 while offset <= max_id begin conditions = ['id >= ? AND id < ? AND language = ? AND keyword RLIKE ?',language] # Clear keywords that matched the old stop word if stop_word_changed? and not @new_record Keyword.update_all 'stopword = 0',conditions + [stop_word_was] end Keyword.update_all 'stopword = 1',conditions + [stop_word] rescue StandardError => e logger.error "Skipping batch of #{UPDATE_KEYWORDS_BATCH_SIZE} keywords at offset #{offset}" logger.error "#{e.message}: #{e.backtrace.join "\n "}" ensure offset += UPDATE_KEYWORDS_BATCH_SIZE end end end
多谢你们!
解决方法
你想要ActiveModel :: Dirty.
例子:
person = Person.find_by_name('Uncle Bob') person.changed? # => false person.name = 'Bob' person.changed? # => true person.name_changed? # => true person.name_was # => 'Uncle Bob' person.name_change # => ['Uncle Bob','Bob']
完整文档:http://api.rubyonrails.org/classes/ActiveModel/Dirty.html