taski 0.1.0 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 260795a6ff4d622bd95b3bfb2668ba6123f17339650ea9371ea54d2210691d98
4
- data.tar.gz: 60d7f97a6c736ed91e50bd2e8e9f48c163f76024e7407c0da96c9d77f4394514
3
+ metadata.gz: 8b66b9afd145af8c5a07839b1886212336ffbdcb935897a278afae10315162e8
4
+ data.tar.gz: 7f43e874932671c2a41adc776514022e7d44d9ec0018935e2bd2e7e4d4537543
5
5
  SHA512:
6
- metadata.gz: 71ccbe4131ac8409636071355af2de6cfda0862e21fb012e70b2f2fe60cb3bbc7023d640f9d3dc889f6759e8885a5dba5a1c0d543a6aa05e9299aa9ff401df54
7
- data.tar.gz: a0a291d6fdd8d9ec1a122dbca9d115aa5c36f8008e7812120b0356e218ea895f46b292eab4404404c611c1f1b69e6dc9e4247f18d2cfc7a079b7d1f0ec2716d6
6
+ metadata.gz: 8175467340490afdd0e9f1d4ba5c2e3db257e7d4c62850f88a138676056ea140753e2c38042803dd46c6c071731245314c2b597af83eb8c237be28d20e998e60
7
+ data.tar.gz: d1695488ca402cdb18800bd4b8623c64ed3c681a5dd6039c651c117c99793045fa59cca85f82cde0fde311b0ada0d81b8a66ba4d8cbb9b576ebe00fe71e760cf
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ahogappa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md CHANGED
@@ -1,65 +1,333 @@
1
1
  # Taski
2
2
 
3
- **Taski** is a Ruby-based task runner designed for small, composable processing steps.
4
- With Taski, you define tasks and the outputs they depend on. Taski then statically resolves task dependencies and determines the correct execution order.
3
+ > **🚧 Development Status:** Taski is currently under active development and the API may change. Not yet recommended for production use.
5
4
 
6
- Tasks are executed in a topologically sorted order, ensuring that all dependencies are resolved before a task is run. Reverse execution is also supported, making it easy to clean up intermediate files after a build process.
5
+ **Taski** is a powerful Ruby framework for building task dependency graphs with automatic resolution and execution. It provides two complementary APIs for different use cases: static dependencies through exports and dynamic dependencies through define.
7
6
 
8
- > **🚧 Development Status:** Taski is currently under active development and the API may change.
7
+ ## 🎯 Key Features
9
8
 
10
- > **⚠️ Limitation:** Circular dependencies are **not** supported at this time.
9
+ - **Automatic Dependency Resolution**: Dependencies are detected automatically through static analysis and runtime evaluation
10
+ - **Two Complementary APIs**: Choose the right approach for your use case
11
+ - **Exports API**: For simple, static dependencies
12
+ - **Define API**: For complex, dynamic dependencies based on runtime conditions
13
+ - **Thread-Safe Execution**: Safe for concurrent access with Monitor-based synchronization
14
+ - **Circular Dependency Detection**: Prevents infinite loops with clear error messages
15
+ - **Memory Leak Prevention**: Built-in reset mechanisms for long-running applications
16
+ - **Topological Execution**: Tasks execute in correct dependency order automatically
17
+ - **Reverse Cleanup**: Clean operations run in reverse dependency order
11
18
 
12
- ### Features
19
+ ## 🚀 Quick Start
13
20
 
