taski 0.3.1 โ†’ 0.4.1

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +101 -271
  3. data/Steepfile +19 -0
  4. data/docs/advanced-features.md +625 -0
  5. data/docs/api-guide.md +509 -0
  6. data/docs/error-handling.md +684 -0
  7. data/examples/README.md +98 -42
  8. data/examples/context_demo.rb +118 -0
  9. data/examples/data_pipeline_demo.rb +231 -0
  10. data/examples/parallel_progress_demo.rb +72 -0
  11. data/examples/quick_start.rb +4 -4
  12. data/examples/reexecution_demo.rb +127 -0
  13. data/examples/{section_configuration.rb โ†’ section_demo.rb} +49 -60
  14. data/lib/taski/context.rb +50 -0
  15. data/lib/taski/execution/coordinator.rb +63 -0
  16. data/lib/taski/execution/parallel_progress_display.rb +201 -0
  17. data/lib/taski/execution/registry.rb +72 -0
  18. data/lib/taski/execution/task_wrapper.rb +255 -0
  19. data/lib/taski/section.rb +26 -254
  20. data/lib/taski/static_analysis/analyzer.rb +46 -0
  21. data/lib/taski/static_analysis/dependency_graph.rb +90 -0
  22. data/lib/taski/static_analysis/visitor.rb +130 -0
  23. data/lib/taski/task.rb +199 -0
  24. data/lib/taski/version.rb +1 -1
  25. data/lib/taski.rb +68 -39
  26. data/rbs_collection.lock.yaml +116 -0
  27. data/rbs_collection.yaml +19 -0
  28. data/sig/taski.rbs +269 -62
  29. metadata +36 -32
  30. data/examples/advanced_patterns.rb +0 -119
  31. data/examples/progress_demo.rb +0 -166
  32. data/examples/tree_demo.rb +0 -205
  33. data/lib/taski/dependency_analyzer.rb +0 -232
  34. data/lib/taski/exceptions.rb +0 -17
  35. data/lib/taski/logger.rb +0 -158
  36. data/lib/taski/logging/formatter_factory.rb +0 -34
  37. data/lib/taski/logging/formatter_interface.rb +0 -19
  38. data/lib/taski/logging/json_formatter.rb +0 -26
  39. data/lib/taski/logging/simple_formatter.rb +0 -16
  40. data/lib/taski/logging/structured_formatter.rb +0 -44
  41. data/lib/taski/progress/display_colors.rb +0 -17
  42. data/lib/taski/progress/display_manager.rb +0 -117
  43. data/lib/taski/progress/output_capture.rb +0 -105
  44. data/lib/taski/progress/spinner_animation.rb +0 -49
  45. data/lib/taski/progress/task_formatter.rb +0 -25
  46. data/lib/taski/progress/task_status.rb +0 -38
  47. data/lib/taski/progress/terminal_controller.rb +0 -35
  48. data/lib/taski/progress_display.rb +0 -57
  49. data/lib/taski/reference.rb +0 -40
  50. data/lib/taski/task/base.rb +0 -91
  51. data/lib/taski/task/define_api.rb +0 -156
  52. data/lib/taski/task/dependency_resolver.rb +0 -73
  53. data/lib/taski/task/exports_api.rb +0 -29
  54. data/lib/taski/task/instance_management.rb +0 -201
  55. data/lib/taski/tree_colors.rb +0 -91
  56. data/lib/taski/utils/dependency_resolver_helper.rb +0 -85
  57. data/lib/taski/utils/tree_display_helper.rb +0 -68
  58. data/lib/taski/utils.rb +0 -107
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80c881415617aa09fb7ae24441c38c677af816c20f356cc06ea82bba50639820
4
- data.tar.gz: cc0c6e07e3f53c312844dc237ebebe344a0201bb772f1c6c20ee8c6476b9db30
3
+ metadata.gz: 46cb1222487f928c10241827374311ed65541773bd1cd6d40d8fd2e6ea8af64e
4
+ data.tar.gz: c16aad068cef227130a8051fd71c8847845212dc3f990ae706b2d1b10104a9d6
5
5
  SHA512:
6
- metadata.gz: 5e40462d8b2b359c17f28f16ec03524ee649324eb4e21eaf365fd147ccedad6bb3d683ece9021975683b1b121c93ad1da2bdaa0fed82ec37797090fadc79129c
7
- data.tar.gz: 441c9553cded529843af032347f4590f9b6a289bd00f3a8ccbbe996b6be43d2154bc6e2fc4f84a94c1f18c35eb4cfcf5408ff07f6b8878b4f50e41c32a407756
6
+ metadata.gz: a37a616b526c7c67811286e2c50dd5319c9ce98779f4edf495f6337c214c9f0915b46694f4b8edfd5a8a6b7f66ce9fb9d946ffd36cf0be2f2a4991452875a765
7
+ data.tar.gz: ff577241138a18dcf8018c4c0c68ea8137301e33bfa6b31d58e564862eeb8f520a9120c755aa0c6acf61b5c1f18b71bb728dd785627ee278c3c6eee08d31dc30
data/README.md CHANGED
@@ -4,368 +4,198 @@
4
4
  [![Codecov](https://codecov.io/gh/ahogappa/taski/branch/master/graph/badge.svg)](https://codecov.io/gh/ahogappa/taski)
5
5
  [![Gem Version](https://badge.fury.io/rb/taski.svg)](https://badge.fury.io/rb/taski)
6
6
 
7
- > **๐Ÿšง Development Status:** Taski is currently under active development. Not yet recommended for production use.
8
-
9
- **Taski** is a Ruby framework for building task dependency graphs with automatic resolution and execution. It provides three APIs: static dependencies through **Exports**, dynamic dependencies through **Define**, and abstraction layers through **Section**.
7
+ **Taski** is a Ruby framework for building task dependency graphs with automatic resolution and parallel execution.
10
8
 
11
9
  > **Name Origin**: "Taski" comes from the Japanese word "่ฅท" (tasuki), a sash used in relay races. Just like how runners pass the sash to the next teammate, tasks in Taski pass dependencies to one another in a continuous chain.
12
10
 
13
- ## ๐Ÿš€ Quick Start
11
+ ## Features
12
+
13
+ - **Automatic Dependency Resolution**: Dependencies detected via static analysis
14
+ - **Parallel Execution**: Independent tasks run concurrently for maximum performance
15
+ - **Two Simple APIs**: Exports (value sharing) and Section (runtime selection)
16
+ - **Real-time Progress**: Visual feedback with parallel task progress display
17
+ - **Thread-Safe**: Built on Monitor-based synchronization for reliable concurrent execution
18
+
19
+ ## Quick Start
14
20
 
15
21
  ```ruby
16
22
  require 'taski'
17
23
 
18
- # Static dependency using Exports API
19
24
  class DatabaseSetup < Taski::Task
20
25
  exports :connection_string
21
26
 
22
- def build
27
+ def run
23
28
  @connection_string = "postgresql://localhost/myapp"
24
- puts "Database configured"
29
+ end
30
+ end
31
+
32
+ class CacheSetup < Taski::Task
33
+ exports :cache_url
34
+
35
+ def run
36
+ @cache_url = "redis://localhost:6379"
25
37
  end
26
38
  end
27
39
 
28
40
  class APIServer < Taski::Task
29
- def build
30
- puts "Starting API with #{DatabaseSetup.connection_string}"
41
+ def run
42
+ # Dependencies execute automatically and in parallel
43
+ puts "DB: #{DatabaseSetup.connection_string}"
44
+ puts "Cache: #{CacheSetup.cache_url}"
31
45
  end
32
46
  end
33
47
 
34
- APIServer.build
35
- # => Database configured
36
- # => Starting API with postgresql://localhost/myapp
48
+ APIServer.run
37
49
  ```
38
50
 
39
- ## ๐Ÿ“š API Guide
40
-
41
- ### Exports API - Static Dependencies
42
-
43
- For simple, predictable dependencies:
51
+ ## Installation
44
52
 
45
53
  ```ruby
46
- class ConfigLoader < Taski::Task
47
- exports :app_name, :version
48
-
49
- def build
50
- @app_name = "MyApp"
51
- @version = "1.0.0"
52
- puts "Config loaded: #{@app_name} v#{@version}"
53
- end
54
- end
55
-
56
- class Deployment < Taski::Task
57
- def build
58
- @deploy_url = "https://#{ConfigLoader.app_name}.example.com"
59
- puts "Deploying to #{@deploy_url}"
60
- end
61
- end
62
-
63
- Deployment.build
64
- # => Config loaded: MyApp v1.0.0
65
- # => Deploying to https://MyApp.example.com
54
+ gem 'taski'
66
55
  ```
67
56
 
68
- ### Define API - Dynamic Dependencies
57
+ ## Core Concepts
58
+
59
+ ### Exports - Value Sharing Between Tasks
69
60
 
70
- For dependencies that change based on runtime conditions:
61
+ Share computed values between tasks:
71
62
 
72
63
  ```ruby
73
- class EnvironmentConfig < Taski::Task
74
- define :database_service, -> {
75
- case ENV['RAILS_ENV']
76
- when 'production'
77
- "production-db.example.com"
78
- else
79
- "localhost:5432"
80
- end
81
- }
64
+ class Config < Taski::Task
65
+ exports :app_name, :port
82
66
 
83
- def build
84
- puts "Using database: #{database_service}"
85
- puts "Environment: #{ENV['RAILS_ENV'] || 'development'}"
67
+ def run
68
+ @app_name = "MyApp"
69
+ @port = 3000
86
70
  end
87
71
  end
88
72
 
89
- EnvironmentConfig.build
90
- # => Using database: localhost:5432
91
- # => Environment: development
92
-
93
- ENV['RAILS_ENV'] = 'production'
94
- EnvironmentConfig.reset!
95
- EnvironmentConfig.build
96
- # => Using database: production-db.example.com
97
- # => Environment: production
73
+ class Server < Taski::Task
74
+ def run
75
+ puts "Starting #{Config.app_name} on port #{Config.port}"
76
+ end
77
+ end
98
78
  ```
99
79
 
100
- ### Section API - Abstraction Layers
80
+ ### Section - Runtime Implementation Selection
101
81
 
102
- For environment-specific implementations with clean interfaces:
82
+ Switch implementations based on environment:
103
83
 
104
84
  ```ruby
105
85
  class DatabaseSection < Taski::Section
106
- interface :host, :port
107
-
108
- def impl
109
- ENV['RAILS_ENV'] == 'production' ? Production : Development
110
- end
86
+ interfaces :host, :port
111
87
 
112
88
  class Production < Taski::Task
113
- def build
114
- @host = "prod-db.example.com"
89
+ def run
90
+ @host = "prod.example.com"
115
91
  @port = 5432
116
92
  end
117
93
  end
118
94
 
119
95
  class Development < Taski::Task
120
- def build
96
+ def run
121
97
  @host = "localhost"
122
98
  @port = 5432
123
99
  end
124
100
  end
101
+
102
+ def impl
103
+ ENV['RAILS_ENV'] == 'production' ? Production : Development
104
+ end
125
105
  end
126
106
 
127
- # Usage is simple - Section works like any Task
128
107
  class App < Taski::Task
129
- def build
130
- puts "DB: #{DatabaseSection.host}:#{DatabaseSection.port}"
108
+ def run
109
+ puts "Connecting to #{DatabaseSection.host}:#{DatabaseSection.port}"
131
110
  end
132
111
  end
133
-
134
- App.build # => DB: localhost:5432
135
112
  ```
136
113
 
137
- ### When to Use Each API
138
-
139
- - **Define API**: Best for dynamic runtime dependencies. Cannot contain side effects in definition blocks. Dependencies are analyzed at class definition time, not runtime.
140
- - **Exports API**: Ideal for static dependencies. Supports side effects in build methods.
141
- - **Section API**: Perfect for abstraction layers where you need different implementations based on runtime conditions while maintaining static analysis capabilities.
142
-
143
- | Use Case | API | Example |
144
- |----------|-----|---------|
145
- | Configuration values | Exports | File paths, settings |
146
- | Environment-specific logic | Define/Section | Different services per env |
147
- | Side effects | Exports | Database connections, I/O |
148
- | Conditional processing | Define | Algorithm selection |
149
- | Implementation abstraction | Section | Database/API adapters |
150
- | Multi-environment configs | Section | Dev/Test/Prod settings |
114
+ > **Note**: Nested implementation classes automatically inherit Section's `interfaces` as `exports`.
151
115
 
152
- **Note**: Define API analyzes dependencies when the class is defined. Conditional dependencies like `ENV['USE_NEW'] ? TaskA : TaskB` will only include the task selected at class definition time, not runtime. Use Section API when you need true runtime selection.
116
+ ## Advanced Usage
153
117
 
154
- ## โœจ Key Features
118
+ ### Context - Runtime Information and Options
155
119
 
156
- - **Automatic Dependency Resolution**: Dependencies detected through static analysis
157
- - **Thread-Safe**: Safe for concurrent access
158
- - **Circular Dependency Detection**: Clear error messages with detailed paths
159
- - **Granular Execution**: Build individual tasks or complete graphs
160
- - **Memory Management**: Built-in reset mechanisms
161
- - **Progress Display**: Visual feedback with spinners and output capture
162
- - **Dependency Tree Visualization**: Visual representation of task relationships
163
-
164
- ### Dependency Tree Visualization
165
-
166
- Visualize task dependencies with the `tree` method:
120
+ Pass custom options and access execution context from any task:
167
121
 
168
122
  ```ruby
169
- class Database < Taski::Task
170
- exports :connection
171
- def build; @connection = "db-conn"; end
172
- end
123
+ class DeployTask < Taski::Task
124
+ def run
125
+ # User-defined options
126
+ env = Taski.context[:env]
127
+ debug = Taski.context.fetch(:debug, false)
173
128
 
174
- class Cache < Taski::Task
175
- exports :redis_url
176
- def build; @redis_url = "redis://localhost"; end
177
- end
178
-
179
- class Config < Taski::Task
180
- exports :settings
181
- def build
182
- @settings = {
183
- database: Database.connection,
184
- cache: Cache.redis_url
185
- }
129
+ # Runtime information
130
+ puts "Working directory: #{Taski.context.working_directory}"
131
+ puts "Started at: #{Taski.context.started_at}"
132
+ puts "Root task: #{Taski.context.root_task}"
133
+ puts "Deploying to: #{env}"
186
134
  end
187
135
  end
188
136
 
189
- class WebServer < Taski::Task
190
- def build
191
- puts "Starting with #{Config.settings}"
192
- end
193
- end
194
-
195
- puts WebServer.tree
196
- # => WebServer
197
- # => โ””โ”€โ”€ Config
198
- # => โ”œโ”€โ”€ Database
199
- # => โ””โ”€โ”€ Cache
200
-
201
- # Sections also appear in dependency trees
202
- puts AppServer.tree
203
- # => AppServer
204
- # => โ””โ”€โ”€ DatabaseSection
137
+ # Pass options when running
138
+ DeployTask.run(context: { env: "production", debug: true })
205
139
  ```
206
140
 
207
- ### Progress Display
141
+ Context API:
142
+ - `Taski.context[:key]` - Get option value (nil if not set)
143
+ - `Taski.context.fetch(:key, default)` - Get with default value
144
+ - `Taski.context.key?(:key)` - Check if option exists
145
+ - `Taski.context.working_directory` - Execution directory
146
+ - `Taski.context.started_at` - Execution start time
147
+ - `Taski.context.root_task` - First task class called
208
148
 
209
- Taski provides visual feedback during task execution with animated spinners and real-time output capture:
149
+ ### Re-execution
210
150
 
211
151
  ```ruby
212
- class LongRunningTask < Taski::Task
213
- def build
214
- puts "Starting process..."
215
- sleep(1.0)
216
- puts "Processing data..."
217
- puts "Almost done..."
218
- sleep(0.5)
219
- puts "Completed!"
220
- end
221
- end
222
-
223
- LongRunningTask.build
224
- # During execution shows:
225
- # โ ง LongRunningTask
226
- # Starting process...
227
- # Processing data...
228
- # Almost done...
229
- # Completed!
230
- #
231
- # After completion shows:
232
- # โœ… LongRunningTask (1500ms)
233
- ```
152
+ # Cached execution (default)
153
+ RandomTask.value # => 42
154
+ RandomTask.value # => 42 (cached)
234
155
 
235
- **Progress Display Features:**
236
- - **Spinner Animation**: Dots-style spinner during task execution
237
- - **Output Capture**: Real-time display of task output (last 5 lines)
238
- - **Status Indicators**: โœ… for success, โŒ for failure
239
- - **Execution Time**: Shows task duration after completion
240
- - **TTY Detection**: Clean output when redirected to files
156
+ # Fresh execution
157
+ RandomTask.new.run # => 123 (new instance)
241
158
 
242
- ### Granular Task Execution
243
-
244
- Execute any task individually - Taski builds only required dependencies:
245
-
246
- ```ruby
247
- # Build specific components
248
- ConfigLoader.build # Builds only ConfigLoader
249
- # => Config loaded: MyApp v1.0.0
250
-
251
- EnvironmentConfig.build # Builds EnvironmentConfig and its dependencies
252
- # => Using database: localhost:5432
253
- # => Environment: development
254
-
255
- # Access values (triggers build if needed)
256
- puts ConfigLoader.version # Builds ConfigLoader if not built
257
- # => 1.0.0
159
+ # Reset all caches
160
+ RandomTask.reset!
258
161
  ```
259
162
 
260
- ### Lifecycle Management
261
-
262
- Tasks can define both build and clean methods. Clean operations run in reverse dependency order:
263
-
264
- ```ruby
265
- class DatabaseSetup < Taski::Task
266
- exports :connection
267
-
268
- def build
269
- @connection = "db-connection"
270
- puts "Database connected"
271
- end
272
-
273
- def clean
274
- puts "Database disconnected"
275
- end
276
- end
277
-
278
- class WebServer < Taski::Task
279
- def build
280
- puts "Web server started with #{DatabaseSetup.connection}"
281
- end
282
-
283
- def clean
284
- puts "Web server stopped"
285
- end
286
- end
163
+ ### Progress Display
287
164
 
288
- WebServer.build
289
- # => Database connected
290
- # => Web server started with db-connection
165
+ Enable real-time progress visualization:
291
166
 
292
- WebServer.clean
293
- # => Web server stopped
294
- # => Database disconnected
167
+ ```bash
168
+ TASKI_FORCE_PROGRESS=1 ruby your_script.rb
295
169
  ```
296
170
 
297
- ### Clean Method Idempotency
298
-
299
- **Important**: The `clean` method must be idempotent - safe to call multiple times without errors.
300
-
301
- ```ruby
302
- class FileTask < Taski::Task
303
- exports :output_file
304
-
305
- def build
306
- @output_file = '/tmp/data.csv'
307
- File.write(@output_file, process_data)
308
- end
309
-
310
- def clean
311
- # โŒ Bad: Raises error if file doesn't exist
312
- # File.delete(@output_file)
313
-
314
- # โœ… Good: Check before delete
315
- File.delete(@output_file) if File.exist?(@output_file)
316
- end
317
- end
318
171
  ```
319
-
320
- ### Error Handling
321
-
322
- ```ruby
323
- begin
324
- TaskWithCircularDep.build
325
- rescue Taski::CircularDependencyError => e
326
- puts "Circular dependency: #{e.message}"
327
- end
328
- # => Circular dependency: Circular dependency detected!
329
- # => Cycle: TaskA โ†’ TaskB โ†’ TaskA
330
- # =>
331
- # => The dependency chain is:
332
- # => 1. TaskA is trying to build โ†’ TaskB
333
- # => 2. TaskB is trying to build โ†’ TaskA
172
+ โ ‹ DatabaseSetup (running)
173
+ โ ™ CacheSetup (running)
174
+ โœ… DatabaseSetup (123.4ms)
175
+ โœ… CacheSetup (98.2ms)
334
176
  ```
335
177
 
336
- ## ๐Ÿ“ฆ Installation
178
+ ### Tree Visualization
337
179
 
338
180
  ```ruby
339
- gem 'taski'
340
- ```
341
-
342
- ```bash
343
- bundle install
181
+ puts WebServer.tree
182
+ # => WebServer
183
+ # => โ””โ”€โ”€ Config
184
+ # => โ”œโ”€โ”€ Database
185
+ # => โ””โ”€โ”€ Cache
344
186
  ```
345
187
 
346
- ## ๐Ÿงช Testing
188
+ ## Development
347
189
 
348
190
  ```bash
349
- bundle exec rake test
191
+ rake test # Run all tests
192
+ rake standard # Check code style
350
193
  ```
351
194
 
352
- ## ๐Ÿ›๏ธ Architecture
353
-
354
- - **Task Base**: Core framework
355
- - **Exports API**: Static dependency resolution
356
- - **Define API**: Dynamic dependency resolution
357
- - **Section API**: Abstraction layer with runtime implementation selection
358
- - **Instance Management**: Thread-safe lifecycle
359
- - **Dependency Resolver**: Topological sorting with Section support
360
-
361
- ## Contributing
195
+ ## Support
362
196
 
363
197
  Bug reports and pull requests welcome at https://github.com/ahogappa/taski.
364
198
 
365
199
  ## License
366
200
 
367
201
  MIT License
368
-
369
- ---
370
-
371
- **Taski** - Build dependency graphs with elegant Ruby code. ๐Ÿš€
data/Steepfile ADDED
@@ -0,0 +1,19 @@
1
+ # Steepfile for Taski gem
2
+
3
+ target :lib do
4
+ signature "sig"
5
+
6
+ check "lib"
7
+
8
+ # Ignore test and temporary files
9
+ ignore "test"
10
+ ignore "tmp"
11
+ ignore "examples"
12
+
13
+ # Standard library dependencies
14
+ library "monitor"
15
+ library "tsort"
16
+
17
+ # Use gems from rbs_collection
18
+ collection_config "rbs_collection.yaml"
19
+ end