taski 0.3.0 → 0.4.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.
Files changed (119) hide show
  1. checksums.yaml +4 -4
  2. data/.gem_rbs_collection/ast/2.4/.rbs_meta.yaml +9 -0
  3. data/.gem_rbs_collection/ast/2.4/ast.rbs +73 -0
  4. data/.gem_rbs_collection/minitest/5.25/.rbs_meta.yaml +9 -0
  5. data/.gem_rbs_collection/minitest/5.25/minitest/abstract_reporter.rbs +52 -0
  6. data/.gem_rbs_collection/minitest/5.25/minitest/assertion.rbs +17 -0
  7. data/.gem_rbs_collection/minitest/5.25/minitest/assertions.rbs +590 -0
  8. data/.gem_rbs_collection/minitest/5.25/minitest/backtrace_filter.rbs +23 -0
  9. data/.gem_rbs_collection/minitest/5.25/minitest/bench_spec.rbs +102 -0
  10. data/.gem_rbs_collection/minitest/5.25/minitest/benchmark.rbs +259 -0
  11. data/.gem_rbs_collection/minitest/5.25/minitest/composite_reporter.rbs +25 -0
  12. data/.gem_rbs_collection/minitest/5.25/minitest/compress.rbs +13 -0
  13. data/.gem_rbs_collection/minitest/5.25/minitest/error_on_warning.rbs +3 -0
  14. data/.gem_rbs_collection/minitest/5.25/minitest/expectation.rbs +2 -0
  15. data/.gem_rbs_collection/minitest/5.25/minitest/expectations.rbs +21 -0
  16. data/.gem_rbs_collection/minitest/5.25/minitest/guard.rbs +64 -0
  17. data/.gem_rbs_collection/minitest/5.25/minitest/mock.rbs +64 -0
  18. data/.gem_rbs_collection/minitest/5.25/minitest/parallel/executor.rbs +46 -0
  19. data/.gem_rbs_collection/minitest/5.25/minitest/parallel/test/class_methods.rbs +5 -0
  20. data/.gem_rbs_collection/minitest/5.25/minitest/parallel/test.rbs +3 -0
  21. data/.gem_rbs_collection/minitest/5.25/minitest/parallel.rbs +2 -0
  22. data/.gem_rbs_collection/minitest/5.25/minitest/pride_io.rbs +62 -0
  23. data/.gem_rbs_collection/minitest/5.25/minitest/pride_lol.rbs +19 -0
  24. data/.gem_rbs_collection/minitest/5.25/minitest/progress_reporter.rbs +11 -0
  25. data/.gem_rbs_collection/minitest/5.25/minitest/reportable.rbs +53 -0
  26. data/.gem_rbs_collection/minitest/5.25/minitest/reporter.rbs +5 -0
  27. data/.gem_rbs_collection/minitest/5.25/minitest/result.rbs +28 -0
  28. data/.gem_rbs_collection/minitest/5.25/minitest/runnable.rbs +163 -0
  29. data/.gem_rbs_collection/minitest/5.25/minitest/skip.rbs +6 -0
  30. data/.gem_rbs_collection/minitest/5.25/minitest/spec/dsl/instance_methods.rbs +48 -0
  31. data/.gem_rbs_collection/minitest/5.25/minitest/spec/dsl.rbs +129 -0
  32. data/.gem_rbs_collection/minitest/5.25/minitest/spec.rbs +11 -0
  33. data/.gem_rbs_collection/minitest/5.25/minitest/statistics_reporter.rbs +81 -0
  34. data/.gem_rbs_collection/minitest/5.25/minitest/summary_reporter.rbs +18 -0
  35. data/.gem_rbs_collection/minitest/5.25/minitest/test/lifecycle_hooks.rbs +92 -0
  36. data/.gem_rbs_collection/minitest/5.25/minitest/test.rbs +69 -0
  37. data/.gem_rbs_collection/minitest/5.25/minitest/unexpected_error.rbs +12 -0
  38. data/.gem_rbs_collection/minitest/5.25/minitest/unexpected_warning.rbs +6 -0
  39. data/.gem_rbs_collection/minitest/5.25/minitest/unit/test_case.rbs +3 -0
  40. data/.gem_rbs_collection/minitest/5.25/minitest/unit.rbs +4 -0
  41. data/.gem_rbs_collection/minitest/5.25/minitest.rbs +115 -0
  42. data/.gem_rbs_collection/parallel/1.20/.rbs_meta.yaml +9 -0
  43. data/.gem_rbs_collection/parallel/1.20/parallel.rbs +86 -0
  44. data/.gem_rbs_collection/parser/3.2/.rbs_meta.yaml +9 -0
  45. data/.gem_rbs_collection/parser/3.2/manifest.yaml +7 -0
  46. data/.gem_rbs_collection/parser/3.2/parser.rbs +193 -0
  47. data/.gem_rbs_collection/parser/3.2/polyfill.rbs +4 -0
  48. data/.gem_rbs_collection/rainbow/3.0/.rbs_meta.yaml +9 -0
  49. data/.gem_rbs_collection/rainbow/3.0/global.rbs +7 -0
  50. data/.gem_rbs_collection/rainbow/3.0/presenter.rbs +209 -0
  51. data/.gem_rbs_collection/rainbow/3.0/rainbow.rbs +5 -0
  52. data/.gem_rbs_collection/rake/13.0/.rbs_meta.yaml +9 -0
  53. data/.gem_rbs_collection/rake/13.0/manifest.yaml +2 -0
  54. data/.gem_rbs_collection/rake/13.0/rake.rbs +39 -0
  55. data/.gem_rbs_collection/regexp_parser/2.8/.rbs_meta.yaml +9 -0
  56. data/.gem_rbs_collection/regexp_parser/2.8/regexp_parser.rbs +17 -0
  57. data/.gem_rbs_collection/rubocop/1.57/.rbs_meta.yaml +9 -0
  58. data/.gem_rbs_collection/rubocop/1.57/rubocop.rbs +129 -0
  59. data/.gem_rbs_collection/rubocop-ast/1.30/.rbs_meta.yaml +9 -0
  60. data/.gem_rbs_collection/rubocop-ast/1.30/rubocop-ast.rbs +771 -0
  61. data/.gem_rbs_collection/simplecov/0.22/.rbs_meta.yaml +9 -0
  62. data/.gem_rbs_collection/simplecov/0.22/simplecov.rbs +54 -0
  63. data/README.md +137 -248
  64. data/Steepfile +19 -0
  65. data/docs/advanced-features.md +625 -0
  66. data/docs/api-guide.md +509 -0
  67. data/docs/error-handling.md +684 -0
  68. data/examples/README.md +95 -42
  69. data/examples/context_demo.rb +112 -0
  70. data/examples/data_pipeline_demo.rb +231 -0
  71. data/examples/parallel_progress_demo.rb +72 -0
  72. data/examples/quick_start.rb +4 -4
  73. data/examples/reexecution_demo.rb +127 -0
  74. data/examples/{section_configuration.rb → section_demo.rb} +49 -66
  75. data/lib/taski/context.rb +52 -0
  76. data/lib/taski/execution/coordinator.rb +63 -0
  77. data/lib/taski/execution/parallel_progress_display.rb +201 -0
  78. data/lib/taski/execution/registry.rb +72 -0
  79. data/lib/taski/execution/task_wrapper.rb +255 -0
  80. data/lib/taski/section.rb +26 -250
  81. data/lib/taski/static_analysis/analyzer.rb +34 -0
  82. data/lib/taski/static_analysis/dependency_graph.rb +90 -0
  83. data/lib/taski/static_analysis/visitor.rb +114 -0
  84. data/lib/taski/task.rb +173 -0
  85. data/lib/taski/version.rb +1 -1
  86. data/lib/taski.rb +45 -39
  87. data/rbs_collection.lock.yaml +116 -0
  88. data/rbs_collection.yaml +19 -0
  89. data/sig/taski.rbs +269 -62
  90. metadata +97 -32
  91. data/examples/advanced_patterns.rb +0 -119
  92. data/examples/progress_demo.rb +0 -166
  93. data/examples/tree_demo.rb +0 -205
  94. data/lib/taski/dependency_analyzer.rb +0 -231
  95. data/lib/taski/exceptions.rb +0 -17
  96. data/lib/taski/logger.rb +0 -158
  97. data/lib/taski/logging/formatter_factory.rb +0 -34
  98. data/lib/taski/logging/formatter_interface.rb +0 -19
  99. data/lib/taski/logging/json_formatter.rb +0 -26
  100. data/lib/taski/logging/simple_formatter.rb +0 -16
  101. data/lib/taski/logging/structured_formatter.rb +0 -44
  102. data/lib/taski/progress/display_colors.rb +0 -17
  103. data/lib/taski/progress/display_manager.rb +0 -115
  104. data/lib/taski/progress/output_capture.rb +0 -105
  105. data/lib/taski/progress/spinner_animation.rb +0 -46
  106. data/lib/taski/progress/task_formatter.rb +0 -25
  107. data/lib/taski/progress/task_status.rb +0 -38
  108. data/lib/taski/progress/terminal_controller.rb +0 -35
  109. data/lib/taski/progress_display.rb +0 -59
  110. data/lib/taski/reference.rb +0 -40
  111. data/lib/taski/task/base.rb +0 -90
  112. data/lib/taski/task/define_api.rb +0 -154
  113. data/lib/taski/task/dependency_resolver.rb +0 -73
  114. data/lib/taski/task/exports_api.rb +0 -31
  115. data/lib/taski/task/instance_management.rb +0 -203
  116. data/lib/taski/tree_colors.rb +0 -91
  117. data/lib/taski/utils/dependency_resolver_helper.rb +0 -85
  118. data/lib/taski/utils/tree_display_helper.rb +0 -71
  119. data/lib/taski/utils.rb +0 -107
