Skip to content

Commit

Permalink
Simple activity, context propagation, and message passing samples
Browse files Browse the repository at this point in the history
  • Loading branch information
cretz committed Jan 21, 2025
1 parent 45502eb commit e49257e
Show file tree
Hide file tree
Showing 26 changed files with 808 additions and 10 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ jobs:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-12, macos-latest]
os: [ubuntu-latest, macos-13, macos-latest]
# Earliest and latest supported
rubyVersion: ["3.1", "3.3"]
rubyVersion: ["3.2", "3.4"]
runs-on: ${{ matrix.os }}
steps:
- name: Checkout repository
Expand Down
46 changes: 44 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,46 @@
AllCops:
NewCops: enable
TargetRubyVersion: 3.1
SuggestExtensions: false
TargetRubyVersion: 3.2
SuggestExtensions: false

# Don't need super for activities or workflows
Lint/MissingSuper:
AllowedParentClasses:
- Temporalio::Activity::Definition
- Temporalio::Workflow::Definition

# The default is too small and triggers simply setting lots of values on a proto
Metrics/AbcSize:
Max: 200

# The default is too small
Metrics/BlockLength:
Max: 100

# The default is too small
Metrics/ClassLength:
Max: 1000

# The default is too small
Metrics/CyclomaticComplexity:
Max: 100

# The default is too small
Metrics/MethodLength:
Max: 100

# The default is too small
Metrics/ModuleLength:
Max: 1000

# The default is too small
Metrics/PerceivedComplexity:
Max: 40

# Don't need API docs for samples
Style/Documentation:
Enabled: false

# Don't need API docs for samples
Style/DocumentationMethod:
Enabled: false
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

source 'https://rubygems.org'

gem 'temporalio'
gem 'temporalio', path: '../temporal-sdk-ruby/temporalio'

group :development do
gem 'minitest'
Expand Down
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,19 @@ until the SDK is marked stable.

Prerequisites:

