amit-temporalio 0.3.0-x86_64-darwin

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. checksums.yaml +7 -0
  2. data/.yardopts +2 -0
  3. data/Gemfile +23 -0
  4. data/Rakefile +101 -0
  5. data/lib/temporalio/activity/complete_async_error.rb +11 -0
  6. data/lib/temporalio/activity/context.rb +116 -0
  7. data/lib/temporalio/activity/definition.rb +189 -0
  8. data/lib/temporalio/activity/info.rb +64 -0
  9. data/lib/temporalio/activity.rb +12 -0
  10. data/lib/temporalio/api/activity/v1/message.rb +25 -0
  11. data/lib/temporalio/api/batch/v1/message.rb +31 -0
  12. data/lib/temporalio/api/cloud/account/v1/message.rb +28 -0
  13. data/lib/temporalio/api/cloud/cloudservice/v1/request_response.rb +126 -0
  14. data/lib/temporalio/api/cloud/cloudservice/v1/service.rb +25 -0
  15. data/lib/temporalio/api/cloud/cloudservice.rb +3 -0
  16. data/lib/temporalio/api/cloud/identity/v1/message.rb +41 -0
  17. data/lib/temporalio/api/cloud/namespace/v1/message.rb +42 -0
  18. data/lib/temporalio/api/cloud/nexus/v1/message.rb +31 -0
  19. data/lib/temporalio/api/cloud/operation/v1/message.rb +28 -0
  20. data/lib/temporalio/api/cloud/region/v1/message.rb +24 -0
  21. data/lib/temporalio/api/cloud/resource/v1/message.rb +23 -0
  22. data/lib/temporalio/api/cloud/sink/v1/message.rb +24 -0
  23. data/lib/temporalio/api/cloud/usage/v1/message.rb +31 -0
  24. data/lib/temporalio/api/command/v1/message.rb +46 -0
  25. data/lib/temporalio/api/common/v1/grpc_status.rb +23 -0
  26. data/lib/temporalio/api/common/v1/message.rb +47 -0
  27. data/lib/temporalio/api/enums/v1/batch_operation.rb +22 -0
  28. data/lib/temporalio/api/enums/v1/command_type.rb +21 -0
  29. data/lib/temporalio/api/enums/v1/common.rb +26 -0
  30. data/lib/temporalio/api/enums/v1/event_type.rb +21 -0
  31. data/lib/temporalio/api/enums/v1/failed_cause.rb +26 -0
  32. data/lib/temporalio/api/enums/v1/namespace.rb +23 -0
  33. data/lib/temporalio/api/enums/v1/query.rb +22 -0
  34. data/lib/temporalio/api/enums/v1/reset.rb +23 -0
  35. data/lib/temporalio/api/enums/v1/schedule.rb +21 -0
  36. data/lib/temporalio/api/enums/v1/task_queue.rb +25 -0
  37. data/lib/temporalio/api/enums/v1/update.rb +22 -0
  38. data/lib/temporalio/api/enums/v1/workflow.rb +30 -0
  39. data/lib/temporalio/api/errordetails/v1/message.rb +42 -0
  40. data/lib/temporalio/api/export/v1/message.rb +24 -0
  41. data/lib/temporalio/api/failure/v1/message.rb +35 -0
  42. data/lib/temporalio/api/filter/v1/message.rb +27 -0
  43. data/lib/temporalio/api/history/v1/message.rb +90 -0
  44. data/lib/temporalio/api/namespace/v1/message.rb +31 -0
  45. data/lib/temporalio/api/nexus/v1/message.rb +40 -0
  46. data/lib/temporalio/api/operatorservice/v1/request_response.rb +49 -0
  47. data/lib/temporalio/api/operatorservice/v1/service.rb +23 -0
  48. data/lib/temporalio/api/operatorservice.rb +3 -0
  49. data/lib/temporalio/api/payload_visitor.rb +1513 -0
  50. data/lib/temporalio/api/protocol/v1/message.rb +23 -0
  51. data/lib/temporalio/api/query/v1/message.rb +27 -0
  52. data/lib/temporalio/api/replication/v1/message.rb +26 -0
  53. data/lib/temporalio/api/schedule/v1/message.rb +43 -0
  54. data/lib/temporalio/api/sdk/v1/enhanced_stack_trace.rb +25 -0
  55. data/lib/temporalio/api/sdk/v1/task_complete_metadata.rb +21 -0
  56. data/lib/temporalio/api/sdk/v1/user_metadata.rb +23 -0
  57. data/lib/temporalio/api/sdk/v1/workflow_metadata.rb +23 -0
  58. data/lib/temporalio/api/taskqueue/v1/message.rb +45 -0
  59. data/lib/temporalio/api/testservice/v1/request_response.rb +31 -0
  60. data/lib/temporalio/api/testservice/v1/service.rb +23 -0
  61. data/lib/temporalio/api/update/v1/message.rb +33 -0
  62. data/lib/temporalio/api/version/v1/message.rb +26 -0
  63. data/lib/temporalio/api/workflow/v1/message.rb +43 -0
  64. data/lib/temporalio/api/workflowservice/v1/request_response.rb +204 -0
  65. data/lib/temporalio/api/workflowservice/v1/service.rb +23 -0
  66. data/lib/temporalio/api/workflowservice.rb +3 -0
  67. data/lib/temporalio/api.rb +14 -0
  68. data/lib/temporalio/cancellation.rb +170 -0
  69. data/lib/temporalio/client/activity_id_reference.rb +32 -0
  70. data/lib/temporalio/client/async_activity_handle.rb +85 -0
  71. data/lib/temporalio/client/connection/cloud_service.rb +726 -0
  72. data/lib/temporalio/client/connection/operator_service.rb +201 -0
  73. data/lib/temporalio/client/connection/service.rb +42 -0
  74. data/lib/temporalio/client/connection/test_service.rb +111 -0
  75. data/lib/temporalio/client/connection/workflow_service.rb +1041 -0
  76. data/lib/temporalio/client/connection.rb +316 -0
  77. data/lib/temporalio/client/interceptor.rb +416 -0
  78. data/lib/temporalio/client/schedule.rb +967 -0
  79. data/lib/temporalio/client/schedule_handle.rb +126 -0
  80. data/lib/temporalio/client/workflow_execution.rb +100 -0
  81. data/lib/temporalio/client/workflow_execution_count.rb +36 -0
  82. data/lib/temporalio/client/workflow_execution_status.rb +18 -0
  83. data/lib/temporalio/client/workflow_handle.rb +389 -0
  84. data/lib/temporalio/client/workflow_query_reject_condition.rb +14 -0
  85. data/lib/temporalio/client/workflow_update_handle.rb +65 -0
  86. data/lib/temporalio/client/workflow_update_wait_stage.rb +17 -0
  87. data/lib/temporalio/client.rb +484 -0
  88. data/lib/temporalio/common_enums.rb +41 -0
  89. data/lib/temporalio/converters/data_converter.rb +99 -0
  90. data/lib/temporalio/converters/failure_converter.rb +202 -0
  91. data/lib/temporalio/converters/payload_codec.rb +26 -0
  92. data/lib/temporalio/converters/payload_converter/binary_null.rb +34 -0
  93. data/lib/temporalio/converters/payload_converter/binary_plain.rb +35 -0
  94. data/lib/temporalio/converters/payload_converter/binary_protobuf.rb +42 -0
  95. data/lib/temporalio/converters/payload_converter/composite.rb +66 -0
  96. data/lib/temporalio/converters/payload_converter/encoding.rb +35 -0
  97. data/lib/temporalio/converters/payload_converter/json_plain.rb +44 -0
  98. data/lib/temporalio/converters/payload_converter/json_protobuf.rb +41 -0
  99. data/lib/temporalio/converters/payload_converter.rb +71 -0
  100. data/lib/temporalio/converters/raw_value.rb +20 -0
  101. data/lib/temporalio/converters.rb +9 -0
  102. data/lib/temporalio/error/failure.rb +219 -0
  103. data/lib/temporalio/error.rb +155 -0
  104. data/lib/temporalio/internal/bridge/api/activity_result/activity_result.rb +34 -0
  105. data/lib/temporalio/internal/bridge/api/activity_task/activity_task.rb +31 -0
  106. data/lib/temporalio/internal/bridge/api/child_workflow/child_workflow.rb +33 -0
  107. data/lib/temporalio/internal/bridge/api/common/common.rb +26 -0
  108. data/lib/temporalio/internal/bridge/api/core_interface.rb +40 -0
  109. data/lib/temporalio/internal/bridge/api/external_data/external_data.rb +27 -0
  110. data/lib/temporalio/internal/bridge/api/nexus/nexus.rb +33 -0
  111. data/lib/temporalio/internal/bridge/api/workflow_activation/workflow_activation.rb +56 -0
  112. data/lib/temporalio/internal/bridge/api/workflow_commands/workflow_commands.rb +57 -0
  113. data/lib/temporalio/internal/bridge/api/workflow_completion/workflow_completion.rb +30 -0
  114. data/lib/temporalio/internal/bridge/api.rb +3 -0
  115. data/lib/temporalio/internal/bridge/client.rb +95 -0
  116. data/lib/temporalio/internal/bridge/runtime.rb +53 -0
  117. data/lib/temporalio/internal/bridge/temporalio_bridge.bundle +0 -0
  118. data/lib/temporalio/internal/bridge/testing.rb +66 -0
  119. data/lib/temporalio/internal/bridge/worker.rb +85 -0
  120. data/lib/temporalio/internal/bridge.rb +36 -0
  121. data/lib/temporalio/internal/client/implementation.rb +700 -0
  122. data/lib/temporalio/internal/metric.rb +122 -0
  123. data/lib/temporalio/internal/proto_utils.rb +133 -0
  124. data/lib/temporalio/internal/worker/activity_worker.rb +376 -0
  125. data/lib/temporalio/internal/worker/multi_runner.rb +213 -0
  126. data/lib/temporalio/internal/worker/workflow_instance/child_workflow_handle.rb +54 -0
  127. data/lib/temporalio/internal/worker/workflow_instance/context.rb +333 -0
  128. data/lib/temporalio/internal/worker/workflow_instance/details.rb +44 -0
  129. data/lib/temporalio/internal/worker/workflow_instance/external_workflow_handle.rb +32 -0
  130. data/lib/temporalio/internal/worker/workflow_instance/externally_immutable_hash.rb +22 -0
  131. data/lib/temporalio/internal/worker/workflow_instance/handler_execution.rb +25 -0
  132. data/lib/temporalio/internal/worker/workflow_instance/handler_hash.rb +41 -0
  133. data/lib/temporalio/internal/worker/workflow_instance/illegal_call_tracer.rb +97 -0
  134. data/lib/temporalio/internal/worker/workflow_instance/inbound_implementation.rb +62 -0
  135. data/lib/temporalio/internal/worker/workflow_instance/outbound_implementation.rb +415 -0
  136. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_logger.rb +37 -0
  137. data/lib/temporalio/internal/worker/workflow_instance/replay_safe_metric.rb +40 -0
  138. data/lib/temporalio/internal/worker/workflow_instance/scheduler.rb +163 -0
  139. data/lib/temporalio/internal/worker/workflow_instance.rb +730 -0
  140. data/lib/temporalio/internal/worker/workflow_worker.rb +236 -0
  141. data/lib/temporalio/internal.rb +7 -0
  142. data/lib/temporalio/metric.rb +109 -0
  143. data/lib/temporalio/retry_policy.rb +74 -0
  144. data/lib/temporalio/runtime.rb +314 -0
  145. data/lib/temporalio/scoped_logger.rb +96 -0
  146. data/lib/temporalio/search_attributes.rb +343 -0
  147. data/lib/temporalio/testing/activity_environment.rb +136 -0
  148. data/lib/temporalio/testing/workflow_environment.rb +383 -0
  149. data/lib/temporalio/testing.rb +10 -0
  150. data/lib/temporalio/version.rb +5 -0
  151. data/lib/temporalio/worker/activity_executor/fiber.rb +49 -0
  152. data/lib/temporalio/worker/activity_executor/thread_pool.rb +46 -0
  153. data/lib/temporalio/worker/activity_executor.rb +55 -0
  154. data/lib/temporalio/worker/interceptor.rb +362 -0
  155. data/lib/temporalio/worker/thread_pool.rb +237 -0
  156. data/lib/temporalio/worker/tuner.rb +189 -0
  157. data/lib/temporalio/worker/workflow_executor/thread_pool.rb +230 -0
  158. data/lib/temporalio/worker/workflow_executor.rb +26 -0
  159. data/lib/temporalio/worker/workflow_replayer.rb +343 -0
  160. data/lib/temporalio/worker.rb +569 -0
  161. data/lib/temporalio/workflow/activity_cancellation_type.rb +20 -0
  162. data/lib/temporalio/workflow/child_workflow_cancellation_type.rb +21 -0
  163. data/lib/temporalio/workflow/child_workflow_handle.rb +43 -0
  164. data/lib/temporalio/workflow/definition.rb +566 -0
  165. data/lib/temporalio/workflow/external_workflow_handle.rb +41 -0
  166. data/lib/temporalio/workflow/future.rb +151 -0
  167. data/lib/temporalio/workflow/handler_unfinished_policy.rb +13 -0
  168. data/lib/temporalio/workflow/info.rb +82 -0
  169. data/lib/temporalio/workflow/parent_close_policy.rb +19 -0
  170. data/lib/temporalio/workflow/update_info.rb +20 -0
  171. data/lib/temporalio/workflow.rb +529 -0
  172. data/lib/temporalio/workflow_history.rb +47 -0
  173. data/lib/temporalio.rb +11 -0
  174. data/temporalio.gemspec +28 -0
  175. metadata +236 -0
