Skip to content

Commit 67681ab

Browse files
richardjhardingRichard
andauthored
Add fsharp Akka streams template (#412)
* initial streams template * update readme * update streams doc for f# --------- Co-authored-by: Richard <[email protected]>
1 parent 66fffd7 commit 67681ab

File tree

10 files changed

+318
-114
lines changed

10 files changed

+318
-114
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The following templates are available as part of the `Akka.Templates` package:
3737
|--------------|-------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------|
3838
| [Akka.Cluster.WebApi](https://github.com/akkadotnet/akkadotnet-templates/blob/dev/docs/WebApiTemplate.md) | akka.cluster.webapi | A template for building ASP.NET HTTP APIs on top of an Akka.NET Cluster. Uses Akka.Cluster.Sharding and, optionally: Akka.Management + Akka.Persistence.Azure + Akka.Azure.Discovery. This template is meant as a starter for building distributed systems with Akka.NET | C# |
3939
| [Akka.Console](https://github.com/akkadotnet/akkadotnet-templates/blob/dev/docs/ConsoleTemplate.md) | akka.console | This is a simple template designed to incorporate local [Akka.NET](https://getakka.net/) into a console application. | C#, F# |
40-
| [Akka.Streams](https://github.com/akkadotnet/akkadotnet-templates/blob/dev/docs/AkkaStreamsTemplate.md) | akka.streams | This is a simple template designed to incorporate [Akka.NET](https://getakka.net/)'s [Akka.Streams APIs](https://getakka.net/articles/streams/introduction.html) into a local console template. | C# |
40+
| [Akka.Streams](https://github.com/akkadotnet/akkadotnet-templates/blob/dev/docs/AkkaStreamsTemplate.md) | akka.streams | This is a simple template designed to incorporate [Akka.NET](https://getakka.net/)'s [Akka.Streams APIs](https://getakka.net/articles/streams/introduction.html) into a local console template. | C#, F# |
4141

4242
See [the official `dotnet new` documentation](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-new) for more information on the sorts of options that are available when using project templates.
4343

docs/AkkaStreamsTemplate.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ dotnet new -i "Akka.Templates::*"
1313
From there, you can use this template via the following command:
1414

1515
```
16-
dotnet new akka.streams -n "your project name"
16+
# For C#
17+
dotnet new akka.streams -n "your-project-name"
18+
19+
# For F#
20+
dotnet new akka.streams -n "your-project-name" -lang F#
1721
```
1822

1923
## How It Works
@@ -65,4 +69,42 @@ await Source.From(Enumerable.Range(1, 1000))
6569
.RunForeach(Console.WriteLine, system); // write all output to console
6670
```
6771

72+
<details>
73+
<summary><b>F# Implementation</b></summary>
74+
75+
```fsharp
76+
let hostbuilder = HostBuilder()
77+
78+
hostbuilder.ConfigureServices(fun services ->
79+
services.AddAkka("MyActorSystem", fun b ->
80+
81+
b.WithActors(fun sys reg ->
82+
let helloActor = sys.ActorOf(Props.Create<HelloActor>(fun () -> HelloActor()), "hello-actor")
83+
reg.Register<HelloActor>(helloActor)) |> ignore
84+
85+
b.WithActors(fun sys reg resolver ->
86+
let timerActorProps = resolver.Props<TimerActor>()
87+
let timerActor = sys.ActorOf(timerActorProps, "timer-actor")
88+
reg.Register<TimerActor>(timerActor)) |> ignore
89+
90+
) |> ignore
91+
) |> ignore
92+
93+
let host = hostbuilder.Build()
94+
host.RunAsync().Wait()
95+
96+
// example transform actor
97+
type TransformActor() as this =
98+
inherit ReceiveActor()
99+
100+
do
101+
this.Receive<string> (fun (message:string)->
102+
let actor = this :> IInternalActor
103+
actor.ActorContext.Sender.Tell (message.ToUpper())
104+
)
105+
106+
```
107+
108+
</details>
109+
68110
This is a simple, finite stream that uses some of [Akka.Streams' built-in stages](https://getakka.net/articles/streams/builtinstages.html) to demonstrate asynchronous stream processing as well as [Akka.NET actor integration with Akka.Streams](https://getakka.net/articles/streams/integration.html).

scripts/test-templates.ps1

Lines changed: 115 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,116 @@
1-
# Inspired by https://github.com/AvaloniaUI/avalonia-dotnet-templates/blob/main/tests/build-test.ps1
2-
# Enable common parameters e.g. -Verbose
3-
[CmdletBinding()]
4-
param()
5-
6-
Set-StrictMode -Version latest
7-
$ErrorActionPreference = "Stop"
8-
9-
# Taken from psake https://github.com/psake/psake
10-
<#
11-
.SYNOPSIS
12-
This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
13-
to see if an error occcured. If an error is detected then an exception is thrown.
14-
This function allows you to run command-line programs without having to
15-
explicitly check the $lastexitcode variable.
16-
.EXAMPLE
17-
exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
18-
#>
19-
function Exec
20-
{
21-
[CmdletBinding()]
22-
param(
23-
[Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
24-
[Parameter(Position=1,Mandatory=0)][string]$errorMessage = ("Error executing command {0}" -f $cmd)
25-
)
26-
27-
# Convert the ScriptBlock to a string and expand the variables
28-
$expandedCmdString = $ExecutionContext.InvokeCommand.ExpandString($cmd.ToString())
29-
Write-Verbose "Executing command: $expandedCmdString"
30-
31-
Invoke-Command -ScriptBlock $cmd
32-
33-
if ($lastexitcode -ne 0) {
34-
throw ("Exec: " + $errorMessage)
35-
}
36-
}
37-
38-
function Test-Template {
39-
param (
40-
[Parameter(Position=0,Mandatory=1)][string]$template,
41-
[Parameter(Position=1,Mandatory=1)][string]$name,
42-
[Parameter(Position=2,Mandatory=1)][string]$lang,
43-
[Parameter(Position=3,Mandatory=1)][string]$parameterName,
44-
[Parameter(Position=4,Mandatory=1)][string]$value,
45-
[Parameter(Position=5,Mandatory=0)][string]$bl
46-
)
47-
48-
$folderName = $name + $parameterName + $value
49-
50-
# Remove dots and - from folderName because in sln it will cause errors when building project
51-
$folderName = $folderName -replace "[.-]"
52-
53-
# Create the project
54-
Exec { dotnet new $template -o output//$lang/$folderName -$parameterName $value -lang $lang }
55-
56-
# Build
57-
Exec { dotnet build output/$lang/$folderName -bl:$bl }
58-
Exec { dotnet test output/$lang/$folderName -bl:$bl } # some templates might include unit tests
59-
Exec { dotnet publish -c Release -t:PublishContainer output/$lang/$folderName -bl:$bl }
60-
}
61-
62-
function Create-And-Build {
63-
param (
64-
[Parameter(Position=0,Mandatory=1)][string]$template,
65-
[Parameter(Position=1,Mandatory=1)][string]$name,
66-
[Parameter(Position=2,Mandatory=1)][string]$lang,
67-
[Parameter(Position=3,Mandatory=1)][string]$parameterName,
68-
[Parameter(Position=4,Mandatory=1)][string]$value,
69-
[Parameter(Position=5,Mandatory=0)][string]$bl
70-
)
71-
72-
$folderName = $name + $parameterName + $value
73-
74-
# Remove dots and - from folderName because in sln it will cause errors when building project
75-
$folderName = $folderName -replace "[.-]"
76-
77-
# Create the project
78-
Exec { dotnet new $template -o output/$lang/$folderName -$parameterName $value -lang $lang }
79-
80-
# Build
81-
Exec { dotnet build output/$lang/$folderName -bl:$bl }
82-
}
83-
84-
# Clear file system from possible previous runs
85-
Write-Output "Clearing outputs from possible previous runs"
86-
if (Test-Path "output" -ErrorAction SilentlyContinue) {
87-
Remove-Item -Recurse -Force "output"
88-
}
89-
$outDir = [IO.Path]::GetFullPath([IO.Path]::Combine($pwd, "..", "output"))
90-
if (Test-Path $outDir -ErrorAction SilentlyContinue) {
91-
Remove-Item -Recurse -Force $outDir
92-
}
93-
$binLogDir = [IO.Path]::GetFullPath([IO.Path]::Combine($pwd, "..", "binlog"))
94-
if (Test-Path $binLogDir -ErrorAction SilentlyContinue) {
95-
Remove-Item -Recurse -Force $binLogDir
96-
}
97-
98-
# Use same log file for all executions
99-
$binlog = [IO.Path]::GetFullPath([IO.Path]::Combine($pwd, "..", "binlog", "test.binlog"))
100-
101-
Create-And-Build "akka.console" "AkkaConsole" "C#" "f" "net9.0" $binlog
102-
Create-And-Build "akka.console" "AkkaConsole" "C#" "f" "net8.0" $binlog
103-
104-
Create-And-Build "akka.console" "AkkaConsole" "F#" "f" "net9.0" $binlog
105-
Create-And-Build "akka.console" "AkkaConsole" "F#" "f" "net8.0" $binlog
106-
107-
Create-And-Build "akka.streams" "AkkaStreams" "C#" "f" "net9.0" $binlog
108-
Create-And-Build "akka.streams" "AkkaStreams" "C#" "f" "net8.0" $binlog
109-
110-
Test-Template "akka.cluster.webapi" "ClusterWebTemplate" "C#" "f" "net9.0" $binlog
111-
Test-Template "akka.cluster.webapi" "ClusterWebTemplate" "C#" "f" "net8.0" $binlog
112-
1+
# Inspired by https://github.com/AvaloniaUI/avalonia-dotnet-templates/blob/main/tests/build-test.ps1
2+
# Enable common parameters e.g. -Verbose
3+
[CmdletBinding()]
4+
param()
5+
6+
Set-StrictMode -Version latest
7+
$ErrorActionPreference = "Stop"
8+
9+
# Taken from psake https://github.com/psake/psake
10+
<#
11+
.SYNOPSIS
12+
This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
13+
to see if an error occcured. If an error is detected then an exception is thrown.
14+
This function allows you to run command-line programs without having to
15+
explicitly check the $lastexitcode variable.
16+
.EXAMPLE
17+
exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
18+
#>
19+
function Exec
20+
{
21+
[CmdletBinding()]
22+
param(
23+
[Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
24+
[Parameter(Position=1,Mandatory=0)][string]$errorMessage = ("Error executing command {0}" -f $cmd)
25+
)
26+
27+
# Convert the ScriptBlock to a string and expand the variables
28+
$expandedCmdString = $ExecutionContext.InvokeCommand.ExpandString($cmd.ToString())
29+
Write-Verbose "Executing command: $expandedCmdString"
30+
31+
Invoke-Command -ScriptBlock $cmd
32+
33+
if ($lastexitcode -ne 0) {
34+
throw ("Exec: " + $errorMessage)
35+
}
36+
}
37+
38+
function Test-Template {
39+
param (
40+
[Parameter(Position=0,Mandatory=1)][string]$template,
41+
[Parameter(Position=1,Mandatory=1)][string]$name,
42+
[Parameter(Position=2,Mandatory=1)][string]$lang,
43+
[Parameter(Position=3,Mandatory=1)][string]$parameterName,
44+
[Parameter(Position=4,Mandatory=1)][string]$value,
45+
[Parameter(Position=5,Mandatory=0)][string]$bl
46+
)
47+
48+
$folderName = $name + $parameterName + $value
49+
50+
# Remove dots and - from folderName because in sln it will cause errors when building project
51+
$folderName = $folderName -replace "[.-]"
52+
53+
# Create the project
54+
Exec { dotnet new $template -o output//$lang/$folderName -$parameterName $value -lang $lang }
55+
56+
# Build
57+
Exec { dotnet build output/$lang/$folderName -bl:$bl }
58+
Exec { dotnet test output/$lang/$folderName -bl:$bl } # some templates might include unit tests
59+
Exec { dotnet publish -c Release -t:PublishContainer output/$lang/$folderName -bl:$bl }
60+
}
61+
62+
function Create-And-Build {
63+
param (
64+
[Parameter(Position=0,Mandatory=1)][string]$template,
65+
[Parameter(Position=1,Mandatory=1)][string]$name,
66+
[Parameter(Position=2,Mandatory=1)][string]$lang,
67+
[Parameter(Position=3,Mandatory=1)][string]$parameterName,
68+
[Parameter(Position=4,Mandatory=1)][string]$value,
69+
[Parameter(Position=5,Mandatory=0)][string]$bl
70+
)
71+
72+
$folderName = $name + $parameterName + $value
73+
74+
# Remove dots and - from folderName because in sln it will cause errors when building project
75+
$folderName = $folderName -replace "[.-]"
76+
77+
# Create the project
78+
Exec { dotnet new $template -o output/$lang/$folderName -$parameterName $value -lang $lang }
79+
80+
# Build
81+
Exec { dotnet build output/$lang/$folderName -bl:$bl }
82+
}
83+
84+
# Clear file system from possible previous runs
85+
Write-Output "Clearing outputs from possible previous runs"
86+
if (Test-Path "output" -ErrorAction SilentlyContinue) {
87+
Remove-Item -Recurse -Force "output"
88+
}
89+
$outDir = [IO.Path]::GetFullPath([IO.Path]::Combine($pwd, "..", "output"))
90+
if (Test-Path $outDir -ErrorAction SilentlyContinue) {
91+
Remove-Item -Recurse -Force $outDir
92+
}
93+
$binLogDir = [IO.Path]::GetFullPath([IO.Path]::Combine($pwd, "..", "binlog"))
94+
if (Test-Path $binLogDir -ErrorAction SilentlyContinue) {
95+
Remove-Item -Recurse -Force $binLogDir
96+
}
97+
98+
# Use same log file for all executions
99+
$binlog = [IO.Path]::GetFullPath([IO.Path]::Combine($pwd, "..", "binlog", "test.binlog"))
100+
101+
Create-And-Build "akka.console" "AkkaConsole" "C#" "f" "net9.0" $binlog
102+
Create-And-Build "akka.console" "AkkaConsole" "C#" "f" "net8.0" $binlog
103+
104+
Create-And-Build "akka.console" "AkkaConsole" "F#" "f" "net9.0" $binlog
105+
Create-And-Build "akka.console" "AkkaConsole" "F#" "f" "net8.0" $binlog
106+
107+
Create-And-Build "akka.streams" "AkkaStreams" "C#" "f" "net9.0" $binlog
108+
Create-And-Build "akka.streams" "AkkaStreams" "C#" "f" "net8.0" $binlog
109+
110+
Create-And-Build "akka.streams" "AkkaStreams" "F#" "f" "net9.0" $binlog
111+
Create-And-Build "akka.streams" "AkkaStreams" "F#" "f" "net8.0" $binlog
112+
113+
Test-Template "akka.cluster.webapi" "ClusterWebTemplate" "C#" "f" "net9.0" $binlog
114+
Test-Template "akka.cluster.webapi" "ClusterWebTemplate" "C#" "f" "net8.0" $binlog
115+
113116
# Ignore errors when files are still used by another process
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"$schema": "http://json.schemastore.org/dotnetcli.host",
3+
"symbolInfo": {
4+
"Framework": {
5+
"longName": "framework"
6+
}
7+
}
8+
}
8.04 KB
Loading
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"$schema": "http://json.schemastore.org/vs-2017.3.host",
3+
"defaultSymbolVisibility": true,
4+
"icon": "icon.png",
5+
"learnMoreLink": "https://github.com/akkadotnet/akkadotnet-templates/blob/dev/docs/ConsoleTemplate.md",
6+
"order": 0,
7+
"symbolInfo": [
8+
]
9+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"$schema": "http://json.schemastore.org/template",
3+
"author": "Akka",
4+
"classifications": ["Akka.NET", "Akka.Streams", "Streaming"],
5+
"name": "Akka.Streams Console Application",
6+
"description": "A simple console application that uses Akka.Streams and Akka.Hosting to asynchonrously process data.",
7+
"groupIdentity": "Akka.Streams",
8+
"identity": "Akka.Streams.FSharp",
9+
"shortName": "akka.streams",
10+
"defaultName": "AkkaStreams1",
11+
"tags": {
12+
"language": "F#",
13+
"type": "project"
14+
},
15+
"sourceName": "AkkaStreamsTemplate",
16+
"preferNameDirectory": true,
17+
"primaryOutputs": [
18+
{ "path": "AkkaStreamsTemplate.fsproj" },
19+
{
20+
"condition": "(HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")",
21+
"path": "Program.fs"
22+
}
23+
],
24+
"symbols": {
25+
"Framework": {
26+
"type": "parameter",
27+
"description": "The target framework for the project.",
28+
"datatype": "choice",
29+
"choices": [
30+
{
31+
"choice": "net8.0",
32+
"description": "Target net8.0"
33+
},
34+
{
35+
"choice": "net9.0",
36+
"description": "Target net9.0"
37+
}
38+
],
39+
"replaces": "FrameworkParameter",
40+
"defaultValue": "net9.0"
41+
}
42+
},
43+
"postActions": [
44+
{
45+
"id": "restore",
46+
"condition": "(!skipRestore)",
47+
"description": "Restore NuGet packages required by this project.",
48+
"manualInstructions": [
49+
{ "text": "Run 'dotnet restore'" }
50+
],
51+
"actionId": "210D431B-A78B-4D2F-B762-4ED3E3EA9025",
52+
"continueOnError": true
53+
},
54+
{
55+
"id": "editor",
56+
"condition": "(HostIdentifier != \"dotnetcli\" && HostIdentifier != \"dotnetcli-preview\")",
57+
"description": "Opens Program.cs in the editor",
58+
"manualInstructions": [ ],
59+
"actionId": "84C0DA21-51C8-4541-9940-6CA19AF04EE6",
60+
"args": {
61+
"files": "1"
62+
},
63+
"continueOnError": true
64+
}
65+
]
66+
}

0 commit comments

Comments
 (0)