Return-Path: X-Original-To: apmail-isis-users-archive@www.apache.org Delivered-To: apmail-isis-users-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id A56C710FFF for ; Sun, 8 Sep 2013 07:22:02 +0000 (UTC) Received: (qmail 56851 invoked by uid 500); 8 Sep 2013 07:22:00 -0000 Delivered-To: apmail-isis-users-archive@isis.apache.org Received: (qmail 56815 invoked by uid 500); 8 Sep 2013 07:21:58 -0000 Mailing-List: contact users-help@isis.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: users@isis.apache.org Delivered-To: mailing list users@isis.apache.org Received: (qmail 56806 invoked by uid 99); 8 Sep 2013 07:21:56 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 08 Sep 2013 07:21:56 +0000 X-ASF-Spam-Status: No, hits=1.5 required=5.0 tests=HTML_MESSAGE,RCVD_IN_DNSWL_LOW X-Spam-Check-By: apache.org Received-SPF: error (athena.apache.org: local policy) Received: from [209.85.128.47] (HELO mail-qe0-f47.google.com) (209.85.128.47) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 08 Sep 2013 07:21:51 +0000 Received: by mail-qe0-f47.google.com with SMTP id b4so2531985qen.34 for ; Sun, 08 Sep 2013 00:21:10 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:from:date :message-id:subject:to:cc:content-type; bh=GVsPCG/zTzTpzhn6dpLOUdmhJ1GUZeu0INBi1QFRuio=; b=XtpbYUtBDGXFiwdx0GcdVHndZZVUKyHvsIDfcpSAwLolFvR2Y/c815stSDxKQWH5vQ hwiZgJGYWQ+cR+CpDIKM6toJUWw1uDDAXH2LCwTVqpoqFZeUbPwyvPDXYd2dzKESwlyn PbZThRbPdKaNmsZFIwhVLOm9sHf+Q6dRQWA/VJniLV7jo/5zfSJdSBJBOajUwQ+00d91 hr3G4JLtUZRWoIE3V5XCJWYlN7kO1r3cuwqb6MkVUK/OPte7OBUEVncjlm6Mln8dEDgt sBOhWqyc7q9JUp2BZ28kPZECNqNoAkLPacCUzPoTpAEsU4zGJbXtTvFgsvDgknI03McW rCSA== X-Gm-Message-State: ALoCoQmSaB/cMAB/I42KFKZDPpCNUlZgCpO8i73nDdg8tZCEqwwQCvmRwKtJ9wovIajACD8Y8A7Q X-Received: by 10.49.105.230 with SMTP id gp6mr235173qeb.71.1378624870685; Sun, 08 Sep 2013 00:21:10 -0700 (PDT) MIME-Version: 1.0 Received: by 10.49.64.70 with HTTP; Sun, 8 Sep 2013 00:20:50 -0700 (PDT) X-Originating-IP: [31.51.107.217] In-Reply-To: <1FA8DFFA-5B17-4289-910C-D7EB029DF347@gesconsultor.com> References: <521CEEC0.30052.101E5B@kevin.kmz.co.za> <1FA8DFFA-5B17-4289-910C-D7EB029DF347@gesconsultor.com> From: Dan Haywood Date: Sun, 8 Sep 2013 08:20:50 +0100 Message-ID: Subject: Re: Using JDO helper methods to check existence of an object for a test To: dev Cc: users Content-Type: multipart/alternative; boundary=047d7b676eaab2309a04e5da1d54 X-Virus-Checked: Checked by ClamAV on apache.org --047d7b676eaab2309a04e5da1d54 Content-Type: text/plain; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable 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 - =D3scar Bou 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 i= s > 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 BD= D > 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 method= s, > 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.IsisJdoSuppor= tImpl; > > /** > * Requires the Gherkin's capture in the format '([entitySingularName] > [(.+?) > * name(d)|(.+?) reference|(.+?) id] \"[entityId]\")'. > *

> * For example: > *

    > *
  • 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"
  • > *
> *

> * For matching the first one we will need the following Gherkin regular > * expression: > *

    > *
  • > * \\@When("The company's (employee with name \"[^\"]*\") has a role > assigned")
  • > *
> *

> * From there, we know that: > *

    > *
  • The Entity singular name is "employee".
  • > *
  • We must search by name
  • > *
  • The name must be PETER
  • > *
> * > */ > public class GenericIsisJdoTransformer extends > NullRecognizingTransformer { > > private static Map > specificationsBySingularName; > > /** > * Tries to obtain the Entity class name, id and search field from th= e > * 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 =3D=3D null) = { > IsisJdoTransformer.specificationsBySingularName =3D new > HashMap(); > > for (final ObjectSpecification current : > IsisContext.getSpecificationLoader().allSpecifications()) { > > > IsisJdoTransformer.specificationsBySingularName.put(current.getSingularNa= me().toLowerCase(), > current); > } > > } > return > IsisJdoTransformer.specificationsBySingularName.get(entityName.toLowerCas= e()); > > } > > private final Class entityClassFrom(final String capture) { > > // The Entity Id will be between "". > String entityName =3D ""; > > final String[] recognizedPatterns =3D { "( with name )", "( named > )", "( with reference )", "( referenced )", "( with id )" }; > > for (final String currentPattern : recognizedPatterns) { > final Pattern p =3D Pattern.compile(currentPattern); > final Matcher m =3D p.matcher(capture); > if (m.find()) { > entityName =3D capture.substring(0, m.start()); > break; > } > } > > if (entityName =3D=3D "") { > 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 =3D > this.specificationOf(entityName); > if (specification =3D=3D 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=3D=3D\"%s\"", > this.entityIdFrom(capture)); > } > > // Find by "reference". > if (capture.matches("(.+?)reference(.+?)")) { > return String.format("reference=3D=3D\"%s\"", > this.entityIdFrom(capture)); > } > > // Find by "id". > if (capture.matches("(.+?)id(.+?)")) { > return String.format("id=3D=3D\"%s\"", this.entityIdFrom(capt= ure)); > } > > 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 =3D Pattern.compile("\"(.+?)\""); > final Matcher m =3D 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 =3D > ScenarioExecution.current().service(IsisJdoSupportImpl.class).getJdoPersi= stenceManager().newQuery(); > query.setClass(this.entityClassFrom(capture)); > query.setFilter(this.filterFrom(capture)); > query.setUnique(true); > > final Object result =3D query.execute(); > > return result; > > } > } > > > > > --------------------- > > > > > > El 27/08/2013, a las 20:24, Kevin Meyer - KMZ escribi= =F3: > > > 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 doNotYetComplete() { > > return allMatches( > > new QueryDefault(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: > >> > >> > >> > >> 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 colum= n > 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 servi= ce > methods to construct the object. > >> > >> The "exists" method contains this code: > >> > >> public boolean exists(ServiceClass serviceClass) { > >> final QueryFindByPattern query =3D new > QueryFindByPattern(ServiceClass.class, serviceClass); > >> final ServiceClass firstMatch =3D firstMatch(query); > >> > >> return firstMatch !=3D 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: > >> > >> > > --047d7b676eaab2309a04e5da1d54--