@@ -0,0 +1,383 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+ require 'temporalio/api'
5
+ require 'temporalio/api/testservice/v1/request_response'
6
+ require 'temporalio/client'
7
+ require 'temporalio/client/connection/test_service'
8
+ require 'temporalio/client/workflow_handle'
9
+ require 'temporalio/converters'
10
+ require 'temporalio/internal/bridge/testing'
11
+ require 'temporalio/internal/proto_utils'
12
+ require 'temporalio/runtime'
13
+ require 'temporalio/version'
14
+
15
+ module Temporalio
16
+ module Testing
17
+ # Test environment with a Temporal server for running workflows and more.
18
+ class WorkflowEnvironment
19
+ # @return [Client] Client for the server.
20
+ attr_reader :client
21
+
22
+ # Start a local dev server. This is a full Temporal dev server from the CLI that by default downloaded to tmp if
23
+ # not already present. The dev server is run as a child process. All options that start with +dev_server_+ are for
24
+ # this specific implementation and therefore are not stable and may be changed as the underlying implementation
25
+ # changes.
26
+ #
27
+ # If a block is given it is passed the environment and the environment is shut down after. If a block is not
28
+ # given, the environment is returned and {shutdown} needs to be called manually.
29
+ #
30
+ # @param namespace [String] Namespace for the server.
31
+ # @param data_converter [Converters::DataConverter] Data converter for the client.
32
+ # @param interceptors [Array<Client::Interceptor>] Interceptors for the client.
33
+ # @param logger [Logger] Logger for the client.
34
+ # @param default_workflow_query_reject_condition [WorkflowQueryRejectCondition, nil] Default rejection condition
35
+ # for the client.
36
+ # @param ip [String] IP to bind to.
37
+ # @param port [Integer, nil] Port to bind on, or +nil+ for random.
38
+ # @param ui [Boolean] If +true+, also starts the UI.
39
+ # @param runtime [Runtime] Runtime for the server and client.
40
+ # @param dev_server_existing_path [String, nil] Existing CLI path to use instead of downloading and caching to
41
+ # tmp.
42
+ # @param dev_server_database_filename [String, nil] Persistent SQLite filename to use across local server runs.
43
+ # Default of +nil+ means in-memory only.
44
+ # @param dev_server_log_format [String] Log format for CLI dev server.
45
+ # @param dev_server_log_level [String] Log level for CLI dev server.
46
+ # @param dev_server_download_version [String] Version of dev server to download and cache.
47
+ # @param dev_server_download_dest_dir [String, nil] Where to download. Defaults to tmp.
48
+ # @param dev_server_extra_args [Array<String>] Any extra arguments for the CLI dev server.
49
+ #
50
+ # @yield [environment] If a block is given, it is called with the environment and upon complete the environment is
51
+ # shutdown.
52
+ # @yieldparam environment [WorkflowEnvironment] Environment that is shut down upon block completion.
53
+ #
54
+ # @return [WorkflowEnvironment, Object] Started local server environment with client if there was no block given,
55
+ # or block result if block was given.
56
+ def self.start_local(
57
+ namespace: 'default',
58
+ data_converter: Converters::DataConverter.default,
59
+ interceptors: [],
60
+ logger: Logger.new($stdout, level: Logger::WARN),
61
+ default_workflow_query_reject_condition: nil,
62
+ ip: '127.0.0.1',
63
+ port: nil,
64
+ ui: false, # rubocop:disable Naming/MethodParameterName
65
+ runtime: Runtime.default,
66
+ dev_server_existing_path: nil,
67
+ dev_server_database_filename: nil,
68
+ dev_server_log_format: 'pretty',
69
+ dev_server_log_level: 'warn',
70
+ dev_server_download_version: 'default',
71
+ dev_server_download_dest_dir: nil,
72
+ dev_server_extra_args: [],
73
+ &
74
+ )
75
+ server_options = Internal::Bridge::Testing::EphemeralServer::StartDevServerOptions.new(
76
+ existing_path: dev_server_existing_path,
77
+ sdk_name: 'sdk-ruby',
78
+ sdk_version: VERSION,
79
+ download_version: dev_server_download_version,
80
+ download_dest_dir: dev_server_download_dest_dir,
81
+ namespace:,
82
+ ip:,
83
+ port:,
84
+ database_filename: dev_server_database_filename,
85
+ ui:,
86
+ log_format: dev_server_log_format,
87
+ log_level: dev_server_log_level,
88
+ extra_args: dev_server_extra_args
89
+ )
90
+ _with_core_server(
91
+ core_server: Internal::Bridge::Testing::EphemeralServer.start_dev_server(
92
+ runtime._core_runtime, server_options
93
+ ),
94
+ namespace:,
95
+ data_converter:,
96
+ interceptors:,
97
+ logger:,
98
+ default_workflow_query_reject_condition:,
99
+ runtime:,
100
+ supports_time_skipping: false,
101
+ & # steep:ignore
102
+ )
103
+ end
104
+
105
+ # Start a time-skipping test server. This server can skip time but may not have all of the Temporal features of
106
+ # the {start_local} form. By default, the server is downloaded to tmp if not already present. The test server is
107
+ # run as a child process. All options that start with +test_server_+ are for this specific implementation and
108
+ # therefore are not stable and may be changed as the underlying implementation changes.
109
+ #
110
+ # If a block is given it is passed the environment and the environment is shut down after. If a block is not
111
+ # given, the environment is returned and {shutdown} needs to be called manually.
112
+ #
113
+ # @param data_converter [Converters::DataConverter] Data converter for the client.
114
+ # @param interceptors [Array<Client::Interceptor>] Interceptors for the client.
115
+ # @param logger [Logger] Logger for the client.
116
+ # @param default_workflow_query_reject_condition [WorkflowQueryRejectCondition, nil] Default rejection condition
117
+ # for the client.
118
+ # @param port [Integer, nil] Port to bind on, or +nil+ for random.
119
+ # @param runtime [Runtime] Runtime for the server and client.
120
+ # @param test_server_existing_path [String, nil] Existing CLI path to use instead of downloading and caching to
121
+ # tmp.
122
+ # @param test_server_download_version [String] Version of test server to download and cache.
123
+ # @param test_server_download_dest_dir [String, nil] Where to download. Defaults to tmp.
124
+ # @param test_server_extra_args [Array<String>] Any extra arguments for the test server.
125
+ #
126
+ # @yield [environment] If a block is given, it is called with the environment and upon complete the environment is
127
+ # shutdown.
128
+ # @yieldparam environment [WorkflowEnvironment] Environment that is shut down upon block completion.
129
+ #
130
+ # @return [WorkflowEnvironment, Object] Started local server environment with client if there was no block given,
131
+ # or block result if block was given.
132
+ def self.start_time_skipping(
133
+ data_converter: Converters::DataConverter.default,
134
+ interceptors: [],
135
+ logger: Logger.new($stdout, level: Logger::WARN),
136
+ default_workflow_query_reject_condition: nil,
137
+ port: nil,
138
+ runtime: Runtime.default,
139
+ test_server_existing_path: nil,
140
+ test_server_download_version: 'default',
141
+ test_server_download_dest_dir: nil,
142
+ test_server_extra_args: [],
143
+ &
144
+ )
145
+ server_options = Internal::Bridge::Testing::EphemeralServer::StartTestServerOptions.new(
146
+ existing_path: test_server_existing_path,
147
+ sdk_name: 'sdk-ruby',
148
+ sdk_version: VERSION,
149
+ download_version: test_server_download_version,
150
+ download_dest_dir: test_server_download_dest_dir,
151
+ port:,
152
+ extra_args: test_server_extra_args
153
+ )
154
+ _with_core_server(
155
+ core_server: Internal::Bridge::Testing::EphemeralServer.start_test_server(
156
+ runtime._core_runtime, server_options
157
+ ),
158
+ namespace: 'default',
159
+ data_converter:,
160
+ interceptors:,
161
+ logger:,
162
+ default_workflow_query_reject_condition:,
163
+ runtime:,
164
+ supports_time_skipping: true,
165
+ & # steep:ignore
166
+ )
167
+ end
168
+
169
+ # @!visibility private
170
+ def self._with_core_server(
171
+ core_server:,
172
+ namespace:,
173
+ data_converter:,
174
+ interceptors:,
175
+ logger:,
176
+ default_workflow_query_reject_condition:,
177
+ runtime:,
178
+ supports_time_skipping:
179
+ )
180
+ # Try to connect, shutdown if we can't
181
+ begin
182
+ client = Client.connect(
183
+ core_server.target,
184
+ namespace,
185
+ data_converter:,
186
+ interceptors:,
187
+ logger:,
188
+ default_workflow_query_reject_condition:,
189
+ runtime:
190
+ )
191
+ server = Ephemeral.new(client, core_server, supports_time_skipping:)
192
+ rescue Exception # rubocop:disable Lint/RescueException
193
+ core_server.shutdown
194
+ raise
195
+ end
196
+ if block_given?
197
+ begin
198
+ yield server
199
+ ensure
200
+ server.shutdown
201
+ end
202
+ else
203
+ server
204
+ end
205
+ end
206
+
207
+ # Create workflow environment to an existing server with the given client.
208
+ #
209
+ # @param client [Client] Client to existing server.
210
+ def initialize(client)
211
+ @client = client
212
+ end
213
+
214
+ # Shutdown this workflow environment.
215
+ def shutdown
216
+ # Do nothing by default
217
+ end
218
+
219
+ # @return [Boolean] Whether this environment supports time skipping.
220
+ def supports_time_skipping?
221
+ false
222
+ end
223
+
224
+ # Advanced time.
225
+ #
226
+ # If this server supports time skipping, this will immediately advance time and return. If it does not, this is
227
+ # a standard {::sleep}.
228
+ #
229
+ # @param duration [Float] Duration seconds.
230
+ def sleep(duration)
231
+ Kernel.sleep(duration)
232
+ end
233
+
234
+ # Current time of the environment.
235
+ #
236
+ # If this server supports time skipping, this will be the current time as known to the environment. If it does
237
+ # not, this is a standard {::Time.now}.
238
+ #
239
+ # @return [Time] Current time.
240
+ def current_time
241
+ Time.now
242
+ end
243
+
244
+ # Run a block with automatic time skipping disabled. This just runs the block for environments that don't support
245
+ # time skipping.
246
+ #
247
+ # @yield Block to run.
248
+ # @return [Object] Result of the block.
249
+ def auto_time_skipping_disabled(&)
250
+ raise 'Block required' unless block_given?
251
+
252
+ yield
253
+ end
254
+
255
+ # @!visibility private
256
+ class Ephemeral < WorkflowEnvironment
257
+ def initialize(client, core_server, supports_time_skipping:)
258
+ # Add our interceptor at the end of the existing interceptors that skips time
259
+ client_options = client.options.with(
260
+ interceptors: client.options.interceptors + [TimeSkippingClientInterceptor.new(self)]
261
+ )
262
+ client = Client.new(**client_options.to_h) # steep:ignore
263
+ super(client)
264
+
265
+ @auto_time_skipping = true
266
+ @core_server = core_server
267
+ @test_service = Client::Connection::TestService.new(client.connection) if supports_time_skipping
268
+ end
269
+
270
+ # @!visibility private
271
+ def shutdown
272
+ @core_server.shutdown
273
+ end
274
+
275
+ # @!visibility private
276
+ def supports_time_skipping?
277
+ !@test_service.nil?
278
+ end
279
+
280
+ # @!visibility private
281
+ def sleep(duration)
282
+ return super unless supports_time_skipping?
283
+
284
+ @test_service.unlock_time_skipping_with_sleep(
285
+ Api::TestService::V1::SleepRequest.new(duration: Internal::ProtoUtils.seconds_to_duration(duration))
286
+ )
287
+ end
288
+
289
+ # @!visibility private
290
+ def current_time
291
+ return super unless supports_time_skipping?
292
+
293
+ resp = @test_service.get_current_time(Google::Protobuf::Empty.new)
294
+ Internal::ProtoUtils.timestamp_to_time(resp.time) or raise 'Time missing'
295
+ end
296
+
297
+ # @!visibility private
298
+ def auto_time_skipping_disabled(&)
299
+ raise 'Block required' unless block_given?
300
+ return super unless supports_time_skipping?
301
+
302
+ already_disabled = @auto_time_skipping
303
+ @auto_time_skipping = false
304
+ begin
305
+ yield
306
+ ensure
307
+ @auto_time_skipping = true unless already_disabled
308
+ end
309
+ end
310
+
311
+ # @!visibility private
312
+ def time_skipping_unlocked(&)
313
+ # If disabled or unsupported, no locking/unlocking, just run and return
314
+ return yield if !supports_time_skipping? || !@auto_time_skipping
315
+
316
+ # Unlock to start time skipping, lock again to stop it
317
+ @test_service.unlock_time_skipping(Api::TestService::V1::UnlockTimeSkippingRequest.new)
318
+ user_code_success = false
319
+ begin
320
+ result = yield
321
+ user_code_success = true
322
+ result
323
+ ensure
324
+ # Lock it back
325
+ begin
326
+ @test_service.lock_time_skipping(Api::TestService::V1::LockTimeSkippingRequest.new)
327
+ rescue StandardError => e
328
+ # Re-raise if user code succeeded, otherwise swallow
329
+ raise if user_code_success
330
+
331
+ client.options.logger.error('Failed locking time skipping after error')
332
+ client.options.logger.error(e)
333
+ end
334
+ end
335
+ end
336
+ end
337
+
338
+ private_constant :Ephemeral
339
+
340
+ # @!visibility private
341
+ class TimeSkippingClientInterceptor
342
+ include Client::Interceptor
343
+
344
+ def initialize(env)
345
+ @env = env
346
+ end
347
+
348
+ # @!visibility private
349
+ def intercept_client(next_interceptor)
350
+ Outbound.new(next_interceptor, @env)
351
+ end
352
+
353
+ # @!visibility private
354
+ class Outbound < Client::Interceptor::Outbound
355
+ def initialize(next_interceptor, env)
356
+ super(next_interceptor)
357
+ @env = env
358
+ end
359
+
360
+ # @!visibility private
361
+ def start_workflow(input)
362
+ TimeSkippingWorkflowHandle.new(super, @env)
363
+ end
364
+ end
365
+
366
+ # @!visibility private
367
+ class TimeSkippingWorkflowHandle < SimpleDelegator
368
+ def initialize(handle, env)
369
+ super(handle) # steep:ignore
370
+ @env = env
371
+ end
372
+
373
+ # @!visibility private
374
+ def result(follow_runs: true, rpc_options: nil)
375
+ @env.time_skipping_unlocked { super(follow_runs:, rpc_options:) }
376
+ end
377
+ end
378
+ end
379
+
380
+ private_constant :TimeSkippingClientInterceptor
381
+ end
382
+ end
383
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/testing/activity_environment'
4
+ require 'temporalio/testing/workflow_environment'
5
+
6
+ module Temporalio
7
+ # Module for all testing environments.
8
+ module Testing
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Temporalio
4
+ VERSION = '0.3.0'
5
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/error'
4
+ require 'temporalio/worker/activity_executor'
5
+
6
+ module Temporalio
7
+ class Worker
8
+ class ActivityExecutor
9
+ # Activity executor for scheduling activites as fibers.
10
+ class Fiber
11
+ # @return [Fiber] Default/shared Fiber executor instance.
12
+ def self.default
13
+ @default ||= new
14
+ end
15
+
16
+ # @see ActivityExecutor.initialize_activity
17
+ def initialize_activity(defn)
18
+ # If there is not a current scheduler, we're going to preemptively
19
+ # fail the registration
20
+ return unless ::Fiber.current_scheduler.nil?
21
+
22
+ raise ArgumentError, "Activity '#{defn.name}' wants a fiber executor but no current fiber scheduler"
23
+ end
24
+
25
+ # @see ActivityExecutor.initialize_activity
26
+ def execute_activity(_defn, &)
27
+ ::Fiber.schedule(&)
28
+ end
29
+
30
+ # @see ActivityExecutor.activity_context
31
+ def activity_context
32
+ ::Fiber[:temporal_activity_context]
33
+ end
34
+
35
+ # @see ActivityExecutor.set_activity_context
36
+ def set_activity_context(defn, context)
37
+ ::Fiber[:temporal_activity_context] = context
38
+ # If they have opted in to raising on cancel, wire that up
39
+ return unless defn.cancel_raise
40
+
41
+ fiber = ::Fiber.current
42
+ context&.cancellation&.add_cancel_callback do
43
+ fiber.raise(Error::CanceledError.new('Activity canceled'))
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/worker/thread_pool'
4
+
5
+ module Temporalio
6
+ class Worker
7
+ class ActivityExecutor
8
+ # Activity executor for scheduling activities in their own thread using {Worker::ThreadPool}.
9
+ class ThreadPool < ActivityExecutor
10
+ # @return [ThreadPool] Default/shared thread pool executor using default thread pool.
11
+ def self.default
12
+ @default ||= new
13
+ end
14
+
15
+ # Create a new thread pool executor.
16
+ #
17
+ # @param thread_pool [Worker::ThreadPool] Thread pool to use.
18
+ def initialize(thread_pool = Worker::ThreadPool.default) # rubocop:disable Lint/MissingSuper
19
+ @thread_pool = thread_pool
20
+ end
21
+
22
+ # @see ActivityExecutor.execute_activity
23
+ def execute_activity(_defn, &)
24
+ @thread_pool.execute(&)
25
+ end
26
+
27
+ # @see ActivityExecutor.activity_context
28
+ def activity_context
29
+ Thread.current[:temporal_activity_context]
30
+ end
31
+
32
+ # @see ActivityExecutor.set_activity_context
33
+ def set_activity_context(defn, context)
34
+ Thread.current[:temporal_activity_context] = context
35
+ # If they have opted in to raising on cancel, wire that up
36
+ return unless defn.cancel_raise
37
+
38
+ thread = Thread.current
39
+ context&.cancellation&.add_cancel_callback do
40
+ thread.raise(Error::CanceledError.new('Activity canceled')) if thread[:temporal_activity_context] == context
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'temporalio/worker/activity_executor/fiber'
4
+ require 'temporalio/worker/activity_executor/thread_pool'
5
+
6
+ module Temporalio
7
+ class Worker
8
+ # Base class to be extended by activity executor implementations. Most users will not use this, but rather keep with
9
+ # the two defaults of thread pool and fiber executors.
10
+ class ActivityExecutor
11
+ # @return [Hash<Symbol, ActivityExecutor>] Default set of executors (immutable).
12
+ def self.defaults
13
+ @defaults ||= {
14
+ default: ThreadPool.default,
15
+ thread_pool: ThreadPool.default,
16
+ fiber: Fiber.default
17
+ }.freeze
18
+ end
19
+
20
+ # Initialize an activity. This is called on worker initialize for every activity that will use this executor. This
21
+ # allows executor implementations to do eager validation based on the definition. This does not have to be
22
+ # implemented and the default is a no-op.
23
+ #
24
+ # @param defn [Activity::Definition::Info] Activity definition info.
25
+ def initialize_activity(defn)
26
+ # Default no-op
27
+ end
28
+
29
+ # Execute the given block in the executor. The block is built to never raise and need no arguments. Implementers
30
+ # must implement this.
31
+ #
32
+ # @param defn [Activity::Definition::Info] Activity definition info.
33
+ # @yield Block to execute.
34
+ def execute_activity(defn, &)
35
+ raise NotImplementedError
36
+ end
37
+
38
+ # @return [Activity::Context, nil] Get the current activity context. This is called by users from inside the
39
+ # activity. Implementers must implement this.
40
+ def activity_context
41
+ raise NotImplementedError
42
+ end
43
+
44
+ # Set the current activity context (or unset if nil). This is called by the system from within the block given to
45
+ # {execute_activity} with a context before user code is executed and with nil after user code is complete.
46
+ # Implementers must implement this.
47
+ #
48
+ # @param defn [Activity::Definition::Info] Activity definition info.
49
+ # @param context [Activity::Context, nil] The value to set.
50
+ def set_activity_context(defn, context)
51
+ raise NotImplementedError
52
+ end
53
+ end
54
+ end
55
+ end