More on gradle closures

satya - 7/25/2018, 9:46:11 AM

There are lots of them, these closures in Gradle

There are closures on a project object that are documented in the API for a project

it is important to know what the delegate for a Closure is

Closure uses the delegate to resolve methods and properties

Closures in Gradle are used often to call methods on its delegate to configure that delegate

when a closure on a particular task is used, you are configuring the properties of that task to change its behavior

when you use a closure on a maven repository you are calling maven repository objects properties to add URLs.

satya - 7/25/2018, 9:47:37 AM

Consider this


buildscript {
   repositories {
       maven { url = "some-url" }
   }
}

satya - 7/25/2018, 10:03:21 AM

Explanation

1. project defines a method with closure called buildscript. Passes ScriptHandler as the delegate

2. So ScriptHandler has a method repositories() that takes a closure. This closure uses RepoistoryHandler as the delegate.

3. maven is a method on RepositoryHandler with a closure. Documentation says it passes a Maven handler as a "parameter". Not a delegate.

4. However it appears as if it is also passed as a delegate

5. maven handler has a get/set property setUrl(url) to set the url(s) //i think

satya - 7/25/2018, 10:03:37 AM

groovy maven url property

groovy maven url property

Search for: groovy maven url property

satya - 7/25/2018, 10:03:54 AM

this, delegate, it, owner in groovy closures

this, delegate, it, owner in groovy closures

Search for: this, delegate, it, owner in groovy closures

satya - 7/25/2018, 10:05:01 AM

You can do this in any closure


buildscript {
   repositories {
       maven { 
         url = "some-url" 
         println this
         println it
         println delegate
         println owner
       }
   }
}

satya - 7/25/2018, 10:05:48 AM

Start here for groovy closures

Start here for groovy closures

satya - 7/25/2018, 10:07:07 AM

Owner, delegate, this is explained here

Owner, delegate, this is explained here

satya - 7/25/2018, 10:07:26 AM

The "it" is defined here

The "it" is defined here

satya - 7/25/2018, 10:11:49 AM

DefaultArtifactMavenRepository.groovy source code

DefaultArtifactMavenRepository.groovy source code

Search for: DefaultArtifactMavenRepository.groovy source code

satya - 7/25/2018, 10:17:35 AM

Here is the source code for that

Here is the source code for that

satya - 7/25/2018, 10:20:25 AM

Odd, how can you add multiple urls for a single URL property in maven repository of Gradle?

Odd, how can you add multiple urls for a single URL property in maven repository of Gradle?

Search for: Odd, how can you add multiple urls for a single URL property in maven repository of Gradle?

satya - 7/25/2018, 10:22:27 AM

This is explored here

This is explored here

satya - 7/25/2018, 10:22:50 AM

Source code is correct. URL is a single value field.

Source code is correct. URL is a single value field.

satya - 7/25/2018, 10:23:13 AM

So this is wrong


repositories {
    mavenCentral()
    maven {
        url "http://maven.springframework.org/release"
        url "http://maven.restlet.org"
    }
}

satya - 7/25/2018, 10:23:30 AM

Correct way


repositories {
  maven { url "http://maven.springframework.org/release" }
  maven { url "https://maven.fabric.io/public" }
}

satya - 7/25/2018, 10:27:16 AM

Because

if you see the "maven" method on the repositories handler it says this closure will "add and configure". Means each line or method call is an add of a new repo of maven!

That makes sense

satya - 8/16/2018, 12:23:30 PM

In understanding this further you will need a link to the nature of RepositoryHandler

In understanding this further you will need a link to the nature of RepositoryHandler

satya - 8/16/2018, 12:40:20 PM

RepositoryHandler is a sub class of ArtifactRepositoryContainer

RepositoryHandler is a sub class of ArtifactRepositoryContainer

satya - 8/16/2018, 12:40:39 PM

What is a ResolverContainer in Gradle?

What is a ResolverContainer in Gradle?

Search for: What is a ResolverContainer in Gradle?

satya - 8/16/2018, 12:44:43 PM

RepositoryHandler

It is a collection of ArtifactRepositories

RepositoryHandler has a number of methods to add specific repositories

Its base calass ArtifactRepositoryContainer and its super classes can be used to retrieve the individual repositories

satya - 8/16/2018, 2:42:38 PM

Basic explanation of closure objects


this - usual this object in java
   true even if the closure is a nested closure

owner - The parent object of the current clsoure
   may be same as this for direct closures
   will differ for inner closure
   for an inner closure the the it is 
   the previous closure.

delegate - An external object acts like an owner
  variables are scoped against the delegate as well
  By default, delegate is set to owner

satya - 8/16/2018, 2:44:08 PM

Note that delegate is set and hence an object

Note that delegate is set and hence an object

satya - 8/16/2018, 2:44:23 PM

Consider these classes


class Person {
    String name
}
class Thing {
    String name
}

