fractor 0.1.4 → 0.1.7

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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop-https---raw-githubusercontent-com-riboseinc-oss-guides-main-ci-rubocop-yml +552 -0
  3. data/.rubocop.yml +14 -8
  4. data/.rubocop_todo.yml +284 -43
  5. data/README.adoc +111 -950
  6. data/docs/.lycheeignore +16 -0
  7. data/docs/Gemfile +24 -0
  8. data/docs/README.md +157 -0
  9. data/docs/_config.yml +151 -0
  10. data/docs/_features/error-handling.adoc +1192 -0
  11. data/docs/_features/index.adoc +80 -0
  12. data/docs/_features/monitoring.adoc +589 -0
  13. data/docs/_features/signal-handling.adoc +202 -0
  14. data/docs/_features/workflows.adoc +1235 -0
  15. data/docs/_guides/continuous-mode.adoc +736 -0
  16. data/docs/_guides/cookbook.adoc +1133 -0
  17. data/docs/_guides/index.adoc +55 -0
  18. data/docs/_guides/pipeline-mode.adoc +730 -0
  19. data/docs/_guides/troubleshooting.adoc +358 -0
  20. data/docs/_pages/architecture.adoc +1390 -0
  21. data/docs/_pages/core-concepts.adoc +1392 -0
  22. data/docs/_pages/design-principles.adoc +862 -0
  23. data/docs/_pages/getting-started.adoc +290 -0
  24. data/docs/_pages/installation.adoc +143 -0
  25. data/docs/_reference/api.adoc +1080 -0
  26. data/docs/_reference/error-reporting.adoc +670 -0
  27. data/docs/_reference/examples.adoc +181 -0
  28. data/docs/_reference/index.adoc +96 -0
  29. data/docs/_reference/troubleshooting.adoc +862 -0
  30. data/docs/_tutorials/complex-workflows.adoc +1022 -0
  31. data/docs/_tutorials/data-processing-pipeline.adoc +740 -0
  32. data/docs/_tutorials/first-application.adoc +384 -0
  33. data/docs/_tutorials/index.adoc +48 -0
  34. data/docs/_tutorials/long-running-services.adoc +931 -0
  35. data/docs/assets/images/favicon-16.png +0 -0
  36. data/docs/assets/images/favicon-32.png +0 -0
  37. data/docs/assets/images/favicon-48.png +0 -0
  38. data/docs/assets/images/favicon.ico +0 -0
  39. data/docs/assets/images/favicon.png +0 -0
  40. data/docs/assets/images/favicon.svg +45 -0
  41. data/docs/assets/images/fractor-icon.svg +49 -0
  42. data/docs/assets/images/fractor-logo.svg +61 -0
  43. data/docs/index.adoc +131 -0
  44. data/docs/lychee.toml +39 -0
  45. data/examples/api_aggregator/README.adoc +627 -0
  46. data/examples/api_aggregator/api_aggregator.rb +376 -0
  47. data/examples/auto_detection/README.adoc +407 -29
  48. data/examples/auto_detection/auto_detection.rb +9 -9
  49. data/examples/continuous_chat_common/message_protocol.rb +53 -0
  50. data/examples/continuous_chat_fractor/README.adoc +217 -0
  51. data/examples/continuous_chat_fractor/chat_client.rb +303 -0
  52. data/examples/continuous_chat_fractor/chat_common.rb +83 -0
  53. data/examples/continuous_chat_fractor/chat_server.rb +167 -0
  54. data/examples/continuous_chat_fractor/simulate.rb +345 -0
  55. data/examples/continuous_chat_server/README.adoc +135 -0
  56. data/examples/continuous_chat_server/chat_client.rb +303 -0
  57. data/examples/continuous_chat_server/chat_server.rb +359 -0
  58. data/examples/continuous_chat_server/simulate.rb +343 -0
  59. data/examples/error_reporting.rb +207 -0
  60. data/examples/file_processor/README.adoc +170 -0
  61. data/examples/file_processor/file_processor.rb +615 -0
  62. data/examples/file_processor/sample_files/invalid.csv +1 -0
  63. data/examples/file_processor/sample_files/orders.xml +24 -0
  64. data/examples/file_processor/sample_files/products.json +23 -0
  65. data/examples/file_processor/sample_files/users.csv +6 -0
  66. data/examples/hierarchical_hasher/README.adoc +629 -41
  67. data/examples/hierarchical_hasher/hierarchical_hasher.rb +12 -8
  68. data/examples/image_processor/README.adoc +610 -0
  69. data/examples/image_processor/image_processor.rb +349 -0
  70. data/examples/image_processor/processed_images/sample_10_processed.jpg.json +12 -0
  71. data/examples/image_processor/processed_images/sample_1_processed.jpg.json +12 -0
  72. data/examples/image_processor/processed_images/sample_2_processed.jpg.json +12 -0
  73. data/examples/image_processor/processed_images/sample_3_processed.jpg.json +12 -0
  74. data/examples/image_processor/processed_images/sample_4_processed.jpg.json +12 -0
  75. data/examples/image_processor/processed_images/sample_5_processed.jpg.json +12 -0
  76. data/examples/image_processor/processed_images/sample_6_processed.jpg.json +12 -0
  77. data/examples/image_processor/processed_images/sample_7_processed.jpg.json +12 -0
  78. data/examples/image_processor/processed_images/sample_8_processed.jpg.json +12 -0
  79. data/examples/image_processor/processed_images/sample_9_processed.jpg.json +12 -0
  80. data/examples/image_processor/test_images/sample_1.png +1 -0
  81. data/examples/image_processor/test_images/sample_10.png +1 -0
  82. data/examples/image_processor/test_images/sample_2.png +1 -0
  83. data/examples/image_processor/test_images/sample_3.png +1 -0
  84. data/examples/image_processor/test_images/sample_4.png +1 -0
  85. data/examples/image_processor/test_images/sample_5.png +1 -0
  86. data/examples/image_processor/test_images/sample_6.png +1 -0
  87. data/examples/image_processor/test_images/sample_7.png +1 -0
  88. data/examples/image_processor/test_images/sample_8.png +1 -0
  89. data/examples/image_processor/test_images/sample_9.png +1 -0
  90. data/examples/log_analyzer/README.adoc +662 -0
  91. data/examples/log_analyzer/log_analyzer.rb +579 -0
  92. data/examples/log_analyzer/sample_logs/apache.log +20 -0
  93. data/examples/log_analyzer/sample_logs/json.log +15 -0
  94. data/examples/log_analyzer/sample_logs/nginx.log +15 -0
  95. data/examples/log_analyzer/sample_logs/rails.log +29 -0
  96. data/examples/multi_work_type/README.adoc +576 -26
  97. data/examples/multi_work_type/multi_work_type.rb +30 -29
  98. data/examples/performance_monitoring.rb +120 -0
  99. data/examples/pipeline_processing/README.adoc +740 -26
  100. data/examples/pipeline_processing/pipeline_processing.rb +16 -16
  101. data/examples/priority_work_example.rb +155 -0
  102. data/examples/producer_subscriber/README.adoc +889 -46
  103. data/examples/producer_subscriber/producer_subscriber.rb +20 -16
  104. data/examples/scatter_gather/README.adoc +829 -27
  105. data/examples/scatter_gather/scatter_gather.rb +29 -28
  106. data/examples/simple/README.adoc +347 -0
  107. data/examples/simple/sample.rb +5 -5
  108. data/examples/specialized_workers/README.adoc +622 -26
  109. data/examples/specialized_workers/specialized_workers.rb +88 -45
  110. data/examples/stream_processor/README.adoc +206 -0
  111. data/examples/stream_processor/stream_processor.rb +284 -0
  112. data/examples/web_scraper/README.adoc +625 -0
  113. data/examples/web_scraper/web_scraper.rb +285 -0
  114. data/examples/workflow/README.adoc +406 -0
  115. data/examples/workflow/circuit_breaker/README.adoc +360 -0
  116. data/examples/workflow/circuit_breaker/circuit_breaker_workflow.rb +225 -0
  117. data/examples/workflow/conditional/README.adoc +483 -0
  118. data/examples/workflow/conditional/conditional_workflow.rb +215 -0
  119. data/examples/workflow/dead_letter_queue/README.adoc +374 -0
  120. data/examples/workflow/dead_letter_queue/dead_letter_queue_workflow.rb +217 -0
  121. data/examples/workflow/fan_out/README.adoc +381 -0
  122. data/examples/workflow/fan_out/fan_out_workflow.rb +202 -0
  123. data/examples/workflow/retry/README.adoc +248 -0
  124. data/examples/workflow/retry/retry_workflow.rb +195 -0
  125. data/examples/workflow/simple_linear/README.adoc +267 -0
  126. data/examples/workflow/simple_linear/simple_linear_workflow.rb +175 -0
  127. data/examples/workflow/simplified/README.adoc +329 -0
  128. data/examples/workflow/simplified/simplified_workflow.rb +222 -0
  129. data/exe/fractor +10 -0
  130. data/lib/fractor/cli.rb +288 -0
  131. data/lib/fractor/configuration.rb +307 -0
  132. data/lib/fractor/continuous_server.rb +183 -0
  133. data/lib/fractor/error_formatter.rb +72 -0
  134. data/lib/fractor/error_report_generator.rb +152 -0
  135. data/lib/fractor/error_reporter.rb +244 -0
  136. data/lib/fractor/error_statistics.rb +147 -0
  137. data/lib/fractor/execution_tracer.rb +162 -0
  138. data/lib/fractor/logger.rb +230 -0
  139. data/lib/fractor/main_loop_handler.rb +406 -0
  140. data/lib/fractor/main_loop_handler3.rb +135 -0
  141. data/lib/fractor/main_loop_handler4.rb +299 -0
  142. data/lib/fractor/performance_metrics_collector.rb +181 -0
  143. data/lib/fractor/performance_monitor.rb +215 -0
  144. data/lib/fractor/performance_report_generator.rb +202 -0
  145. data/lib/fractor/priority_work.rb +93 -0
  146. data/lib/fractor/priority_work_queue.rb +189 -0
  147. data/lib/fractor/result_aggregator.rb +33 -1
  148. data/lib/fractor/shutdown_handler.rb +168 -0
  149. data/lib/fractor/signal_handler.rb +80 -0
  150. data/lib/fractor/supervisor.rb +430 -144
  151. data/lib/fractor/supervisor_logger.rb +88 -0
  152. data/lib/fractor/version.rb +1 -1
  153. data/lib/fractor/work.rb +12 -0
  154. data/lib/fractor/work_distribution_manager.rb +151 -0
  155. data/lib/fractor/work_queue.rb +88 -0
  156. data/lib/fractor/work_result.rb +181 -9
  157. data/lib/fractor/worker.rb +75 -1
  158. data/lib/fractor/workflow/builder.rb +210 -0
  159. data/lib/fractor/workflow/chain_builder.rb +169 -0
  160. data/lib/fractor/workflow/circuit_breaker.rb +183 -0
  161. data/lib/fractor/workflow/circuit_breaker_orchestrator.rb +208 -0
  162. data/lib/fractor/workflow/circuit_breaker_registry.rb +112 -0
  163. data/lib/fractor/workflow/dead_letter_queue.rb +334 -0
  164. data/lib/fractor/workflow/execution_hooks.rb +39 -0
  165. data/lib/fractor/workflow/execution_strategy.rb +225 -0
  166. data/lib/fractor/workflow/execution_trace.rb +134 -0
  167. data/lib/fractor/workflow/helpers.rb +191 -0
  168. data/lib/fractor/workflow/job.rb +290 -0
  169. data/lib/fractor/workflow/job_dependency_validator.rb +120 -0
  170. data/lib/fractor/workflow/logger.rb +110 -0
  171. data/lib/fractor/workflow/pre_execution_context.rb +193 -0
  172. data/lib/fractor/workflow/retry_config.rb +156 -0
  173. data/lib/fractor/workflow/retry_orchestrator.rb +184 -0
  174. data/lib/fractor/workflow/retry_strategy.rb +93 -0
  175. data/lib/fractor/workflow/structured_logger.rb +30 -0
  176. data/lib/fractor/workflow/type_compatibility_validator.rb +222 -0
  177. data/lib/fractor/workflow/visualizer.rb +211 -0
  178. data/lib/fractor/workflow/workflow_context.rb +132 -0
  179. data/lib/fractor/workflow/workflow_executor.rb +669 -0
  180. data/lib/fractor/workflow/workflow_result.rb +55 -0
  181. data/lib/fractor/workflow/workflow_validator.rb +295 -0
  182. data/lib/fractor/workflow.rb +333 -0
  183. data/lib/fractor/wrapped_ractor.rb +66 -91
  184. data/lib/fractor/wrapped_ractor3.rb +161 -0
  185. data/lib/fractor/wrapped_ractor4.rb +242 -0
  186. data/lib/fractor.rb +93 -3
  187. metadata +192 -6
  188. data/tests/sample.rb.bak +0 -309
  189. data/tests/sample_working.rb.bak +0 -209