14
- - Simple and declarative task definitions
15
- - Static dependency resolution
16
- - Topological execution order
17
- - Reverse execution for teardown or cleanup
18
- - Built entirely in Ruby
21
+ ```ruby
22
+ require 'taski'
19
23
 
20
- ## Installation
24
+ # Simple static dependency using Exports API
25
+ class DatabaseSetup < Taski::Task
26
+ exports :connection_string
21
27
 
22
- Install the gem and add to the application's Gemfile by executing:
28
+ def build
29
+ @connection_string = "postgresql://localhost/myapp"
30
+ puts "Database configured"
31
+ end
32
+ end
23
33
 
24
- ```bash
25
- bundle add taski
34
+ class APIServer < Taski::Task
35
+ exports :port
36
+
37
+ def build
38
+ # Automatic dependency: DatabaseSetup will be built first
39
+ puts "Starting API with #{DatabaseSetup.connection_string}"
40
+ @port = 3000
41
+ end
42
+ end
43
+
44
+ # Execute - dependencies are resolved automatically
45
+ APIServer.build
46
+ # => Database configured
47
+ # => Starting API with postgresql://localhost/myapp
26
48
  ```
27
49
 
28
- If bundler is not being used to manage dependencies, install the gem by executing:
50
+ ## 📚 API Guide
29
51
 
30
- ```bash
31
- gem install taski
52
+ ### Exports API - Static Dependencies
53
+
54
+ Use the **Exports API** when you have simple, predictable dependencies:
55
+
56
+ ```ruby
57
+ class ConfigLoader < Taski::Task
58
+ exports :app_name, :version
59
+
60
+ def build
61
+ @app_name = "MyApp"
62
+ @version = "1.0.0"
63
+ end
64
+ end
65
+
66
+ class Deployment < Taski::Task
67
+ exports :deploy_url
68
+
69
+ def build
70
+ # Static dependency - always uses ConfigLoader
71
+ @deploy_url = "https://#{ConfigLoader.app_name}.example.com"
72
+ end
73
+ end
32
74
  ```
33
75
 
34
- ## Usage
76
+ ### Define API - Dynamic Dependencies
77
+
78
+ Use the **Define API** when dependencies change based on runtime conditions:
35
79
 
36
80
  ```ruby
37
- class TaskA < Taski::Task
38
- definition :task_a, -> { "Task A" }
81
+ class EnvironmentConfig < Taski::Task
82
+ define :database_service, -> {
83
+ # Dynamic dependency based on environment
84
+ case ENV['RAILS_ENV']
85
+ when 'production'
86
+ ProductionDatabase.setup
87
+ when 'staging'
88
+ StagingDatabase.setup
89
+ else
90
+ DevelopmentDatabase.setup
91
+ end
92
+ }
93
+
94
+ define :cache_strategy, -> {
95
+ # Dynamic dependency based on feature flags
96
+ if FeatureFlag.enabled?(:redis_cache)
97
+ RedisCache.configure
98
+ else
99
+ MemoryCache.configure
100
+ end
101
+ }
39
102
 
40
103
  def build
41
- task_a
104
+ puts "Using #{database_service}"
105
+ puts "Cache: #{cache_strategy}"
42
106
  end
43
107
  end
108
+ ```
109
+
110
+ > **⚠️ Note:** The `define` API uses dynamic method definition, which may generate Ruby warnings about method redefinition. This is expected behavior due to the dependency resolution mechanism and does not affect functionality.
111
+
112
+ ### When to Use Each API
113
+
114
+ | Use Case | Recommended API | Example |
115
+ |----------|----------------|---------|
116
+ | Simple value exports | Exports API | Configuration values, file paths |
117
+ | Environment-specific logic | Define API | Different services per environment |
118
+ | Feature flag dependencies | Define API | Optional components based on flags |
119
+ | Conditional processing | Define API | Different algorithms based on input |
120
+ | Static file dependencies | Exports API | Build artifacts, compiled assets |
121
+
122
+ ## 🔧 Advanced Features
123
+
124
+ ### Thread Safety
125
+
126
+ Taski is thread-safe and handles concurrent access gracefully:
127
+
128
+ ```ruby
129
+ # Multiple threads can safely access the same task
130
+ threads = 5.times.map do
131
+ Thread.new { MyTask.some_value }
132
+ end
133
+
134
+ # All threads get the same instance - built only once
135
+ results = threads.map(&:value)
136
+ ```
137
+
138
+ ### Error Handling
139
+
140
+ Comprehensive error handling with custom exception types:
141
+
142
+ ```ruby
143
+ begin
144
+ TaskWithCircularDep.build
145
+ rescue Taski::CircularDependencyError => e
146
+ puts "Circular dependency detected: #{e.message}"
147
+ rescue Taski::TaskBuildError => e
148
+ puts "Build failed: #{e.message}"
149
+ end
150
+ ```
44
151
 
