Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

version_at returns self if timestamp is after object was destroyed #365

Closed
maxfelsher opened this issue May 2, 2014 · 3 comments
Closed
Milestone

Comments

@maxfelsher
Copy link

Steps to reproduce:

  1. Create a model and add "has_paper_trail" to it
  2. Create an instance of the model (w = Widget.create!)
  3. Destroy the instance (w.destroy)
  4. Call w.version_at(Time.now)

Expected behavior:
Returns nil (symmetric with calling version_at with a timestamp before the instance was created)

Actual behavior:
Returns the instance of the model (w)

Note that in this case, you could at least call w.destroyed? to see if it was destroyed. But if you use the solution mentioned in #204, which reifies the latest non-destroyed version of the object and then calls version_at, your call to destroyed? will return false.

I'm assuming this isn't intended behavior, but maybe I'm missing something?

It looks like the issue is here, where version_at sees that it can't find an entry in the versions table with a late enough timestamp. It then assumes that the object is live and further assumes that self is the live version of the object.

I overrode version_at in my model with the following, which calls find_by on the model class if there's no entry in the versions table to reify. That should return the live instance if one exists or nil if it's been destroyed. (I avoided find since that would raise an exception if the object has been destroyed.) I'm not sure if this causes other issues or if it'd need to be adjusted to deal with primary keys that are not called "id".

  # Returns the object (not a Version) as it was at the given timestamp.
  def version_at(timestamp, reify_options={})
    # Because a version stores how its object looked *before* the change,
    # we need to look for the first version created *after* the timestamp.
    v = send(self.class.versions_association_name).subsequent(timestamp).first
    v ? v.reify(reify_options) : self.class.find_by(id: id)
  end
@batter
Copy link
Collaborator

batter commented Jun 5, 2014

I believe this is the same issue as #354. There is a PR (#375) which contains a proposed fix for this type of thing. Look for this to be remedied in version 3.1.0.

@maxfelsher
Copy link
Author

I might be misunderstanding, but I don't think this is the same issue. This isn't really related to the timestamps, as far as I can tell--if you wait a while between my steps 3 and 4 above, you should get the same thing. The problem is that in the first line below, v is assigned nil if the object has been destroyed (because there are no versions after the timestamp if the timestamp is after the object has been destroyed). Then, the second line returns self, which makes some sense for a live object, but doesn't make sense (at least to me) for an object that has been destroyed. Note especially that self can be a reified old version of the object, which leads version_at to say that the version after destruction is that old version.

        v = send(self.class.versions_association_name).subsequent(timestamp, true).first
        v ? v.reify(reify_options) : self

@batter batter added this to the 3.0.3 milestone Jun 12, 2014
@batter batter closed this as completed in 3323f21 Jun 18, 2014
@batter
Copy link
Collaborator

batter commented Jun 18, 2014

Thanks for clarifying, sorry for the confusion, I should've read the initial issue report a little more closely. I agree with your assessment of how the method should behave, and I believe 3323f21 should correct the issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants