fractor 0.1.6 → 0.1.8

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 (172) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +227 -102
  3. data/README.adoc +113 -1940
  4. data/docs/.lycheeignore +16 -0
  5. data/docs/Gemfile +24 -0
  6. data/docs/README.md +157 -0
  7. data/docs/_config.yml +151 -0
  8. data/docs/_features/error-handling.adoc +1192 -0
  9. data/docs/_features/index.adoc +80 -0
  10. data/docs/_features/monitoring.adoc +589 -0
  11. data/docs/_features/signal-handling.adoc +202 -0
  12. data/docs/_features/workflows.adoc +1235 -0
  13. data/docs/_guides/continuous-mode.adoc +736 -0
  14. data/docs/_guides/cookbook.adoc +1133 -0
  15. data/docs/_guides/index.adoc +55 -0
  16. data/docs/_guides/pipeline-mode.adoc +730 -0
  17. data/docs/_guides/troubleshooting.adoc +358 -0
  18. data/docs/_pages/architecture.adoc +1390 -0
  19. data/docs/_pages/core-concepts.adoc +1392 -0
  20. data/docs/_pages/design-principles.adoc +862 -0
  21. data/docs/_pages/getting-started.adoc +290 -0
  22. data/docs/_pages/installation.adoc +143 -0
  23. data/docs/_reference/api.adoc +1080 -0
  24. data/docs/_reference/error-reporting.adoc +670 -0
  25. data/docs/_reference/examples.adoc +181 -0
  26. data/docs/_reference/index.adoc +96 -0
  27. data/docs/_reference/troubleshooting.adoc +862 -0
  28. data/docs/_tutorials/complex-workflows.adoc +1022 -0
  29. data/docs/_tutorials/data-processing-pipeline.adoc +740 -0
  30. data/docs/_tutorials/first-application.adoc +384 -0
  31. data/docs/_tutorials/index.adoc +48 -0
  32. data/docs/_tutorials/long-running-services.adoc +931 -0
  33. data/docs/assets/images/favicon-16.png +0 -0
  34. data/docs/assets/images/favicon-32.png +0 -0
  35. data/docs/assets/images/favicon-48.png +0 -0
  36. data/docs/assets/images/favicon.ico +0 -0
  37. data/docs/assets/images/favicon.png +0 -0
  38. data/docs/assets/images/favicon.svg +45 -0
  39. data/docs/assets/images/fractor-icon.svg +49 -0
  40. data/docs/assets/images/fractor-logo.svg +61 -0
  41. data/docs/index.adoc +131 -0
  42. data/docs/lychee.toml +39 -0
  43. data/examples/api_aggregator/README.adoc +627 -0
  44. data/examples/api_aggregator/api_aggregator.rb +376 -0
  45. data/examples/auto_detection/README.adoc +407 -29
  46. data/examples/continuous_chat_common/message_protocol.rb +1 -1
  47. data/examples/error_reporting.rb +207 -0
  48. data/examples/file_processor/README.adoc +170 -0
  49. data/examples/file_processor/file_processor.rb +615 -0
  50. data/examples/file_processor/sample_files/invalid.csv +1 -0
  51. data/examples/file_processor/sample_files/orders.xml +24 -0
  52. data/examples/file_processor/sample_files/products.json +23 -0
  53. data/examples/file_processor/sample_files/users.csv +6 -0
  54. data/examples/hierarchical_hasher/README.adoc +629 -41
  55. data/examples/image_processor/README.adoc +610 -0
  56. data/examples/image_processor/image_processor.rb +349 -0
  57. data/examples/image_processor/processed_images/sample_10_processed.jpg.json +12 -0
  58. data/examples/image_processor/processed_images/sample_1_processed.jpg.json +12 -0
  59. data/examples/image_processor/processed_images/sample_2_processed.jpg.json +12 -0
  60. data/examples/image_processor/processed_images/sample_3_processed.jpg.json +12 -0
  61. data/examples/image_processor/processed_images/sample_4_processed.jpg.json +12 -0
  62. data/examples/image_processor/processed_images/sample_5_processed.jpg.json +12 -0
  63. data/examples/image_processor/processed_images/sample_6_processed.jpg.json +12 -0
  64. data/examples/image_processor/processed_images/sample_7_processed.jpg.json +12 -0
  65. data/examples/image_processor/processed_images/sample_8_processed.jpg.json +12 -0
  66. data/examples/image_processor/processed_images/sample_9_processed.jpg.json +12 -0
  67. data/examples/image_processor/test_images/sample_1.png +1 -0
  68. data/examples/image_processor/test_images/sample_10.png +1 -0
  69. data/examples/image_processor/test_images/sample_2.png +1 -0
  70. data/examples/image_processor/test_images/sample_3.png +1 -0
  71. data/examples/image_processor/test_images/sample_4.png +1 -0
  72. data/examples/image_processor/test_images/sample_5.png +1 -0
  73. data/examples/image_processor/test_images/sample_6.png +1 -0
  74. data/examples/image_processor/test_images/sample_7.png +1 -0
  75. data/examples/image_processor/test_images/sample_8.png +1 -0
  76. data/examples/image_processor/test_images/sample_9.png +1 -0
  77. data/examples/log_analyzer/README.adoc +662 -0
  78. data/examples/log_analyzer/log_analyzer.rb +579 -0
  79. data/examples/log_analyzer/sample_logs/apache.log +20 -0
  80. data/examples/log_analyzer/sample_logs/json.log +15 -0
  81. data/examples/log_analyzer/sample_logs/nginx.log +15 -0
  82. data/examples/log_analyzer/sample_logs/rails.log +29 -0
  83. data/examples/multi_work_type/README.adoc +576 -26
  84. data/examples/performance_monitoring.rb +120 -0
  85. data/examples/pipeline_processing/README.adoc +740 -26
  86. data/examples/pipeline_processing/pipeline_processing.rb +2 -2
  87. data/examples/priority_work_example.rb +155 -0
  88. data/examples/producer_subscriber/README.adoc +889 -46
  89. data/examples/scatter_gather/README.adoc +829 -27
  90. data/examples/simple/README.adoc +347 -0
  91. data/examples/specialized_workers/README.adoc +622 -26
  92. data/examples/specialized_workers/specialized_workers.rb +44 -8
  93. data/examples/stream_processor/README.adoc +206 -0
  94. data/examples/stream_processor/stream_processor.rb +284 -0
  95. data/examples/web_scraper/README.adoc +625 -0
  96. data/examples/web_scraper/web_scraper.rb +285 -0
  97. data/examples/workflow/README.adoc +406 -0
  98. data/examples/workflow/circuit_breaker/README.adoc +360 -0
  99. data/examples/workflow/circuit_breaker/circuit_breaker_workflow.rb +225 -0
  100. data/examples/workflow/conditional/README.adoc +483 -0
  101. data/examples/workflow/conditional/conditional_workflow.rb +215 -0
  102. data/examples/workflow/dead_letter_queue/README.adoc +374 -0
  103. data/examples/workflow/dead_letter_queue/dead_letter_queue_workflow.rb +217 -0
  104. data/examples/workflow/fan_out/README.adoc +381 -0
  105. data/examples/workflow/fan_out/fan_out_workflow.rb +202 -0
  106. data/examples/workflow/retry/README.adoc +248 -0
  107. data/examples/workflow/retry/retry_workflow.rb +195 -0
  108. data/examples/workflow/simple_linear/README.adoc +267 -0
  109. data/examples/workflow/simple_linear/simple_linear_workflow.rb +175 -0
  110. data/examples/workflow/simplified/README.adoc +329 -0
  111. data/examples/workflow/simplified/simplified_workflow.rb +222 -0
  112. data/exe/fractor +10 -0
  113. data/lib/fractor/cli.rb +288 -0
  114. data/lib/fractor/configuration.rb +307 -0
  115. data/lib/fractor/continuous_server.rb +60 -65
  116. data/lib/fractor/error_formatter.rb +72 -0
  117. data/lib/fractor/error_report_generator.rb +152 -0
  118. data/lib/fractor/error_reporter.rb +244 -0
  119. data/lib/fractor/error_statistics.rb +147 -0
  120. data/lib/fractor/execution_tracer.rb +162 -0
  121. data/lib/fractor/logger.rb +230 -0
  122. data/lib/fractor/main_loop_handler.rb +406 -0
  123. data/lib/fractor/main_loop_handler3.rb +135 -0
  124. data/lib/fractor/main_loop_handler4.rb +299 -0
  125. data/lib/fractor/performance_metrics_collector.rb +181 -0
  126. data/lib/fractor/performance_monitor.rb +215 -0
  127. data/lib/fractor/performance_report_generator.rb +202 -0
  128. data/lib/fractor/priority_work.rb +93 -0
  129. data/lib/fractor/priority_work_queue.rb +189 -0
  130. data/lib/fractor/result_aggregator.rb +32 -0
  131. data/lib/fractor/shutdown_handler.rb +168 -0
  132. data/lib/fractor/signal_handler.rb +80 -0
  133. data/lib/fractor/supervisor.rb +382 -269
  134. data/lib/fractor/supervisor_logger.rb +88 -0
  135. data/lib/fractor/version.rb +1 -1
  136. data/lib/fractor/work.rb +12 -0
  137. data/lib/fractor/work_distribution_manager.rb +151 -0
  138. data/lib/fractor/work_queue.rb +20 -0
  139. data/lib/fractor/work_result.rb +181 -9
  140. data/lib/fractor/worker.rb +73 -0
  141. data/lib/fractor/workflow/builder.rb +210 -0
  142. data/lib/fractor/workflow/chain_builder.rb +169 -0
  143. data/lib/fractor/workflow/circuit_breaker.rb +183 -0
  144. data/lib/fractor/workflow/circuit_breaker_orchestrator.rb +208 -0
  145. data/lib/fractor/workflow/circuit_breaker_registry.rb +112 -0
  146. data/lib/fractor/workflow/dead_letter_queue.rb +334 -0
  147. data/lib/fractor/workflow/execution_hooks.rb +39 -0
  148. data/lib/fractor/workflow/execution_strategy.rb +225 -0
  149. data/lib/fractor/workflow/execution_trace.rb +134 -0
  150. data/lib/fractor/workflow/helpers.rb +191 -0
  151. data/lib/fractor/workflow/job.rb +290 -0
  152. data/lib/fractor/workflow/job_dependency_validator.rb +120 -0
  153. data/lib/fractor/workflow/logger.rb +110 -0
  154. data/lib/fractor/workflow/pre_execution_context.rb +193 -0
  155. data/lib/fractor/workflow/retry_config.rb +156 -0
  156. data/lib/fractor/workflow/retry_orchestrator.rb +184 -0
  157. data/lib/fractor/workflow/retry_strategy.rb +93 -0
  158. data/lib/fractor/workflow/structured_logger.rb +30 -0
  159. data/lib/fractor/workflow/type_compatibility_validator.rb +222 -0
  160. data/lib/fractor/workflow/visualizer.rb +211 -0
  161. data/lib/fractor/workflow/workflow_context.rb +132 -0
  162. data/lib/fractor/workflow/workflow_executor.rb +669 -0
  163. data/lib/fractor/workflow/workflow_result.rb +55 -0
  164. data/lib/fractor/workflow/workflow_validator.rb +295 -0
  165. data/lib/fractor/workflow.rb +333 -0
  166. data/lib/fractor/wrapped_ractor.rb +66 -101
  167. data/lib/fractor/wrapped_ractor3.rb +161 -0
  168. data/lib/fractor/wrapped_ractor4.rb +242 -0
  169. data/lib/fractor.rb +92 -4
  170. metadata +179 -6
  171. data/tests/sample.rb.bak +0 -309
  172. data/tests/sample_working.rb.bak +0 -209
