I'm confused. I was under the impression that the only case in which
save would not raise on error on failure is when validation fails (the
other situations I could think would be an SQL error or connection
failure which both raise errors). In this case, it's not the code that's
the problem but rather user input. Right? Unless the argument is that
you should always call resource.valid? before calling resource.save . At
least in the case of validations causing the failure, I'd much rather
not have to handle an exception (and I don't consider an end-user
mistyping something an exceptional situation). Am I missing something?
Jared
Philip Silva wrote:
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]
<mailto:[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] <mailto:[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 <http://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] <mailto:[email protected]>.
To unsubscribe from this group, send email to
[email protected]
<mailto: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]
<mailto:[email protected]>.
To unsubscribe from this group, send email to
[email protected]
<mailto: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.
--
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.