From: Peter Vandenabeele Date: 2011-12-16T17:33:21+09:00 Subject: [ruby-core:41695] Re: Documentation of the language itself (syntax, meanings, etc) --002354471080ae023c04b4317370 Content-Type: text/plain; charset=UTF-8 On Fri, Dec 16, 2011 at 6:17 AM, Yong Li wrote: > > > > Still, I don't like using exceptions as a control flow mechanism for > entirely un-exceptional situations. > > > I strongly agree with this, even though I have myself met a similar > situation in the past: > > def action > check_input > > return if handle_special_input1_and_return # This is valid input > and return logic, so exception is inappropriate > > return if handle_special_input2_and_return > > handle_common_cases > end > > I did not want to inline "handle_special_inputx_and_return" because it > would make the action method too long and blur the main logic. > However, extracting them to private methods would make the code more > verbose as I cannot return "action" from within > "handle_special_input_and_return", and this forces me to return a > value from "handle_special_input_and_return" and check them in > "action". > > I was also trying hard to get around this, but finally settled on this > for the following reasons: > * The only way to break out multiple levels of code (nested methods or > loops or both) is to use a "go-to" like operation, which allows you to > jump to anywhere in you code. Exceptions can be used for this purpose. > But again, this is very bad. > * go-to operations are rarely good to use > * the purpose and condition of return is clear and explicit, which > reduces confusions for other people > > Maybe I could re-design my code to avoid such situations in the first > place, but that would be a completely different story. > > I would be very glad to hear a more succinct solution and throw my > silly argument under the bus :-D > Certainly a problem I was also bitten by this more than I wanted ... The problem: (meta-code) def show user = User.find(params[:id]) if user.secret? flash[:warning] = "sorry, this is secret" redirect_to :action => "index" return end if user.vip? redirect_to :action => "vip_lounge", :id => params[:id] # forgot return end if user.long_distance? redirect_to :action => "long_distance", id => params[:id] # line above will BOOM for vip, long_distance user return end end and there you have it ... I forgot 1 return ... A user that is vip? and intercontinental? will cause an exception (and even if I tested each separate use case, I may have forgotten to test this _combination_ ...). Also, duplicating the "return" after each redirect_to or render seems not DRY. At one point, I was grepping my code for each "redirect_to", "render" to see if I had written a return after it ... A solution ? Would this be a good use case for Catch / Throw ? Then the Web Framework call to User#show could be wrapped in a catch block like class WebFramework ... catch :render_or_redirect do User.show end end and then we defined a function def redirect_to_with_throw(*arguments) redirect_to(*arguments) throw :render_or_redirect end which can be used in the controller as: def show user = User.find(params[:id]) if user.secret? flash[:warning] = "sorry, this is secret" redirect_to_with_throw :action => "index" end ... That might be a nice extension to the web framework, to catch a :render_or_redirect throw. If I understand correctly, the catch in the framework would not hinder the existing code or developers not using the feature (that is, never throwing :render_or_redirect). HTH, Peter --002354471080ae023c04b4317370 Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable
On Fri, Dec 16, 2011 at 6:17 AM, Yong Li <gilbertly@gmail.com> wrote:
>
> Still, I don't like using exceptions as a control flow mechanism f= or entirely un-exceptional situations.
>
I strongly agree with this, even though I have myself met a similar situation in the past:

def action
=C2=A0check_input

=C2=A0return if handle_special_input1_and_return =C2=A0# This is valid inp= ut
and return logic, so exception is inappropriate

=C2=A0return if handle_special_input2_and_return

=C2=A0handle_common_cases
end

I did not want to inline "handle_special_inputx_and_return" becau= se it
would make the action method too long and blur the main logic.
However, extracting them to private methods would make the code more
verbose as I cannot return "action" from within
"handle_special_input_and_return", and this forces me to return a=
value from "handle_special_input_and_return" and check them in "action".

I was also trying hard to get around this, but finally settled on this
for the following reasons:
* The only way to break out multiple levels of code (nested methods or
loops or both) is to use a "go-to" like operation, which allows y= ou to
jump to anywhere in you code. Exceptions can be used for this purpose.
But again, this is very bad.
* go-to operations are rarely good to use
* the purpose and condition of return is clear and explicit, which
reduces confusions for other people

Maybe I could re-design my code to avoid such situations in the first
place, but that would be a completely different story.

I would be very glad to hear a more succinct solution and throw my
silly argument under the bus :-D


Certainly a problem I was also bitten by this more than I wanted .= ..

The problem:

(meta-cod= e)

def show
=C2=A0 user =3D User.find(params[:id= ])
=C2=A0 if user.secret?
=C2=A0 =C2=A0 flash[:warning]= =3D "sorry, this is secret"
=C2=A0 =C2=A0 redirect_to = :action =3D> "index"
=C2=A0 =C2=A0 return
=C2=A0 end
=C2=A0 if user.vip= ?
=C2=A0 =C2=A0 redirect_to :action =3D> "vip_lounge"= ;, :id =3D> params[:id]
=C2=A0 =C2=A0 # forgot return
=C2=A0 end
=C2=A0 if user.long_distance?
=C2=A0 =C2=A0 redirect_to :action =3D> "long_distance", i= d =3D> params[:id]
=C2=A0 =C2=A0 # line above will BOOM for vi= p, long_distance user
=C2=A0 =C2=A0 return
=C2=A0 end
end

and there you have it ... I forgot 1 return ... A user that is vip? and
intercontinental? will cause an exception (and even if I tested each=
separate use case, I may have forgotten=C2=A0to test this _combi= nation_ ...).

Also, duplicating the "return" after each red= irect_to or render
seems not DRY.

At one= point, I was grepping my code for each "redirect_to",
"render"=C2=A0to see if I had written a return after it ...=C2=A0=

A solution ?

Would this = be a good use case for Catch / Throw ?

Then the We= b Framework =C2=A0call to User#show could be
wrapped in a catch block like

class WebFramew= ork
...
=C2=A0 catch :render_or_redirect do
= =C2=A0 =C2=A0 User.show
=C2=A0 end

end

and then we defined a function

def redirect_t= o_with_throw(*arguments)
=C2=A0 redirect_to(*arguments)
=C2=A0 throw :render_or_redirect
end

wh= ich can be used in the controller as:

def show
=C2=A0 user =3D User.find(param= s[:id])
=C2=A0 if user.secret?
=C2=A0 =C2=A0 flash[:war= ning] =3D "sorry, this is secret"
=C2=A0 =C2=A0 redirec= t_to_with_throw :action =3D> "index"
=C2=A0 end
...

That might be = a nice extension to the web framework,
to catch a :render_or_redi= rect=C2=A0throw. If I understand
correctly, the catch in the fram= ework would not
hinder the existing code or developers not using
the feature= (that is, never throwing :render_or_redirect).

HT= H,

Peter
--002354471080ae023c04b4317370--