@@ -1,166 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Taski Progress Display Demo
5
- #
6
- # This comprehensive example demonstrates all progress display features:
7
- # 1. Basic spinner animation with success/failure indicators
8
- # 2. Output capture with 5-line tail display
9
- # 3. Real-world build scenario with external command simulation
10
- # 4. TTY detection for clean file output
11
- #
12
- # Run: ruby examples/progress_demo.rb
13
- # Try: ruby examples/progress_demo.rb > build.log 2>&1 && cat build.log
14
-
15
- require_relative "../lib/taski"
16
-
17
- puts "🎯 Taski Progress Display Demo"
18
- puts "=" * 50
19
-
20
- # SECTION 1: Basic Spinner Animation
21
- puts "\n📍 SECTION 1: Basic Spinner & Success/Failure Indicators"
22
- puts "-" * 50
23
-
24
- class ConfigTask < Taski::Task
25
- exports :database_url, :cache_url
26
-
27
- def build
28
- sleep 0.8
29
- @database_url = "postgres://localhost/myapp"
30
- @cache_url = "redis://localhost:6379"
31
- end
32
- end
33
-
34
- class DatabaseTask < Taski::Task
35
- exports :connection
36
-
37
- def build
38
- sleep 1.2
39
- @connection = "Connected to #{ConfigTask.database_url}"
40
- end
41
- end
42
-
43
- class ApplicationTask < Taski::Task
44
- exports :status
45
-
46
- def build
47
- sleep 1.0
48
- db = DatabaseTask.connection
49
- @status = "App ready! #{db}"
50
- end
51
- end
52
-
53
- ApplicationTask.build
54
- puts "🎉 Application Status: #{ApplicationTask.status}"
55
-
56
- # SECTION 2: Output Capture Demo
57
- puts "\n📍 SECTION 2: Output Capture with 5-Line Tail"
58
- puts "-" * 50
59
-
60
- class VerboseTask < Taski::Task
61
- exports :result
62
-
63
- def build
64
- puts "Starting task initialization..."
65
- sleep 0.3
66
-
67
- puts "Loading configuration files..."
68
- puts "Connecting to database..."
69
- puts "Connection established: localhost:5432"
70
- sleep 0.3
71
-
72
- puts "Running initial checks..."
73
- puts "Checking schema version..."
74
- puts "Schema is up to date"
75
- puts "Performing data validation..."
76
- puts "Validating user records..."
77
- puts "Validating product records..."
78
- puts "All validations passed"
79
- sleep 0.4
80
-
81
- puts "Task completed successfully!"
82
- @result = "All operations completed"
83
- end
84
- end
85
-
86
- VerboseTask.build
87
- puts "📊 Verbose Task Result: #{VerboseTask.result}"
88
-
89
- # SECTION 3: Production Build Scenario
90
- puts "\n📍 SECTION 3: Production Build Scenario"
91
- puts "-" * 50
92
-
93
- class CompileTask < Taski::Task
94
- exports :result
95
-
96
- def build
97
- puts "Starting compilation process..."
98
- sleep 0.8
99
-
100
- puts "Checking source files..."
101
- puts "Found: main.c, utils.c, config.h"
102
- sleep 0.6
103
-
104
- puts "Running gcc compilation..."
105
- puts "gcc -Wall -O2 -c main.c"
106
- puts "gcc -Wall -O2 -c utils.c"
107
- puts "main.c: In function 'main':"
108
- puts "main.c:42: warning: unused variable 'temp'"
109
- puts "utils.c: In function 'parse_config':"
110
- puts "utils.c:15: warning: implicit declaration of function 'strcpy'"
111
- sleep 0.8
112
-
113
- puts "Linking objects..."
114
- puts "gcc -o myapp main.o utils.o"
115
- puts "Compilation successful!"
116
-
117
- @result = "myapp binary created"
118
- end
119
- end
120
-
121
- class TestTask < Taski::Task
122
- exports :test_result
123
-
124
- def build
125
- puts "Running test suite..."
126
- sleep 0.2
127
-
128
- (1..8).each do |i|
129
- puts "Test #{i}/8: #{["PASS", "PASS", "FAIL", "PASS", "PASS", "PASS", "PASS", "PASS"][i - 1]}"
130
- sleep 0.4
131
- end
132
-
133
- puts "Test summary: 7/8 passed, 1 failed"
134
- @test_result = "Tests completed with 1 failure"
135
- end
136
- end
137
-
138
- CompileTask.build
139
- puts "📦 Compilation: #{CompileTask.result}"
140
-
141
- TestTask.build
142
- puts "🧪 Test Result: #{TestTask.test_result}"
143
-
144
- # SECTION 4: Error Handling Demo
145
- puts "\n📍 SECTION 4: Error Handling Demo"
146
- puts "-" * 50
147
-
148
- class FailingTask < Taski::Task
149
- def build
150
- puts "Attempting network connection..."
151
- sleep 1.0 # Watch it spin before failing
152
- puts "Connection timeout after 30 seconds"
153
- puts "Retrying connection..."
154
- sleep 0.5
155
- raise StandardError, "Network connection failed!"
156
- end
157
- end
158
-
159
- begin
160
- FailingTask.build
161
- rescue Taski::TaskBuildError => e
162
- puts "🛡️ Error handled gracefully: #{e.message}"
163
- end
164
-
165
- puts "\n✨ Demo Complete!"
166
- puts "Note: Rich spinner display only appears in terminals, not when output is redirected."
@@ -1,205 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Tree Display Demo
5
- #
6
- # This example demonstrates the tree display functionality that shows
7
- # task dependency relationships in a visual tree format.
8
- #
9
- # Run: ruby examples/tree_demo.rb
10
-
11
- require_relative "../lib/taski"
12
-
13
- puts "🌲 Taski Tree Display Demo"
14
- puts "=" * 40
15
-
16
- # Create a dependency chain for demonstration
17
- class Database < Taski::Task
18
- exports :connection_string
19
-
20
- def build
21
- @connection_string = "postgres://localhost/myapp"
22
- end
23
- end
24
-
25
- class Cache < Taski::Task
26
- exports :redis_url
27
-
28
- def build
29
- @redis_url = "redis://localhost:6379"
30
- end
31
- end
32
-
33
- class Config < Taski::Task
34
- exports :settings
35
-
36
- def build
37
- @settings = {
38
- database: Database.connection_string,
39
- cache: Cache.redis_url,
40
- port: 3000
41
- }
42
- end
43
- end
44
-
45
- class Logger < Taski::Task
46
- exports :log_level
47
-
48
- def build
49
- @log_level = "info"
50
- end
51
- end
52
-
53
- class WebServer < Taski::Task
54
- exports :server_instance
55
-
56
- def build
57
- @server_instance = "WebServer configured with #{Config.settings[:database]} and #{Logger.log_level}"
58
- end
59
- end
60
-
61
- class Application < Taski::Task
62
- def build
63
- puts "Starting application..."
64
- puts "Web server: #{WebServer.server_instance}"
65
- puts "Config: #{Config.settings}"
66
- end
67
- end
68
-
69
- puts "\n📊 Application Dependency Tree:"
70
- puts Application.tree
71
-
72
- puts "\n🔍 Individual Component Trees:"
73
- puts "\nWebServer dependencies:"
74
- puts WebServer.tree
75
-
76
- puts "\nConfig dependencies:"
77
- puts Config.tree
78
-
79
- puts "\n🔧 Section-based Architecture (Dynamic Implementation Selection):"
80
-
81
- # Create database section with multiple implementation options
82
- class DatabaseSection < Taski::Section
83
- interface :connection_string, :pool_size
84
-
85
- def self.impl
86
- if ENV["DATABASE"] == "postgres"
87
- PostgresImplementation
88
- elsif ENV["DATABASE"] == "mysql"
89
- MysqlImplementation
90
- else
91
- SQLiteImplementation
92
- end
93
- end
94
-
95
- class PostgresImplementation < Taski::Task
96
- exports :connection_string, :pool_size
97
-
98
- def build
99
- Logger.log_level
100
- @connection_string = "postgresql://localhost/production_app"
101
- @pool_size = 20
102
- end
103
- end
104
-
105
- class MysqlImplementation < Taski::Task
106
- exports :connection_string, :pool_size
107
-
108
- def build
109
- Logger.log_level
110
- @connection_string = "mysql://localhost/production_app"
111
- @pool_size = 15
112
- end
113
- end
114
-
115
- class SQLiteImplementation < Taski::Task
116
- exports :connection_string, :pool_size
117
-
118
- def build
119
- @connection_string = "sqlite:///tmp/development.db"
120
- @pool_size = 1
121
- end
122
- end
123
- end
124
-
125
- # Cache section with Redis/Memory options
126
- class CacheSection < Taski::Section
127
- interface :cache_url
128
-
129
- def self.impl
130
- if ENV["CACHE"] == "redis"
131
- RedisCache
132
- else
133
- MemoryCache
134
- end
135
- end
136
-
137
- class RedisCache < Taski::Task
138
- exports :cache_url
139
- def build
140
- DatabaseSection.connection_string
141
- @cache_url = "redis://localhost:6379"
142
- end
143
- end
144
-
145
- class MemoryCache < Taski::Task
146
- exports :cache_url
147
- def build
148
- @cache_url = "memory://local"
149
- end
150
- end
151
- end
152
-
153
- puts "\n📋 Section Trees (Show Available Implementations):"
154
- puts "\nDatabaseSection.tree:"
155
- puts DatabaseSection.tree
156
-
157
- puts "\nCacheSection.tree:"
158
- puts CacheSection.tree
159
-
160
- puts "\n🔍 Individual Implementation Trees (Show Actual Dependencies):"
161
- puts "\nDatabaseSection::PostgresImplementation.tree:"
162
- puts DatabaseSection::PostgresImplementation.tree
163
-
164
- puts "\nDatabaseSection::SQLiteImplementation.tree:"
165
- puts DatabaseSection::SQLiteImplementation.tree
166
-
167
- puts "\nCacheSection::RedisCache.tree:"
168
- puts CacheSection::RedisCache.tree
169
-
170
- puts "\n🔄 Section vs Implementation Comparison:"
171
- puts "Section shows POSSIBLE implementations:"
172
- puts DatabaseSection.tree
173
- puts "\nBut implementation shows ACTUAL dependencies:"
174
- puts DatabaseSection::PostgresImplementation.tree
175
-
176
- puts "\n💡 Workflow:"
177
- puts "1. Use DatabaseSection.tree to see what implementations are available"
178
- puts "2. Use DatabaseSection::PostgresImplementation.tree to see specific dependencies"
179
- puts "3. Runtime selects implementation based on ENV variables"
180
-
181
- puts "\n🎨 Colored Tree Display (if TTY supports colors):"
182
-
183
- # Enable colors for demonstration
184
- Taski::TreeColors.enabled = true
185
-
186
- puts "\nDatabaseSection.tree (with colors):"
187
- puts DatabaseSection.tree(color: true)
188
-
189
- puts "\nCacheSection.tree (with colors):"
190
- puts CacheSection.tree(color: true)
191
-
192
- puts "\nDatabaseSection::PostgresImplementation.tree (with colors):"
193
- puts DatabaseSection::PostgresImplementation.tree(color: true)
194
-
195
- puts "\n🎯 Color Legend:"
196
- puts "#{Taski::TreeColors.section("Blue")} = Section names (dynamic selection layer)"
197
- puts "#{Taski::TreeColors.task("Green")} = Task names (concrete implementations)"
198
- puts "#{Taski::TreeColors.implementations("Yellow")} = Implementation candidates"
199
- puts "#{Taski::TreeColors.connector("Gray")} = Tree connectors"
200
-
201
- # Reset colors to auto-detection
202
- Taski::TreeColors.enabled = nil
203
-
204
- puts "\n▶️ Building Application (to verify dependencies work):"
205
- Application.build
@@ -1,231 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "prism"
4
-
5
- module Taski
6
- module DependencyAnalyzer
7
- class << self
8
- def analyze_method(klass, method_name)
9
- return [] unless klass.instance_methods(false).include?(method_name)
10
-
11
- method = klass.instance_method(method_name)
12
- source_location = method.source_location
13
- return [] unless source_location
14
-
15
- file_path, line_number = source_location
16
- return [] unless File.exist?(file_path)
17
-
18
- parse_source_file(file_path, line_number, klass, method_name)
19
- end
20
-
21
- private
22
-
23
- # Parse source file and extract dependencies with proper error handling
24
- # @param file_path [String] Path to source file
25
- # @param line_number [Integer] Line number of method definition
26
- # @param klass [Class] Class containing the method
27
- # @param method_name [Symbol] Method name being analyzed
28
- # @return [Array<Class>] Array of dependency classes
29
- def parse_source_file(file_path, line_number, klass, method_name)
30
- result = Prism.parse_file(file_path)
31
- handle_parse_errors(result, file_path, klass, method_name)
32
- extract_dependencies_from_node(result.value, line_number, klass, method_name)
33
- rescue IOError, SystemCallError => e
34
- Taski.logger.error("Failed to read source file",
35
- file: file_path,
36
- error: e.message,
37
- method: "#{klass}##{method_name}")
38
- []
39
- rescue => e
40
- Taski.logger.error("Failed to analyze method dependencies",
41
- class: klass.name,
42
- method: method_name,
43
- error: e.message,
44
- error_class: e.class.name)
45
- []
46
- end
47
-
48
- # Handle parse errors and warnings from Prism parsing
49
- # @param result [Prism::ParseResult] Parse result from Prism
50
- # @param file_path [String] Path to source file
51
- # @param klass [Class] Class containing the method
52
- # @param method_name [Symbol] Method name being analyzed
53
- # @return [Array] Empty array if errors found
54
- # @raise [RuntimeError] If parse fails
55
- def handle_parse_errors(result, file_path, klass, method_name)
56
- unless result.success?
57
- Taski.logger.error("Parse errors in source file",
58
- file: file_path,
59
- errors: result.errors.map(&:message),
60
- method: "#{klass}##{method_name}")
61
- return []
62
- end
63
-
64
- # Handle warnings if present
65
- if result.warnings.any?
66
- Taski.logger.warn("Parse warnings in source file",
67
- file: file_path,
68
- warnings: result.warnings.map(&:message),
69
- method: "#{klass}##{method_name}")
70
- end
71
- end
72
-
73
- # Extract dependencies from parsed AST node
74
- # @param root_node [Prism::Node] Root AST node
75
- # @param line_number [Integer] Line number of method definition
76
- # @param klass [Class] Class containing the method
77
- # @param method_name [Symbol] Method name being analyzed
78
- # @return [Array<Class>] Array of unique dependency classes
79
- def extract_dependencies_from_node(root_node, line_number, klass, method_name)
80
- dependencies = []
81
- method_node = find_method_node(root_node, method_name, line_number)
82
-
83
- if method_node
84
- visitor = TaskDependencyVisitor.new(klass)
85
- visitor.visit(method_node)
86
- dependencies = visitor.dependencies
87
- end
88
-
89
- dependencies.uniq
90
- end
91
-
92
- def find_method_node(node, method_name, target_line)
93
- return nil unless node
94
-
95
- case node
96
- when Prism::DefNode
97
- if node.name == method_name && node.location.start_line <= target_line && node.location.end_line >= target_line
98
- return node
99
- end
100
- when Prism::ClassNode, Prism::ModuleNode
101
- if node.respond_to?(:body)
102
- return find_method_node(node.body, method_name, target_line)
103
- end
104
- when Prism::StatementsNode
105
- node.body.each do |child|
106
- result = find_method_node(child, method_name, target_line)
107
- return result if result
108
- end
109
- end
110
-
111
- # Recursively search child nodes
112
- if node.respond_to?(:child_nodes)
113
- node.child_nodes.each do |child|
114
- result = find_method_node(child, method_name, target_line)
115
- return result if result
116
- end
117
- end
118
-
119
- nil
120
- end
121
-
122
- # Task dependency visitor using Prism's visitor pattern
123
- class TaskDependencyVisitor < Prism::Visitor
124
- attr_reader :dependencies
125
-
126
- def initialize(context_class = nil)
127
- @dependencies = []
128
- @constant_cache = {}
129
- @context_class = context_class
130
- end
131
-
132
- def visit_constant_read_node(node)
133
- check_task_constant(node.name.to_s)
134
- super
135
- end
136
-
137
- def visit_constant_path_node(node)
138
- const_path = extract_constant_path(node)
139
- check_task_constant(const_path) if const_path
140
- super
141
- end
142
-
143
- def visit_call_node(node)
144
- # Check for method calls on constants (e.g., TaskA.result)
145
- case node.receiver
146
- when Prism::ConstantReadNode
147
- check_task_constant(node.receiver.name.to_s)
148
- when Prism::ConstantPathNode
149
- const_path = extract_constant_path(node.receiver)
150
- check_task_constant(const_path) if const_path
151
- end
152
- super
153
- end
154
-
155
- private
156
-
157
- def check_task_constant(const_name)
158
- return unless const_name
159
-
160
- # Use caching to avoid repeated constant resolution
161
- cached_result = @constant_cache[const_name]
162
- return cached_result if cached_result == false # Cached negative result
163
- return @dependencies << cached_result if cached_result # Cached positive result
164
-
165
- begin
166
- resolved_class = nil
167
-
168
- # 1. Try absolute reference first (existing logic)
169
- if Object.const_defined?(const_name)
170
- resolved_class = Object.const_get(const_name)
171
- # 2. Try relative reference within namespace context
172
- elsif @context_class
173
- resolved_class = resolve_relative_constant(const_name)
174
- end
175
-
176
- if resolved_class&.is_a?(Class) && (resolved_class < Taski::Task || resolved_class < Taski::Section)
177
- @constant_cache[const_name] = resolved_class
178
- @dependencies << resolved_class
179
- else
180
- @constant_cache[const_name] = false
181
- end
182
- rescue NameError, ArgumentError
183
- @constant_cache[const_name] = false
184
- end
185
- end
186
-
187
- def resolve_relative_constant(const_name)
188
- return nil unless @context_class
189
-
190
- # Get the namespace from the context class
191
- namespace = get_namespace_from_class(@context_class)
192
- return nil unless namespace
193
-
194
- # Try to resolve the constant within the namespace
195
- full_const_name = "#{namespace}::#{const_name}"
196
- Object.const_get(full_const_name) if Object.const_defined?(full_const_name)
197
- rescue NameError, ArgumentError
198
- nil
199
- end
200
-
201
- def get_namespace_from_class(klass)
202
- # Extract namespace from class name (e.g., "A::AB" -> "A")
203
- class_name = klass.name
204
- return nil unless class_name&.include?("::")
205
-
206
- # Split by "::" and take all but the last part
207
- parts = class_name.split("::")
208
- return nil if parts.length <= 1 # No namespace
209
-
210
- parts[0..-2].join("::")
211
- end
212
-
213
- def extract_constant_path(node)
214
- case node
215
- when Prism::ConstantReadNode
216
- node.name.to_s
217
- when Prism::ConstantPathNode
218
- parent_path = extract_constant_path(node.parent) if node.parent
219
- child_name = node.name.to_s
220
-
221
- if parent_path && child_name
222
- "#{parent_path}::#{child_name}"
223
- else
224
- child_name
225
- end
226
- end
227
- end
228
- end
229
- end
230
- end
231
- end
@@ -1,17 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Taski
4
- # Custom exceptions for Taski framework
5
-
6
- # Raised when circular dependencies are detected between tasks
7
- class CircularDependencyError < StandardError; end
8
-
9
- # Raised when task analysis fails (e.g., constant resolution errors)
10
- class TaskAnalysisError < StandardError; end
11
-
12
- # Raised when task building fails during execution
13
- class TaskBuildError < StandardError; end
14
-
15
- # Raised when section implementation method is missing
16
- class SectionImplementationError < StandardError; end
17
- end