For sure!

Unless you or Jeremy plan to use it immediately let me check and improve it 
this week through some BDD features we must implement.

Thanks,

Oscar


El 08/09/2013, a las 09:20, Dan Haywood <[email protected]> 
escribió:

> 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
> <[email protected]>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 <[email protected]> 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>
>> 
>> 

Reply via email to