data/lib/fractor.rb CHANGED
@@ -2,14 +2,104 @@
2
2
 
3
3
  # Require all component files
4
4
  require_relative "fractor/version"
5
- require_relative "fractor/worker"
5
+ require_relative "fractor/configuration"
6
+ require_relative "fractor/logger"
6
7
  require_relative "fractor/work"
7
8
  require_relative "fractor/work_result"
8
- require_relative "fractor/result_aggregator"
9
+ require_relative "fractor/work_queue"
10
+ require_relative "fractor/priority_work"
11
+ require_relative "fractor/priority_work_queue"
9
12
  require_relative "fractor/wrapped_ractor"
13
+ require_relative "fractor/wrapped_ractor3"
14
+ require_relative "fractor/wrapped_ractor4"
15
+ require_relative "fractor/worker"
16
+ require_relative "fractor/work_distribution_manager"
17
+ require_relative "fractor/shutdown_handler"
18
+ require_relative "fractor/main_loop_handler"
19
+ require_relative "fractor/main_loop_handler3"
20
+ require_relative "fractor/main_loop_handler4"
21
+ require_relative "fractor/signal_handler"
22
+ require_relative "fractor/supervisor_logger"
10
23
  require_relative "fractor/supervisor"
24
+ require_relative "fractor/result_aggregator"
25
+ require_relative "fractor/continuous_server"
26
+ require_relative "fractor/workflow"
27
+ require_relative "fractor/performance_monitor"
28
+ require_relative "fractor/performance_metrics_collector"
29
+ require_relative "fractor/performance_report_generator"
30
+ require_relative "fractor/error_reporter"
31
+ require_relative "fractor/error_statistics"
32
+ require_relative "fractor/error_report_generator"
33
+ require_relative "fractor/error_formatter"
34
+ require_relative "fractor/execution_tracer"
35
+
36
+ # Optional: CLI (only load if Thor is available)
37
+ begin
38
+ require "thor"
39
+ require_relative "fractor/cli"
40
+ rescue LoadError
41
+ # Thor not available, CLI commands will not be available
42
+ end
11
43
 
