Model setters, override attribute=(attribute)?

Hi,

I have a model similar Basket shown below. If customer_type is set
then it should also set customer to nil. Likewise, if I set customer,
it should inspect the customer_type attached to the customer and set
accordingly within Basket. However implementing it as below doesn't
seem to work.

Am I missing a better way to achieve this? I had also considered using
a before_save :set_customer_type kind of thing but felt the code may
loose a little readability. I'm coming from a Java world with
protected attributes, accessed via domain influence
methods, .set_customer, .process_order etc... I wasn't really sure how
best to interpret this into ruby on rails models.

class Basket < ActiveRecord::Base

  belongs_to :customer_type
  belongs_to :customer

  def customer=(_customer)
    write_attribute(:customer, _customer)
    customer_type = _customer.customer_type unless _customer.nil?
  end

end

Thanks, Andrew.

Andrew Edwards wrote:

I have a model similar Basket shown below. If customer_type is set
then it should also set customer to nil. Likewise, if I set customer,
it should inspect the customer_type attached to the customer and set
accordingly within Basket. However implementing it as below doesn't
seem to work.
...
class Basket < ActiveRecord::Base
  belongs_to :customer_type
  belongs_to :customer

  def customer=(_customer)
    write_attribute(:customer, _customer)
    customer_type = _customer.customer_type unless _customer.nil?
  end
end

http://groups.google.com/group/rubyonrails-talk/browse_thread/thread/4fe057494c6e23e8

I prefer to do this sort of stuff in a callback triggered by .save, such as before_save, after_save, etc. That way it’s not destructive until I “commit” it.

Just my .02.

Thanks for the thoughts.

I like the idea of using the callbacks, I think in many simple cases
of setting associated fields it works well. The situations I think I
am more referring to are those where you are responding to an event
within the model. Such an event would trigger other methods and
possibly execute within a database transaction. I suppose in reality
these are not often tied to simple updates of a single field, in which
case I would probably create a new all-encompassing method anyway.

I suppose I'm just used to encapsulating attributes behind setters and
model event methods. The AR way of exposing everything probably means
a different approach/mindset? Is it okay not to worry about
accidentally setting an attribute that really should only be set as
part of a larger transaction?

Make the callbacks public. You can call them when you want.

before_save :do_my_stuff

def do_my_stuff

does stuff

end

The thing to remember is that Ruby is not Java, or C#… Essesntially, everything is public (even private stuff), so encapsulating things behind accessors isn’t really necessary. Often times, it’s not even desireable.

When I do

user.first_name = “Brian”

I don’t want that saved to the db.

So when I do

foo.bar = “something”

I would expect that to work the same way.

There’s no real right or wrong answer though :slight_smile: Do what makes you comfortable. One thing you will find with Ruby is that simple is better, and almost always ends up working better too. That’s what I love about it.

The last line in #customer= should be "self.customer_type = ... "

Remember, attribute writers cannot stand alone or ruby interprets them
as local variables. Most likely, your code is not working because of
this bug, not because of a bigger design issue.

It's a nuisance, and has cost me hours of debugging in the past too.

regards, Jürgen

HIi,

I m not really sure and I just would like to mention it:
my_basket.customer_id = 1 will not call your setter