4

How can I have helper functions with the same name in different rake tasks? The following code does not work:

# bar.rake
namespace :bar do

  desc 'bar'
  task :bar => :environment do
    bar
  end

  def bar
    puts "bar bar"
  end

end

# foo.rake
namespace :foo do

  desc 'bar'
  task :bar => :environment do
    bar
  end

  def bar
    puts "foo bar"
  end

end

The (wrong) result is:

$ rake bar:bar
foo bar

That's because rake's namespace does not work like ruby's module, so the bar function in the 2nd task redefines the bar function of the 1st task.

So what's a good solution to have local helper functions in rake tasks?

thorstenhirsch
  • 199
  • 3
  • 14
  • Well, you can wrap the namespace in the module. – BroiSatse Feb 27 '16 at 12:29
  • 1
    That doesn't work. Wrapping everything in a module results in: `NoMethodError: undefined method 'namespace' for Foo:Module`. And leaving the namespace out of the module, thus only wrapping everything inside the namespace in a module throws `NoMethodError: undefined method 'desc' for Foo:Module`. So I guess rake tasks cannot be in a module. I can only put my helper function in a module, but that way I would need to call bar with explicit namespace (Foo::bar in foo.rake and Bar::bar in bar.rake). – thorstenhirsch Feb 27 '16 at 13:04
  • instead of `module Foo` try, `Module.new do`. This will evaluate the block int he context of anonymous module without breaking method lookup scope. – BroiSatse Feb 27 '16 at 14:46
  • Sounds like a great solution, but I get the same errors as with named modules. – thorstenhirsch Feb 28 '16 at 19:02
  • Hmmm, that actually makes sense. In that case the cleanest way I would try is `module Foo; extend Rake::DSL` – BroiSatse Feb 29 '16 at 08:45

3 Answers3

4

You can wrap the whole definition within a module, but you need to extend it with Rake DSL:

# bar.rake
Module.new do
  extend Rake::DSL

  namespace :bar do

    desc 'bar'
    task :bar => :environment do
      bar
    end

    def self.bar
      puts "bar bar"
    end

  end

# foo.rake
Module.new do
  extend Rake::DSL

  namespace :foo do

    desc 'bar'
    task :bar => :environment do
      bar
    end

    def self.bar
      puts "foo bar"
    end

  end
end
BroiSatse
  • 44,031
  • 8
  • 61
  • 86
2

More of a style preference, but typically I will create a Task class with static methods (or build an instance if you need to manage some state) and use them within the rake task. This way, these methods are also easily testable. In your case:

namespace :bar do
  task :bar => :environment do
    StaticTask.fetch
  end
end

# OR

namespace :foo do
  task :bar => :environment do
    idea = AnotherIdea.new
    idea.fetch
  end
end

More of a pattern I like to follow personally, but it creates a nice separation of concerns between tasks.

(It's also crucial to require whichever class you're bringing in)

Bobby Matson
  • 1,448
  • 12
  • 14
2

Another way to have local helper functions is to have local helper functions :)

namespace :bar do

  desc 'bar'
  task :bar => :environment do
    helper = -> {
      puts 'bar bar'
    }

    helper.call
  end

end
Sergio Tulentsev
  • 226,338
  • 43
  • 373
  • 367
  • Yeah... but now that's a bit too local. ;-) I was looking for functions that are local to the rake file (rake's namespace) and usable in all tasks in that rake file. – thorstenhirsch Mar 04 '16 at 14:31