12
44
  # Fractor: Function-driven Ractors framework
13
45
  module Fractor
14
- # The module is defined in the individual files
46
+ # Exception raised when trying to push to a closed queue
47
+ class ClosedQueueError < StandardError; end
48
+
49
+ # Configure Fractor with a block.
50
+ #
51
+ # @yield [Configuration] The configuration object
52
+ #
53
+ # @example
54
+ # Fractor.configure do |config|
55
+ # config.debug = true
56
+ # config.logger = Logger.new(STDOUT)
57
+ # config.default_worker_timeout = 60
58
+ # end
59
+ def self.configure(&)
60
+ Configuration.configure(&)
61
+ end
62
+
63
+ # Load configuration from a YAML file.
64
+ #
65
+ # @param file_path [String] Path to the YAML configuration file
66
+ def self.configure_from_file(file_path)
67
+ Configuration.configure_from_file(file_path)
68
+ end
69
+
70
+ # Load configuration from environment variables.
71
+ # Environment variables should be prefixed with FRACTOR_.
72
+ #
73
+ # @example
74
+ # # Set environment variables
75
+ # # export FRACTOR_DEBUG=true
76
+ # # export FRACTOR_DEFAULT_WORKER_TIMEOUT=60
77
+ #
78
+ # Fractor.configure_from_env
79
+ def self.configure_from_env
80
+ Configuration.configure_from_env
81
+ end
82
+
83
+ # Access the global configuration instance.
84
+ #
85
+ # @return [Configuration] The configuration instance
86
+ def self.config
87
+ Configuration.config
88
+ end
89
+
90
+ # Reset all global state to ensure isolation between different uses of Fractor.
91
+ # This is important for testing and when multiple gems use Fractor together.
92
+ #
93
+ # @example Reset state between tests
94
+ # Fractor.reset! # Clears logger, tracer, and other global state
95
+ def self.reset!
96
+ Configuration.reset!
97
+ ExecutionTracer.reset!
98
+ reset_logger!
99
+ end
100
+
101
+ # Reset logger state (internal method, use reset! instead)
102
+ def self.reset_logger!
103
+ @logger = nil if defined?(@logger)
104
+ end
15
105
  end
