taski 0.4.1 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46cb1222487f928c10241827374311ed65541773bd1cd6d40d8fd2e6ea8af64e
4
- data.tar.gz: c16aad068cef227130a8051fd71c8847845212dc3f990ae706b2d1b10104a9d6
3
+ metadata.gz: 537249ee49e056c8841cc1c4d4061eccf7be201b3690a197af8d5a5b65939173
4
+ data.tar.gz: f012517341ea31b2e36f4a87e51ab199cb24d70d7ba10519653a2d290c78dcaf
5
5
  SHA512:
6
- metadata.gz: a37a616b526c7c67811286e2c50dd5319c9ce98779f4edf495f6337c214c9f0915b46694f4b8edfd5a8a6b7f66ce9fb9d946ffd36cf0be2f2a4991452875a765
7
- data.tar.gz: ff577241138a18dcf8018c4c0c68ea8137301e33bfa6b31d58e564862eeb8f520a9120c755aa0c6acf61b5c1f18b71bb728dd785627ee278c3c6eee08d31dc30
6
+ metadata.gz: e15871fa35cf862eae794b823ab760adcd1ca1156cb23b0d27d705e8c5fb0ddfa3f2dd17859f7d238a6f0e9dbf558bedf5148330ffbf239a7291f3c99b037e07
7
+ data.tar.gz: d1ca33867403c337c2d1638acae3a2fd47ca77a707e7df2b6076d8c22c8d289ceae73ba6e4f1f30d413c784c30f999d60648a270c3b49a398e3144d85fd63837
data/lib/taski/section.rb CHANGED
@@ -9,6 +9,13 @@ module Taski
9
9
  def interfaces(*interface_methods)
10
10
  exports(*interface_methods)
11
11
  end
12
+
13
+ # Section does not have static dependencies for execution.
14
+ # The impl method is called at runtime to determine the actual implementation.
15
+ # Static dependencies (impl candidates) are only used for tree display and circular detection.
16
+ def cached_dependencies
17
+ Set.new
18
+ end
12
19
  end
13
20
 
14
21
  def run
@@ -6,8 +6,17 @@ require_relative "visitor"
6
6
  module Taski
7
7
  module StaticAnalysis
8
8
  class Analyzer
9
+ # Analyzes a task class and returns its static dependencies.
10
+ # For Task: dependencies detected from run method (SomeTask.method calls)
11
+ # For Section: impl candidates detected from impl method (constants returned)
12
+ #
13
+ # Static dependencies are used for:
14
+ # - Tree display visualization
15
+ # - Circular dependency detection
16
+ # - Task execution (for Task only; Section resolves impl at runtime)
17
+ #
9
18
  # @param task_class [Class] The task class to analyze
10
- # @return [Set<Class>] Set of task classes that are dependencies
19
+ # @return [Set<Class>] Set of task classes that are static dependencies
11
20
  def self.analyze(task_class)
12
21
  target_method = target_method_for(task_class)
13
22
  source_location = extract_method_location(task_class, target_method)
@@ -7,6 +7,8 @@ module Taski
7
7
  class Visitor < Prism::Visitor
8
8
  attr_reader :dependencies
9
9
 
10
+ # @param target_task_class [Class] The task class to analyze
11
+ # @param target_method [Symbol] The method name to analyze (:run or :impl)
10
12
  def initialize(target_task_class, target_method = :run)
11
13
  super()
12
14
  @target_task_class = target_task_class
@@ -40,12 +42,14 @@ module Taski
40
42
  end
41
43
 
42
44
  def visit_constant_read_node(node)
43
- detect_return_constant(node) if @in_target_method && @target_method == :impl
45
+ # For Section.impl, detect constants as impl candidates (static dependencies)
46
+ detect_impl_candidate(node) if in_impl_method?
44
47
  super
45
48
  end
46
49
 
47
50
  def visit_constant_path_node(node)
48
- detect_return_constant(node) if @in_target_method && @target_method == :impl
51
+ # For Section.impl, detect constants as impl candidates (static dependencies)
52
+ detect_impl_candidate(node) if in_impl_method?
49
53
  super
50
54
  end
51
55
 
@@ -66,6 +70,15 @@ module Taski
66
70
  node.slice
67
71
  end
68
72
 
73
+ def in_impl_method?
74
+ @in_target_method && @target_method == :impl
75
+ end
76
+
77
+ def detect_impl_candidate(node)
78
+ constant_name = node.slice
79
+ resolve_and_add_dependency(constant_name)
80
+ end
81
+
69
82
  def detect_task_dependency(node)
