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> > >