metadata CHANGED
@@ -1,55 +1,241 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fractor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ronald Tse
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-10-09 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2026-01-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logger
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: benchmark
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
13
41
  description: Fractor is a lightweight Ruby framework designed to simplify the process
14
42
  of distributing computational work across multiple Ractors.
15
43
  email:
16
44
  - ronald.tse@ribose.com
17
- executables: []
45
+ executables:
46
+ - fractor
18
47
  extensions: []
19
48
  extra_rdoc_files: []
20
49
  files:
21
50
  - ".rspec"
51
+ - ".rubocop-https---raw-githubusercontent-com-riboseinc-oss-guides-main-ci-rubocop-yml"
22
52
  - ".rubocop.yml"
23
53
  - ".rubocop_todo.yml"
24
54
  - CODE_OF_CONDUCT.md
25
55
  - README.adoc
26
56
  - Rakefile
57
+ - docs/.lycheeignore
58
+ - docs/Gemfile
59
+ - docs/README.md
60
+ - docs/_config.yml
61
+ - docs/_features/error-handling.adoc
62
+ - docs/_features/index.adoc
63
+ - docs/_features/monitoring.adoc
64
+ - docs/_features/signal-handling.adoc
65
+ - docs/_features/workflows.adoc
66
+ - docs/_guides/continuous-mode.adoc
67
+ - docs/_guides/cookbook.adoc
68
+ - docs/_guides/index.adoc
69
+ - docs/_guides/pipeline-mode.adoc
70
+ - docs/_guides/troubleshooting.adoc
71
+ - docs/_pages/architecture.adoc
72
+ - docs/_pages/core-concepts.adoc
73
+ - docs/_pages/design-principles.adoc
74
+ - docs/_pages/getting-started.adoc
75
+ - docs/_pages/installation.adoc
76
+ - docs/_reference/api.adoc
77
+ - docs/_reference/error-reporting.adoc
78
+ - docs/_reference/examples.adoc
79
+ - docs/_reference/index.adoc
80
+ - docs/_reference/troubleshooting.adoc
81
+ - docs/_tutorials/complex-workflows.adoc
82
+ - docs/_tutorials/data-processing-pipeline.adoc
83
+ - docs/_tutorials/first-application.adoc
84
+ - docs/_tutorials/index.adoc
85
+ - docs/_tutorials/long-running-services.adoc
86
+ - docs/assets/images/favicon-16.png
87
+ - docs/assets/images/favicon-32.png
88
+ - docs/assets/images/favicon-48.png
89
+ - docs/assets/images/favicon.ico
90
+ - docs/assets/images/favicon.png
91
+ - docs/assets/images/favicon.svg
92
+ - docs/assets/images/fractor-icon.svg
93
+ - docs/assets/images/fractor-logo.svg
94
+ - docs/index.adoc
95
+ - docs/lychee.toml
96
+ - examples/api_aggregator/README.adoc
97
+ - examples/api_aggregator/api_aggregator.rb
27
98
  - examples/auto_detection/README.adoc
