isis-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Dan Haywood <...@haywood-associates.co.uk>
Subject Re: Using JDO helper methods to check existence of an object for a test
Date Sun, 08 Sep 2013 07:20:50 GMT
Looks very good, Oscar, exactly the sort of thing that should be in the
framework, I think.

Could you raise a ticket and provide a commit?  If you have the time,
perhaps you could also refactor the ToDo app to show its use?

Many thanks

Cheers
Dan



On 7 September 2013 19:05, GESCONSULTOR - Óscar Bou
<o.bou@gesconsultor.com>wrote:

>
> I've just been thinking about a way to avoid to define a Spec Transformer
> for every Entity class in the Domain.
>
> As it's just "infrastructure software", I thought that was "just enough".
>
> The attached code allows to use just one Spec Transformer when the Glue is
> written like this:
> -  the employee with name "PETER"
> - the employee named "PETER"
> - the property with reference "REF-001"
> - the property referenced "REF-001"
> - the product with id "PR-001"
>
> From there, we know that:
> - The Entity singular name is "employee" (so it can be obtained from the
> Isis Object Specifications, reinforcing the Ubiquitous Language use on BDD
> specifications).
> - We must search by name
> - The name must be PETER
>
>
>
> For using it, I must define a Glue like this one:
>
> Define a Glue like this one:
> @When("The company (employee with name \"[^\"]*\") has a role assigned")
> public void
> the_company_employee_with_name(@Transform(GenericIsisJdoTransformer.class)
> final  Employee employee) {
>    ...
> }
>
>
>
> First "proof-of-concept" code version follows this text.
>
> It can be improved in many ways, for allowing customization through
> inheritance, avoiding JDO through the use of the Apache Isis query methods,
> etc.
>
>
> But I would let you know, right to know if you think it can be useful.
>
> On this way, the only implemented classes  are the ones supporting the
> Glues (and only the Spec Transformers for more specific use cases).
>
>
> HTH,
>
> Oscar
>
>
>
>
>
> ---------------------
>
>
>
>
> package com.xms.framework.testing.integration.spectransformers;
>
> import java.util.HashMap;
> import java.util.Map;
> import java.util.regex.Matcher;
> import java.util.regex.Pattern;
>
> import javax.jdo.Query;
>
> import org.opensaml.artifact.InvalidArgumentException;
>
> import org.apache.isis.applib.DomainObjectContainer;
> import org.apache.isis.core.metamodel.spec.ObjectSpecification;
> import org.apache.isis.core.runtime.system.context.IsisContext;
> import org.apache.isis.core.specsupport.scenarios.ScenarioExecution;
> import
> org.apache.isis.objectstore.jdo.datanucleus.service.support.IsisJdoSupportImpl;
>
> /**
>  * Requires the Gherkin's capture in the format '([entitySingularName]
> [(.+?)
>  * name(d)|(.+?) reference|(.+?) id] \"[entityId]\")'.
>  * <p>
>  * For example:
>  * <ul>
>  * <li>the employee with name "PETER"</li>
>  * <li>the employee named "PETER"</li>
>  * <li>the property with reference "REF-001"</li>
>  * <li>the property referenced "REF-001"</li>
>  * <li>the product with id "PR-001"</li>
>  * </ul>
>  * <p>
>  * For matching the first one we will need the following Gherkin regular
>  * expression:
>  * <ul>
>  * <li>
>  * \\@When("The company's (employee with name \"[^\"]*\") has a role
> assigned")</li>
>  * </ul>
>  * <p>
>  * From there, we know that:
>  * <ul>
>  * <li>The Entity singular name is "employee".</li>
>  * <li>We must search by name</li>
>  * <li>The name must be PETER</li>
>  * </ul>
>  *
>  */
> public class GenericIsisJdoTransformer extends
> NullRecognizingTransformer<Object> {
>
>     private static Map<String, ObjectSpecification>
> specificationsBySingularName;
>
>     /**
>      * Tries to obtain the Entity class name, id and search field from the
>      * Cucumber capture, and find it on the JDO Object Store.
>      *
>      * @see com.xms.framework.testing.integration.spectransformers.
>      *      NullRecognizingTransformer#transformNonNull(java.lang.String)
>      */
>     @Override
>     protected Object transformNonNull(final String capture) {
>
>         return this.findFromCapture(capture);
>
>     }
>
>     /**
>      * @param entityName
>      *            Name of the Entity specified on the Gherkin's capture.
>      * @return
>      */
>     private ObjectSpecification specificationOf(final String entityName) {
>
>         if (IsisJdoTransformer.specificationsBySingularName == null) {
>             IsisJdoTransformer.specificationsBySingularName = new
> HashMap<String, ObjectSpecification>();
>
>             for (final ObjectSpecification current :
> IsisContext.getSpecificationLoader().allSpecifications()) {
>
>
> IsisJdoTransformer.specificationsBySingularName.put(current.getSingularName().toLowerCase(),
> current);
>             }
>
>         }
>         return
> IsisJdoTransformer.specificationsBySingularName.get(entityName.toLowerCase());
>
>     }
>
>     private final Class<?> entityClassFrom(final String capture) {
>
>         // The Entity Id will be between "".
>         String entityName = "";
>
>         final String[] recognizedPatterns = { "( with name )", "( named
> )", "( with reference )", "( referenced )", "( with id )" };
>
>         for (final String currentPattern : recognizedPatterns) {
>             final Pattern p = Pattern.compile(currentPattern);
>             final Matcher m = p.matcher(capture);
>             if (m.find()) {
>                 entityName = capture.substring(0, m.start());
>                 break;
>             }
>         }
>
>         if (entityName == "") {
>             throw new InvalidArgumentException(String.format("Cannot find
> the entity's name on the capture %s. The format must be
> '([entitySingularName] [with name|with reference|named|referenced]
> \"[entityId]\")'", capture));
>         }
>
>         final ObjectSpecification specification =
> this.specificationOf(entityName);
>         if (specification == null) {
>             throw new InvalidArgumentException(String.format("There is no
> Entity registered in Isis with '%s' as it's singular name", entityName));
>         }
>         return specification.getCorrespondingClass();
>
>     }
>
>     /**
>      * @param capture
>      *            The Gherkin's capture
>      * @return
>      */
>     private final String filterFrom(final String capture) {
>         // Find by "name".
>         if (capture.matches("(.+?)name(.+?)")) {
>             return String.format("name==\"%s\"",
> this.entityIdFrom(capture));
>         }
>
>         // Find by "reference".
>         if (capture.matches("(.+?)reference(.+?)")) {
>             return String.format("reference==\"%s\"",
> this.entityIdFrom(capture));
>         }
>
>         // Find by "id".
>         if (capture.matches("(.+?)id(.+?)")) {
>             return String.format("id==\"%s\"", this.entityIdFrom(capture));
>         }
>
>         throw new InvalidArgumentException(String.format("The entity id
> has not been found on the capture '%s'. It must be between two \"
> characters.", capture));
>     }
>
>     private final Object entityIdFrom(final String capture) {
>         // The Entity Id will be between "".
>         final Pattern p = Pattern.compile("\"(.+?)\"");
>         final Matcher m = p.matcher(capture);
>         if (m.find()) {
>             return m.group().replace("\"", "");
>         } else {
>             throw new InvalidArgumentException(String.format("The entity
> id has not been found on the capture '%s'. It must be between two \"
> characters.", capture));
>         }
>     }
>
>     private final Object findFromCapture(final String capture) {
>
>         // Need to flush().
>         // The Entity can be created on the same transaction.
>         // If we don't flush() the changes, perhaps the entity have not
> been
>         // saved to the object store yet, and will not be found.
>
> ScenarioExecution.current().service(DomainObjectContainer.class).flush();
>
>         final Query query =
> ScenarioExecution.current().service(IsisJdoSupportImpl.class).getJdoPersistenceManager().newQuery();
>         query.setClass(this.entityClassFrom(capture));
>         query.setFilter(this.filterFrom(capture));
>         query.setUnique(true);
>
>         final Object result = query.execute();
>
>         return result;
>
>     }
> }
>
>
>
>
> ---------------------
>
>
>
>
>
> El 27/08/2013, a las 20:24, Kevin Meyer - KMZ <kevin@kmz.co.za> escribió:
>
> > Hi Jeremy,
> >
> > If I remember correctly, Dan has examples for overriding the "Finder"
> > methods in the ToDo demo / artefact that use JDO search classes to
> > build queries.
> >
> > In fact:
> >
> > In the  "ToDoItemsJdo" class (which extends an
> > AbstractFactoryAndRepository):
> >    // {{ notYetComplete (action)
> >    @Override
> >    protected List<ToDoItem> doNotYetComplete() {
> >        return allMatches(
> >                new QueryDefault<ToDoItem>(ToDoItem.class,
> >                        "todo_notYetComplete",
> >                        "ownedBy", currentUserName()));
> >    }
> >    // }}
> >
> > This appears in an old copy of the repo I have in the wicket JDO
> > quickstart project.
> >
> > So while you can't use "FindByPattern", JDO has implemented a
> > useful alternative.
> >
> > Regards,
> > Kevin
> >
> >
> > On 27 Aug 2013 at 14:37, Jeremy Gurr wrote:
> >
> >> I'm playing around with the cucumber support tools in isis (a testing
> framework for behavior driven development, for those who don't know), and
> have created a test that basically looks like this:
> >>
> >> <snip>
> >>
> >> It's very convenient that cucumber instantiates my ServiceClass model
> object and automatically plugs in fields according to the feature spec
> column header. This enables me to add new fields simply by adding a column
> in the spec, and adding the corresponding column in my model object. It
> skips the extra hassle of having to update the glue code as well as service
> methods to construct the object.
> >>
> >> The "exists" method contains this code:
> >>
> >> public boolean exists(ServiceClass serviceClass) {
> >> final QueryFindByPattern<ServiceClass> query = new
> QueryFindByPattern<ServiceClass>(ServiceClass.class, serviceClass);
> >> final ServiceClass firstMatch = firstMatch(query);
> >>
> >> return firstMatch != null;
> >> }
> >>
> >> I'm just trying to verify that an object exists with fields matching
> the incoming object, some fields of which may be null, meaning that they
> can be any value. The QueryFindByPattern class seemed to be a perfect fit
> since I'm passing around ServiceClass instances anyway. However, running it
> executes code that is not yet implemented:
> >>
> >> <snip>
>
>

Mime
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message