Skip to content
This repository was archived by the owner on Sep 1, 2025. It is now read-only.

Commit 11ab862

Browse files
feature: create a hello channel extension point
closes #3 Signed-off-by: Jorge Aguilera <[email protected]>
1 parent e6544a5 commit 11ab862

File tree

5 files changed

+75
-17
lines changed

5 files changed

+75
-17
lines changed

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
This project shows how to implement a simple Nextflow plugin named `nf-hello` that intercepts
44
workflow execution events to print a message when the execution starts and on workflow completion.
55

6+
Also, this plugin enriches the `channel` with a `producer` a `consumer` methods (`sayHello` and `goodbye`)
7+
allowing to include them into the script
8+
69
## Plugin assets
710
811
- `settings.gradle`
@@ -38,6 +41,18 @@ workflow execution events to print a message when the execution starts and on wo
3841

3942
The plugin unit tests.
4043

44+
## ExtensionPointS
45+
46+
ExtensionPoint is the basic interface who use nextflow-core to integrate plugins into it.
47+
It's only a basic interface and serves as starting point for more specialized extensions.
48+
49+
Among others, nextflow-core integrate following sub ExtensionPointS:
50+
51+
- `TraceObserverFactory` to provide a list of TraceObserverS
52+
- `ChannelExtensionPoint` to enrich the channel with custom methods
53+
54+
In this plugin you can find examples for both of them
55+
4156
## Compile & run unit tests
4257

4358
Run the following command in the project root directory (ie. where the file `settings.gradle` is located):