@@ -0,0 +1,290 @@
1
+ ---
2
+ layout: default
3
+ title: Getting started
4
+ nav_order: 2
5
+ ---
6
+ = Getting started
7
+
8
+ == Choosing your mode
9
+
10
+ Fractor supports two operating modes, each optimized for different use cases:
11
+
12
+ [cols="1,2,2",options="header"]
13
+ |===
14
+ |Mode |Best for |Key characteristics
15
+
16
+ |Pipeline Mode
17
+ |Batch processing, one-time jobs
18
+ |Finite duration, predefined work, batch result collection
19
+
20
+ |Continuous Mode
21
+ |Long-running servers, streaming
22
+ |Indefinite duration, dynamic work arrival, callback-based results
23
+ |===
24
+
25
+ === When to use Pipeline Mode
26
+
27
+ Choose Pipeline Mode when:
28
+
29
+ * You have a complete dataset to process upfront
30
+ * Processing has a clear start and end
31
+ * You need all results aggregated after completion
32
+ * The task is one-time or scheduled periodically
33
+
34
+ Examples: File processing, data transformations, batch jobs, ETL pipelines
35
+
36
+ === When to use Continuous Mode
37
+
38
+ Choose Continuous Mode when:
39
+
40
+ * Work arrives over time from external sources
41
+ * Your application runs as a long-lived server
42
+ * You need to process items as they arrive
43
+ * Results should be handled immediately via callbacks
44
+
45
+ Examples: Chat servers, web servers, background job processors, event streams
46
+
47
+ == Quick start: Pipeline Mode
48
+
49
+ === Step 1: Define your Work class
50
+
51
+ [source,ruby]
52
+ ----
53
+ require 'fractor'
54
+
55
+ class MyWork < Fractor::Work
56
+ def initialize(value)
57
+ super({ value: value })
58
+ end
59
+
60
+ def value
61
+ input[:value]
62
+ end
63
+ end
64
+ ----
65
+
66
+ === Step 2: Define your Worker class
67
+
68
+ [source,ruby]
69
+ ----
70
+ class MyWorker < Fractor::Worker
71
+ def process(work)
72
+ result = work.value * 2
73
+
74
+ Fractor::WorkResult.new(result: result, work: work)
75
+ rescue => e
76
+ Fractor::WorkResult.new(error: e.message, work: work)
77
+ end
78
+ end
79
+ ----
80
+
81
+ === Step 3: Create and run the Supervisor
82
+
83
+ The Supervisor class orchestrates the entire framework, managing worker Ractors, distributing work, and collecting results.
84
+
85
+ [source,ruby]
86
+ ----
87
+ # Create supervisor
88
+ supervisor = Fractor::Supervisor.new(
89
+ worker_pools: [
90
+ { worker_class: MyWorker } # Auto-detects number of workers
91
+ ]
92
+ )
93
+
94
+ # Add work items
95
+ supervisor.add_work_items([
96
+ MyWork.new(1),
97
+ MyWork.new(2),
98
+ MyWork.new(3)
99
+ ])
100
+
101
+ # Run processing
102
+ supervisor.run
103
+
104
+ # Access results
105
+ puts "Results: #{supervisor.results.results.map(&:result)}"
106
+ # => Results: [2, 4, 6]
107
+ ----
108
+
109
+ ==== Instance-based configuration (for multi-gem usage)
110
+
111
+ If your application uses Fractor alongside other gems that also use Fractor, you can use instance-based configuration to avoid global state pollution:
112
+
113
+ [source,ruby]
114
+ ----
115
+ # Use instance-specific logger to avoid polluting global state
116
+ my_logger = Logger.new(STDOUT)
117
+ supervisor = Fractor::Supervisor.new(
118
+ worker_pools: [
119
+ { worker_class: MyWorker }
120
+ ],
121
+ logger: my_logger
122
+ )
123
+
124
+ # Enable tracing for this specific supervisor
125
+ supervisor = Fractor::Supervisor.new(
126
+ worker_pools: [
127
+ { worker_class: MyWorker }
128
+ ],
129
+ tracer_enabled: true,
130
+ tracer_stream: File.open("trace.log", "w")
131
+ )
132
+ ----
133
+
134
+ ==== Test isolation
135
+
136
+ When testing Fractor code, use `Fractor.reset!` before each test to ensure clean state:
137
+
138
+ [source,ruby]
139
+ ----
140
+ RSpec.configure do |config|
141
+ config.before(:each) do
142
+ Fractor.reset! # Resets global logger and tracer state
143
+ end
144
+ end
145
+ ----
146
+
147
+ That's it! You now have parallel processing working.
148
+
149
+ == Quick start: Continuous Mode
150
+
151
+ === Step 1: Define Work and Worker classes
152
+
153
+ [source,ruby]
154
+ ----
155
+ require 'fractor'
156
+
157
+ class MessageWork < Fractor::Work
158
+ def initialize(client_id, message)
159
+ super({ client_id: client_id, message: message })
160
+ end
161
+
162
+ def client_id
163
+ input[:client_id]
164
+ end
165
+
166
+ def message
167
+ input[:message]
168
+ end
169
+ end
170
+
171
+ class MessageWorker < Fractor::Worker
172
+ def process(work)
173
+ processed = "Echo: #{work.message}"
174
+
175
+ Fractor::WorkResult.new(
176
+ result: { client_id: work.client_id, response: processed },
177
+ work: work
178
+ )
179
+ rescue => e
180
+ Fractor::WorkResult.new(error: e.message, work: work)
181
+ end
182
+ end
183
+ ----
184
+
185
+ === Step 2: Create WorkQueue and ContinuousServer
186
+
187
+ [source,ruby]
188
+ ----
189
+ # Create thread-safe work queue
190
+ work_queue = Fractor::WorkQueue.new
191
+
192
+ # Create continuous server with callbacks
193
+ server = Fractor::ContinuousServer.new(
194
+ worker_pools: [
195
+ { worker_class: MessageWorker, num_workers: 4 }
196
+ ],
197
+ work_queue: work_queue
198
+ )
199
+
200
+ # Define result handlers
201
+ server.on_result do |result|
202
+ puts "Response: #{result.result[:response]}"
203
+ end
204
+
205
+ server.on_error do |error_result|
206
+ puts "Error: #{error_result.error}"
207
+ end
208
+ ----
209
+
210
+ === Step 3: Run server and add work
211
+
212
+ [source,ruby]
213
+ ----
214
+ # Start server in background
215
+ server_thread = Thread.new { server.run }
216
+
217
+ # Add work dynamically
218
+ work_queue << MessageWork.new(1, "Hello")
219
+ work_queue << MessageWork.new(2, "World")
220
+
221
+ # Server runs indefinitely
222
+ # Use Ctrl+C for graceful shutdown
223
+ ----
224
+
225
+ == Common patterns
226
+
227
+ === Auto-detection vs explicit worker count
228
+
229
+ [source,ruby]
230
+ ----
231
+ # Auto-detect (recommended)
232
+ supervisor = Fractor::Supervisor.new(
233
+ worker_pools: [
234
+ { worker_class: MyWorker } # Uses Etc.nprocessors
235
+ ]
236
+ )
237
+
238
+ # Explicit count
239
+ supervisor = Fractor::Supervisor.new(
240
+ worker_pools: [
241
+ { worker_class: MyWorker, num_workers: 4 }
242
+ ]
243
+ )
244
+ ----
245
+
246
+ === Multiple worker pools
247
+
248
+ [source,ruby]
249
+ ----
250
+ supervisor = Fractor::Supervisor.new(
251
+ worker_pools: [
252
+ { worker_class: FastWorker, num_workers: 8 },
253
+ { worker_class: SlowWorker, num_workers: 2 }
254
+ ]
255
+ )
256
+ ----
257
+
258
+ === Error handling
259
+
260
+ [source,ruby]
261
+ ----
262
+ class MyWorker < Fractor::Worker
263
+ def process(work)
264
+ # Validate input
265
+ if work.value < 0
266
+ return Fractor::WorkResult.new(
267
+ error: "Negative values not allowed",
268
+ work: work
269
+ )
270
+ end
271
+
272
+ # Process
273
+ result = work.value * 2
274
+ Fractor::WorkResult.new(result: result, work: work)
275
+ rescue => e
276
+ # Catch unexpected errors
277
+ Fractor::WorkResult.new(error: e.message, work: work)
278
+ end
279
+ end
280
+ ----
281
+
282
+ == Next steps
283
+
284
+ * Learn about link:core-concepts/[Core Concepts] - Component details and interactions
285
+ * Read link:../architecture/[Architecture Guide] - System design and implementation
286
+ * Read link:design-principles/[Design Principles] - Philosophy and rationale
287
+ * Explore link:../guides/pipeline-mode/[Pipeline Mode] in detail
288
+ * Explore link:../guides/continuous-mode/[Continuous Mode] in detail
289
+ * Try link:../features/workflows/[Workflows] for complex processing
290
+ * See link:../reference/examples/[Examples] for real-world patterns
@@ -0,0 +1,143 @@
1
+ ---
2
+ layout: default
3
+ title: Installation
4
+ nav_order: 3
5
+ ---
6
+ = Installation
7
+
8
+ == System requirements
9
+
10
+ Fractor requires Ruby 3.0 or later. It relies on Ractor, which is available in
11
+ Ruby 3.0+.
12
+
13
+ Fractor supports:
14
+
15
+ * **Ruby 3.0 - 3.x**: Full support using `Ractor.yield` for message passing
16
+ * **Ruby 4.0+**: Full support using `Ractor::Port` for improved communication patterns
17
+
18
+ The user-facing API is identical across both versions. Fractor automatically
19
+ detects your Ruby version and uses the appropriate internal implementation. See
20
+ link:architecture.adoc#ruby-version-compatibility[Architecture: Ruby Version Compatibility]
21
+ for details on the internal differences.
22
+
23
+ To check your Ruby version:
24
+
25
+ [source,sh]
26
+ ----
27
+ ruby --version
28
+ ----
29
+
30
+ If you need to install or upgrade Ruby, see https://www.ruby-lang.org/en/downloads/.
31
+
32
+ == Installation methods
33
+
34
+ === Using RubyGems
35
+
36
+ Install Fractor directly from RubyGems:
37
+
38
+ [source,sh]
39
+ ----
40
+ gem install fractor
41
+ ----
42
+
43
+ === Using Bundler
44
+
45
+ Add this line to your application's `Gemfile`:
46
+
47
+ [source,ruby]
48
+ ----
49
+ gem 'fractor'
50
+ ----
51
+
52
+ Then execute:
53
+
54
+ [source,sh]
55
+ ----
56
+ bundle install
57
+ ----
58
+
59
+ === From source
60
+
61
+ To install Fractor from source:
62
+
63
+ [source,sh]
64
+ ----
65
+ git clone https://github.com/metanorma/fractor.git
66
+ cd fractor
67
+ bundle install
68
+ bundle exec rake install
69
+ ----
70
+
71
+ == Verifying installation
72
+
73
+ After installation, verify that Fractor is available:
74
+
75
+ [source,ruby]
76
+ ----
77
+ require 'fractor'
78
+ puts Fractor::VERSION
79
+ ----
80
+
81
+ == Troubleshooting
82
+
83
+ === Ruby version issues
84
+
85
+ If you encounter errors about Ractor not being available, ensure you're using
86
+ Ruby 3.0 or later:
87
+
88
+ [source,sh]
89
+ ----
90
+ ruby --version
91
+ # Should show 3.0.0 or higher
92
+ ----
93
+
94
+ === Installation errors
95
+
96
+ If you encounter permission errors during installation:
97
+
98
+ [source,sh]
99
+ ----
100
+ # Use --user-install flag
101
+ gem install fractor --user-install
102
+
103
+ # Or use a Ruby version manager like rbenv or rvm
104
+ ----
105
+
106
+ === Platform-specific issues
107
+
108
+ ==== macOS
109
+
110
+ On macOS, you may need to install Ruby via Homebrew if the system Ruby is too old:
111
+
112
+ [source,sh]
113
+ ----
114
+ brew install ruby
115
+ ----
116
+
117
+ ==== Windows
118
+
119
+ On Windows, use RubyInstaller (https://rubyinstaller.org/) to install Ruby 3.0+.
120
+
121
+ ==== Linux
122
+
123
+ Most Linux distributions provide Ruby in their package managers:
124
+
125
+ [source,sh]
126
+ ----
127
+ # Ubuntu/Debian
128
+ sudo apt-get install ruby-full
129
+
130
+ # Fedora/CentOS
131
+ sudo dnf install ruby
132
+
133
+ # Arch Linux
134
+ sudo pacman -S ruby
135
+ ----
136
+
137
+ === Getting help
138
+
139
+ If you encounter issues not covered here:
140
+
141
+ * Check the https://github.com/metanorma/fractor/issues[GitHub Issues]
142
+ * Open a new issue with details about your environment and error messages
143
+ * Join the discussion in the https://github.com/metanorma/fractor/discussions[GitHub Discussions]