Well the point is that saving without raising an error is the dangerous
version. Instead of having to write "handle_error unless o.save" in a real
world app you often just have to write "o.save" (o.save!..) This saves code.
E.g.:
# encapsulate the db stuff in a function
def some_db_function a
# ...
o.save
my_ret_value
end
def some_func
begin
a, b = do_some_stuff
res = some_db_function a
do_other_stuff res, b
rescue => e
# Some smart error handling that maybe restarts the whole operation
# but uses some more safe conditions in the first place
# ...
end
end
# ...
some_func
# ...
Using the boolean variant you end up using Exceptions anyway for a efficent
error handling -- or you use a bunch a boilerplate code to end up at the
right point of the stack to retry or just give up the operation.
Thinking about the whole problem I can't really image a real world app that
would be more efficient to write using the C like error-handling. Image a
lazy person that is to lazy to create any error handling facility. With the
raise-on-error version no problem: To whole program interrupts if the code
is faulty, he changes it, restarts and it works. Using the boolean-version
the lazy person always needs explicit error handling to catch trivial
writing errors (E.g. forgetting to set a required variable that isn't used
anyway...)
Philip
2010/7/20 Paul Barry <[email protected]>
> How about adopting the ActiveRecord convention of save that returns a
> boolean and save! that raises an exception?
>
>
> On Mon, Jul 19, 2010 at 11:46 PM, David Masover <[email protected]>wrote:
>
>> On Sunday, July 18, 2010 11:59:41 am Martin Gamsjaeger wrote:
>> > I think the motivation behind DM not raising an exception on any save
>> > failure by default is a very simple and intuitive one.
>> >
>> > DM cannot assume to know exactly when a failing save can really be
>> > considered *exceptional* behavior.
>>
>> Really?
>>
>> irb(main):001:0> 1/0
>> ZeroDivisionError: divided by 0
>> from (irb):1:in `/'
>> from (irb):1
>> from /home/ruby19/bin/irb:12:in `<main>'
>>
>> Who is the core library to say that this is an exception? Why not just
>> create
>> an "Indefinite" constant and return that? Or take the limit and return
>> Infinity?
>>
>> Because it IS exceptional behavior. If it's not, that's why we have catch
>> --
>> and there are things we can do to avoid the exception. For example, in
>> this
>> case, I could've simply checked myself if it was 0 and done something
>> else.
>>
>> > Rather than forcing the user to
>> > treat every single save failure as an exception (which simply isn't
>> > true), it decided to inform users in a friendly and more quiet way,
>>
>> I'm sorry, no, it's not "more friendly" to force me to revert to C-style
>> code,
>> where I have to check the return value of every other function to make
>> sure
>> something didn't fail.
>>
>> Things should fail early and noisily. If it truly wasn't exceptional
>> behavior,
>> that's OK, I can deal with that. The minor irritation of occasionally
>> having
>> to deal with an exception is well worth avoiding SILENTLY IGNORED ERRORS,
>> which is what DM's default behavior entails.
>>
>> > that doesn't involve the overhead of raising an exception either.
>>
>> This might be a valid reason. I can also see cases like make_resourceful,
>> where it's nice to be able to do this:
>>
>> if current_object.save
>> ...
>> else
>> ...
>>
>> So yes, it makes library code mildly nicer, at the cost of making
>> application
>> code much more difficult to debug. I don't think that's a good trade,
>> especially considering that it actually makes things worse, if people were
>> to
>> ever actually use raise_on_save_failure. Now, instead of my library code
>> looking like this:
>>
>> begin
>> current_object.save
>> ...
>> rescue whatever
>> ...
>> end
>>
>> It'd look like this:
>>
>> old_rosf = current_object.raise_on_save_failure
>> if current_object.save
>> save_successful!
>> else
>> save_failed!
>> end
>> current_object.raise_on_save_failure = old_rosf
>>
>> Wait, that's the naive, non-threadsafe way to do it. Guess I have no
>> choice
>> but to do it like this:
>>
>> if current_object.raise_on_save_failure
>> begin
>> current_object.save
>> save_successful!
>> rescue whatever
>> save_failed!
>> end
>> elsif current_object.save
>> save_successful!
>> else
>> save_failed!
>> end
>>
>> This is, bar none, my least favorite thing about DataMapper. I think I
>> might
>> turn raise_on_save_failure on globally and see what breaks. Short of that,
>> I've actually written code like this:
>>
>> module Base
>> def self.included klass
>> klass.class_eval do
>> include DataMapper::Resource
>> include ClassMethods
>> end
>> end
>> ...
>>
>> # And now, how save should have worked:
>> def store
>> save || raise "Save failed!"
>> end
>> def store!
>> save
>> end
>> def store!!
>> save!
>> end
>> end
>>
>>
>> That way, I can safely use plugins which assume the default behavior, but
>> (hopefully) my own code will be somewhat safe.
>>
>> But please, please make it the default, and provide a _separate_method_
>> for
>> people to use when they want true/false returned, preferably something
>> that
>> makes it obvious how dangerous it is. Think about it -- the current
>> behavior
>> is effectively setting whether saves are exceptional based on the object
>> (or
>> class, or application), and not on the context -- but it's the context
>> which
>> defines how errors are handled, if, indeed, they're handled at all.
>>
>> Or does anyone, ever, think something like this is a good idea:
>>
>> u = User.new
>> u.name = 'John Smith'
>> u.save
>> puts 'Saved successfully! (I hope.)'
>>
>> ...really? Would you ever just blindly fire off a save and not care
>> whether it
>> succeeded or not?
>>
>> Then why does your public API encourage this by making it so easy to do,
>> even
>> by accident?
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "DataMapper" group.
>> To post to this group, send email to [email protected].
>> To unsubscribe from this group, send email to
>> [email protected]<datamapper%[email protected]>
>> .
>> For more options, visit this group at
>> http://groups.google.com/group/datamapper?hl=en.
>>
>>
> --
> You received this message because you are subscribed to the Google Groups
> "DataMapper" group.
> To post to this group, send email to [email protected].
> To unsubscribe from this group, send email to
> [email protected]<datamapper%[email protected]>
> .
> For more options, visit this group at
> http://groups.google.com/group/datamapper?hl=en.
>
--
You received this message because you are subscribed to the Google Groups
"DataMapper" 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/datamapper?hl=en.