Am 22.07.2009 um 05:22 schrieb Learn By Doing:
>
> Thanks everyone for your responses. Felix, your before_save
> suggestion sounds promising. But will before_save be able to skip the
> save operation if the "do some stuff" fails?
>
> What I want to do is this: always do "some stuff" whenever the x
> attribute is updated. But if "some stuff" returns nil which means it
> has failed, do not update x, and then also return nil to the call to
> update x to let the caller know that the update operation of x has
> failed.
>
> I looked up before_save on the API guide and online but still don't
> know the answer.
>
> Right now the way I am implementing this is:
>
> def set_x(val)
> catch :some_stuff_has_failed do
> do_some_stuff # throw :some_stuff_has_failed if it fails
>
> self.x = val
> save!
> end
> end
>
> I try to make the setter "x=" private so that anybody using my class
> has to use "set_x" to set a value for x. However, I am getting the
> "attempt to call private method" error which causes me to post this
> thread.
>
> Please let me know if my description of the problem is still
> incomplete.
>
> Thanks all for your help.
That sounds a lot like a validation, apart from saving the model in
the setter, which you shouldn't do. From what I've gathered till now,
rails assumes you always work on a model as a whole, not on a single
attribute of the model. Another thing that you must always take care
of: If for whatever reason you decide to override any default method
or whatever of any API you use, make sure to always have the same
return value. Rails assumes a setter for an attribute of an
ActiveRecord object returns the attribute, heck even ruby assumes a
setter returns the object being set (or fail) for _whatever_ object:
"""
$ irb
>> x = 3
=> 3
>> x = :some_funky_symbol
=> :some_funky_symbol
>> x = some_variable_not_set
NameError: undefined local variable or method `some_variable_not_set'
for main:Object
from (irb):3
"""
Anyway, if your some_stuff is a validation, i.e. doesn't modify the
value of x, try this:
=== In the model ===
validate :some_validation
private
def some_validation
# do some stuff with self.x
# return true or whatever you want if it validates, return false or
an exception if not
end
=== In the controller ===
@my_model.x = "some_value"
@my_model.valid? # returns false if not all validations pass
@my_model.save # also fails (can't remember if it returns false) if
not all validations pass, saves the model to the db if they do
(you obviously don't need the valid? step, as it is called in save
too, but may this will help you in some way)
This separation between the data (model), and the business logic, i.e.
what you do with the data (controller) is just a consequence of the
strong MVC separation in rails.
One thing that worries me, is that I still don't know if the
some_stuff you want to do with x also changes x. If it does, you
should separate the x processing part from the validation, put the
processing part in the public setter x=(val), and make it return the
processed x value, and put the validation as explained above.
For completeness' sake: if some part of before_save fails, save will
also fail, although I'm not sure if a false return value will work, or
if you need to throw an exception.
In the end, I would urge you to rethink your MVC model: a setter
should return the object that gets to the internal attribute/object, a
setter should never save the object in the process (I'd even go as far
as saying that no function in the model should ever call save or
save!, because in 99% of the cases, it might bite your dog), the
validation of the attributes, be it sanity, existence or compliance to
some format, should occur on save, and the handling of failing
validations or a failing save should happen in the controller, not in
the model (maybe have a look at the defaults in a scaffolded object
and model: the update method in the controller for example fetches the
object, writes the parameters it got in the attributes of the object,
and the does if @the_object.save do some_happy_stuff else be_sad end).
I hope I was clear enough, but if there still is something unclear to
you, feel free to ask :-)
HTH,
Felix
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Ruby
on Rails: Talk" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/rubyonrails-talk?hl=en
-~----------~----~----~----~------~----~------~--~---