45
- class TaskB < Taski::Task
46
- definition :simple_task, -> { "Task result is #{TaskA.task_a}" }
152
+ ### Lifecycle Management
47
153
 
154
+ Full control over task lifecycle:
155
+
156
+ ```ruby
157
+ class ProcessingTask < Taski::Task
48
158
  def build
49
- puts simple_task
159
+ # Setup and processing logic
160
+ puts "Processing data..."
161
+ end
162
+
163
+ def clean
164
+ # Cleanup logic (runs in reverse dependency order)
165
+ puts "Cleaning up temporary files..."
50
166
  end
51
167
  end
52
168
 
53
- TaskB.build
54
- # => Task result is Task A
169
+ # Build dependencies in correct order
170
+ ProcessingTask.build
171
+
172
+ # Clean in reverse order
173
+ ProcessingTask.clean
55
174
  ```
56
175
 
57
- ## Development
176
+ ## 🏗️ Complex Example
58
177
 
59
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
178
+ Here's a realistic example showing both APIs working together:
60
179
 
61
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
180
+ ```ruby
181
+ # Environment configuration using Define API
182
+ class Environment < Taski::Task
183
+ define :database_url, -> {
184
+ case ENV['RAILS_ENV']
185
+ when 'production'
186
+ ProductionDB.connection_string
187
+ when 'test'
188
+ TestDB.connection_string
189
+ else
190
+ "sqlite3://development.db"
191
+ end
192
+ }
193
+
194
+ define :redis_config, -> {
195
+ if FeatureFlag.enabled?(:redis_cache)
196
+ RedisService.configuration
197
+ else
198
+ nil
199
+ end
200
+ }
201
+ end
202
+
203
+ # Static configuration using Exports API
204
+ class AppConfig < Taski::Task
205
+ exports :app_name, :version, :port
206
+
207
+ def build
208
+ @app_name = "MyWebApp"
209
+ @version = "2.1.0"
210
+ @port = ENV.fetch('PORT', 3000).to_i
211
+ end
212
+ end
213
+
214
+ # Application startup combining both APIs
215
+ class Application < Taski::Task
216
+ def build
217
+ puts "Starting #{AppConfig.app_name} v#{AppConfig.version}"
218
+ puts "Database: #{Environment.database_url}"
219
+ puts "Redis: #{Environment.redis_config || 'disabled'}"
220
+ puts "Port: #{AppConfig.port}"
221
+
222
+ # Start the application...
223
+ end
224
+
225
+ def clean
226
+ puts "Shutting down #{AppConfig.app_name}..."
227
+ # Cleanup logic...
228
+ end
229
+ end
230
+
231
+ # Everything runs in the correct order automatically
232
+ Application.build
233
+ ```
234
+
235
+ ## 📦 Installation
236
+
237
+ > **⚠️ Warning:** Taski is currently in development. API changes may occur. Use at your own risk in production environments.
238
+
239
+ Add this line to your application's Gemfile:
240
+
241
+ ```ruby
242
+ gem 'taski'
243
+ ```
244
+
245
+ And then execute:
246
+
247
+ ```bash
248
+ bundle install
249
+ ```
250
+
251
+ Or install it yourself as:
252
+
253
+ ```bash
254
+ gem install taski
255
+ ```
256
+
257
+ For development and testing purposes, you can also install directly from the repository:
258
+
259
+ ```ruby
260
+ # In your Gemfile
261
+ gem 'taski', git: 'https://github.com/[USERNAME]/taski.git'
262
+ ```
263
+
264
+ ## 🧪 Testing
265
+
266
+ Taski includes comprehensive test coverage. Run the test suite:
267
+
268
+ ```bash
269
+ bundle exec rake test
270
+ ```
271
+
272
+ > **ℹ️ Note:** Test output may include warnings about method redefinition from the `define` API. These warnings are expected and can be safely ignored.
273
+
274
+
275
+ ## 🏛️ Architecture
276
+
277
+ Taski is built with a modular architecture:
278
+
279
+ - **Task Base**: Core framework and constants
280
+ - **Exports API**: Static dependency resolution
281
+ - **Define API**: Dynamic dependency resolution
282
+ - **Instance Management**: Thread-safe lifecycle management
283
+ - **Dependency Resolver**: Topological sorting and analysis
284
+ - **Static Analyzer**: AST-based dependency detection
285
+
286
+ ## 🚧 Development Status
287
+
288
+ **Taski is currently in active development and should be considered experimental.** While the core functionality is working and well-tested, the API may undergo changes as we refine the framework based on feedback and real-world usage.
289
+
290
+ ### Current Development Phase
291
+
292
+ - ✅ **Core Framework**: Dependency resolution, both APIs, thread safety
293
+ - ✅ **Testing**: Comprehensive test suite with 38+ tests
294
+ - ✅ **Type Safety**: RBS definitions and Steep integration
295
+ - 🚧 **API Stability**: Some breaking changes may occur
296
+ - 🚧 **Performance**: Optimizations for large dependency graphs
297
+ - 🚧 **Documentation**: Examples and best practices
298
+
299
+ ### Known Limitations
300
+
301
+ - **API Changes**: Breaking changes may occur in minor version updates
302
+ - **Production Readiness**: Not yet recommended for production environments
303
+ - **Static Analysis**: Works best with straightforward Ruby code patterns
304
+ - **Metaprogramming**: Complex metaprogramming may require manual dependency specification
305
+ - **Performance**: Not yet optimized for very large dependency graphs (1000+ tasks)
306
+ - **Method Redefinition Warnings**: Using `define` API may generate Ruby warnings about method redefinition (this is expected behavior)
307
+
308
+ ### Future Development
309
+
310
+ The future direction of Taski will be determined based on community feedback, real-world usage, and identified needs. Development priorities may include areas such as performance optimization, enhanced static analysis, and improved documentation, but specific roadmap items have not yet been finalized.
311
+
312
+ ### Contributing to Development
313
+
314
+ We welcome contributions during this development phase! Areas where help is especially appreciated:
315
+
316
+ - **Real-world Testing**: Try Taski in your projects and report issues
317
+ - **Performance Testing**: Test with large dependency graphs
318
+ - **API Feedback**: Suggest improvements to the developer experience
319
+ - **Documentation**: Help improve examples and guides
62
320
 
