ryanmrubin
ryanmrubin

Reputation: 728

stale association data on ActiveRecord model instance?

I'm using Rails 2, and I have a one-to-many association between the Project model and the Schedule model in this app.

I have an observer check when attributes of various things change, and in response, it iterates over an array of hashes with which to populate new Schedule instances.

Each Schedule instance should have an attribute, disp_order, which eventually tells the front end where to display it when showing them in a list. The disp_order is populated upon adding the schedule to the project, so that it equals one more than the current highest disp_order.

The problem is in the iteration in the observer. When I iterate over the array of hashes filled with Schedule data, each time through the iteration should calculate the disp_order as one higher than the previous one, to account for the Schedule it just added. However, in practice, it doesn't--unless I refresh the Project object in the middle of the iteration with project = Project.find(project.id), or else it seems always to calculate the same max value of the disp_orders, and doesn't have an accurate list of those Schedule instances to go on.

Is there a better way to do this? (Note: I just mean is there a better way so that I don't have to tell project to re-find itself. There are a number of places I'm actively cleaning up the rest of the code which follows, but for this question I'm really only interested in this one line and things that impact it.)

project.rb

class Project < ActiveRecord::Base
  # ...
  has_many :schedules, :dependent => :destroy
  # ...
  belongs_to :data, :polymorphic => true, :dependent => :destroy
    accepts_nested_attributes_for :data
  # ...

schedule.rb

class Schedule < ActiveRecord::Base
  belongs_to :project, :include => [:data]
  # ...

project_event_observer.rb

class ProjectEventObserver < ActiveRecord::Observer
  # ...          
  def perform_actions(project, actions)
    # ...
    actions.each { |action|
      action.each { |a_type, action_list|
        action_list.each { |action_data|
          self.send(action_type.to_s.singularize, action_data, project)
          project = Project.find(project.id) #My question is about this line.
        }
      }
    }
  # ...

sample actions for the iteration above

[    
  {:add_tasks => [
    {:name => "Do a thing", :section => "General"},
    {:name => "Do another thing", :section => "General"},
   ]
  },
  # More like this one.
]

the observer method that receives the rest of the action data

def add_task(hash, project)
  # ...
  sched = Schedule.new
  hash.each { |key, val|
    # ...
    sched.write_attribute(key, val)
    # ...
  }
  sched.add_to(project)
end

schedule.rb

def add_to(proj)
  schedules = proj.schedules
  return if schedules.map(&:name).include?(self.name)
  section_tasks = schedules.select{|sched| sched.section == self.section}
  if section_tasks.empty?
    self.disp_order = 1
  else
    self.disp_order = section_tasks.map(&:disp_order).max + 1 #always display after previously existing schedules
  end
  self.project = proj
  self.save
end

Upvotes: 4

Views: 1104

Answers (1)

yekta
yekta

Reputation: 3433

You're running into this issue because you're working off of a in-memory cached object.

Take a look at the association documentation.

You can pass the argument of true in your Schedule model's add_to function to reload the query.

Other options would include inverse_of but that's not available to you because you're using polymorphic associations, as per the docs.

Upvotes: 3

Related Questions