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].
For more options, visit this group at 
http://groups.google.com/group/datamapper?hl=en.

Reply via email to