63
321
  ## Contributing
64
322
 
65
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/taski.
323
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ahogappa/taski.
324
+
325
+ ## License
326
+
327
+ The gem is available as open source under the [MIT License](LICENSE).
328
+
329
+ ---
330
+
331
+ **Taski** - Build complex dependency graphs with simple, elegant Ruby code. 🚀
332
+
333
+ > **Experimental Software**: Please use responsibly and provide feedback to help us reach v1.0!
data/Steepfile ADDED
@@ -0,0 +1,20 @@
1
+ # Steepfile
2
+
3
+ D = Steep::Diagnostic
4
+
5
+ target :lib do
6
+ signature "sig"
7
+
8
+ check "lib"
9
+
10
+ library "monitor", "prism"
11
+
12
+ # Configure diagnostics with lenient settings for metaprogramming-heavy code
13
+ configure_code_diagnostics do |hash|
14
+ hash[D::Ruby::UnannotatedEmptyCollection] = :information
15
+ hash[D::Ruby::UnknownInstanceVariable] = :information
16
+ hash[D::Ruby::FallbackAny] = :information
17
+ hash[D::Ruby::NoMethod] = :warning
18
+ hash[D::Ruby::UndeclaredMethodDefinition] = :information
19
+ end
20
+ end
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env ruby
2
+ # Complex example from README showing both APIs
3
+
4
+ require_relative '../lib/taski'
5
+
6
+ # Mock classes for the example
7
+ class ProductionDB < Taski::Task
8
+ exports :connection_string
9
+ def build
10
+ @connection_string = "postgres://prod-server/app"
11
+ end
12
+ end
13
+
14
+ class TestDB < Taski::Task
15
+ exports :connection_string
16
+ def build
17
+ @connection_string = "postgres://test-server/app_test"
18
+ end
19
+ end
20
+
21
+ module FeatureFlag
22
+ def self.enabled?(flag)
23
+ ENV["FEATURE_#{flag.to_s.upcase}"] == 'true'
24
+ end
25
+ end
26
+
27
+ class RedisService < Taski::Task
28
+ exports :configuration
29
+ def build
30
+ @configuration = "redis://localhost:6379"
31
+ end
32
+ end
33
+
34
+ # Environment configuration using Define API
35
+ class Environment < Taski::Task
36
+ define :database_url, -> {
37
+ case ENV['RAILS_ENV']
38
+ when 'production'
39
+ ProductionDB.connection_string
40
+ when 'test'
41
+ TestDB.connection_string
42
+ else
43
+ "sqlite3://development.db"
44
+ end
45
+ }
46
+
47
+ define :redis_config, -> {
48
+ if FeatureFlag.enabled?(:redis_cache)
49
+ RedisService.configuration
50
+ else
51
+ nil
52
+ end
53
+ }
54
+
55
+ def build
56
+ # Environment configuration is handled by define blocks
57
+ end
58
+ end
59
+
60
+ # Static configuration using Exports API
61
+ class AppConfig < Taski::Task
62
+ exports :app_name, :version, :port
63
+
64
+ def build
65
+ @app_name = "MyWebApp"
66
+ @version = "2.1.0"
67
+ @port = ENV.fetch('PORT', 3000).to_i
68
+ end
69
+ end
70
+
71
+ # Application startup combining both APIs
72
+ class Application < Taski::Task
73
+ def build
74
+ puts "Starting #{AppConfig.app_name} v#{AppConfig.version}"
75
+ puts "Database: #{Environment.database_url}"
76
+ puts "Redis: #{Environment.redis_config || 'disabled'}"
77
+ puts "Port: #{AppConfig.port}"
78
+ end
79
+
80
+ def clean
81
+ puts "Shutting down #{AppConfig.app_name}..."
82
+ end
83
+ end
84
+
85
+ # Test different environments
86
+ puts "=== Complex Example ==="
87
+
88
+ puts "\n1. Development Environment (default):"
89
+ ENV.delete('RAILS_ENV')
90
+ ENV.delete('FEATURE_REDIS_CACHE')
91
+ Application.build
92
+ Application.reset!
93
+
94
+ puts "\n2. Test Environment:"
95
+ ENV['RAILS_ENV'] = 'test'
96
+ # Reset Environment to re-evaluate define blocks
97
+ Environment.reset!
98
+ Application.build
99
+ Application.reset!
100
+
101
+ puts "\n3. Production with Redis:"
102
+ ENV['RAILS_ENV'] = 'production'
103
+ ENV['FEATURE_REDIS_CACHE'] = 'true'
104
+ # Reset Environment to re-evaluate define blocks
105
+ Environment.reset!
106
+ Application.build
107
+
108
+ puts "\n4. Cleanup:"
109
+ Application.clean
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+ # Quick Start example from README
3
+
4
+ require_relative '../lib/taski'
5
+
6
+ # Simple static dependency using Exports API
7
+ class DatabaseSetup < Taski::Task
8
+ exports :connection_string
9
+
10
+ def build
11
+ @connection_string = "postgresql://localhost/myapp"
12
+ puts "Database configured"
13
+ end
14
+ end
15
+
16
+ class APIServer < Taski::Task
17
+ exports :port
18
+
19
+ def build
20
+ # Automatic dependency: DatabaseSetup will be built first
21
+ puts "Starting API with #{DatabaseSetup.connection_string}"
22
+ @port = 3000
23
+ end
24
+ end
25
+
26
+ # Execute - dependencies are resolved automatically
27
+ puts "=== Quick Start Example ==="
28
+ APIServer.build
29
+
30
+ puts "\nResult: APIServer running on port #{APIServer.port}"