Re: Fluent API addition request

"Jurgen Doll" <[email protected]> Thu, 04 Jul 2024 15:25:14 +0200
Newsgroups gmane.comp.java.cayenne.devel
Organization iVory EMR
Message-ID <op.2qdzkc0yy9kazd@admin-pc>
Hi Andrus and others

I'm glad you like the idea.

I'm hesitant about using "map" for the method name as that implies  
changing object A into object B, like in the stream API, so I don't think  
that's a good fit. Also it may be a bit confusing in Expression as there's  
a transform( Function ) method ?

I started out with "onCond" but thought that might sound like it referred  
to the "join on" condition. So then went with "ifCond" and then proposed  
"addIf". Any other ideas ?


As to the parameter signature it's a matter of style I suppose. I also  
started out with the ternary examples you gave and then thought that code  
would look neater if the condition test was encapsulated away and so ended  
up with the proposed API.

The downside of my proposal is that it doesn't handle the else branch  
directly I suppose.
Although one could do:

.addif( !c, q -> q.and(exp2) )
.addif( c, q -> q.and(exp1) )

Which may be more readable(?), depending on the complexity of exp1 and  
exp2, than:

.map( q -> c ? q.and(exp1) : q.and(exp2) )

We could also have both forms in ObjectSelect and Expression:

.???( boolean, Consumer/UnaryOperator )  // for simple cases
.???( Consumer/UnaryOperator ) // for complex cases

Thoughts ?

Regards
Jurgen


On Wed, 03 Jul 2024 14:43:49 +0200, Andrus Adamchik <[email protected]>  
wrote:

> Hi Jurgen,
>
> I support the idea behind this API. Let's discuss the shape of it though.
>
> In DFLib (a completely unrelated library that I am working on), we  
> solved a similar problem with a more generic "map(..)" method that  
> performs an arbitrary transformation on the object, that can include  
> condition checking:
>
> https://github.com/dflib/dflib/blob/main/dflib/src/main/java/org/dflib/DataFrame.java#L214
>
> So how about this :
>
>   ObjectSelect.query(A.class).map(q -> c ? q.and(exp) : q);
>   ObjectSelect.query(A.class).map(q -> c ? q.and(exp1) : q.and(exp2));
>
> Cheers,
> Andrus
>
>> On Jul 3, 2024, at 7:32 AM, Jurgen Doll <jurgen-taZlj2xCH1qIRpjhvZ/[email protected]> wrote:
>>
>> Hi All
>>
>> I really like the fluent style in Cayenne 4.
>>
>> However I found that sometimes the fluent style in my code needs to be  
>> interrupted if some condition has to be tested before configuring the  
>> select any further, which then breaks the nice flow of code.
>>
>> So I suggest adding the following API to ObjectSelect and to Expression  
>> that'll smooth this over:
>>
>> Add to ObjectSelect:
>>     /**
>>      * Conditionally append/chain the provided operation.
>>      * @param condition if true then accept is invoked on the Consumer  
>> parameter
>>      * @param op provides the ObjectSelect to be modified if condition  
>> is true
>>      * @return
>>      */
>>     public ObjectSelect addIf( boolean condition, Consumer> op )
>>     {
>>         if ( condition ) op.accept( this );
>>         return this;
>>     }
>> Examples from my code base:
>>     .addIf( yearRange != null, q -> q.where( APPOINTMENT_DATE.gt(  
>> yearRange ) ) )
>>
>>
>>     .addIf( includePublic, q -> q.or( createDateRangeExpression( 9999 )  
>> ) )
>>
>>
>>     .addIf( withAttachments, q -> q.prefetch(  
>> Message.ATTACHMENTS.joint() ) )
>>
>> ----------------------
>>
>> Add to Expression:
>>     /**
>>      * Conditionally append the provided operation to the current  
>> expression.
>>      * @param condition if true then apply is invoked on the  
>> UnaryOperator parameter
>>      * @param op provides the expression to be modified if condition is  
>> true
>>      * @return
>>      */
>>     public Expression addIf( boolean condition, UnaryOperator op )
>>     {
>>         return (condition) ? op.apply( this ) : this;
>>     }
>> Examples from my code base:
>>     DefaultValue.USER.eq( userId ).andExp( DefaultValue.VALUE.ne( "" ) )
>>     .addIf( keyName != null, exp -> exp.andExp( DefaultValue.NAME.eq(  
>> keyName ) ) )
>>
>>
>>     ExpressionFactory.matchExp( PatientNew.DATE_OF_BIRTH_PROPERTY, dob )
>>     .addIf( idNo.length() > 6, exp -> exp.andExp(  
>> PatientNew.ID_NO.like( idNo.substring(0,6)+"%" ) ) )
>>
>>
>>     User.FIRSTNAME.eq( userParts[0] )
>>     .addIf( userParts.length == 2, exp -> exp.andExp( User.SURNAME.eq(  
>> userParts[1] ) ) )
>>
>>
>>     AppointmentSms.APPOINTMENT_DATE.eq( aptDate )
>>     .addIf( marketing, q -> q.andExp( AppointmentSms.MARKETING.eq( true  
>> ) ) )
>>
>> What do you guys think ?
>>
>> I can submit a PR if it passes as a good feature to add.
>> The name of the method can be changed if anyone has a better suggestion.
>>
>> Thanks, regards
>> Jurgen
>


-- 
Using Opera's mail client: http://www.opera.com/mail/