70
83
  return unless node.receiver
71
84
 
@@ -73,11 +86,6 @@ module Taski
73
86
  resolve_and_add_dependency(constant_name) if constant_name
74
87
  end
75
88
 
76
- def detect_return_constant(node)
77
- constant_name = node.slice
78
- resolve_and_add_dependency(constant_name)
79
- end
80
-
81
89
  def extract_receiver_constant(receiver)
82
90
  case receiver
83
91
  when Prism::ConstantReadNode, Prism::ConstantPathNode
data/lib/taski/task.rb CHANGED
@@ -71,7 +71,7 @@ module Taski
71
71
  end
72
72
 
73
73
  def tree
74
- build_tree(self, "", Set.new, false)
74
+ build_tree(self, "", {}, false)
75
75
  end
76
76
 
77
77
  private
@@ -86,28 +86,41 @@ module Taski
86
86
  name: "\e[1m" # bold
87
87
  }.freeze
88
88
 
89
- def build_tree(task_class, prefix, visited, is_impl)
89
+ def build_tree(task_class, prefix, task_index_map, is_impl, ancestors = Set.new)
90
90
  type_label = colored_type_label(task_class)
91
91
  impl_prefix = is_impl ? "#{COLORS[:impl]}[impl]#{COLORS[:reset]} " : ""
92
+ task_number = get_task_number(task_class, task_index_map)
92
93
  name = "#{COLORS[:name]}#{task_class.name}#{COLORS[:reset]}"
93
- result = "#{impl_prefix}#{name} #{type_label}\n"
94
- return result if visited.include?(task_class)
95
94
 
96
- visited.add(task_class)
97
- dependencies = task_class.cached_dependencies.to_a
95
+ # Detect circular reference
96
+ if ancestors.include?(task_class)
97
+ circular_marker = "#{COLORS[:impl]}(circular)#{COLORS[:reset]}"
98
+ return "#{impl_prefix}#{task_number} #{name} #{type_label} #{circular_marker}\n"
99
+ end
100
+
101
+ result = "#{impl_prefix}#{task_number} #{name} #{type_label}\n"
102
+
103
+ # Register task number if not already registered
104
+ task_index_map[task_class] = task_index_map.size + 1 unless task_index_map.key?(task_class)
105
+
106
+ # Add to ancestors for circular detection
107
+ new_ancestors = ancestors + [task_class]
108
+
109
+ # Use static analysis to include Section.impl candidates for visualization
110
+ dependencies = StaticAnalysis::Analyzer.analyze(task_class).to_a
98
111
  is_section = section_class?(task_class)
99
112
 
100
113
  dependencies.each_with_index do |dep, index|
101
114
  is_last = (index == dependencies.size - 1)
102
- result += format_dependency_branch(dep, prefix, is_last, visited, is_section)
115
+ result += format_dependency_branch(dep, prefix, is_last, task_index_map, is_section, new_ancestors)
103
116
  end
104
117
 
105
118
  result
106
119
  end
107
120
 
108
- def format_dependency_branch(dep, prefix, is_last, visited, is_impl)
121
+ def format_dependency_branch(dep, prefix, is_last, task_index_map, is_impl, ancestors)
109
122
  connector, extension = tree_connector_chars(is_last)
110
- dep_tree = build_tree(dep, "#{prefix}#{extension}", visited, is_impl)
123
+ dep_tree = build_tree(dep, "#{prefix}#{extension}", task_index_map, is_impl, ancestors)
111
124
 
112
125
  result = "#{prefix}#{COLORS[:tree]}#{connector}#{COLORS[:reset]}"
113
126
  lines = dep_tree.lines
@@ -124,6 +137,11 @@ module Taski
124
137
  end
125
138
  end
126
139
 
140
+ def get_task_number(task_class, task_index_map)
141
+ number = task_index_map[task_class] || (task_index_map.size + 1)
142
+ "#{COLORS[:tree]}[#{number}]#{COLORS[:reset]}"
143
+ end
144
+
127
145
  def colored_type_label(klass)
128
146
  if section_class?(klass)
129
147
  "#{COLORS[:section]}(Section)#{COLORS[:reset]}"
data/lib/taski/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Taski
4
- VERSION = "0.4.1"
4
+ VERSION = "0.4.2"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: taski
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - ahogappa