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

Reply via email to