diff --git a/README.md b/README.md index 192b59cd..52cb8ab1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ There's an excellent [Railscast on implementing Undo with Paper Trail](http://ra * Allows you to get at every version, including the original, even once destroyed. * Allows you to get at every version even if the schema has since changed. * Allows you to get at the version as of a particular time. -* Automatically restores the `has_one` associations as they were at the time. +* Option to automatically restore `has_one` associations as they were at the time. * Automatically records who was responsible via your controller. PaperTrail calls `current_user` by default, if it exists, but you can have it call any method you like. * Allows you to set who is responsible at model-level (useful for migrations). * Allows you to store arbitrary model-level metadata with each version (useful for filtering versions). @@ -337,7 +337,7 @@ If you can think of a good way to achieve this, please let me know. ## Has-One Associations -PaperTrail automatically restores `:has_one` associations as they were at (actually, 3 seconds before) the time. +PaperTrail can restore `:has_one` associations as they were at (actually, 3 seconds before) the time. class Treasure < ActiveRecord::Base has_one :location @@ -349,21 +349,17 @@ PaperTrail automatically restores `:has_one` associations as they were at (actua >> treasure.update_attributes :amount => 153 >> treasure.location.update_attributes :latitude => 54.321 - >> t = treasure.versions.last.reify + >> t = treasure.versions.last.reify(:has_one => true) >> t.amount # 100 >> t.location.latitude # 12.345 The implementation is complicated by the edge case where the parent and child are updated in one go, e.g. in one web request or database transaction. PaperTrail doesn't know about different models being updated "together", so you can't ask it definitively to get the child as it was before the joint parent-and-child update. -The correct solution is to make PaperTrail aware of requests or transactions (c.f. [Efficiency's transaction ID middleware](http://github.com/efficiency20/ops_middleware/blob/master/lib/e20/ops/middleware/transaction_id_middleware.rb)). In the meantime we work around the problem by finding the child as it was a few seconds before the parent was updated. By default we go 3 seconds before but you can change this by passing the `:has_one` option to `reify`: +The correct solution is to make PaperTrail aware of requests or transactions (c.f. [Efficiency's transaction ID middleware](http://github.com/efficiency20/ops_middleware/blob/master/lib/e20/ops/middleware/transaction_id_middleware.rb)). In the meantime we work around the problem by finding the child as it was a few seconds before the parent was updated. By default we go 3 seconds before but you can change this by passing the desired number of seconds to the `:has_one` option: >> t = treasure.versions.last.reify(:has_one => 1) # look back 1 second instead of 3 -If you are shuddering, take solace from knowing you can opt out of these shenanigans: - - >> t = treasure.versions.last.reify(:has_one => false) # I say no to "workarounds"! - -Opting out means your `:has_one` associated objects will be the live ones, not the ones the user saw at the time. Since PaperTrail doesn't auto-restore `:has_many` associations (I can't get it to work) or `:belongs_to` (I ran out of time looking at `:has_many`), this at least makes your associations wrong consistently ;) +If you are shuddering, take solace from knowing PaperTrail opts out of these shenanigans by default. This means your `:has_one` associated objects will be the live ones, not the ones the user saw at the time. Since PaperTrail doesn't auto-restore `:has_many` associations (I can't get it to work) or `:belongs_to` (I ran out of time looking at `:has_many`), this at least makes your associations wrong consistently ;) diff --git a/lib/paper_trail/version.rb b/lib/paper_trail/version.rb index 836a088b..97187343 100644 --- a/lib/paper_trail/version.rb +++ b/lib/paper_trail/version.rb @@ -32,7 +32,8 @@ class Version < ActiveRecord::Base # sub-second datetimes if you want them). def reify(options = {}) without_identity_map do - options.reverse_merge! :has_one => 3 + options[:has_one] = 3 if options[:has_one] == true + options.reverse_merge! :has_one => false unless object.nil? attrs = YAML::load object diff --git a/test/unit/model_test.rb b/test/unit/model_test.rb index e8f255ba..b5651d90 100644 --- a/test/unit/model_test.rb +++ b/test/unit/model_test.rb @@ -117,11 +117,17 @@ class HasPaperTrailModelTest < ActiveSupport::TestCase context 'and has one associated object' do setup do @wotsit = @widget.create_wotsit :name => 'John' - @reified_widget = @widget.versions.last.reify + end + + should 'not copy the has_one association by default when reifying' do + reified_widget = @widget.versions.last.reify + assert_equal @wotsit, reified_widget.wotsit # association hasn't been affected by reifying + assert_equal @wotsit, @widget.wotsit # confirm that the association is correct end - should 'copy the has_one association when reifying' do - assert_nil @reified_widget.wotsit # wotsit wasn't there at the last version + should 'copy the has_one association when reifying with :has_one => true' do + reified_widget = @widget.versions.last.reify(:has_one => true) + assert_nil reified_widget.wotsit # wotsit wasn't there at the last version assert_equal @wotsit, @widget.wotsit # wotsit came into being on the live object end end