Joseph K. Strauss
Joseph K. Strauss

Reputation: 4913

Bitbucket - Groovy Pre-receive Hook

I am trying to understand the following code snippet following site.

<% refChanges.getCommits(repository).each { commit -> %>
    - ${commit.author.name} | ${commit.displayId} | ${commit.message} | ${commit.authorTimestamp}
<% } %>

The script is using a getCommits method, but when I look at the documentation for the RefChange interface I do not see any such method.

I consider myself an expert Java developer, but I have no workable knowledge in Groovy, so I assume that I am misunderstanding Groovy or the BitBucket documentation (or both).

Upvotes: 0

Views: 418

Answers (1)

Emmanuel Rosa
Emmanuel Rosa

Reputation: 9895

In Groovy it's possible to add methods to a class or interface at run-time via meta-programming. Since the RefChange interface does not include getCommits(), it must be that the method is being added after-the-fact. Based on their example code, it looks like they're using the meta-class.

Where is getCommits()?

For example, in Groovy the Collection interface gets the method findAll() (along with many other methods). I can confirm this as follows:

assert Collection.metaClass.metaMethods*.name.contains('findAll') == true

The code above grabs the names of all the meta methods and then uses contains() to see if a match is found. You can confirm the same for getCommits() in a similar way:

assert Collection.metaClass.metaMethods*.name.contains('getCommits') == true

Note that I specified Collection rather than RefChange because refChanges is a Collection of RefChange. And so I think Atlasssian stuck getCommits() into Collection as a convenience method.

How does it work?

To understand what's going, I'll remove the templating code:

refChanges.getCommits(repository).each { commit ->
    "${commit.author.name} | ${commit.displayId} | ${commit.message} | ${commit.authorTimestamp}"
}
  1. getCommits() returns a Collection of com.atlassian.bitbucket.commit.Commit.
  2. Object.each(Closure) is added by the Groovy GDK (yes, into the Object class) and it calls the Closure repeatedly; each time with an element of the Collection.
  3. Although it's not apparent in the example code, the line within the each(Closure) is a GString. Basically the expressions within the ${...} are evaluated and the whole thing is concatenated into a String.

Upvotes: 2

Related Questions