You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@isis.apache.org by Dan Haywood <da...@haywood-associates.co.uk> on 2016/01/04 17:38:24 UTC

Re: How should we handle void and null results

The RoutingService (ISIS-666) was implemented in 1.11.0, and I've now also
added an FAQ [1] which shows how a custom implementation can use the
breadcrumbs to display the previous object in the result of an object being
deleted and returning void/null.

This should give the effect that the OP (Yuri) was original after...

Thx
Dan

[1]
http://isis.apache.org/guides/ugfun.html#_ugfun_faqs_how-to-handle-void-and-null-results

On 23 December 2015 at 14:55, Dan Haywood <da...@haywood-associates.co.uk>
wrote:

> I've raised ISIS-1282 [1] for this idea.
>
> Thx
> Dan
>
> [1] https://issues.apache.org/jira/browse/ISIS-1282
>
> On 23 December 2015 at 09:01, Óscar Bou - GOVERTIS <o....@govertis.com>
> wrote:
>
>> Hi, Dan.
>>
>> I like your proposal to extend the @Action annotation.
>>
>> So, for example, for routing to “this” when returning void or null, would
>> it be annotated with something like ”routeUsing=ThisRouter.class”.
>>
>> Perhaps something clearer would be to have a complementary one like
>> “routeTo=THIS” or similar.
>>
>> Cheers,
>>
>> Oscar
>>
>>
>>
>>
>> > El 22 dic 2015, a las 19:13, Dan Haywood <da...@haywood-associates.co.uk>
>> escribió:
>> >
>> > On 22 December 2015 at 15:19, Óscar Bou - GOVERTIS <o....@govertis.com>
>> > wrote:
>> >
>> >>
>> >> Hi all.
>> >>
>> >> I find the SPI Service really useful for advanced use cases.
>> >>
>> >> But in order to have the desired behavior clearly specified on the same
>> >> class definition, shouldn't be helpful to also have an annotation
>> >> indicating the desired behavior (like returning this instance)?
>> >>
>> >>
>> > Seems better to provide a lower level SPI service that can work in all
>> use
>> > cases, and then use that as a building block for higher level
>> abstractions
>> > that might include annotations (or more likely, a new attribute of the
>> > @Action annotation).
>> >
>> > But to start that conversation, we could have a new "strategy"
>> attribute,
>> > eg:
>> >
>> > @Action(
>> >    routeUsing=MyRouter.class  // implements some sort of "Router"
>> > interface, similar to our Specification interface
>> > )
>> > public void whatever(...) { ... }
>> >
>> > Cheers
>> > Dan
>> >
>> >
>> >
>> >
>> >> Christmas here also :))
>> >>
>> >> HTH,
>> >>
>> >> Oscar
>> >>
>> >>> El 22 dic 2015, a las 15:54, Cesar Lugo <ce...@sisorg.com.mx>
>> >> escribió:
>> >>>
>> >>> Dan,
>> >>>
>> >>> I think your proposal is quite good to have a configurable "default"
>> >> behavior, and having the option to return whatever the developer wants
>> is
>> >> quite useful as well. Probably this could evolve to some kind of
>> wizard (I
>> >> saw a work in progress in Isis add-ons), or even some integration with
>> some
>> >> BPM tool. Just thinking loud here, it's christmas  :) .
>> >>>
>> >>> Cesar.
>> >>>
>> >>> -----Original Message-----
>> >>> From: Dan Haywood [mailto:dan@haywood-associates.co.uk]
>> >>> Sent: Tuesday, December 22, 2015 6:02 AM
>> >>> To: users
>> >>> Subject: Re: How should we handle void and null results
>> >>>
>> >>> There was a related discussion a while back [1], revolving around
>> >> whether the Wicket viewer should always render the returned object, or
>> some
>> >> other object (eg the owning object/aggregate root).  I raised a ticket
>> >> ISIS-666 [2] for this, and have also now referenced this thread [3].
>> >>>
>> >>> To pick up on a couple of points made:
>> >>>
>> >>> * should the object returned by the action determine the next object
>> >> rendered (ie do domain object actions act as "controllers")?  Yu Ri
>> says
>> >> no, whereas in the previous conversation (Oscar, Jeroen, Dan) I think
>> we
>> >> were always saying its ok, but to provide the ability for this to be
>> >> modified (eg to the aggregate root rather than leaf).  We didn't
>> discuss
>> >> the case of returning void, though.
>> >>>
>> >>> * Martin was wondering about whether Person#delete() even makes sense.
>> >> I think it does though; at least an end-user would want to press a
>> button
>> >> called "delete" on a domain object.  Behind the covers that might be a
>> >> contributed action or mixin or double-dispatch back to a repository
>> >> service.  But that's an implementation detail: the core responsibility
>> is
>> >> for a domain object to know how to get itself deleted
>> >>>
>> >>> * Steve suggests that where there is no obvious answer to "which
>> object
>> >> should be shown next", then the home page might be a reasonable
>> default.  I
>> >> agree, and think we should provide such a capability.
>> >>>
>> >>> * Cesar illustrates how to return a parent object, eg for both delete
>> >> and also for add.
>> >>>
>> >>> To me it seems that it's unlikely to be a single policy that will
>> >> support all use cases.  So I propose a new optional SPI service that,
>> if
>> >> present, the viewer will consult to determine which object to show
>> next.  I
>> >> see this a quite low-level service and we might use it as a building
>> block
>> >> to some higher-level strategy (eg based on new annotations) at a later
>> date.
>> >>>
>> >>> The SPI I suggest is:
>> >>>
>> >>> public interface RoutingService {
>> >>>
>> >>>    public Object route(Object original);
>> >>>
>> >>> }
>> >>>
>> >>> A default implementation could be something like:
>> >>>
>> >>> public class RoutingServiceDefault {
>> >>>
>> >>>   public Object route(Object original) {
>> >>>       return original != null? original: homePage();
>> >>>   }
>> >>>
>> >>>   private Object homePage() { ... code to find the @HomePage object if
>> >> any ... } }
>> >>>
>> >>> This behaviour could be overridden eg to support the aggregate object
>> >> idea as discussed in [1].
>> >>>
>> >>> So, that's my proposal.
>> >>>
>> >>> Cheers
>> >>> Dan
>> >>>
>> >>>
>> >>>
>> >>> [1] http://markmail.org/message/xhmeq62ywr2vqvje .
>> >>> [2] https://issues.apache.org/jira/browse/ISIS-666
>> >>>
>> >>>
>> >>>> On 21 December 2015 at 14:46, Cesar Lugo <ce...@sisorg.com.mx>
>> >> wrote:
>> >>>>
>> >>>> Hello. I am just another Isis user like you, but I thought this might
>> >> help:
>> >>>>
>> >>>> Wicket viewer shows what you "return" in your action method, so,
>> >>>> because you are returning void, Wicket is showing "no results"
>> >>>> message. Usually, you return the thing you create or update, but you
>> >>>> can return the parent if that's what you want, or anything else you
>> >>>> need, just get it in your code and return it. For example, I have a
>> >>>> method that adds a Deliverer that belongs to a BusinessLocation, and
>> >>>> after created shows the parent BusinessLocation entity object instead
>> >>>> of showing the Deliverer object just created (in my case, when
>> showing
>> >>>> the BusinessLocation parent, the Deliverer just  created shows in the
>> >>>> collection section, which is what I wanted, because Business have
>> that
>> >>>> collection). If you adapt this code to your deletePerson method I
>> think
>> >> it can work.
>> >>>>
>> >>>> My sample code is:
>> >>>>
>> >>>>   @Action(
>> >>>>           domainEvent = CreateDomainEvent.class
>> >>>>   )
>> >>>>   @MemberOrder(name = "deliverers",sequence = "24")
>> >>>>   public BusinessLocation addDeliverer(
>> >>>>           final @ParameterLayout(named="Business Location")
>> >>>> BusinessLocation businessLocation,
>> >>>>           final @ParameterLayout(named="Deliverer Id") String
>> >>>> delivererId,
>> >>>>           final @ParameterLayout(named="First Name") String
>> firstName,
>> >>>>           final @ParameterLayout(named="Middle Name")
>> >>>> @Parameter(optionality = Optionality.OPTIONAL)String middleName,
>> >>>>           final @ParameterLayout(named="Last Name") String lastName,
>> >>>>           final @ParameterLayout(named="Last Name 2")
>> >>>> @Parameter(optionality = Optionality.OPTIONAL)String lastName2,
>> >>>>           final @ParameterLayout(named="Contact Phone")
>> >>>> @Parameter(optionality = Optionality.OPTIONAL)Long contactPhone,
>> >>>>           final @ParameterLayout(named="Delivery Phone")
>> >>>> @Parameter(optionality = Optionality.OPTIONAL)Long deliveryPhone,
>> >>>>           final @ParameterLayout(named="Deliverer Picture")
>> >>>> @Parameter(optionality = Optionality.OPTIONAL) Blob delivererPicture
>> >>>>   )
>> >>>>   {
>> >>>>       final Deliverer obj =
>> >>>> container.newTransientInstance(Deliverer.class);
>> >>>>       obj.setBusinessLocation(businessLocation);
>> >>>>       obj.setDelivererId(delivererId);
>> >>>>       obj.setFirstName(firstName);
>> >>>>       obj.setMiddleName(middleName);
>> >>>>       obj.setLastName(lastName);
>> >>>>       obj.setLastName2(lastName2);
>> >>>>       obj.setContactPhone(contactPhone);
>> >>>>       obj.setDeliveryPhone(deliveryPhone);
>> >>>>       obj.setDelivererPicture(delivererPicture);
>> >>>>       obj.setCreationTime(clockService.nowAsDateTime());
>> >>>>       container.persistIfNotAlready(obj);
>> >>>>       return obj.getBusinessLocation();
>> >>>>   }
>> >>>>
>> >>>> If you choose to return the parent of the Person object being
>> deleted,
>> >>>> make sure you get the parent before you Delete the person. Have fun!
>> >>>>
>> >>>> Cesar.
>> >>>>
>> >>>> -----Original Message-----
>> >>>> From: Y.R Tan [mailto:mail@yuritan.nl]
>> >>>> Sent: Saturday, December 19, 2015 5:44 AM
>> >>>> To: users
>> >>>> Subject: How should we handle void and null results
>> >>>>
>> >>>> Hi everyone,
>> >>>>
>> >>>> When using a void action, let’s say a remove action, the user is
>> >>>> redirected to a page "no results". When clicking the back button in
>> >>>> the browser the user sees "Object not found" (since you’ve just
>> >>>> deleted this object).
>> >>>>
>> >>>> Example:
>> >>>>
>> >>>> public class Person {
>> >>>>   ....
>> >>>>   public void remove() {
>> >>>>       ...
>> >>>>   }
>> >>>> }
>> >>>>
>> >>>> You can return a list for example to prevent the user from being
>> >>>> redirect to a "No results" page, but I think it’s not the
>> >>>> responsibility of the controllers in the domain model. A solution
>> >>>> could be that wicket viewer goes back one page when encountering a
>> >>>> deleted object. And refresh the current page when receiving a null
>> >> response or invoking a void action.
>> >>>>
>> >>>> What do you guys think that is the best solution? Or do you have
>> >>>> another view on this situation?
>> >>>>
>> >>>> Looking forward hearing from you.
>> >>>>
>> >>>> Regards,
>> >>>>
>> >>>> Yu Ri Tan
>> >>>>
>> >>>>
>> >>>> ---
>> >>>> This email has been checked for viruses by Avast antivirus software.
>> >>>> https://www.avast.com/antivirus
>> >>>
>> >>>
>> >>> ---
>> >>> This email has been checked for viruses by Avast antivirus software.
>> >>> https://www.avast.com/antivirus
>> >>>
>> >>
>>
>>
>