* Ruby 3.1+
* Ruby 3.2+
* Local Temporal server running (can [install CLI](https://docs.temporal.io/cli#install) then
[run a dev server](https://docs.temporal.io/cli#start-dev-server))
* `bundle install` run in the root

## Samples

<!-- Keep this list in alphabetical order -->
* [activity_simple](activity_simple) - Simple workflow that calls two activities.
* [activity_worker](activity_worker) - Use Ruby activities from a workflow in another language.
* [context_propagation](context_propagation) - Use interceptors to propagate thread/fiber local data from clients
through workflows/activities.
* [message_passing_simple](message_passing_simple) - Simple workflow that accepts signals, queries, and updates.

## Development

Expand Down
23 changes: 23 additions & 0 deletions activity_simple/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Activity Simple

This sample shows calling a couple of simple activities from a simple workflow.

To run, first see [README.md](../README.md) for prerequisites. Then, in another terminal, start the Ruby worker
from this directory:

bundle exec ruby worker.rb

Finally in another terminal, use the Ruby client to the workflow from this directory:

bundle exec ruby starter.rb

The Ruby code will invoke the workflow which will execute two activities and return. The output of the final command
should be:

```
Executing workflow
Workflow result: some-db-value from table some-db-table <appended-value>
```

There is also a [test](../test/activity_simple/my_workflow_test.rb) that demonstrates mocking an activity during the
test.
33 changes: 33 additions & 0 deletions activity_simple/my_activities.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# frozen_string_literal: true

require 'temporalio/activity'

module ActivitySimple
module MyActivities
# Fake database client
class MyDatabaseClient
def select_value(table)
"some-db-value from table #{table}"
end
end

# Stateful activity that is created only once by worker creation code
class SelectFromDatabase < Temporalio::Activity::Definition
def initialize(db_client)
@db_client = db_client
end

def execute(table)
@db_client.select_value(table)
end
end

# Stateless activity that is passed as class to worker creation code,
# thereby instantiating every attempt
class AppendSuffix < Temporalio::Activity::Definition
def execute(append_to)
"#{append_to} <appended-value>"
end
end
end
end
29 changes: 29 additions & 0 deletions activity_simple/my_workflow.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# frozen_string_literal: true

require 'temporalio/workflow'
require_relative 'my_activities'

module ActivitySimple
class MyWorkflow < Temporalio::Workflow::Definition
def execute
# Run an activity that needs some state like a database connection
result1 = Temporalio::Workflow.execute_activity(
MyActivities::SelectFromDatabase,
'some-db-table',
start_to_close_timeout: 5 * 60 # 5 minutes
)
Temporalio::Workflow.logger.info("Activity result 1: #{result1}")

# Run a stateless activity (note no difference on the caller side)
result2 = Temporalio::Workflow.execute_activity(
MyActivities::AppendSuffix,
result1,
start_to_close_timeout: 5 * 60
)
Temporalio::Workflow.logger.info("Activity result 2: #{result2}")

# We'll go ahead and return this result
result2
end
end
end
16 changes: 16 additions & 0 deletions activity_simple/starter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# frozen_string_literal: true

require 'temporalio/client'
require_relative 'my_workflow'

# Create a client
client = Temporalio::Client.connect('localhost:7233', 'default')

# Run workflow
puts 'Executing workflow'
result = client.execute_workflow(
ActivitySimple::MyWorkflow,
id: 'activity-simple-sample-workflow-id',
task_queue: 'activity-simple-sample'
)
puts "Workflow result: #{result}"
32 changes: 32 additions & 0 deletions activity_simple/worker.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# frozen_string_literal: true

require_relative 'my_activities'
require_relative 'my_workflow'
require 'logger'
require 'temporalio/client'
require 'temporalio/worker'

# Create a Temporal client
client = Temporalio::Client.connect(
'localhost:7233',
'default',
logger: Logger.new($stdout, level: Logger::INFO)
)

# Use an instance for the stateful DB activity, other activity we will pass
# in as class meaning it is instantiated each attempt
db_client = ActivitySimple::MyActivities::MyDatabaseClient.new
select_from_db_activity = ActivitySimple::MyActivities::SelectFromDatabase.new(db_client)

# Create worker with the activities and workflow
worker = Temporalio::Worker.new(
client:,
task_queue: 'activity-simple-sample',
activities: [select_from_db_activity, ActivitySimple::MyActivities::AppendSuffix],
workflows: [ActivitySimple::MyWorkflow],
workflow_executor: Temporalio::Worker::WorkflowExecutor::ThreadPool.default
)

# Run the worker until SIGINT
puts 'Starting worker (ctrl+c to exit)'
worker.run(shutdown_signals: ['SIGINT'])
5 changes: 3 additions & 2 deletions activity_worker/activity_worker.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require_relative 'activity'
require_relative 'say_hello_activity'
require 'temporalio/client'
require 'temporalio/worker'

Expand All @@ -14,7 +14,8 @@
# By providing the class to the activity, it will be instantiated for every
# attempt. If we provide an instance (e.g. SayHelloActivity.new), the same
# instance is reused.
activities: [ActivityWorker::SayHelloActivity]
activities: [ActivityWorker::SayHelloActivity],
workflow_executor: Temporalio::Worker::WorkflowExecutor::ThreadPool.default
)

# Run the worker until SIGINT
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

module ActivityWorker
# Activity is a class with execute implemented
class SayHelloActivity < Temporalio::Activity
class SayHelloActivity < Temporalio::Activity::Definition
def execute(name)
"Hello, #{name}!"
end
Expand Down
16 changes: 16 additions & 0 deletions context_propagation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Context Propagation

This sample shows how a thread/fiber local can be propagated through workflows and activities using an interceptor.

To run, first see [README.md](../README.md) for prerequisites. Then, in another terminal, start the Ruby worker
from this directory:

bundle exec ruby worker.rb

Finally in another terminal, use the Ruby client to the workflow from this directory:

bundle exec ruby starter.rb

The Ruby code will invoke the workflow which will execute an activity and return. Note the log output from the worker
that contains logs on which user is calling the workflow/activity, information which we set as thread local on the
client and was automatically propagated through to the workflow and activity.
Loading

0 comments on commit e49257e

Please sign in to comment.