20

I have two Rake tasks under the same namespace like following:

namespace :db do
  task :first_task => :environment do
         server_name='myserver'
         connect_to(server_name)
  end

  task :second_task => :environment do
          server_name='myserver'
          do_something_with(server_name)
  end
end

As you see, both tasks are under the same namespace and both tasks use server_name='myserver' constant variable.

It really looks ugly to define the server_name variable twice under the same namespace, how can I have one place defining this variable so both tasks can use it?

Lætitia
  • 1,388
  • 1
  • 20
  • 30
Mellon
  • 37,586
  • 78
  • 186
  • 264

2 Answers2

22

Try this:

namespace :db do
  server_name='myserver'
  task :first_task => :environment do
    connect_to(server_name)
  end

  task :second_task => :environment do
    do_something_with(server_name)
  end
end

Namespaces have access to variables declared before their scope.

David Sulc
  • 25,946
  • 3
  • 52
  • 54
  • But get attention, if you define `server_name` before you define the namespace. Then the variable can be modified outside the namespace definition. See also https://stackoverflow.com/questions/30328197 – knut Jun 01 '21 at 13:57
6

I'd like to build on David Sulc's answer, but I recommend using an instance variable instead:

namespace :db do
  @server_name = 'myserver'

  task first_task: :environment do
    connect_to @server_name
  end

  task second_task: :environment do
    do_something_with @server_name
  end
end

The advantage here is that later code can modify @server_name -- something you can't do with a local variable:

namespace :db do
  @server_name = 'server_2'
end
Community
  • 1
  • 1
David J.
  • 31,569
  • 22
  • 122
  • 174
  • No, you shouldn't do that. The reason is that you don't know, where that ivar goes to. Neither do I. A rake task is declared using a block, not a class, and therefore the context of the ivar is unclear. – meandre Aug 27 '14 at 11:35
  • @meandre What is the problem with using an instance variable in this way? Can you demonstrate where it could go wrong? Do you have a better suggestion? See the section "Instance Variables in Tasks" in http://daneharrigan.com/2010/06/rake-tasks-102/ for more detailed discussion. – David J. Aug 28 '14 at 06:34
  • @meandre Actually, instance variables defined in the way I show get evaluated in the context of `Rake.application.in_namespace(name, &block)` (see https://github.com/jimweirich/rake/blob/v10.3.2/lib/rake/dsl_definition.rb#L140). I'm not sure what your concern is. – David J. Aug 28 '14 at 06:39
  • 5
    Instance variables are evaluated in some object's context (not in a context of a method like `in_namespace()`. A block/`Proc` in Ruby gets bound to an object at the moment the block is declared (not when `call`ed/`yield`ed - except for `instance_eval` and some `UnboundMethod#bind` magic you don't want to know about). As I see, `TaskManager#in_namespace` makes no use of `instance_eval` thus leading to sharing all instance variables among all `rake` tasks. If that mess works for you, it's ok for you, but that's definitely not **a better way**. – meandre Sep 01 '14 at 11:43
  • @meandre Thanks for digging in and explaining. Having an instance variable shared across all rake tasks is indeed global state at its 'finest'. Would you suggest an alternative way of parameterizing the rake tasks such as `server_name` as my example showed? (That example is one reason why I suggested this approach, but I'm certainly open to better ways.) – David J. Sep 01 '14 at 17:27