taski 0.4.0 → 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.
- checksums.yaml +4 -4
- data/README.md +49 -110
- data/examples/README.md +8 -5
- data/examples/context_demo.rb +22 -16
- data/lib/taski/context.rb +36 -38
- data/lib/taski/static_analysis/analyzer.rb +17 -5
- data/lib/taski/static_analysis/visitor.rb +22 -6
- data/lib/taski/task.rb +39 -13
- data/lib/taski/version.rb +1 -1
- data/lib/taski.rb +23 -0
- metadata +1 -62
- data/.gem_rbs_collection/ast/2.4/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/ast/2.4/ast.rbs +0 -73
- data/.gem_rbs_collection/minitest/5.25/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/minitest/5.25/minitest/abstract_reporter.rbs +0 -52
- data/.gem_rbs_collection/minitest/5.25/minitest/assertion.rbs +0 -17
- data/.gem_rbs_collection/minitest/5.25/minitest/assertions.rbs +0 -590
- data/.gem_rbs_collection/minitest/5.25/minitest/backtrace_filter.rbs +0 -23
- data/.gem_rbs_collection/minitest/5.25/minitest/bench_spec.rbs +0 -102
- data/.gem_rbs_collection/minitest/5.25/minitest/benchmark.rbs +0 -259
- data/.gem_rbs_collection/minitest/5.25/minitest/composite_reporter.rbs +0 -25
- data/.gem_rbs_collection/minitest/5.25/minitest/compress.rbs +0 -13
- data/.gem_rbs_collection/minitest/5.25/minitest/error_on_warning.rbs +0 -3
- data/.gem_rbs_collection/minitest/5.25/minitest/expectation.rbs +0 -2
- data/.gem_rbs_collection/minitest/5.25/minitest/expectations.rbs +0 -21
- data/.gem_rbs_collection/minitest/5.25/minitest/guard.rbs +0 -64
- data/.gem_rbs_collection/minitest/5.25/minitest/mock.rbs +0 -64
- data/.gem_rbs_collection/minitest/5.25/minitest/parallel/executor.rbs +0 -46
- data/.gem_rbs_collection/minitest/5.25/minitest/parallel/test/class_methods.rbs +0 -5
- data/.gem_rbs_collection/minitest/5.25/minitest/parallel/test.rbs +0 -3
- data/.gem_rbs_collection/minitest/5.25/minitest/parallel.rbs +0 -2
- data/.gem_rbs_collection/minitest/5.25/minitest/pride_io.rbs +0 -62
- data/.gem_rbs_collection/minitest/5.25/minitest/pride_lol.rbs +0 -19
- data/.gem_rbs_collection/minitest/5.25/minitest/progress_reporter.rbs +0 -11
- data/.gem_rbs_collection/minitest/5.25/minitest/reportable.rbs +0 -53
- data/.gem_rbs_collection/minitest/5.25/minitest/reporter.rbs +0 -5
- data/.gem_rbs_collection/minitest/5.25/minitest/result.rbs +0 -28
- data/.gem_rbs_collection/minitest/5.25/minitest/runnable.rbs +0 -163
- data/.gem_rbs_collection/minitest/5.25/minitest/skip.rbs +0 -6
- data/.gem_rbs_collection/minitest/5.25/minitest/spec/dsl/instance_methods.rbs +0 -48
- data/.gem_rbs_collection/minitest/5.25/minitest/spec/dsl.rbs +0 -129
- data/.gem_rbs_collection/minitest/5.25/minitest/spec.rbs +0 -11
- data/.gem_rbs_collection/minitest/5.25/minitest/statistics_reporter.rbs +0 -81
- data/.gem_rbs_collection/minitest/5.25/minitest/summary_reporter.rbs +0 -18
- data/.gem_rbs_collection/minitest/5.25/minitest/test/lifecycle_hooks.rbs +0 -92
- data/.gem_rbs_collection/minitest/5.25/minitest/test.rbs +0 -69
- data/.gem_rbs_collection/minitest/5.25/minitest/unexpected_error.rbs +0 -12
- data/.gem_rbs_collection/minitest/5.25/minitest/unexpected_warning.rbs +0 -6
- data/.gem_rbs_collection/minitest/5.25/minitest/unit/test_case.rbs +0 -3
- data/.gem_rbs_collection/minitest/5.25/minitest/unit.rbs +0 -4
- data/.gem_rbs_collection/minitest/5.25/minitest.rbs +0 -115
- data/.gem_rbs_collection/parallel/1.20/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/parallel/1.20/parallel.rbs +0 -86
- data/.gem_rbs_collection/parser/3.2/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/parser/3.2/manifest.yaml +0 -7
- data/.gem_rbs_collection/parser/3.2/parser.rbs +0 -193
- data/.gem_rbs_collection/parser/3.2/polyfill.rbs +0 -4
- data/.gem_rbs_collection/rainbow/3.0/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rainbow/3.0/global.rbs +0 -7
- data/.gem_rbs_collection/rainbow/3.0/presenter.rbs +0 -209
- data/.gem_rbs_collection/rainbow/3.0/rainbow.rbs +0 -5
- data/.gem_rbs_collection/rake/13.0/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rake/13.0/manifest.yaml +0 -2
- data/.gem_rbs_collection/rake/13.0/rake.rbs +0 -39
- data/.gem_rbs_collection/regexp_parser/2.8/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/regexp_parser/2.8/regexp_parser.rbs +0 -17
- data/.gem_rbs_collection/rubocop/1.57/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubocop/1.57/rubocop.rbs +0 -129
- data/.gem_rbs_collection/rubocop-ast/1.30/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/rubocop-ast/1.30/rubocop-ast.rbs +0 -771
- data/.gem_rbs_collection/simplecov/0.22/.rbs_meta.yaml +0 -9
- data/.gem_rbs_collection/simplecov/0.22/simplecov.rbs +0 -54
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 46cb1222487f928c10241827374311ed65541773bd1cd6d40d8fd2e6ea8af64e
|
|
4
|
+
data.tar.gz: c16aad068cef227130a8051fd71c8847845212dc3f990ae706b2d1b10104a9d6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a37a616b526c7c67811286e2c50dd5319c9ce98779f4edf495f6337c214c9f0915b46694f4b8edfd5a8a6b7f66ce9fb9d946ffd36cf0be2f2a4991452875a765
|
|
7
|
+
data.tar.gz: ff577241138a18dcf8018c4c0c68ea8137301e33bfa6b31d58e564862eeb8f520a9120c755aa0c6acf61b5c1f18b71bb728dd785627ee278c3c6eee08d31dc30
|
data/README.md
CHANGED
|
@@ -4,15 +4,11 @@
|
|
|
4
4
|
[](https://codecov.io/gh/ahogappa/taski)
|
|
5
5
|
[](https://badge.fury.io/rb/taski)
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
**Taski** is a Ruby framework for building task dependency graphs with automatic resolution and **parallel execution**.
|
|
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
|
-
##
|
|
14
|
-
|
|
15
|
-
Build complex workflows by defining tasks and their dependencies - Taski automatically resolves execution order and executes them in parallel.
|
|
11
|
+
## Features
|
|
16
12
|
|
|
17
13
|
- **Automatic Dependency Resolution**: Dependencies detected via static analysis
|
|
18
14
|
- **Parallel Execution**: Independent tasks run concurrently for maximum performance
|
|
@@ -20,18 +16,16 @@ Build complex workflows by defining tasks and their dependencies - Taski automat
|
|
|
20
16
|
- **Real-time Progress**: Visual feedback with parallel task progress display
|
|
21
17
|
- **Thread-Safe**: Built on Monitor-based synchronization for reliable concurrent execution
|
|
22
18
|
|
|
23
|
-
##
|
|
19
|
+
## Quick Start
|
|
24
20
|
|
|
25
21
|
```ruby
|
|
26
22
|
require 'taski'
|
|
27
23
|
|
|
28
|
-
# Define tasks with dependencies
|
|
29
24
|
class DatabaseSetup < Taski::Task
|
|
30
25
|
exports :connection_string
|
|
31
26
|
|
|
32
27
|
def run
|
|
33
28
|
@connection_string = "postgresql://localhost/myapp"
|
|
34
|
-
puts "Database configured"
|
|
35
29
|
end
|
|
36
30
|
end
|
|
37
31
|
|
|
@@ -40,31 +34,32 @@ class CacheSetup < Taski::Task
|
|
|
40
34
|
|
|
41
35
|
def run
|
|
42
36
|
@cache_url = "redis://localhost:6379"
|
|
43
|
-
puts "Cache configured"
|
|
44
37
|
end
|
|
45
38
|
end
|
|
46
39
|
|
|
47
40
|
class APIServer < Taski::Task
|
|
48
41
|
def run
|
|
49
42
|
# Dependencies execute automatically and in parallel
|
|
50
|
-
puts "
|
|
51
|
-
puts "
|
|
43
|
+
puts "DB: #{DatabaseSetup.connection_string}"
|
|
44
|
+
puts "Cache: #{CacheSetup.cache_url}"
|
|
52
45
|
end
|
|
53
46
|
end
|
|
54
47
|
|
|
55
|
-
# Run any task - dependencies resolve and execute in parallel
|
|
56
48
|
APIServer.run
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Installation
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
gem 'taski'
|
|
61
55
|
```
|
|
62
56
|
|
|
63
57
|
## Core Concepts
|
|
64
58
|
|
|
65
|
-
|
|
59
|
+
### Exports - Value Sharing Between Tasks
|
|
60
|
+
|
|
61
|
+
Share computed values between tasks:
|
|
66
62
|
|
|
67
|
-
### 1. Exports API - Value Sharing Between Tasks
|
|
68
63
|
```ruby
|
|
69
64
|
class Config < Taski::Task
|
|
70
65
|
exports :app_name, :port
|
|
@@ -77,26 +72,27 @@ end
|
|
|
77
72
|
|
|
78
73
|
class Server < Taski::Task
|
|
79
74
|
def run
|
|
80
|
-
# Automatically depends on Config task
|
|
81
75
|
puts "Starting #{Config.app_name} on port #{Config.port}"
|
|
82
76
|
end
|
|
83
77
|
end
|
|
84
78
|
```
|
|
85
79
|
|
|
86
|
-
###
|
|
80
|
+
### Section - Runtime Implementation Selection
|
|
81
|
+
|
|
82
|
+
Switch implementations based on environment:
|
|
83
|
+
|
|
87
84
|
```ruby
|
|
88
85
|
class DatabaseSection < Taski::Section
|
|
89
86
|
interfaces :host, :port
|
|
90
87
|
|
|
91
|
-
|
|
92
|
-
class ProductionDB < Taski::Task
|
|
88
|
+
class Production < Taski::Task
|
|
93
89
|
def run
|
|
94
90
|
@host = "prod.example.com"
|
|
95
91
|
@port = 5432
|
|
96
92
|
end
|
|
97
93
|
end
|
|
98
94
|
|
|
99
|
-
class
|
|
95
|
+
class Development < Taski::Task
|
|
100
96
|
def run
|
|
101
97
|
@host = "localhost"
|
|
102
98
|
@port = 5432
|
|
@@ -104,97 +100,67 @@ class DatabaseSection < Taski::Section
|
|
|
104
100
|
end
|
|
105
101
|
|
|
106
102
|
def impl
|
|
107
|
-
|
|
108
|
-
ENV['RAILS_ENV'] == 'production' ? ProductionDB : LocalDB
|
|
103
|
+
ENV['RAILS_ENV'] == 'production' ? Production : Development
|
|
109
104
|
end
|
|
110
105
|
end
|
|
111
106
|
|
|
112
107
|
class App < Taski::Task
|
|
113
108
|
def run
|
|
114
|
-
# Access through Section - implementation selected at runtime
|
|
115
109
|
puts "Connecting to #{DatabaseSection.host}:#{DatabaseSection.port}"
|
|
116
110
|
end
|
|
117
111
|
end
|
|
118
|
-
|
|
119
|
-
App.run
|
|
120
|
-
# In development (RAILS_ENV != 'production'):
|
|
121
|
-
# => Connecting to localhost:5432
|
|
122
|
-
|
|
123
|
-
# In production (RAILS_ENV == 'production'):
|
|
124
|
-
# => Connecting to prod.example.com:5432
|
|
125
112
|
```
|
|
126
113
|
|
|
127
|
-
**
|
|
128
|
-
- **Exports**: Share computed values or side effects between tasks
|
|
129
|
-
- **Section**: Switch implementations based on environment or conditions
|
|
114
|
+
> **Note**: Nested implementation classes automatically inherit Section's `interfaces` as `exports`.
|
|
130
115
|
|
|
131
|
-
|
|
116
|
+
## Advanced Usage
|
|
132
117
|
|
|
133
|
-
###
|
|
118
|
+
### Context - Runtime Information and Options
|
|
134
119
|
|
|
135
|
-
|
|
120
|
+
Pass custom options and access execution context from any task:
|
|
136
121
|
|
|
137
122
|
```ruby
|
|
138
123
|
class DeployTask < Taski::Task
|
|
139
124
|
def run
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
125
|
+
# User-defined options
|
|
126
|
+
env = Taski.context[:env]
|
|
127
|
+
debug = Taski.context.fetch(:debug, false)
|
|
128
|
+
|
|
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}"
|
|
143
134
|
end
|
|
144
135
|
end
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
**Available context:**
|
|
148
|
-
- `working_directory`: Directory where execution started
|
|
149
|
-
- `started_at`: Time when execution began
|
|
150
|
-
- `root_task`: The first task class that was called
|
|
151
136
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
137
|
+
# Pass options when running
|
|
138
|
+
DeployTask.run(context: { env: "production", debug: true })
|
|
139
|
+
```
|
|
155
140
|
|
|
156
|
-
|
|
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
|
|
157
148
|
|
|
158
|
-
|
|
149
|
+
### Re-execution
|
|
159
150
|
|
|
160
151
|
```ruby
|
|
161
|
-
|
|
162
|
-
exports :value
|
|
163
|
-
|
|
164
|
-
def run
|
|
165
|
-
@value = rand(1000)
|
|
166
|
-
end
|
|
167
|
-
end
|
|
168
|
-
|
|
169
|
-
# Cached execution (same result)
|
|
152
|
+
# Cached execution (default)
|
|
170
153
|
RandomTask.value # => 42
|
|
171
154
|
RandomTask.value # => 42 (cached)
|
|
172
155
|
|
|
173
|
-
# Fresh execution
|
|
156
|
+
# Fresh execution
|
|
174
157
|
RandomTask.new.run # => 123 (new instance)
|
|
175
|
-
RandomTask.new.run # => 456 (another new instance)
|
|
176
158
|
|
|
177
159
|
# Reset all caches
|
|
178
160
|
RandomTask.reset!
|
|
179
|
-
RandomTask.value # => 789 (fresh after reset)
|
|
180
161
|
```
|
|
181
162
|
|
|
182
|
-
|
|
183
|
-
- `TaskClass.run` / `TaskClass.value`: Normal execution with caching (recommended for dependency graphs)
|
|
184
|
-
- `TaskClass.new.run`: Re-execute only this task (dependencies still use cache)
|
|
185
|
-
- `TaskClass.reset!`: Clear all caches and re-execute everything
|
|
186
|
-
|
|
187
|
-
## Key Features
|
|
188
|
-
|
|
189
|
-
- **Parallel Execution**: Independent tasks run concurrently using threads
|
|
190
|
-
- **Static Analysis**: Dependencies detected automatically via Prism AST parsing
|
|
191
|
-
- **Thread-Safe**: Monitor-based synchronization ensures safe concurrent access
|
|
192
|
-
- **Progress Display**: Real-time visual feedback with spinner animations and timing
|
|
193
|
-
- **Tree Visualization**: See your dependency graph structure
|
|
194
|
-
- **Graceful Abort**: Stop execution cleanly without starting new tasks (Ctrl+C)
|
|
195
|
-
- **Runtime Context**: Access execution information from any task
|
|
196
|
-
|
|
197
|
-
### Parallel Progress Display
|
|
163
|
+
### Progress Display
|
|
198
164
|
|
|
199
165
|
Enable real-time progress visualization:
|
|
200
166
|
|
|
@@ -202,14 +168,11 @@ Enable real-time progress visualization:
|
|
|
202
168
|
TASKI_FORCE_PROGRESS=1 ruby your_script.rb
|
|
203
169
|
```
|
|
204
170
|
|
|
205
|
-
Output example:
|
|
206
171
|
```
|
|
207
172
|
⠋ DatabaseSetup (running)
|
|
208
173
|
⠙ CacheSetup (running)
|
|
209
174
|
✅ DatabaseSetup (123.4ms)
|
|
210
175
|
✅ CacheSetup (98.2ms)
|
|
211
|
-
⠸ WebServer (running)
|
|
212
|
-
✅ WebServer (45.1ms)
|
|
213
176
|
```
|
|
214
177
|
|
|
215
178
|
### Tree Visualization
|
|
@@ -222,33 +185,13 @@ puts WebServer.tree
|
|
|
222
185
|
# => └── Cache
|
|
223
186
|
```
|
|
224
187
|
|
|
225
|
-
##
|
|
226
|
-
|
|
227
|
-
```ruby
|
|
228
|
-
gem 'taski'
|
|
229
|
-
```
|
|
230
|
-
|
|
231
|
-
```bash
|
|
232
|
-
bundle install
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
## 🧪 Testing
|
|
188
|
+
## Development
|
|
236
189
|
|
|
237
190
|
```bash
|
|
238
191
|
rake test # Run all tests
|
|
239
192
|
rake standard # Check code style
|
|
240
193
|
```
|
|
241
194
|
|
|
242
|
-
## 📚 Learn More
|
|
243
|
-
|
|
244
|
-
- **[Examples](examples/)**: Practical examples from basic to advanced patterns
|
|
245
|
-
- **[Tests](test/)**: Comprehensive test suite showing real-world usage
|
|
246
|
-
- **[Source Code](lib/taski/)**: Clean, well-documented implementation
|
|
247
|
-
- `lib/taski/task.rb` - Core Task implementation with exports API
|
|
248
|
-
- `lib/taski/section.rb` - Section API for runtime selection
|
|
249
|
-
- `lib/taski/execution/` - Parallel execution engine
|
|
250
|
-
- `lib/taski/static_analysis/` - Prism-based dependency analyzer
|
|
251
|
-
|
|
252
195
|
## Support
|
|
253
196
|
|
|
254
197
|
Bug reports and pull requests welcome at https://github.com/ahogappa/taski.
|
|
@@ -256,7 +199,3 @@ Bug reports and pull requests welcome at https://github.com/ahogappa/taski.
|
|
|
256
199
|
## License
|
|
257
200
|
|
|
258
201
|
MIT License
|
|
259
|
-
|
|
260
|
-
---
|
|
261
|
-
|
|
262
|
-
**Taski** - Parallel task execution with automatic dependency resolution. 🚀
|
data/examples/README.md
CHANGED
|
@@ -34,18 +34,21 @@ ruby examples/section_demo.rb
|
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
37
|
-
### 3. context_demo.rb - Runtime Context
|
|
37
|
+
### 3. context_demo.rb - Runtime Context and Options
|
|
38
38
|
|
|
39
|
-
Access execution context
|
|
39
|
+
Access execution context and pass custom options to tasks.
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
42
|
ruby examples/context_demo.rb
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
**Covers:**
|
|
46
|
-
- `
|
|
47
|
-
- `Taski
|
|
48
|
-
- `Taski
|
|
46
|
+
- User-defined options via `run(context: {...})`
|
|
47
|
+
- `Taski.context[:key]` for option access
|
|
48
|
+
- `Taski.context.fetch(:key, default)` for defaults
|
|
49
|
+
- `Taski.context.working_directory`
|
|
50
|
+
- `Taski.context.started_at`
|
|
51
|
+
- `Taski.context.root_task`
|
|
49
52
|
|
|
50
53
|
---
|
|
51
54
|
|
data/examples/context_demo.rb
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
# - working_directory: Where execution started
|
|
8
8
|
# - started_at: When execution began
|
|
9
9
|
# - root_task: The first task class that was called
|
|
10
|
+
# - User-defined options: Custom values passed via run(context: {...})
|
|
10
11
|
#
|
|
11
12
|
# Run: ruby examples/context_demo.rb
|
|
12
13
|
|
|
@@ -21,13 +22,15 @@ class SetupTask < Taski::Task
|
|
|
21
22
|
|
|
22
23
|
def run
|
|
23
24
|
puts "Setup running..."
|
|
24
|
-
puts " Working directory: #{Taski
|
|
25
|
-
puts " Started at: #{Taski
|
|
26
|
-
puts " Root task: #{Taski
|
|
25
|
+
puts " Working directory: #{Taski.context.working_directory}"
|
|
26
|
+
puts " Started at: #{Taski.context.started_at}"
|
|
27
|
+
puts " Root task: #{Taski.context.root_task}"
|
|
28
|
+
puts " Environment: #{Taski.context[:env]}"
|
|
27
29
|
|
|
28
30
|
@setup_info = {
|
|
29
|
-
directory: Taski
|
|
30
|
-
timestamp: Taski
|
|
31
|
+
directory: Taski.context.working_directory,
|
|
32
|
+
timestamp: Taski.context.started_at,
|
|
33
|
+
env: Taski.context[:env]
|
|
31
34
|
}
|
|
32
35
|
end
|
|
33
36
|
end
|
|
@@ -38,11 +41,12 @@ class FileProcessor < Taski::Task
|
|
|
38
41
|
|
|
39
42
|
def run
|
|
40
43
|
# Use context to determine output location
|
|
41
|
-
base_dir = Taski
|
|
42
|
-
|
|
44
|
+
base_dir = Taski.context.working_directory
|
|
45
|
+
env = Taski.context.fetch(:env, "development")
|
|
46
|
+
@output_path = File.join(base_dir, "tmp", env, "output.txt")
|
|
43
47
|
|
|
44
48
|
puts "FileProcessor: Would write to #{@output_path}"
|
|
45
|
-
puts " (relative to working directory)"
|
|
49
|
+
puts " (relative to working directory, env: #{env})"
|
|
46
50
|
end
|
|
47
51
|
end
|
|
48
52
|
|
|
@@ -51,11 +55,12 @@ class TimingTask < Taski::Task
|
|
|
51
55
|
exports :duration_info
|
|
52
56
|
|
|
53
57
|
def run
|
|
54
|
-
start_time = Taski
|
|
58
|
+
start_time = Taski.context.started_at
|
|
55
59
|
current_time = Time.now
|
|
56
60
|
elapsed = current_time - start_time
|
|
57
61
|
|
|
58
62
|
puts "TimingTask: #{elapsed.round(3)}s since execution started"
|
|
63
|
+
puts " Debug mode: #{Taski.context.fetch(:debug, false)}"
|
|
59
64
|
|
|
60
65
|
@duration_info = {
|
|
61
66
|
started: start_time,
|
|
@@ -71,7 +76,8 @@ class MainTask < Taski::Task
|
|
|
71
76
|
|
|
72
77
|
def run
|
|
73
78
|
puts "\nMainTask executing..."
|
|
74
|
-
puts " Root task is: #{Taski
|
|
79
|
+
puts " Root task is: #{Taski.context.root_task}"
|
|
80
|
+
puts " Environment: #{Taski.context[:env]}"
|
|
75
81
|
|
|
76
82
|
# Access dependencies
|
|
77
83
|
setup = SetupTask.setup_info
|
|
@@ -82,7 +88,7 @@ class MainTask < Taski::Task
|
|
|
82
88
|
setup: setup,
|
|
83
89
|
output_path: output,
|
|
84
90
|
timing: timing,
|
|
85
|
-
root_task: Taski
|
|
91
|
+
root_task: Taski.context.root_task.to_s
|
|
86
92
|
}
|
|
87
93
|
|
|
88
94
|
puts "\nExecution Summary:"
|
|
@@ -92,15 +98,15 @@ class MainTask < Taski::Task
|
|
|
92
98
|
end
|
|
93
99
|
end
|
|
94
100
|
|
|
95
|
-
puts "\n1. Running MainTask
|
|
101
|
+
puts "\n1. Running MainTask with context options"
|
|
96
102
|
puts "-" * 40
|
|
97
|
-
MainTask.run
|
|
103
|
+
MainTask.run(context: {env: "production", debug: true})
|
|
98
104
|
|
|
99
105
|
puts "\n" + "=" * 40
|
|
100
|
-
puts "\n2. Running SetupTask directly
|
|
106
|
+
puts "\n2. Running SetupTask directly with different context"
|
|
101
107
|
puts "-" * 40
|
|
102
108
|
SetupTask.reset!
|
|
103
|
-
SetupTask.run
|
|
109
|
+
SetupTask.run(context: {env: "staging"})
|
|
104
110
|
|
|
105
111
|
puts "\n" + "=" * 40
|
|
106
112
|
puts "\n3. Dependency Tree"
|
|
@@ -109,4 +115,4 @@ puts MainTask.tree
|
|
|
109
115
|
|
|
110
116
|
puts "\n" + "=" * 40
|
|
111
117
|
puts "Context API demonstration complete!"
|
|
112
|
-
puts "Note: Context provides runtime information without affecting dependency analysis."
|
|
118
|
+
puts "Note: Context provides runtime information and user options without affecting dependency analysis."
|
data/lib/taski/context.rb
CHANGED
|
@@ -3,50 +3,48 @@
|
|
|
3
3
|
require "monitor"
|
|
4
4
|
|
|
5
5
|
module Taski
|
|
6
|
-
# Runtime context accessible from any task
|
|
6
|
+
# Runtime context accessible from any task.
|
|
7
|
+
# Holds user-defined options and execution metadata.
|
|
8
|
+
# Context is immutable after creation - options cannot be modified during task execution.
|
|
7
9
|
class Context
|
|
8
|
-
|
|
10
|
+
attr_reader :started_at, :working_directory, :root_task
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
# @return [Time] The start time
|
|
19
|
-
def started_at
|
|
20
|
-
@monitor.synchronize do
|
|
21
|
-
@started_at ||= Time.now
|
|
22
|
-
end
|
|
23
|
-
end
|
|
12
|
+
# @param options [Hash] User-defined options (immutable after creation)
|
|
13
|
+
# @param root_task [Class] The root task class that initiated execution
|
|
14
|
+
def initialize(options:, root_task:)
|
|
15
|
+
@options = options.dup.freeze
|
|
16
|
+
@root_task = root_task
|
|
17
|
+
@started_at = Time.now
|
|
18
|
+
@working_directory = Dir.pwd
|
|
19
|
+
end
|
|
24
20
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
21
|
+
# Get a user-defined option value
|
|
22
|
+
# @param key [Symbol, String] The option key
|
|
23
|
+
# @return [Object, nil] The option value or nil if not set
|
|
24
|
+
def [](key)
|
|
25
|
+
@options[key]
|
|
26
|
+
end
|
|
31
27
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
28
|
+
# Get a user-defined option value with a default
|
|
29
|
+
# @param key [Symbol, String] The option key
|
|
30
|
+
# @param default [Object] Default value if key is not present
|
|
31
|
+
# @yield Block to compute default value if key is not present
|
|
32
|
+
# @return [Object] The option value or default
|
|
33
|
+
def fetch(key, default = nil, &block)
|
|
34
|
+
if @options.key?(key)
|
|
35
|
+
@options[key]
|
|
36
|
+
elsif block
|
|
37
|
+
block.call
|
|
38
|
+
else
|
|
39
|
+
default
|
|
41
40
|
end
|
|
41
|
+
end
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
49
|
-
end
|
|
43
|
+
# Check if a user-defined option key exists
|
|
44
|
+
# @param key [Symbol, String] The option key
|
|
45
|
+
# @return [Boolean] true if the key exists
|
|
46
|
+
def key?(key)
|
|
47
|
+
@options.key?(key)
|
|
50
48
|
end
|
|
51
49
|
end
|
|
52
50
|
end
|
|
@@ -9,26 +9,38 @@ module Taski
|
|
|
9
9
|
# @param task_class [Class] The task class to analyze
|
|
10
10
|
# @return [Set<Class>] Set of task classes that are dependencies
|
|
11
11
|
def self.analyze(task_class)
|
|
12
|
-
|
|
12
|
+
target_method = target_method_for(task_class)
|
|
13
|
+
source_location = extract_method_location(task_class, target_method)
|
|
13
14
|
return Set.new unless source_location
|
|
14
15
|
|
|
15
16
|
file_path, _line_number = source_location
|
|
16
17
|
parse_result = Prism.parse_file(file_path)
|
|
17
18
|
|
|
18
|
-
visitor = Visitor.new(task_class)
|
|
19
|
+
visitor = Visitor.new(task_class, target_method)
|
|
19
20
|
visitor.visit(parse_result.value)
|
|
20
21
|
visitor.dependencies
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
# @param task_class [Class] The task class
|
|
25
|
+
# @return [Symbol] The method name to analyze (:run for Task, :impl for Section)
|
|
26
|
+
def self.target_method_for(task_class)
|
|
27
|
+
if defined?(Taski::Section) && task_class < Taski::Section
|
|
28
|
+
:impl
|
|
29
|
+
else
|
|
30
|
+
:run
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# @param task_class [Class] The task class
|
|
35
|
+
# @param method_name [Symbol] The method name to extract location for
|
|
24
36
|
# @return [Array<String, Integer>, nil] File path and line number, or nil
|
|
25
|
-
def self.
|
|
26
|
-
task_class.instance_method(
|
|
37
|
+
def self.extract_method_location(task_class, method_name)
|
|
38
|
+
task_class.instance_method(method_name).source_location
|
|
27
39
|
rescue NameError
|
|
28
40
|
nil
|
|
29
41
|
end
|
|
30
42
|
|
|
31
|
-
private_class_method :
|
|
43
|
+
private_class_method :target_method_for, :extract_method_location
|
|
32
44
|
end
|
|
33
45
|
end
|
|
34
46
|
end
|
|
@@ -7,11 +7,12 @@ module Taski
|
|
|
7
7
|
class Visitor < Prism::Visitor
|
|
8
8
|
attr_reader :dependencies
|
|
9
9
|
|
|
10
|
-
def initialize(target_task_class)
|
|
10
|
+
def initialize(target_task_class, target_method = :run)
|
|
11
11
|
super()
|
|
12
12
|
@target_task_class = target_task_class
|
|
13
|
+
@target_method = target_method
|
|
13
14
|
@dependencies = Set.new
|
|
14
|
-
@
|
|
15
|
+
@in_target_method = false
|
|
15
16
|
@current_namespace_path = []
|
|
16
17
|
end
|
|
17
18
|
|
|
@@ -24,17 +25,27 @@ module Taski
|
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
def visit_def_node(node)
|
|
27
|
-
if node.name ==
|
|
28
|
-
@
|
|
28
|
+
if node.name == @target_method && in_target_class?
|
|
29
|
+
@in_target_method = true
|
|
29
30
|
super
|
|
30
|
-
@
|
|
31
|
+
@in_target_method = false
|
|
31
32
|
else
|
|
32
33
|
super
|
|
33
34
|
end
|
|
34
35
|
end
|
|
35
36
|
|
|
36
37
|
def visit_call_node(node)
|
|
37
|
-
detect_task_dependency(node) if @
|
|
38
|
+
detect_task_dependency(node) if @in_target_method
|
|
39
|
+
super
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def visit_constant_read_node(node)
|
|
43
|
+
detect_return_constant(node) if @in_target_method && @target_method == :impl
|
|
44
|
+
super
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def visit_constant_path_node(node)
|
|
48
|
+
detect_return_constant(node) if @in_target_method && @target_method == :impl
|
|
38
49
|
super
|
|
39
50
|
end
|
|
40
51
|
|
|
@@ -62,6 +73,11 @@ module Taski
|
|
|
62
73
|
resolve_and_add_dependency(constant_name) if constant_name
|
|
63
74
|
end
|
|
64
75
|
|
|
76
|
+
def detect_return_constant(node)
|
|
77
|
+
constant_name = node.slice
|
|
78
|
+
resolve_and_add_dependency(constant_name)
|
|
79
|
+
end
|
|
80
|
+
|
|
65
81
|
def extract_receiver_constant(receiver)
|
|
66
82
|
case receiver
|
|
67
83
|
when Prism::ConstantReadNode, Prism::ConstantPathNode
|