28
99
  - examples/auto_detection/auto_detection.rb
100
+ - examples/continuous_chat_common/message_protocol.rb
101
+ - examples/continuous_chat_fractor/README.adoc
102
+ - examples/continuous_chat_fractor/chat_client.rb
103
+ - examples/continuous_chat_fractor/chat_common.rb
104
+ - examples/continuous_chat_fractor/chat_server.rb
105
+ - examples/continuous_chat_fractor/simulate.rb
106
+ - examples/continuous_chat_server/README.adoc
107
+ - examples/continuous_chat_server/chat_client.rb
108
+ - examples/continuous_chat_server/chat_server.rb
109
+ - examples/continuous_chat_server/simulate.rb
110
+ - examples/error_reporting.rb
111
+ - examples/file_processor/README.adoc
112
+ - examples/file_processor/file_processor.rb
113
+ - examples/file_processor/sample_files/invalid.csv
114
+ - examples/file_processor/sample_files/orders.xml
115
+ - examples/file_processor/sample_files/products.json
116
+ - examples/file_processor/sample_files/users.csv
29
117
  - examples/hierarchical_hasher/README.adoc
30
118
  - examples/hierarchical_hasher/hierarchical_hasher.rb
119
+ - examples/image_processor/README.adoc
120
+ - examples/image_processor/image_processor.rb
121
+ - examples/image_processor/processed_images/sample_10_processed.jpg.json
122
+ - examples/image_processor/processed_images/sample_1_processed.jpg.json
123
+ - examples/image_processor/processed_images/sample_2_processed.jpg.json
124
+ - examples/image_processor/processed_images/sample_3_processed.jpg.json
125
+ - examples/image_processor/processed_images/sample_4_processed.jpg.json
126
+ - examples/image_processor/processed_images/sample_5_processed.jpg.json
127
+ - examples/image_processor/processed_images/sample_6_processed.jpg.json
128
+ - examples/image_processor/processed_images/sample_7_processed.jpg.json
129
+ - examples/image_processor/processed_images/sample_8_processed.jpg.json
130
+ - examples/image_processor/processed_images/sample_9_processed.jpg.json
131
+ - examples/image_processor/test_images/sample_1.png
132
+ - examples/image_processor/test_images/sample_10.png
133
+ - examples/image_processor/test_images/sample_2.png
134
+ - examples/image_processor/test_images/sample_3.png
135
+ - examples/image_processor/test_images/sample_4.png
136
+ - examples/image_processor/test_images/sample_5.png
137
+ - examples/image_processor/test_images/sample_6.png
138
+ - examples/image_processor/test_images/sample_7.png
139
+ - examples/image_processor/test_images/sample_8.png
140
+ - examples/image_processor/test_images/sample_9.png
141
+ - examples/log_analyzer/README.adoc
142
+ - examples/log_analyzer/log_analyzer.rb
143
+ - examples/log_analyzer/sample_logs/apache.log
144
+ - examples/log_analyzer/sample_logs/json.log
145
+ - examples/log_analyzer/sample_logs/nginx.log
146
+ - examples/log_analyzer/sample_logs/rails.log
31
147
  - examples/multi_work_type/README.adoc
32
148
  - examples/multi_work_type/multi_work_type.rb
149
+ - examples/performance_monitoring.rb
33
150
  - examples/pipeline_processing/README.adoc
34
151
  - examples/pipeline_processing/pipeline_processing.rb
152
+ - examples/priority_work_example.rb
35
153
  - examples/producer_subscriber/README.adoc
36
154
  - examples/producer_subscriber/producer_subscriber.rb
37
155
  - examples/scatter_gather/README.adoc
38
156
  - examples/scatter_gather/scatter_gather.rb
157
+ - examples/simple/README.adoc
39
158
  - examples/simple/sample.rb
40
159
  - examples/specialized_workers/README.adoc
41
160
  - examples/specialized_workers/specialized_workers.rb
161
+ - examples/stream_processor/README.adoc
162
+ - examples/stream_processor/stream_processor.rb
163
+ - examples/web_scraper/README.adoc
164
+ - examples/web_scraper/web_scraper.rb
165
+ - examples/workflow/README.adoc
166
+ - examples/workflow/circuit_breaker/README.adoc
167
+ - examples/workflow/circuit_breaker/circuit_breaker_workflow.rb
168
+ - examples/workflow/conditional/README.adoc
169
+ - examples/workflow/conditional/conditional_workflow.rb
170
+ - examples/workflow/dead_letter_queue/README.adoc
171
+ - examples/workflow/dead_letter_queue/dead_letter_queue_workflow.rb
172
+ - examples/workflow/fan_out/README.adoc
173
+ - examples/workflow/fan_out/fan_out_workflow.rb
174
+ - examples/workflow/retry/README.adoc
175
+ - examples/workflow/retry/retry_workflow.rb
176
+ - examples/workflow/simple_linear/README.adoc
177
+ - examples/workflow/simple_linear/simple_linear_workflow.rb
178
+ - examples/workflow/simplified/README.adoc
179
+ - examples/workflow/simplified/simplified_workflow.rb
180
+ - exe/fractor
42
181
  - lib/fractor.rb
