From e08122dfa775fe78c418687354ce27ed856c5326 Mon Sep 17 00:00:00 2001 From: Foad Farimani Date: Fri, 18 Apr 2025 11:02:47 +0200 Subject: [PATCH] Fix: Handle Ruby 3.4 caller format in ResourceScenario#treeSum (#300) Ruby 3.4.0 changed the output format of \`caller\`, potentially omitting quotes around method names. The previous code in ResourceScenario#treeSum relied on a regex (\`/''.*''/\`) that expected these quotes. When the regex failed to match on Ruby 3.4, it returned nil, leading to a fatal \`undefined method ''[]'' for nil\` error when generating cache tags. This change introduces a check for Ruby version >= 3.4.0. For these versions, it attempts to parse the method name using a more flexible regex (\`/in\s+\`?([^''\`\s]+)''?/\`) that handles optional quotes. Crucially, a fallback mechanism is added. If parsing the method name still fails (which can happen if the caller format is unexpected, producing warnings), it generates a cache tag using a hash of the raw caller string. This prevents the fatal error and allows TaskJuggler to run, albeit potentially with less optimal caching in those specific edge cases. Fixes #300 --- lib/taskjuggler/ResourceScenario.rb | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/taskjuggler/ResourceScenario.rb b/lib/taskjuggler/ResourceScenario.rb index bfc0fc99..fdeb3262 100644 --- a/lib/taskjuggler/ResourceScenario.rb +++ b/lib/taskjuggler/ResourceScenario.rb @@ -499,10 +499,23 @@ def query_unpaidleave(query) def treeSum(startIdx, endIdx, *args, &block) # Starting with ruby 3.4.0, the out of caller was changed. # https://www.ruby-lang.org/en/news/2024/12/25/ruby-3-4-0-released/ - # This ugly bit of code ensure that older versions still work. + # This code handles the change and ensures older versions still work. + # Ref: https://bugs.ruby-lang.org/issues/19836 - caller format change if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0') - cacheTag = caller[0][/'.*'/][1..-2] + # Ruby 3.4+ might not have backticks/quotes around the method name. + # Use a regex that captures the method name after 'in ' optionally handling quotes. + match_data = caller[0].match(/in\s+`?([^'`\s]+)'?/) + if match_data && match_data[1] + method_name = match_data[1] + # Use the original '.' separator intended for this branch + cacheTag = "#{self.class}.#{method_name}" + else + # Fallback if parsing fails unexpectedly + $stderr.puts "Warning: Could not parse method name from caller: #{caller[0]}" + cacheTag = "#{self.class}.caller.#{caller[0].hash}" # Use a hash as fallback + end else + # Original logic for Ruby versions < 3.4.0 cacheTag = "#{self.class}##{caller[0][/`.*'/][1..-2]}" end treeSumR(cacheTag, startIdx, endIdx, *args, &block)