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 E. wrote:

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


We develop, watch us RoR, in numbers too big to ignore.

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.

On Nov 27, 2007 10:47 AM, Andrew E.
[email protected]

HIi,

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


Volker

On 28 Nov., 16:48, “Jürgen Strobel” [email protected]
wrote:

write_attribute(:customer, _customer)

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

regards, J

On Nov 26, 6:16 pm, Andrew E. wrote

end

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