182
+ - lib/fractor/cli.rb
183
+ - lib/fractor/configuration.rb
184
+ - lib/fractor/continuous_server.rb
185
+ - lib/fractor/error_formatter.rb
186
+ - lib/fractor/error_report_generator.rb
187
+ - lib/fractor/error_reporter.rb
188
+ - lib/fractor/error_statistics.rb
189
+ - lib/fractor/execution_tracer.rb
190
+ - lib/fractor/logger.rb
191
+ - lib/fractor/main_loop_handler.rb
192
+ - lib/fractor/main_loop_handler3.rb
193
+ - lib/fractor/main_loop_handler4.rb
194
+ - lib/fractor/performance_metrics_collector.rb
195
+ - lib/fractor/performance_monitor.rb
196
+ - lib/fractor/performance_report_generator.rb
197
+ - lib/fractor/priority_work.rb
198
+ - lib/fractor/priority_work_queue.rb
43
199
  - lib/fractor/result_aggregator.rb
200
+ - lib/fractor/shutdown_handler.rb
201
+ - lib/fractor/signal_handler.rb
44
202
  - lib/fractor/supervisor.rb
203
+ - lib/fractor/supervisor_logger.rb
45
204
  - lib/fractor/version.rb
46
205
  - lib/fractor/work.rb
206
+ - lib/fractor/work_distribution_manager.rb
207
+ - lib/fractor/work_queue.rb
47
208
  - lib/fractor/work_result.rb
48
209
  - lib/fractor/worker.rb
210
+ - lib/fractor/workflow.rb
211
+ - lib/fractor/workflow/builder.rb
212
+ - lib/fractor/workflow/chain_builder.rb
213
+ - lib/fractor/workflow/circuit_breaker.rb
214
+ - lib/fractor/workflow/circuit_breaker_orchestrator.rb
215
+ - lib/fractor/workflow/circuit_breaker_registry.rb
216
+ - lib/fractor/workflow/dead_letter_queue.rb
217
+ - lib/fractor/workflow/execution_hooks.rb
218
+ - lib/fractor/workflow/execution_strategy.rb
219
+ - lib/fractor/workflow/execution_trace.rb
220
+ - lib/fractor/workflow/helpers.rb
221
+ - lib/fractor/workflow/job.rb
222
+ - lib/fractor/workflow/job_dependency_validator.rb
223
+ - lib/fractor/workflow/logger.rb
224
+ - lib/fractor/workflow/pre_execution_context.rb
225
+ - lib/fractor/workflow/retry_config.rb
226
+ - lib/fractor/workflow/retry_orchestrator.rb
227
+ - lib/fractor/workflow/retry_strategy.rb
228
+ - lib/fractor/workflow/structured_logger.rb
229
+ - lib/fractor/workflow/type_compatibility_validator.rb
230
+ - lib/fractor/workflow/visualizer.rb
231
+ - lib/fractor/workflow/workflow_context.rb
232
+ - lib/fractor/workflow/workflow_executor.rb
233
+ - lib/fractor/workflow/workflow_result.rb
234
+ - lib/fractor/workflow/workflow_validator.rb
49
235
  - lib/fractor/wrapped_ractor.rb
236
+ - lib/fractor/wrapped_ractor3.rb
237
+ - lib/fractor/wrapped_ractor4.rb
50
238
  - sig/fractor.rbs
51
- - tests/sample.rb.bak
52
- - tests/sample_working.rb.bak
53
239
  homepage: https://github.com/ribose/fractor
54
240
  licenses: []
55
241
  metadata:
