taski 0.2.0 → 0.2.2
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/.standard.yml +9 -0
- data/README.md +112 -195
- data/Rakefile +7 -1
- data/examples/README.md +57 -0
- data/examples/{complex_example.rb → advanced_patterns.rb} +31 -21
- data/examples/progress_demo.rb +166 -0
- data/examples/{readme_example.rb → quick_start.rb} +15 -4
- data/examples/tree_demo.rb +80 -0
- data/lib/taski/dependency_analyzer.rb +18 -8
- data/lib/taski/exceptions.rb +4 -4
- data/lib/taski/logger.rb +213 -0
- data/lib/taski/progress_display.rb +356 -0
- data/lib/taski/reference.rb +3 -3
- data/lib/taski/task/base.rb +54 -3
- data/lib/taski/task/define_api.rb +36 -7
- data/lib/taski/task/dependency_resolver.rb +39 -11
- data/lib/taski/task/exports_api.rb +1 -1
- data/lib/taski/task/instance_management.rb +75 -20
- data/lib/taski/utils.rb +107 -0
- data/lib/taski/version.rb +1 -1
- data/lib/taski.rb +7 -5
- data/sig/taski.rbs +39 -2
- metadata +12 -6
- data/Steepfile +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28f2e52366511da4606df64a169c804712fcf4398b55a7b7d6430a1367cc183d
|
4
|
+
data.tar.gz: 97cc63c767ceaf7dda2fbb4345876c47d1fb1bf7e0395c8a2a0369bc8585e817
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4558c7e5ab3ae48301da573a0d2dffa935ecf2bdf7f04ccce1b92add5ea45b3aea1b562dcce0c6356d28b7eacd58fcdca030a9a2748fbafdda9d9046b5a09213
|
7
|
+
data.tar.gz: f440abca3014be0bffdd34cb8440f197b55403fc938660a8eb311e1bb6c64127bf9d033335d7e52f71cefec534ae4b6e41e2b83164d76b893d3a3c8f692bc221
|
data/.standard.yml
ADDED
data/README.md
CHANGED
@@ -1,27 +1,21 @@
|
|
1
1
|
# Taski
|
2
2
|
|
3
|
-
|
3
|
+
[](https://github.com/ahogappa/taski/actions/workflows/ci.yml)
|
4
|
+
[](https://codecov.io/gh/ahogappa/taski)
|
5
|
+
[](https://badge.fury.io/rb/taski)
|
4
6
|
|
5
|
-
|
7
|
+
> **🚧 Development Status:** Taski is currently under active development. Not yet recommended for production use.
|
6
8
|
|
7
|
-
|
9
|
+
**Taski** is a Ruby framework for building task dependency graphs with automatic resolution and execution. It provides two APIs: static dependencies through **Exports** and dynamic dependencies through **Define**.
|
8
10
|
|
9
|
-
|
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
|
+
> **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.
|
18
12
|
|
19
13
|
## 🚀 Quick Start
|
20
14
|
|
21
15
|
```ruby
|
22
16
|
require 'taski'
|
23
17
|
|
24
|
-
#
|
18
|
+
# Static dependency using Exports API
|
25
19
|
class DatabaseSetup < Taski::Task
|
26
20
|
exports :connection_string
|
27
21
|
|
@@ -32,16 +26,11 @@ class DatabaseSetup < Taski::Task
|
|
32
26
|
end
|
33
27
|
|
34
28
|
class APIServer < Taski::Task
|
35
|
-
exports :port
|
36
|
-
|
37
29
|
def build
|
38
|
-
# Automatic dependency: DatabaseSetup will be built first
|
39
30
|
puts "Starting API with #{DatabaseSetup.connection_string}"
|
40
|
-
@port = 3000
|
41
31
|
end
|
42
32
|
end
|
43
33
|
|
44
|
-
# Execute - dependencies are resolved automatically
|
45
34
|
APIServer.build
|
46
35
|
# => Database configured
|
47
36
|
# => Starting API with postgresql://localhost/myapp
|
@@ -51,7 +40,7 @@ APIServer.build
|
|
51
40
|
|
52
41
|
### Exports API - Static Dependencies
|
53
42
|
|
54
|
-
|
43
|
+
For simple, predictable dependencies:
|
55
44
|
|
56
45
|
```ruby
|
57
46
|
class ConfigLoader < Taski::Task
|
@@ -60,274 +49,202 @@ class ConfigLoader < Taski::Task
|
|
60
49
|
def build
|
61
50
|
@app_name = "MyApp"
|
62
51
|
@version = "1.0.0"
|
52
|
+
puts "Config loaded: #{@app_name} v#{@version}"
|
63
53
|
end
|
64
54
|
end
|
65
55
|
|
66
56
|
class Deployment < Taski::Task
|
67
|
-
exports :deploy_url
|
68
|
-
|
69
57
|
def build
|
70
|
-
# Static dependency - always uses ConfigLoader
|
71
58
|
@deploy_url = "https://#{ConfigLoader.app_name}.example.com"
|
59
|
+
puts "Deploying to #{@deploy_url}"
|
72
60
|
end
|
73
61
|
end
|
62
|
+
|
63
|
+
Deployment.build
|
64
|
+
# => Config loaded: MyApp v1.0.0
|
65
|
+
# => Deploying to https://MyApp.example.com
|
74
66
|
```
|
75
67
|
|
76
68
|
### Define API - Dynamic Dependencies
|
77
69
|
|
78
|
-
|
70
|
+
For dependencies that change based on runtime conditions:
|
79
71
|
|
80
72
|
```ruby
|
81
73
|
class EnvironmentConfig < Taski::Task
|
82
74
|
define :database_service, -> {
|
83
|
-
# Dynamic dependency based on environment
|
84
75
|
case ENV['RAILS_ENV']
|
85
76
|
when 'production'
|
86
|
-
|
87
|
-
when 'staging'
|
88
|
-
StagingDatabase.setup
|
77
|
+
"production-db.example.com"
|
89
78
|
else
|
90
|
-
|
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
|
79
|
+
"localhost:5432"
|
100
80
|
end
|
101
81
|
}
|
102
82
|
|
103
83
|
def build
|
104
|
-
puts "Using #{database_service}"
|
105
|
-
puts "
|
84
|
+
puts "Using database: #{database_service}"
|
85
|
+
puts "Environment: #{ENV['RAILS_ENV'] || 'development'}"
|
106
86
|
end
|
107
87
|
end
|
108
|
-
```
|
109
88
|
|
110
|
-
|
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
|
98
|
+
```
|
111
99
|
|
112
100
|
### When to Use Each API
|
113
101
|
|
114
|
-
|
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 |
|
102
|
+
- **Define API**: Best for dynamic runtime dependencies. Cannot contain side effects in definition blocks. Dependencies are analyzed at class definition time, not runtime.
|
103
|
+
- **Exports API**: Ideal for static dependencies. Supports side effects in build methods.
|
121
104
|
|
122
|
-
|
105
|
+
| Use Case | API | Example |
|
106
|
+
|----------|-----|---------|
|
107
|
+
| Configuration values | Exports | File paths, settings |
|
108
|
+
| Environment-specific logic | Define | Different services per env |
|
109
|
+
| Side effects | Exports | Database connections, I/O |
|
110
|
+
| Conditional processing | Define | Algorithm selection |
|
123
111
|
|
124
|
-
|
112
|
+
**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.
|
125
113
|
|
126
|
-
|
114
|
+
## ✨ Key Features
|
127
115
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
116
|
+
- **Automatic Dependency Resolution**: Dependencies detected through static analysis
|
117
|
+
- **Thread-Safe**: Safe for concurrent access
|
118
|
+
- **Circular Dependency Detection**: Clear error messages with detailed paths
|
119
|
+
- **Granular Execution**: Build individual tasks or complete graphs
|
120
|
+
- **Memory Management**: Built-in reset mechanisms
|
133
121
|
|
134
|
-
|
135
|
-
results = threads.map(&:value)
|
136
|
-
```
|
122
|
+
### Granular Task Execution
|
137
123
|
|
138
|
-
|
139
|
-
|
140
|
-
Comprehensive error handling with custom exception types:
|
124
|
+
Execute any task individually - Taski builds only required dependencies:
|
141
125
|
|
142
126
|
```ruby
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
127
|
+
# Build specific components
|
128
|
+
ConfigLoader.build # Builds only ConfigLoader
|
129
|
+
# => Config loaded: MyApp v1.0.0
|
130
|
+
|
131
|
+
EnvironmentConfig.build # Builds EnvironmentConfig and its dependencies
|
132
|
+
# => Using database: localhost:5432
|
133
|
+
# => Environment: development
|
134
|
+
|
135
|
+
# Access values (triggers build if needed)
|
136
|
+
puts ConfigLoader.version # Builds ConfigLoader if not built
|
137
|
+
# => 1.0.0
|
150
138
|
```
|
151
139
|
|
152
140
|
### Lifecycle Management
|
153
141
|
|
154
|
-
|
142
|
+
Tasks can define both build and clean methods. Clean operations run in reverse dependency order:
|
155
143
|
|
156
144
|
```ruby
|
157
|
-
class
|
145
|
+
class DatabaseSetup < Taski::Task
|
146
|
+
exports :connection
|
147
|
+
|
158
148
|
def build
|
159
|
-
|
160
|
-
puts "
|
149
|
+
@connection = "db-connection"
|
150
|
+
puts "Database connected"
|
161
151
|
end
|
162
152
|
|
163
153
|
def clean
|
164
|
-
|
165
|
-
puts "Cleaning up temporary files..."
|
154
|
+
puts "Database disconnected"
|
166
155
|
end
|
167
156
|
end
|
168
157
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
ProcessingTask.clean
|
174
|
-
```
|
158
|
+
class WebServer < Taski::Task
|
159
|
+
def build
|
160
|
+
puts "Web server started with #{DatabaseSetup.connection}"
|
161
|
+
end
|
175
162
|
|
176
|
-
|
163
|
+
def clean
|
164
|
+
puts "Web server stopped"
|
165
|
+
end
|
166
|
+
end
|
177
167
|
|
178
|
-
|
168
|
+
WebServer.build
|
169
|
+
# => Database connected
|
170
|
+
# => Web server started with db-connection
|
179
171
|
|
180
|
-
|
181
|
-
#
|
182
|
-
|
183
|
-
|
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
|
-
}
|
172
|
+
WebServer.clean
|
173
|
+
# => Web server stopped
|
174
|
+
# => Database disconnected
|
175
|
+
```
|
193
176
|
|
194
|
-
|
195
|
-
if FeatureFlag.enabled?(:redis_cache)
|
196
|
-
RedisService.configuration
|
197
|
-
else
|
198
|
-
nil
|
199
|
-
end
|
200
|
-
}
|
201
|
-
end
|
177
|
+
### Clean Method Idempotency
|
202
178
|
|
203
|
-
|
204
|
-
class AppConfig < Taski::Task
|
205
|
-
exports :app_name, :version, :port
|
179
|
+
**Important**: The `clean` method must be idempotent - safe to call multiple times without errors.
|
206
180
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
@port = ENV.fetch('PORT', 3000).to_i
|
211
|
-
end
|
212
|
-
end
|
181
|
+
```ruby
|
182
|
+
class FileTask < Taski::Task
|
183
|
+
exports :output_file
|
213
184
|
|
214
|
-
# Application startup combining both APIs
|
215
|
-
class Application < Taski::Task
|
216
185
|
def build
|
217
|
-
|
218
|
-
|
219
|
-
puts "Redis: #{Environment.redis_config || 'disabled'}"
|
220
|
-
puts "Port: #{AppConfig.port}"
|
221
|
-
|
222
|
-
# Start the application...
|
186
|
+
@output_file = '/tmp/data.csv'
|
187
|
+
File.write(@output_file, process_data)
|
223
188
|
end
|
224
189
|
|
225
190
|
def clean
|
226
|
-
|
227
|
-
#
|
191
|
+
# ❌ Bad: Raises error if file doesn't exist
|
192
|
+
# File.delete(@output_file)
|
193
|
+
|
194
|
+
# ✅ Good: Check before delete
|
195
|
+
File.delete(@output_file) if File.exist?(@output_file)
|
228
196
|
end
|
229
197
|
end
|
230
|
-
|
231
|
-
# Everything runs in the correct order automatically
|
232
|
-
Application.build
|
233
198
|
```
|
234
199
|
|
235
|
-
|
200
|
+
### Error Handling
|
236
201
|
|
237
|
-
|
202
|
+
```ruby
|
203
|
+
begin
|
204
|
+
TaskWithCircularDep.build
|
205
|
+
rescue Taski::CircularDependencyError => e
|
206
|
+
puts "Circular dependency: #{e.message}"
|
207
|
+
end
|
208
|
+
# => Circular dependency: Circular dependency detected!
|
209
|
+
# => Cycle: TaskA → TaskB → TaskA
|
210
|
+
# =>
|
211
|
+
# => The dependency chain is:
|
212
|
+
# => 1. TaskA is trying to build → TaskB
|
213
|
+
# => 2. TaskB is trying to build → TaskA
|
214
|
+
```
|
238
215
|
|
239
|
-
|
216
|
+
## 📦 Installation
|
240
217
|
|
241
218
|
```ruby
|
242
219
|
gem 'taski'
|
243
220
|
```
|
244
221
|
|
245
|
-
And then execute:
|
246
|
-
|
247
222
|
```bash
|
248
223
|
bundle install
|
249
224
|
```
|
250
225
|
|
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
226
|
## 🧪 Testing
|
265
227
|
|
266
|
-
Taski includes comprehensive test coverage. Run the test suite:
|
267
|
-
|
268
228
|
```bash
|
269
229
|
bundle exec rake test
|
270
230
|
```
|
271
231
|
|
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
232
|
## 🏛️ Architecture
|
276
233
|
|
277
|
-
|
278
|
-
|
279
|
-
- **Task Base**: Core framework and constants
|
234
|
+
- **Task Base**: Core framework
|
280
235
|
- **Exports API**: Static dependency resolution
|
281
236
|
- **Define API**: Dynamic dependency resolution
|
282
|
-
- **Instance Management**: Thread-safe lifecycle
|
283
|
-
- **Dependency Resolver**: Topological sorting
|
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
|
237
|
+
- **Instance Management**: Thread-safe lifecycle
|
238
|
+
- **Dependency Resolver**: Topological sorting
|
320
239
|
|
321
240
|
## Contributing
|
322
241
|
|
323
|
-
Bug reports and pull requests
|
242
|
+
Bug reports and pull requests welcome at https://github.com/ahogappa/taski.
|
324
243
|
|
325
244
|
## License
|
326
245
|
|
327
|
-
|
246
|
+
MIT License
|
328
247
|
|
329
248
|
---
|
330
249
|
|
331
|
-
**Taski** - Build
|
332
|
-
|
333
|
-
> **Experimental Software**: Please use responsibly and provide feedback to help us reach v1.0!
|
250
|
+
**Taski** - Build dependency graphs with elegant Ruby code. 🚀
|
data/Rakefile
CHANGED
data/examples/README.md
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
# Taski Examples
|
2
|
+
|
3
|
+
Learn Taski through practical examples, from basic concepts to advanced patterns.
|
4
|
+
|
5
|
+
## Getting Started
|
6
|
+
|
7
|
+
Start with these examples in order:
|
8
|
+
|
9
|
+
### 1. **[quick_start.rb](quick_start.rb)** - Your First Taski Program
|
10
|
+
- Basic task definition with Exports API
|
11
|
+
- Automatic dependency resolution
|
12
|
+
- Simple task execution
|
13
|
+
|
14
|
+
```bash
|
15
|
+
ruby examples/quick_start.rb
|
16
|
+
```
|
17
|
+
|
18
|
+
### 2. **[progress_demo.rb](progress_demo.rb)** - Rich CLI Progress Display
|
19
|
+
- Animated spinner with ANSI colors
|
20
|
+
- Real-time output capture and 5-line tail
|
21
|
+
- Production build scenarios
|
22
|
+
- TTY detection for clean file output
|
23
|
+
|
24
|
+
```bash
|
25
|
+
# Interactive mode with rich spinner
|
26
|
+
ruby examples/progress_demo.rb
|
27
|
+
|
28
|
+
# Clean output mode (no spinner)
|
29
|
+
ruby examples/progress_demo.rb > build.log 2>&1
|
30
|
+
cat build.log
|
31
|
+
```
|
32
|
+
|
33
|
+
### 3. **[advanced_patterns.rb](advanced_patterns.rb)** - Complex Dependency Patterns
|
34
|
+
- Mixed Exports API and Define API usage
|
35
|
+
- Environment-specific dependencies
|
36
|
+
- Feature flags and conditional logic
|
37
|
+
- Task reset and rebuild scenarios
|
38
|
+
|
39
|
+
```bash
|
40
|
+
ruby examples/advanced_patterns.rb
|
41
|
+
```
|
42
|
+
|
43
|
+
## Key Concepts Demonstrated
|
44
|
+
|
45
|
+
- **Exports API**: Static dependencies with `exports :property`
|
46
|
+
- **Define API**: Dynamic dependencies with `define :property, -> { ... }`
|
47
|
+
- **Progress Display**: Rich terminal output with spinners and colors
|
48
|
+
- **Output Capture**: Tail-style display of task output
|
49
|
+
- **Environment Configuration**: Different behavior based on runtime settings
|
50
|
+
- **Error Handling**: Graceful failure with progress indicators
|
51
|
+
|
52
|
+
## Next Steps
|
53
|
+
|
54
|
+
After exploring these examples:
|
55
|
+
- Read the main documentation
|
56
|
+
- Examine the test files for more usage patterns
|
57
|
+
- Check out the source code in `lib/taski/`
|
@@ -1,7 +1,21 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
#
|
2
|
+
# frozen_string_literal: true
|
3
3
|
|
4
|
-
|
4
|
+
# Taski Advanced Patterns
|
5
|
+
#
|
6
|
+
# This example demonstrates advanced Taski patterns:
|
7
|
+
# - Mixed usage of Exports API and Define API
|
8
|
+
# - Environment-specific dependency resolution
|
9
|
+
# - Feature flag integration with dynamic dependencies
|
10
|
+
# - Task reset and rebuild scenarios
|
11
|
+
# - Conditional dependency evaluation
|
12
|
+
#
|
13
|
+
# Run: ruby examples/advanced_patterns.rb
|
14
|
+
|
15
|
+
require_relative "../lib/taski"
|
16
|
+
|
17
|
+
puts "⚡ Advanced Taski Patterns"
|
18
|
+
puts "=" * 40
|
5
19
|
|
6
20
|
# Mock classes for the example
|
7
21
|
class ProductionDB < Taski::Task
|
@@ -11,7 +25,7 @@ class ProductionDB < Taski::Task
|
|
11
25
|
end
|
12
26
|
end
|
13
27
|
|
14
|
-
class TestDB < Taski::Task
|
28
|
+
class TestDB < Taski::Task
|
15
29
|
exports :connection_string
|
16
30
|
def build
|
17
31
|
@connection_string = "postgres://test-server/app_test"
|
@@ -20,7 +34,7 @@ end
|
|
20
34
|
|
21
35
|
module FeatureFlag
|
22
36
|
def self.enabled?(flag)
|
23
|
-
ENV["FEATURE_#{flag.to_s.upcase}"] ==
|
37
|
+
ENV["FEATURE_#{flag.to_s.upcase}"] == "true"
|
24
38
|
end
|
25
39
|
end
|
26
40
|
|
@@ -34,11 +48,11 @@ end
|
|
34
48
|
# Environment configuration using Define API
|
35
49
|
class Environment < Taski::Task
|
36
50
|
define :database_url, -> {
|
37
|
-
case ENV[
|
38
|
-
when
|
51
|
+
case ENV["RAILS_ENV"]
|
52
|
+
when "production"
|
39
53
|
ProductionDB.connection_string
|
40
|
-
when
|
41
|
-
TestDB.connection_string
|
54
|
+
when "test"
|
55
|
+
TestDB.connection_string
|
42
56
|
else
|
43
57
|
"sqlite3://development.db"
|
44
58
|
end
|
@@ -47,8 +61,6 @@ class Environment < Taski::Task
|
|
47
61
|
define :redis_config, -> {
|
48
62
|
if FeatureFlag.enabled?(:redis_cache)
|
49
63
|
RedisService.configuration
|
50
|
-
else
|
51
|
-
nil
|
52
64
|
end
|
53
65
|
}
|
54
66
|
|
@@ -57,14 +69,14 @@ class Environment < Taski::Task
|
|
57
69
|
end
|
58
70
|
end
|
59
71
|
|
60
|
-
# Static configuration using Exports API
|
72
|
+
# Static configuration using Exports API
|
61
73
|
class AppConfig < Taski::Task
|
62
74
|
exports :app_name, :version, :port
|
63
75
|
|
64
76
|
def build
|
65
77
|
@app_name = "MyWebApp"
|
66
78
|
@version = "2.1.0"
|
67
|
-
@port = ENV.fetch(
|
79
|
+
@port = ENV.fetch("PORT", 3000).to_i
|
68
80
|
end
|
69
81
|
end
|
70
82
|
|
@@ -73,7 +85,7 @@ class Application < Taski::Task
|
|
73
85
|
def build
|
74
86
|
puts "Starting #{AppConfig.app_name} v#{AppConfig.version}"
|
75
87
|
puts "Database: #{Environment.database_url}"
|
76
|
-
puts "Redis: #{Environment.redis_config ||
|
88
|
+
puts "Redis: #{Environment.redis_config || "disabled"}"
|
77
89
|
puts "Port: #{AppConfig.port}"
|
78
90
|
end
|
79
91
|
|
@@ -83,27 +95,25 @@ class Application < Taski::Task
|
|
83
95
|
end
|
84
96
|
|
85
97
|
# Test different environments
|
86
|
-
puts "=== Complex Example ==="
|
87
|
-
|
88
98
|
puts "\n1. Development Environment (default):"
|
89
|
-
ENV.delete(
|
90
|
-
ENV.delete(
|
99
|
+
ENV.delete("RAILS_ENV")
|
100
|
+
ENV.delete("FEATURE_REDIS_CACHE")
|
91
101
|
Application.build
|
92
102
|
Application.reset!
|
93
103
|
|
94
104
|
puts "\n2. Test Environment:"
|
95
|
-
ENV[
|
105
|
+
ENV["RAILS_ENV"] = "test"
|
96
106
|
# Reset Environment to re-evaluate define blocks
|
97
107
|
Environment.reset!
|
98
108
|
Application.build
|
99
109
|
Application.reset!
|
100
110
|
|
101
111
|
puts "\n3. Production with Redis:"
|
102
|
-
ENV[
|
103
|
-
ENV[
|
112
|
+
ENV["RAILS_ENV"] = "production"
|
113
|
+
ENV["FEATURE_REDIS_CACHE"] = "true"
|
104
114
|
# Reset Environment to re-evaluate define blocks
|
105
115
|
Environment.reset!
|
106
116
|
Application.build
|
107
117
|
|
108
118
|
puts "\n4. Cleanup:"
|
109
|
-
Application.clean
|
119
|
+
Application.clean
|