plugins/nf-hello/src/main/nextflow/hello/HelloExtension.groovy

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,23 +23,57 @@ import java.util.concurrent.CompletableFuture
2323
@Scoped('hello')
2424
class HelloExtension extends ChannelExtensionPoint{
2525

26+
/*
27+
* A session hold information about current execution of the script
28+
*/
2629
private Session session
2730

31+
/*
32+
* nf-core initializes the plugin once loaded and session is ready
33+
* @param session
34+
*/
2835
@Override
2936
protected void init(Session session) {
3037
this.session = session
3138
}
3239

33-
DataflowWriteChannel sayHello() {
34-
createHelloChannel()
40+
/*
41+
* reverse is a `producer` method and will be available to the script because:
42+
*
43+
* - it's public
44+
* - it returns a DataflowWriteChannel
45+
*
46+
* nf-core will inspect the extension class and allow the script to call all these kind of methods
47+
*
48+
* the method can require arguments but it's not mandatory, it depends of the business logic of the method
49+
*
50+
* business logic can write into the channel once ready and values will be consumed from it
51+
*/
52+
DataflowWriteChannel reverse(String message) {
53+
createReverseChannel(message)
3554
}
3655

3756
static String goodbyeMessage
3857

58+
/*
59+
* goodbye is a `consumer` method as it receives values from a channel to perform some logic.
60+
*
61+
* Consumer methods are introspected by nextflow-core and include into the DSL if the method:
62+
*
63+
* - it's public
64+
* - it returns a DataflowWriteChannel
65+
* - it has only one arguments of DataflowReadChannel class
66+
*
67+
* a consumer method needs to proporcionate 2 closures:
68+
* - a closure to consume items (one by one)
69+
* - a finalizer closure
70+
*
71+
* in this case `goodbye` will consume a message and will store it as an upper case
72+
*/
3973
DataflowWriteChannel goodbye(DataflowReadChannel source) {
4074
final target = CH.createBy(source)
4175
final next = {
42-
goodbyeMessage = "$it"
76+
goodbyeMessage = "$it".toString().toUpperCase()
4377
target.bind(it)
4478
}
4579
final done = {
@@ -49,26 +83,33 @@ class HelloExtension extends ChannelExtensionPoint{
4983
target
5084
}
5185

52-
protected DataflowWriteChannel createHelloChannel(){
86+
protected DataflowWriteChannel createReverseChannel(final String message){
5387
final channel = CH.create()
5488
if( NF.isDsl2() ){
5589
session.addIgniter { ->
56-
sayHelloImpl(channel)
90+
businessLogicHere(channel, message)
5791
}
5892
}else{
59-
sayHelloImpl(channel)
93+
businessLogicHere(channel, message)
6094
}
6195
channel
6296
}
6397

64-
protected sayHelloImpl(final DataflowWriteChannel channel){
98+
/*
99+
* businessLogicHere will send, across the channel, the message reversed
100+
* and after will send an STOP signal to let know the channel it has been finished
101+
*/
102+
protected static businessLogicHere(final DataflowWriteChannel channel, final String message){
65103
def future = CompletableFuture.runAsync({
66-
channel.bind("Hi")
104+
channel.bind(message.reverse())
67105
channel.bind(Channel.STOP)
68106
})
69107
future.exceptionally(this.&handlerException)
70108
}
71109

110+
/*
111+
* an util class to trace exceptions
112+
*/
72113
static private void handlerException(Throwable e) {
73114
final error = e.cause ?: e
74115
log.error(error.message, error)
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Manifest-Version: 1.0
22
Plugin-Id: nf-hello
3-
Plugin-Version: 0.1.0
3+
Plugin-Version: 0.2.0
44
Plugin-Class: nextflow.hello.HelloPlugin
55
Plugin-Provider: nextflow
6-
Plugin-Requires: >=21.04.0
6+
Plugin-Requires: >=22.04.0

plugins/nf-hello/src/test/nextflow/hello/ChannelExtensionHelloTest.groovy

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,14 @@ class ChannelExtensionHelloTest extends Specification{
2222
def helloExtension = new HelloExtension(); helloExtension.init(session)
2323

2424
when:
25-
def result = helloExtension.sayHello()
25+
def result = helloExtension.reverse("Hi")
2626

2727
then:
28-
result.val == 'Hi'
28+
result.val == 'iH'
2929
result.val == Channel.STOP
3030
}
3131

32-
def "should receive a hi from hello"(){
32+
def "should consume a message from script"(){
3333

3434
given:
3535
def session = Mock(Session)
@@ -48,6 +48,6 @@ class ChannelExtensionHelloTest extends Specification{
4848
then:
4949
result.val == 'Goodbye folks'
5050
result.val == Channel.STOP
51-
helloExtension.goodbyeMessage == 'Goodbye folks'
51+
helloExtension.goodbyeMessage == 'Goodbye folks'.toUpperCase()
5252
}
5353
}

plugins/nf-hello/src/test/nextflow/hello/HelloDslTest.groovy

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@ import nextflow.Channel
44
import nextflow.extension.ChannelExtensionDelegate
55
import nextflow.plugin.Plugins
66
import spock.lang.Specification
7+
import spock.lang.Timeout
78

89

910
/**
1011
* @author : jorge <[email protected]>
1112
*
1213
*/
14+
@Timeout(10)
1315
class HelloDslTest extends Specification{
1416

1517
def setup () {
@@ -19,12 +21,12 @@ class HelloDslTest extends Specification{
1921
def 'should perform a hi and create a channel' () {
2022
when:
2123
def SCRIPT = '''
22-
channel.hello.sayHello()
24+
channel.hello.reverse('hi!')
2325
'''
2426
and:
2527
def result = new MockScriptRunner([:]).setScript(SCRIPT).execute()
2628
then:
27-
result.val == 'Hi'
29+
result.val == '!ih'
2830
result.val == Channel.STOP
2931
}
3032

@@ -42,6 +44,6 @@ class HelloDslTest extends Specification{
4244
result.val == Channel.STOP
4345

4446
and:
45-
HelloExtension.goodbyeMessage == 'Bye bye folks'
47+
HelloExtension.goodbyeMessage == 'Bye bye folks'.toUpperCase()
4648
}
4749
}

0 commit comments

Comments
 (0)