Jitendra Chandani
Jitendra Chandani

Reputation: 385

Rake task variable under namespace

I see a strange thing in my rake script today. I have two Rake tasks under the different namespaces like following:

path = "/home/tomcat/tomcat"

namespace :stage do
  path = "/home/tomcat/stage-tomcat"
  desc "Deploys a java application to stage tomcat"
  task :java_deploy do
    puts path # stage:java_deploy should print /home/tomcat/stage-tomcat
  end
end

namespace :production do
  path = "/home/tomcat/production-tomcat"
  desc "Deploys a java application to production tomcat"
  task :java_deploy do
    puts path # production:java_deploy should print /home/tomcat/production-tomcat
  end
end

When I run: rake stage:java_deploy it prints

/home/tomcat/production-tomcat

I was expecting /home/tomcat/stage-tomcat. If I remove the very first line path = "/home/tomcat/tomcat" from the rake file, it works as expected.

Any idea why this kolavari? :)

Thanks in advance!!

Upvotes: 6

Views: 2456

Answers (2)

knut
knut

Reputation: 27845

In addition to the correct answer of matt:

When you remove the first assignment to path, then the variable doesn’t exist in the top level. This means that when you assign to path in each namespace definition you are declaring a new (and separate) local variable in each case.

If you ever define the variable later you may get strange errors and have big problems to find the source of the problem.

So I would recommend to add a little check to detect if you get an error: raise ArgumentError, "Variable path may not defined before thos scope" if defined? path

Example:

require 'rake'
#path = "/home/tomcat/tomcat" #this results in an Exception if uncommented

namespace :stage do
  raise ArgumentError, "Variable path may not defined before thos scope" if defined? path
  path = "/home/tomcat/stage-tomcat"
  desc "Deploys a java application to stage tomcat"
  task :java_deploy do
    puts path # stage:java_deploy should print /home/tomcat/stage-tomcat
  end
end

namespace :production do
  raise ArgumentError, "Variable path may not defined before thos scope" if defined? path
  path = "/home/tomcat/production-tomcat"
  desc "Deploys a java application to production tomcat"
  task :java_deploy do
    puts path # production:java_deploy should print /home/tomcat/production-tomcat
  end
end

Depending on the 2nd line, your script will work or throw an exception:

test.rb:5:in `block in <main>': Variable path may not defined before this scope (ArgumentError)
  from C:/.../rake/task_manager.rb:224:in `in_namespace'
  from C:/.../rake/dsl_definition.rb:141:in `namespace'
  from test.rb:4:in `<main>'

Upvotes: 0

matt
matt

Reputation: 79723

This is not specific to Rake, it is just a consequence of lexical scope and the way Ruby handles local variables, declaring them on first use.

First you assign a value to path:

path = "/home/tomcat/tomcat"

Then you create the stage namespace and reassign the variable:

path = "/home/tomcat/stage-tomcat"

Note that this line is executed whatever task you specify, as it is not in any tasks.

Next you create the java_deploy task, but it doesn’t get run yet. This task refers to the path variable, but when the task is called the value of it might have changed.

Later, when defining the production namespace this variable gets reassigned again. Importantly this is still the same variable:

path = "/home/tomcat/production-tomcat"

When the task is actually run, it refers to the path variable and the value of this variable is the latest value assigned to it, which is /home/tomcat/production-tomcat.

When you remove the first assignment to path, then the variable doesn’t exist in the top level. This means that when you assign to path in each namespace definition you are declaring a new (and separate) local variable in each case.

Upvotes: 7

Related Questions