Re: How should we handle void and null results

Posted by "Y.R Tan" <ma...@yuritan.nl>.
You’ve made me very happy. Thx Dan! 

> On 04 Jan 2016, at 17:38, Dan Haywood <da...@haywood-associates.co.uk> wrote:
> 
> The RoutingService (ISIS-666) was implemented in 1.11.0, and I've now also
> added an FAQ [1] which shows how a custom implementation can use the
> breadcrumbs to display the previous object in the result of an object being
> deleted and returning void/null.
> 
> This should give the effect that the OP (Yuri) was original after...
> 
> Thx
> Dan
> 
> [1]
> http://isis.apache.org/guides/ugfun.html#_ugfun_faqs_how-to-handle-void-and-null-results
> 
> On 23 December 2015 at 14:55, Dan Haywood <da...@haywood-associates.co.uk>
> wrote:
> 
>> I've raised ISIS-1282 [1] for this idea.
>> 
>> Thx
>> Dan
>> 
>> [1] https://issues.apache.org/jira/browse/ISIS-1282
>> 
>> On 23 December 2015 at 09:01, Óscar Bou - GOVERTIS <o....@govertis.com>
>> wrote:
>> 
>>> Hi, Dan.
>>> 
>>> I like your proposal to extend the @Action annotation.
>>> 
>>> So, for example, for routing to “this” when returning void or null, would
>>> it be annotated with something like ”routeUsing=ThisRouter.class”.
>>> 
>>> Perhaps something clearer would be to have a complementary one like
>>> “routeTo=THIS” or similar.
>>> 
>>> Cheers,
>>> 
>>> Oscar
>>> 
>>> 
>>> 
>>> 
>>>> El 22 dic 2015, a las 19:13, Dan Haywood <da...@haywood-associates.co.uk>
>>> escribió:
>>>> 
>>>> On 22 December 2015 at 15:19, Óscar Bou - GOVERTIS <o....@govertis.com>
>>>> wrote:
>>>> 
>>>>> 
>>>>> Hi all.
>>>>> 
>>>>> I find the SPI Service really useful for advanced use cases.
>>>>> 
>>>>> But in order to have the desired behavior clearly specified on the same
>>>>> class definition, shouldn't be helpful to also have an annotation
>>>>> indicating the desired behavior (like returning this instance)?
>>>>> 
>>>>> 
>>>> Seems better to provide a lower level SPI service that can work in all
>>> use
>>>> cases, and then use that as a building block for higher level
>>> abstractions
>>>> that might include annotations (or more likely, a new attribute of the
>>>> @Action annotation).
>>>> 
>>>> But to start that conversation, we could have a new "strategy"
>>> attribute,
>>>> eg:
>>>> 
>>>> @Action(
>>>>   routeUsing=MyRouter.class  // implements some sort of "Router"
>>>> interface, similar to our Specification interface
>>>> )
>>>> public void whatever(...) { ... }
>>>> 
>>>> Cheers
>>>> Dan
>>>> 
>>>> 
>>>> 
>>>> 
>>>>> Christmas here also :))
>>>>> 
>>>>> HTH,
>>>>> 
>>>>> Oscar
>>>>> 
>>>>>> El 22 dic 2015, a las 15:54, Cesar Lugo <ce...@sisorg.com.mx>
>>>>> escribió:
>>>>>> 
>>>>>> Dan,
>>>>>> 
>>>>>> I think your proposal is quite good to have a configurable "default"
>>>>> behavior, and having the option to return whatever the developer wants
>>> is
>>>>> quite useful as well. Probably this could evolve to some kind of
>>> wizard (I
>>>>> saw a work in progress in Isis add-ons), or even some integration with
>>> some
>>>>> BPM tool. Just thinking loud here, it's christmas  :) .
>>>>>> 
>>>>>> Cesar.
>>>>>> 
>>>>>> -----Original Message-----
>>>>>> From: Dan Haywood [mailto:dan@haywood-associates.co.uk]
>>>>>> Sent: Tuesday, December 22, 2015 6:02 AM
>>>>>> To: users
>>>>>> Subject: Re: How should we handle void and null results
>>>>>> 
>>>>>> There was a related discussion a while back [1], revolving around
>>>>> whether the Wicket viewer should always render the returned object, or
>>> some
>>>>> other object (eg the owning object/aggregate root).  I raised a ticket
>>>>> ISIS-666 [2] for this, and have also now referenced this thread [3].
>>>>>> 
>>>>>> To pick up on a couple of points made:
>>>>>> 
>>>>>> * should the object returned by the action determine the next object
>>>>> rendered (ie do domain object actions act as "controllers")?  Yu Ri
>>> says
>>>>> no, whereas in the previous conversation (Oscar, Jeroen, Dan) I think
>>> we
>>>>> were always saying its ok, but to provide the ability for this to be
>>>>> modified (eg to the aggregate root rather than leaf).  We didn't
>>> discuss
>>>>> the case of returning void, though.
>>>>>> 
>>>>>> * Martin was wondering about whether Person#delete() even makes sense.
>>>>> I think it does though; at least an end-user would want to press a
>>> button
>>>>> called "delete" on a domain object.  Behind the covers that might be a
>>>>> contributed action or mixin or double-dispatch back to a repository
>>>>> service.  But that's an implementation detail: the core responsibility
>>> is
>>>>> for a domain object to know how to get itself deleted
>>>>>> 
>>>>>> * Steve suggests that where there is no obvious answer to "which
>>> object
>>>>> should be shown next", then the home page might be a reasonable
>>> default.  I
>>>>> agree, and think we should provide such a capability.
>>>>>> 
>>>>>> * Cesar illustrates how to return a parent object, eg for both delete
>>>>> and also for add.
>>>>>> 
>>>>>> To me it seems that it's unlikely to be a single policy that will
>>>>> support all use cases.  So I propose a new optional SPI service that,
>>> if
>>>>> present, the viewer will consult to determine which object to show
>>> next.  I
>>>>> see this a quite low-level service and we might use it as a building
>>> block
>>>>> to some higher-level strategy (eg based on new annotations) at a later
>>> date.
>>>>>> 
>>>>>> The SPI I suggest is:
>>>>>> 
>>>>>> public interface RoutingService {
>>>>>> 
>>>>>>   public Object route(Object original);
>>>>>> 
>>>>>> }
>>>>>> 
>>>>>> A default implementation could be something like:
>>>>>> 
>>>>>> public class RoutingServiceDefault {
>>>>>> 
>>>>>>  public Object route(Object original) {
>>>>>>      return original != null? original: homePage();
>>>>>>  }
>>>>>> 
>>>>>>  private Object homePage() { ... code to find the @HomePage object if
>>>>> any ... } }
>>>>>> 
>>>>>> This behaviour could be overridden eg to support the aggregate object
>>>>> idea as discussed in [1].
>>>>>> 
>>>>>> So, that's my proposal.
>>>>>> 
>>>>>> Cheers
>>>>>> Dan
>>>>>> 
>>>>>> 
>>>>>> 
>>>>>> [1] http://markmail.org/message/xhmeq62ywr2vqvje .
>>>>>> [2] https://issues.apache.org/jira/browse/ISIS-666
>>>>>> 
>>>>>> 
>>>>>>> On 21 December 2015 at 14:46, Cesar Lugo <ce...@sisorg.com.mx>
>>>>> wrote:
>>>>>>> 
>>>>>>> Hello. I am just another Isis user like you, but I thought this might
>>>>> help:
>>>>>>> 
>>>>>>> Wicket viewer shows what you "return" in your action method, so,
>>>>>>> because you are returning void, Wicket is showing "no results"
>>>>>>> message. Usually, you return the thing you create or update, but you
>>>>>>> can return the parent if that's what you want, or anything else you
>>>>>>> need, just get it in your code and return it. For example, I have a
>>>>>>> method that adds a Deliverer that belongs to a BusinessLocation, and
>>>>>>> after created shows the parent BusinessLocation entity object instead
>>>>>>> of showing the Deliverer object just created (in my case, when
>>> showing
>>>>>>> the BusinessLocation parent, the Deliverer just  created shows in the
>>>>>>> collection section, which is what I wanted, because Business have
>>> that
>>>>>>> collection). If you adapt this code to your deletePerson method I
>>> think
>>>>> it can work.
>>>>>>> 
>>>>>>> My sample code is:
>>>>>>> 
>>>>>>>  @Action(
>>>>>>>          domainEvent = CreateDomainEvent.class
>>>>>>>  )
>>>>>>>  @MemberOrder(name = "deliverers",sequence = "24")
>>>>>>>  public BusinessLocation addDeliverer(
>>>>>>>          final @ParameterLayout(named="Business Location")
>>>>>>> BusinessLocation businessLocation,
>>>>>>>          final @ParameterLayout(named="Deliverer Id") String
>>>>>>> delivererId,
>>>>>>>          final @ParameterLayout(named="First Name") String
>>> firstName,
>>>>>>>          final @ParameterLayout(named="Middle Name")
>>>>>>> @Parameter(optionality = Optionality.OPTIONAL)String middleName,
>>>>>>>          final @ParameterLayout(named="Last Name") String lastName,
>>>>>>>          final @ParameterLayout(named="Last Name 2")
>>>>>>> @Parameter(optionality = Optionality.OPTIONAL)String lastName2,
>>>>>>>          final @ParameterLayout(named="Contact Phone")
>>>>>>> @Parameter(optionality = Optionality.OPTIONAL)Long contactPhone,
>>>>>>>          final @ParameterLayout(named="Delivery Phone")
>>>>>>> @Parameter(optionality = Optionality.OPTIONAL)Long deliveryPhone,
>>>>>>>          final @ParameterLayout(named="Deliverer Picture")
>>>>>>> @Parameter(optionality = Optionality.OPTIONAL) Blob delivererPicture
>>>>>>>  )
>>>>>>>  {
>>>>>>>      final Deliverer obj =
>>>>>>> container.newTransientInstance(Deliverer.class);
>>>>>>>      obj.setBusinessLocation(businessLocation);
>>>>>>>      obj.setDelivererId(delivererId);
>>>>>>>      obj.setFirstName(firstName);
>>>>>>>      obj.setMiddleName(middleName);
>>>>>>>      obj.setLastName(lastName);
>>>>>>>      obj.setLastName2(lastName2);
>>>>>>>      obj.setContactPhone(contactPhone);
>>>>>>>      obj.setDeliveryPhone(deliveryPhone);
>>>>>>>      obj.setDelivererPicture(delivererPicture);
>>>>>>>      obj.setCreationTime(clockService.nowAsDateTime());
>>>>>>>      container.persistIfNotAlready(obj);
>>>>>>>      return obj.getBusinessLocation();
>>>>>>>  }
>>>>>>> 
>>>>>>> If you choose to return the parent of the Person object being
>>> deleted,
>>>>>>> make sure you get the parent before you Delete the person. Have fun!
>>>>>>> 
>>>>>>> Cesar.
>>>>>>> 
>>>>>>> -----Original Message-----
>>>>>>> From: Y.R Tan [mailto:mail@yuritan.nl]
>>>>>>> Sent: Saturday, December 19, 2015 5:44 AM
>>>>>>> To: users
>>>>>>> Subject: How should we handle void and null results
>>>>>>> 
>>>>>>> Hi everyone,
>>>>>>> 
>>>>>>> When using a void action, let’s say a remove action, the user is
>>>>>>> redirected to a page "no results". When clicking the back button in
>>>>>>> the browser the user sees "Object not found" (since you’ve just
>>>>>>> deleted this object).
>>>>>>> 
>>>>>>> Example:
>>>>>>> 
>>>>>>> public class Person {
>>>>>>>  ....
>>>>>>>  public void remove() {
>>>>>>>      ...
>>>>>>>  }
>>>>>>> }
>>>>>>> 
>>>>>>> You can return a list for example to prevent the user from being
>>>>>>> redirect to a "No results" page, but I think it’s not the
>>>>>>> responsibility of the controllers in the domain model. A solution
>>>>>>> could be that wicket viewer goes back one page when encountering a
>>>>>>> deleted object. And refresh the current page when receiving a null
>>>>> response or invoking a void action.
>>>>>>> 
>>>>>>> What do you guys think that is the best solution? Or do you have
>>>>>>> another view on this situation?
>>>>>>> 
>>>>>>> Looking forward hearing from you.
>>>>>>> 
>>>>>>> Regards,
>>>>>>> 
>>>>>>> Yu Ri Tan
>>>>>>> 
>>>>>>> 
>>>>>>> ---
>>>>>>> This email has been checked for viruses by Avast antivirus software.
>>>>>>> https://www.avast.com/antivirus
>>>>>> 
>>>>>> 
>>>>>> ---
>>>>>> This email has been checked for viruses by Avast antivirus software.
>>>>>> https://www.avast.com/antivirus
>>>>>> 
>>>>> 
>>> 
>>> 
>>