Cristian
Cristian

Reputation: 138

Locking an attribute after it has a certain value

I have a model where if it is given a certain status, the status can never be changed again. I've tried to achieve this by putting in a before_save on the model to check what the status is and raise an exception if it is set to that certain status.

Problem is this -

 def raise_if_exported
   if self.exported?
     raise Exception, "Can't change an exported invoice's status"
   end
 end  

which works fine but when I initially set the status to exported by doing the following -

 invoice.status = "Exported"
 invoice.save

the exception is raised because the status is already set the exported on the model not the db (I think)

So is there a way to prevent that attribute from being changed once it has been set to "Exported"?

Upvotes: 1

Views: 1149

Answers (4)

Nick Malcolm
Nick Malcolm

Reputation: 726

I'd go for a mix of @Trip and @Sikachu's answers:

validate :check_if_exported

def check_if_exported
  if status_changed? && status_was.eql?("Exported")
    errors.add(:status, " cannot be changed once exported.")
  end
end

Invalidating the model is a better response than just throwing an error, unless you reeeally want to do that.

Upvotes: 1

Harish Shetty
Harish Shetty

Reputation: 64363

You can use an validator for your requirement

class Invoice < ActiveRecord::Base

  validates_each :status do |record, attr, value|
     if ( attr == :status and status_changed? and status_was == "Exported")
       record.errors.add(:status, "you can't touch this")
     end
  end

end

Now

invoice.status= "Exported"
invoice.save # success

invoice.status= "New"
invoice.save # error

Upvotes: 3

sikachu
sikachu

Reputation: 1221

You can also use ActiveModel::Dirty to track the changes, instead of checking current status:

def raise_if_exported
  if status_changed? && status_was == "Exported"
    raise "Can't change an exported invoice's status"
  end
end

Try this, only if you really want that exception to raise on save. If not, check it during the validation like @Trip suggested

See this page for more detail.

Upvotes: 1

Trip
Trip

Reputation: 27114

Try Rails built in validation in your model :

validate :check_if_exported

def check_if_exported
  if self.exported?
    errors.add(:exported_failure, "This has been exported already.")
  end
end

Upvotes: 0

Related Questions