Using before_save to update a record only once.

I'd like to use before_save to perform an action (I won't get into the
details), but only the first time the record is changed from 0 to 1.
Here's the code I have so far in my model:

def before_save
  if completed == 1
     # perform the action
  end
end

What the above does is perform the action when "completed" is set to
one, but the problem is I don't want this to happen every single time
there is a save. So what I'd like to be able to do is check the previous
value of "completed" and only perform the action if it used to be 0 and
is now 1. For example (psuedo code):

def before_save
  if old_completed == 0 and completed == 1
     # perform the action
  end
end

How would I go about this? Obviously, I could add code outside the
model, but before_save seems like the nicest, cleanest place to for this
code to be. I searched this forum and found a post that kind of helps
answer my question, but it's not super clear and I'm still not sure what
the best solution is. It sounds like I have to do an external find just
to do this check, and that just doesn't seem right to me. Is that really
the only way?

Don't have a specific answer for you, but the acts_as_changed plugin might come in handy for you...

http://agilewebdevelopment.com/plugins/acts_as_changed

-philip

Philip Hallstrom wrote:

What the above does is perform the action when "completed" is set to

How would I go about this? Obviously, I could add code outside the
model, but before_save seems like the nicest, cleanest place to for this
code to be. I searched this forum and found a post that kind of helps
answer my question, but it's not super clear and I'm still not sure what
the best solution is. It sounds like I have to do an external find just
to do this check, and that just doesn't seem right to me. Is that really
the only way?

Don't have a specific answer for you, but the acts_as_changed plugin
might come in handy for you...

http://agilewebdevelopment.com/plugins/acts_as_changed

-philip

Thanks, Philip. I'm having a difficult time understanding exactly what
it does. The one line explanation isn't 100% clear to me. It sort of
looks like a speed related plug-in that simply updates fields that been
changed, rather than going through each field in the entire table, but I
could be wrong about that.

I've never used it myself :slight_smile: ANd I think you're right about that. There's a "changed_attributes" method that returns what's changed though that you could use perhaps...

Guess I thought it might provide more inspiration than an outright answer :slight_smile:

Thinking more about it, you could override the completed method to record that it was changed and then check that in your before_save...

-philip

Philip Hallstrom wrote:

changed, rather than going through each field in the entire table, but I
could be wrong about that.

I've never used it myself :slight_smile: ANd I think you're right about that.
There's a "changed_attributes" method that returns what's changed though
that you could use perhaps...

Guess I thought it might provide more inspiration than an outright
answer
:slight_smile:

Thinking more about it, you could override the completed method to
record
that it was changed and then check that in your before_save...

-philip

Sorry I'm a little late here -- been away for a couple days. That sounds
interesting. Can you explain in more detail how I'd do something like
that?

Hrm... something like this assuming your model has 'completed'
attribute.

class MyModel < ...

   attr :is_complete

   def completed=(val)
     if completed == 0 && val == 1 then
       is_complete = true
     else
       is_complete = false
     end
     completed = val
   end

   def before_save
     if is_complete != true then
       return false
     end
     true
   end

end

MenDAKE :

My code looks something like this:

class MyModel < ActiveRecord::Base
  attr :completed_is_changed

  # This checks to see if the completed fiel was set to true for the
first time.
  def completed=(val)
    if completed == 0 && val == 1
      completed_is_changed = true
    else
      completed_is_changed = false
    end

    completed = val
  end

  def before_save
    if completed_is_changed == true
       #perform the action.
    end
end

The actions are performed, but the completed field value is
now never saved to the database. I would expect that the
line "completed = val" would take care of that, but it doesn't
seem to. Is something missing?

You can write it like that :

def completed=(val)
  completed_is_changed = completed == 0 && val == 1
  write_attribute(:completed, val)
end

   -- Jean-François.

Philip Hallstrom wrote:

answer

that?

Hrm... something like this assuming your model has 'completed'
attribute.

class MyModel < ...

   attr :is_complete

   def completed=(val)
     if completed == 0 && val == 1 then
       is_complete = true
     else
       is_complete = false
     end
     completed = val

Ah, I suspect this is it... do something like...

self['completed'] = val

or whatever rails does by default for this thing...