data/tests/sample.rb.bak DELETED
@@ -1,309 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
-
4
- class Worker
5
- def process(work)
6
- raise NotImplementedError, "This #{self.class} cannot respond to:"
7
- end
8
- end
9
-
10
- class MyWorker < Worker
11
- # This method is called by the Ractor to process the work
12
- # It should return a WorkResult object
13
- # If there is an error, it should raise an exception
14
- # The Ractor will catch the exception and send it back to the main thread
15
- def process(work)
16
- puts "Working on '#{work.inspect}'"
17
-
18
- if work.input == 5
19
- return WorkResult.new(error: "Error processing work #{work.input}", work: work)
20
- end
21
-
22
- calculated = work.input * 2
23
- WorkResult.new(result: calculated, work: work)
24
- end
25
- end
26
-
27
- class Work
28
- attr_reader :input
29
- def initialize(input)
30
- @input = input
31
- end
32
-
33
- def to_s
34
- "Work: #{@input}"
35
- end
36
- end
37
-
38
- class MyWork < Work
39
- def initialize(input)
40
- super
41
- end
42
-
43
- def to_s
44
- "MyWork: #{@input}"
45
- end
46
- end
47
-
48
- class WorkResult
49
- attr_reader :result, :error, :work
50
- def initialize(result: nil, error: nil, work: nil)
51
- @result = result
52
- @error = error
53
- @work = work
54
- end
55
-
56
- def success?
57
- !@error
58
- end
59
-
60
- def to_s
61
- if success?
62
- "Result: #{@result}"
63
- else
64
- "Error: #{@error}, Work: #{@work}"
65
- end
66
- end
67
- end
68
-
69
-
70
- class ResultAggregator
71
- attr_reader :results, :errors
72
-
73
- # This class is used to aggregate the results and errors from the Ractors
74
- # It will store the results and errors in separate arrays
75
- # It will also provide a method to print the results and errors
76
- def initialize
77
- @results = []
78
- @errors = []
79
- end
80
-
81
- def add_result(result)
82
- if result.success?
83
- puts "Work completed successfully: #{result}"
84
- @results << result
85
- else
86
- puts "Error processing work: #{result}"
87
- @errors << result
88
- end
89
- end
90
-
91
- def to_s
92
- "Results: #{@results.each(&:to_s).join(", ")}, Errors: #{@errors.each(&:to_s).join(", ")}"
93
- end
94
-
95
- def inspect
96
- {
97
- results: @results.map(&:to_s),
98
- errors: @errors.map(&:to_s)
99
- }
100
- end
101
- end
102
-
103
- class MyRactor
104
- def initialize(name)
105
- puts "Creating Ractor #{name}"
106
- @name = name
107
- end
108
-
109
- def start
110
- puts "Starting Ractor #{@name}"
111
- @ractor = Ractor.new(@name) do |name|
112
- puts "Ractor #{name} started"
113
- Ractor.yield({ type: :initialize, processor: name })
114
- worker = MyWorker.new
115
-
116
- loop do
117
- puts "Waiting for work in #{name}"
118
- work = Ractor.receive
119
- puts "Received work #{work} in #{name}"
120
- begin
121
- result = worker.process(work)
122
- puts "Sending result #{result} from Ractor #{name}"
123
- Ractor.yield({ type: :result, result: result })
124
- rescue StandardError => e
125
- puts "Error processing work #{work} in Ractor #{name}: #{e.message}"
126
- Ractor.yield({ type: :error, error: e.message, processor: name, work: work })
127
- end
128
- end
129
- end
130
- end
131
-
132
- def ractor
133
- @ractor
134
- end
135
- end
136
-
137
- class Supervisor
138
- # Removed failed_queue from attr_reader
139
- attr_reader :work_queue, :workers, :results
140
-
141
- def initialize(num_workers = 2)
142
- @work_queue = Queue.new
143
- @results = ResultAggregator.new
144
- # @failed_queue = Queue.new # Removed failed_queue
145
- @num_workers = num_workers
146
- @workers = []
147
- @total_work_count = 0 # Track total items initially added
148
- # @shutdown_requested = false # Removed shutdown flag
149
- end
150
-
151
- def add_work(items)
152
- items.each { |item| @work_queue << item }
153
- @total_work_count += items.size # Increment initial work count
154
- puts "Work added. Initial work count: #{@total_work_count}, Queue size: #{@work_queue.size}"
155
- end
156
-
157
- def start_workers
158
- @workers = (1..@num_workers).map do |i|
159
- MyRactor.new("worker #{i}")
160
- end
161
- @workers.each(&:start)
162
- puts "Workers started"
163
- end
164
-
165
- def setup_signal_handler
166
- # No need for Ractor.current here anymore
167
- # Need access to @workers within the trap block
168
- workers_ref = @workers
169
- Signal.trap("INT") do
170
- puts "\nCtrl+C received. Initiating immediate shutdown..."
171
- # Attempt to close worker Ractors before exiting
172
- puts "Attempting to close worker Ractors..."
173
- workers_ref.each do |w|
174
- begin
175
- # Check if ractor exists and is not closed
176
- if w && w.respond_to?(:ractor) && w.ractor && !w.ractor.closed?
177
- w.ractor.close
178
- puts "Closed Ractor: #{w.ractor}"
179
- end
180
- rescue => e # Catch potential errors during close
181
- puts "Error closing Ractor #{w.ractor rescue 'unknown'}: #{e.message}"
182
- end
183
- end
184
- puts "Exiting now."
185
- exit(1) # Exit immediately
186
- end
187
- end
188
-
189
- def run
190
- setup_signal_handler # Sets up the immediate exit trap
191
- start_workers
192
-
193
- # Removed the initial work distribution loop.
194
- # The main loop will handle sending work upon receiving :initialize message.
195
-
196
- # Main loop: Process events until the number of results equals the initial work count.
197
- # The signal trap handles immediate exit.
198
- while (@results.results.size + @results.errors.size) < @total_work_count
199
- processed_count = @results.results.size + @results.errors.size
200
- puts "Waiting for Ractor results. Processed: #{processed_count}/#{@total_work_count}, Queue size: #{@work_queue.size}"
201
-
202
- # Only select from worker ractors now
203
- ready_ractors = @workers.map(&:ractor).compact
204
- # Safety break if all workers somehow finished/closed unexpectedly AND no work left
205
- # This condition might need refinement depending on exact desired behavior if workers die.
206
- break if ready_ractors.empty? && @work_queue.empty? && processed_count < @total_work_count
207
-
208
- # Ractor.select will block until a worker sends a message
209
- # If ready_ractors is empty but loop continues, select would raise error. Added break above.
210
- next if ready_ractors.empty? # Skip iteration if no workers available but loop condition met (e.g., waiting for final results)
211
-
212
- ractor, completed_work = Ractor.select(*ready_ractors)
213
-
214
- puts "Selected Ractor returned: #{ractor}, completed work: #{completed_work}"
215
-
216
- # Process the received message
217
- case completed_work[:type]
218
- when :initialize
219
- puts "Initializing Ractor: #{completed_work[:processor]}"
220
- # Send work if available
221
- if !@work_queue.empty?
222
- queued_work = @work_queue.pop # Pop before sending
223
- puts "Sending initial work #{queued_work} to initialized Ractor: #{ractor}"
224
- ractor.send(MyWork.new(queued_work))
225
- puts "Initial work sent to #{completed_work[:processor]}."
226
- else
227
- puts "Work queue empty when Ractor #{completed_work[:processor]} initialized."
228
- end
229
- when :result
230
- puts "Completed work: #{completed_work[:result]} in Ractor: #{completed_work[:processor]}"
231
- @results.add_result(completed_work[:result])
232
- # No need to decrement a counter here, loop condition checks total results
233
- puts "Result processed. Total processed: #{@results.results.size + @results.errors.size}/#{@total_work_count}"
234
- puts "Results: #{@results.inspect}"
235
- # Call helper to send next work
236
- send_next_work_if_available(ractor)
237
- when :error
238
- error_result = WorkResult.new(error: completed_work[:error], work: completed_work[:work])
239
- puts "Error processing work #{error_result.work} in Ractor: #{completed_work[:processor]}: #{error_result.error}"
240
- # Removed adding to failed_queue
241
- @results.add_result(error_result) # This adds it to the errors array in the aggregator
242
- # No need to decrement a counter here, loop condition checks total results
243
- puts "Error handled. Total processed: #{@results.results.size + @results.errors.size}/#{@total_work_count}"
244
- # Removed Failed Queue size log
245
- puts "Results (including errors): #{@results.inspect}"
246
- # Call helper to send next work
247
- send_next_work_if_available(ractor)
248
- else
249
- # Log unknown message types from workers
250
- puts "Unknown message type received: #{completed_work[:type]} from #{ractor}"
251
- end
252
- # Loop continues based on the while condition at the top
253
- end
254
-
255
- # Removed DEBUG LOG for failed_queue
256
-
257
- # This part might not be reached if exit(1) is called in the trap
258
- puts "Main loop finished."
259
- puts "Final Results: #{@results.inspect}"
260
- # Removed Failed Work Queue size log
261
- # Optionally print failed items
262
- # until @failed_queue.empty?
263
- # puts "Failed: #{@failed_queue.pop.inspect}"
264
- # end
265
- end
266
-
267
- private
268
-
269
- # Helper method to send next work item if available
270
- def send_next_work_if_available(ractor)
271
- # Ensure the ractor is valid before attempting to send
272
- # Ractor.select should only return active ractors, so closed? check is removed.
273
- unless ractor.nil?
274
- if !@work_queue.empty?
275
- queued_work = @work_queue.pop # Pop before sending
276
- puts "Sending next work #{queued_work} to Ractor: #{ractor}"
277
- ractor.send(MyWork.new(queued_work))
278
- puts "Work sent."
279
- else
280
- puts "Work queue empty. Not sending new work to Ractor #{ractor}."
281
- end
282
- else
283
- puts "Attempted to send work to an invalid or closed Ractor."
284
- end
285
- end
286
- end
287
-
288
- # --- Main Execution ---
289
- if __FILE__ == $0
290
- supervisor = Supervisor.new(2) # Create supervisor with 2 workers
291
-
292
- # Add work items
293
- work_items = (1..10).to_a
294
- supervisor.add_work(work_items)
295
-
296
- # Run the supervisor
297
- supervisor.run
298
-
299
- puts "Processing complete."
300
- puts "Final Aggregated Results:"
301
- puts supervisor.results.inspect
302
-
303
- # Print failed items directly from the ResultAggregator's errors array
304
- failed_items = supervisor.results.errors # Access the errors array
305
- puts "\nFailed Work Items (#{failed_items.size}):"
306
- # Inspect each item individually for better readability if they are objects
307
- # The items are already WorkResult objects
308
- puts failed_items.map(&:inspect).inspect
309
- end