satya - 8/16/2018, 2:45:17 PM

Objects out of those


def p = new Person(name: 'Norman')
def t = new Thing(name: 'Teapot')

Also notice the auto named arg constructors.

satya - 8/16/2018, 2:47:57 PM

Define a closure


//Notice name is an unresolved variable
def cl = { name.toUpperCase() }

//Set the delegate to be "p" from above
cl.delegate = p                                 
assert cl() == 'NORMAN' 

//Set the delegate to be "t" from above
cl.delegate = t
assert cl() == 'IGOR' 

satya - 8/16/2018, 2:50:06 PM

Closure.resolveStrategy can be set to

Closure.OWNER_FIRST is the default strategy. If a property/method exists on the owner, then it will be called on the owner. If not, then the delegate is used.

Closure.DELEGATE_FIRST reverses the logic: the delegate is used first, then the owner

Closure.OWNER_ONLY will only resolve the property/method lookup on the owner: the delegate will be ignored.

Closure.DELEGATE_ONLY will only resolve the property/method lookup on the delegate: the owner will be ignored.

Closure.TO_SELF can be used by developers who need advanced meta-programming techniques and wish to implement a custom resolution strategy: the resolution will not be made on the owner or the delegate but only on the closure class itself. It makes only sense to use this if you implement your own subclass of Closure.

satya - 8/16/2018, 2:54:21 PM

It the implicit parameter

When a closure does not explicitly define a parameter list (using ->), a closure always defines an implicit parameter, named it.

satya - 8/16/2018, 2:54:43 PM

Sample


def greeting = { "Hello, $it!" }
assert greeting('Patrick') == 'Hello, Patrick!'

satya - 8/16/2018, 2:55:47 PM

So in the end in your closure it is useful to do this to understand that closure


buildscript {
   repositories {
       maven { 
         url = "some-url" 
         println this
         println it
         println delegate
         println owner
       }
   }
}

satya - 8/16/2018, 2:56:51 PM

In Java lambda expressions are instances of single method interfaces with inner class semantics

In Java lambda expressions are instances of single method interfaces with inner class semantics

satya - 8/16/2018, 2:57:09 PM

In Groovy closures are instances of Closure class with delegation capability

In Groovy closures are instances of Closure class with delegation capability

satya - 8/16/2018, 3:05:39 PM

So lets read this code again


//So this is wrong
repositories {
    mavenCentral()
    maven {
        url "http://maven.springframework.org/release"
        url "http://maven.restlet.org"
    }
}

//Correct way
repositories {
  maven { url "http://maven.springframework.org/release" }
  maven { url "https://maven.fabric.io/public" }
}

satya - 8/16/2018, 3:08:12 PM

Explanation

Remember, Each child of respositories is "A REPOSITORY".

So mavenCentrl() is a repo.

maven (with a url) is ONE Repo

maven IS NOT a collection of URLs, in other words maven() is not a collection of maven repositories.

The underlying repository MavenArtifactRespoistory() has a method setURL() which sets the URL. Calling it again will overr write the previous one.

So it all depends on how the underlying java classes and their methods are implemented.

satya - 8/16/2018, 3:59:50 PM

Consider this


repositories {
    flatDir name: 'libs', dirs: "$projectDir/libs"
    flatDir dirs: ["$projectDir/libs1", "$projectDir/libs2"]
}

satya - 8/16/2018, 4:01:25 PM

Exaplanation

There are 2 repos here

Both of type flatDir, or just a directory to look for assets

First Flat directory

It is called "libs"

it has a root directory specified

Second Flat Directory

it has no name

it is a list of directories

satya - 8/16/2018, 4:02:14 PM

It is better read as


There are 2 repos here
Both of type flatDir, or just a directory to look for assets

First Flat directory
   It is called "libs"
   it has a root directory specified

Second Flat Directory
  it has no name
  it is a list of directories

satya - 8/16/2018, 4:03:03 PM

This is facilitated by flatDir() method signature on RepositoryHandler


FlatDirectoryArtifactRepository flatDir(Map<String, ?> args)

satya - 8/16/2018, 4:05:58 PM

Explanation


It is a map that is the input
It is really an associate array
Meaning it should have been an object definition like

   FileDirSpec
     name
     List<String> dirs;

Instead this is represented as a generic map
This map can have only 2 keys
   name - A string
   dirs - A string, or a list of strings

satya - 8/16/2018, 4:07:45 PM

So I can read this as


repositories {
    flatDir name: 'libs', dirs: "$projectDir/libs"
    flatDir dirs: ["$projectDir/libs1", "$projectDir/libs2"]
}

//First one
map1.add("name", "libs")
map1.add("dirs","$projectDir/libs");
flatDir(map1)

//Similarly second one

satya - 8/16/2018, 4:08:30 PM

See this to understand named arguments to constructors and methods

See this to understand named arguments to constructors and methods