Scope on #children affecting callbacks #44

Closed
cheerfulstoic opened this Issue Apr 22, 2011 · 11 comments

Projects

None yet

7 participants

@cheerfulstoic

Hey,

I don't know if this is anything that you can do or if it needs to be escalated to the Rails team, but I'm curious to hear your thoughts. I just switched from acts_as_tree and everything is working well except for a callback I made in one of my models called Feedback. I have a two level hierarchy (Feedback and their children, which act as the comments). There was a desire to stamp the parent with the date/time of the last child in order to be able to sort by that value, so I created the following:

  after_create :set_last_commented_at
  def set_last_commented_at
    if parent
      parent.update_attribute(:last_commented_at, Time.now)
    end
  end

The problem is that when I go to create a new child like this:

    children.create(:name => User.current.full_name, :email => User.current.email, :body => body)

The set_last_commented_at callback is in the scope of the "children" method from ancestry, so I get an error like this from the first line of the callback (where it's trying to get the parent):

Couldn't find Feedback with ID=9 [WHERE "feedback"."ancestry" = '9' AND (feedback.deleted_at IS NULL)]

The deleted_at part comes from the acts_as_paranoid gem, but you can see it's trying to get the parent by the ID 9 but it's also limiting to children of 9, which mean it gets nothing

I'm looking to implement this callback in a different way and I've already found a way around it by doing this:

  def set_last_commented_at
    self.class.unscoped do
      if parent
        parent.update_attribute(:last_commented_at, Time.now)
      end
    end
  end

But that took me a while to figure out and I'm sure it will trip up other developers in other parts of the app, so I was wondering if you had any thoughts

Owner

I didn't know you could unscope a class like that. There are more people having these issues. Thanks for the tip.

Maybe I should just unscope the class in methods like parent and root that just need to return a single instance.

What do you think?

+upvoted+
Yeah I ran into the same issue, any callbacks or validations that call on root or parent run into this issue... its confusing because it only seems to proc on

`````` children.create()```
but works if you

n.save

I would just unscope the #parent and #root methods, I don't forsee any issues with doing so.
Anyways, mucho gracius to cheerfulstoic for the hotfix, and im lovin this gem stefankroes!

Note that unscoped is rails3 only, in rails2 with_exclusive_scope should be used.

@moiristo moiristo closed this May 26, 2011
@moiristo moiristo reopened this May 26, 2011

Yeah, I'm not sure what to do exactly. It might be good if you could mark that part of the scope for later undoing, but having looked at the Rails source it seems like it wouldn't be a fun thing to implement. You could unscope it, but then you'd get rid of any default_scopes as well as any other scoping that's part of the chain.

For the moment I've created a test to cover this situation:
cheerfulstoic@e30a4ce

I'll open a ticket on the Rails github page and see if they have any brilliant ideas.

Ticket for reference: rails/rails#1350

Pull request for reference: #51

I was wondering, this issue is several months old and I just ran into a situation like this myself. where doing self.website.pages for example would result in a query where it actually looked for pages with a specific ancestry value, even though I didn't scope by that attribute.

I worked around it wrapping the code inside a Page.unscoped block, but it's still something you can easily lose a few hours over, wondering whats going on (or at least I did, haha).

Is there a reason the fix(?) by @cheerfulstoic hasn't been pulled in yet?

Not sure, but I think it didn't get pulled because scoped and unscoped are only available in rails 3.x?

tadman commented Feb 15, 2012

I'm trying to use ancestry with Rails 3.2.1 and I'm hitting this issue when creating children through the model.children relationship. It appears to be the case that the find scope on parent is set and is not finding anything:

ActiveRecord::RecordNotFound: Couldn't find Component with id=141 [WHERE `components`.`ancestry` = '141']

This could be fixed with the unscoped technique @cheerfulstoic demonstrates.

It might be the case that this scoping problem is Rails 3.2 specific, so testing for the presence of unscoped and using it if possible would avoid the problem.

tadman commented Feb 15, 2012

Here's my horrible monkeypatch to fix this issue:

module Ancestry
  module InstanceMethods
    def parent
      if parent_id.blank? then nil else self.base_class.respond_to?(:unscoped) ? self.base_class.unscoped.find(parent_id) : self.base_class.find(parent_id) end
    end
  end
end
Collaborator
StefanH commented Jun 27, 2012

Resolved on master in cd268b6 and e552e10

@StefanH StefanH closed this Jun 27, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment