isis-users mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From GESCONSULTOR - Óscar Bou <o....@gesconsultor.com>
Subject Re: Using JDO helper methods to check existence of an object for a test
Date Sat, 07 Sep 2013 18:05:01 GMT

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
View raw message