On 09/05/2013, at 12:51 AM, Adam Murdoch <adam.murd...@gradleware.com> wrote:

> Hi,
> 
> Just revisiting the old 'how do we deal with names for domain objects' 
> question.
> 
> Currently, our polymorphic container is-a NamedDomainObjectContainer. As such 
> it forces every domain object to have a unique name. The problem with this 
> approach is that I need to encode the type into the name.
> 
> For example, we are planning to use a `binaries` container to own all the 
> binaries produced by the project. If I have a `main` Java library, then we 
> need to add (by convention) a `mainClasses` ClassesDirectory binary and a 
> `mainJar` Jar binary for this library. If I have a `main` Groovy library 
> built for Groovy 1.8 and Groovy 2.0, then I need to add `mainGroovy18Classes` 
> and `mainGroovy20Classes` and `mainGroovy18Jar` and `mainGroovy20Jar`. If I 
> have a `main` C library built for windows and linux with 32-bit and 64-bit 
> and static and shared and debug and release variants, well… then we need lots 
> of names.
> 
> I'd like to change things so we can get rid of the type from the name. This, 
> of course, still leaves the other dimensions packed into the name. We don't 
> have a good solution for this yet, but I suspect one will emerge.
> 
> The approach I'd like to take (which isn't a new idea - can't remember who 
> suggested it) is to make (name, type) the unique identifier for a thing.
> 
> The DSL for defining an object would change from this:
> 
> binaries {
>     mainStaticLibrary(StaticLibraryBinary) { … }
>     mainSharedLibrary(SharedLibraryBinary) { … }
> }
> 
> To:
> 
> binaries {
>     staticLibraries {
>         main { … }
>     }
>     sharedLibraries {
>         main { … }
>     }
> }
> 
> Also:
> 
> publications {
>     maven { 
>         main { … }
>     }
>     ivy { 
>         main { … }
>     }
> }
> 
> 
> To find something:
> 
> // look something up by name
> def main = binaries.main  // fails if there are multiple binaries with name 
> `main`
> 
> // look something up by type and name
> def main = binaries.staticLibraries.main
> 
> // look something up by super type and name
> def main = binaries.nativeLibraries.main // fails if there are multiple 
> native libraries with name `main`

Are we automatically doing the lookup here? i.e. drop package, camel case and 
pluralise? Or is there an explicit mapping between a type and its "dsl name"?

> // look up all things by type
> def libs = binaries.staticLibraries
> 
> And to deal with the other dimensions, you'd use the existing collection 
> stuff:
> 
> // All windows static libs
> def libs = binaries.staticLibraries.matching { it.platform.operatingSystem == 
> operatingSystems.windows }
> 
> We can come up with conveniences for the other dimensions. Perhaps a 
> map-based selector:
> 
> def libs = binaries.staticLibraries(platform.operatingSystem: 
> operatingSystems.windows)
> 
> Thoughts? I don't think this plan quite gets it right, but it feels better 
> than the current DSL.

I don't think it really fixes the problem. I think we want to get away from the 
idea of an immutable name altogether. Instead, we probably want a way to 
extract an identifier based on differentiating characteristics. This seems to 
be how the names are used as we've discussed.

Combining this with some of deferred configuration stuff, I think you want to 
tell Gradle which characteristics to use to construct the identifier. This also 
means you don't have to predict names. 

Disclaimer: I haven't really thought this through beyond reading this email.

Let's say we have the following interface:

interface Identifiable {
        String getIdentifier()
        void setIdentifier(String identifierPattern)
}

An identifier pattern is a tokenisable (at runtime) string, based on properties 
of the thing…

class HttpRepository extends AbstractIdentifiable {     
        …
        String getUrl()
}

def repository = new HttpRepository(url: "http://org.com";) 
repository.identifier = "#url"
assert repository.identifier == "http://org.com";

The benefit here is that types can supply a default naming strategy, which 
could be based on important characteristics. Assuming that we find some way to 
actually "lock" objects after they've been configured with the deferring stuff, 
we could also make the actual value immutable at this time. We could defer 
preventing collisions until this point.

The idea is also that the identifier is only used for output and for deriving 
names (would be good if we could avoid this in the future too). If you are 
programmatically looking for something, you find it by looking up 
characteristics. I don't think plugins do this often anyway. It doesn't seem 
often to me that infrastructure code pulls objects from containers via their 
name. You're usually agnostic to containers at this level and work directly 
with the instances. Configurers (a.k.a users) do this all the time, but they 
should have the knowledge required to identify the thing they want. If you add 
in a singleFile type approach here (i.e. I'm expecting there to be one thing in 
this collection and I want it) then I think it would work.

While we are discussing the DSL we should also consider making creation 
explicit. We have the same problem with our domain object containers as we did 
with dynamic properties. Namely, we can't tell if the user wants to create 
something or configure an existing thing and therefore can't detect typos or 
tell the user that that thing doesn't actually exist.

Another idea that I had (not related to above paragraph) was formalising the 
concept of a lookup path. This could be used to avoid duplication…

publications(IvyPublication, group: "org.foo", version: "1.0") {
        add { module "a" }
        add { module "b" }
        add { module "c" }
}

So publications(IvyPublication, group: "org.foo", version: "1.0") {} creates a 
kind of context that is inherited during the closure. All things created in 
this closure get these properties.

Could also be used for filtering/configuring:

publications(IvyPublication, group: "org.foo", version: "1.0") {
        find(module: "a")
        find(module: "b")       
        …
}

I suspect that situation is also reasonably describable via DSLD and whatever 
the IDEA equivalent is.

-- 
Luke Daley
Principal Engineer, Gradleware 
http://gradleware.com

Join me at the Gradle Summit 2013, June 13th and 14th in Santa Clara, CA: 
http://www.gradlesummit.com


---------------------------------------------------------------------
To unsubscribe from this list, please visit:

    http://xircles.codehaus.org/manage_email


Reply via email to