rake icon indicating copy to clipboard operation
rake copied to clipboard

How to determine whether any prerequisite tasks needed to run at the time they were defined?

Open tomeon opened this issue 6 years ago • 1 comments

I have a use-case in which I need to run a task if and only if at least one of its prerequisite tasks either (1) currently needs to run or (2) needed to run at the time the prerequisite task was defined. Here are some attempts at a Rake::Task subclass:

class MyTask < Rake::Task
  def needed?
    prerequisite_tasks.any?(&:needed?)
  end
end

This approach does not work, since at the time MyTask#needed? is called, prerequisite tasks may have run and therefore return false for #needed?.

class MyTask < Rake::Task
  def needed?
    prerequisite_tasks.any? { |p| p.needed? or p.already_invoked }
  end
end

This also doesn't work, since #already_invoked can return true [even if the task's #execute method was never called]:

          @already_invoked = true


          invoke_prerequisites(task_args, new_chain)
          execute(task_args) if needed?

What ended up working was:

class MyTask < Rake::Task
  def initialize(*)
    @needed = false
    super
  end
  
  def enhance(*)
    super.tap { @needed |= prerequisite_tasks.any?(&:needed?) }
  end

  def needed?
    @needed
  end
end

But that just feels... wrong. Is there something I'm missing here? I'd be more than happy to open a PR implementing what I'm looking for (maybe an #already_executed method?) if this functionality doesn't already exist.

Thanks!

tomeon avatar Jun 29 '18 16:06 tomeon

What you require is state, and you do it well with the @needed attribute.

Considerations:

Your description says:

The task runs if and only if

  1. one of its prerequisite tasks currently needs to run
  2. one of its prerequisite tasks needed to run at the time the prerequisite task was defined.

This could be translated to:

class MyTask < Take::Task
  def needed?
    prerequisite_tasks.any? do |prereq|
       prereq.needed_at_def_time? || prereq.needed?
    end
  end
end

You can't be sure the prerequisites are of the class MyTask. So I would add that special state to the Task class, and also the helper method needed_at_def_time?

class Rake::Task
  attr_accessor :needed_at_def_time
  def enhance(*)
    super
    @needed_at_def_time |= needed?
  end
end

No matter what happens during the executions, @needed_at_def_time == true will dominate the control. Not big problem as already_invoked is verified before executing, so it should not execute multiple times during 1 run. But it is important to keep that in mind for other secondary effects, like if you are using needed? to calculate other logic by your own custom code.

jgomo3 avatar Jun 21 '22 20:06 jgomo3