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
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
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.
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 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.