In Capistrano using the Multi-stage extension I have two environments: prod and testing.
I need a few variables in testing.rb that are not needed in prod.rb and I want some of my tasks to be able to check if the variable is defined and use it if it is, but ignore it if it is not set.
So, in testing.rb I would have something like:
set :foo, 'bar'
prod.rb wouldn't have any reference to :foo since it doesn't need it. In one of my tasks, I would like to do something like:
if defined?(foo)
# do something with foo
else
# do something without foo
end
But I keep getting the error:
undefined local variable or method 'foo'
Is there a way to test for undefined global variables in the task? Or do I have to do something like:
set :foo, ''
In all my environments that don't need the :foo variable?
Try using exists?(:foo) instead of defined?(foo), as recommended in the Capistrano docs.
Related
Consider the following
class Foo
def bar
1
end
end
And then two rake tasks ...
task :something
InvokeSomething(Foo.new)
end
task :test
do_some_testing
end
def do_some_testing
setup_test
`rake something`
check_if_it_did_everything_correctly
end
After executing setup_test, I want to modify the source for class Foo to return 2 for method bar.
Ideas:
Create a rake task that gets invoked before I define the rake task something and I modify the source code directly. So that something is defined with a different source code.
Somehow modify the Rake task so that that particular method is overridden. At the very least I need a reference to the rake task during runtime.
There are multiple ways to do this.
You can get the reference from Rake::Task["something"] during do_some_testing and re-define a method there.
But there is another even simpler way - which worked for me. Set the value in bar as an environment variable. And modify the environment variable when needed.
i'm using irb -I . -r script.rb to require a script before starting an interactive session. functions defined in the global scope are available but variables aren't unless they are declared with #
is there a way to access local variables defined in the global context of a script or a better way to do this
script.rb:
def func() "..." end
a = "str"
#b = 1
then after irb starts:
irb(main):001:0> a
NameError: undefined local variable or method `a' for main:Object
from (irb):1
from /usr/bin/irb:11:in `<main>'
irb(main):002:0> #b
=> 1
irb(main):003:0> func
=> "..."
i'm assuming that the script's contents are executed as if defined in a function (eg: main in C-type languages) and so the variables at the global context are local variables that are not accessible outside that scope
so do most people use # variables when writing their scripts?
the use-case is narrow in scope (script development) and the solution is trivial (search-replace any variable with #variable), but I'm learning the semantics of the language and I'm curious about this. Can't the execution context be exposed and merged into the current context somehow?
No. You can't access local variables from a different scope. That's the whole point of local variables: they are local to the scope they are defined in.
In this case, a is local to the script body of script.rb.
Maybe binding.irb is what you are looking for.
script.rb:
def func() "..." end
a = "str"
#b = 1
binding.irb
From the documentation:
Opens an IRB session where binding.irb is called which allows for
interactive debugging. You can call any methods or variables available
in the current scope, and mutate state if you need to.
I'm using ruby-prolog. I want to run a task to query a fact.
demo.rb:
require 'ruby-prolog'
c = RubyProlog::Core.new
c.instance_eval do
person['name','brian'].fact
person['name','James'].fact
puts 'all the names are: '
p query(person['name', :A])
end
This works great. Now I want to run the query inside of Rake. That is a problem because I don't know how to access person[] from the other file.
Rakefile.rb:
require_relative 'demo.rb'
task :test do |variable|
puts 'all the names are: '
p query(person['name', :A])
end
Error:
all the names are: rake aborted! NameError: undefined local variable
or method `person' for main:Object
I'm hoping this can be solved by passing an object back somehow. I tried accessing c, but it did not work out. Any ideas?
In your demo file, both variables person and c are local variables and will not be accessible from outside of that context. If you require demo.rb into an irb session, the behavior should be the same; neither c nor person will be defined.
A good way of dealing with this in rake tasks is to keep any and all logic out of the rake task itself, and only call out to another object that takes care of the task. For a quick and dirty example, you could alter your code as such:
# demo.rb
require 'ruby-prolog'
class Demo
def self.run_demo
# Existing code:
c = RubyProlog::Core.new
c.instance_eval do
person['name','brian'].fact
person['name','James'].fact
puts 'all the names are: '
p query(person['name', :A])
end
end
end
and
# Rakefile.rb
require_relative 'demo.rb'
task :test do
Demo.run_demo
end
I am trying to refer to a hash written in another file, and tried:
require './filewithhash' #this file has hash
puts name_hash['somename'] #just trying access by the index
and got undefined local variable or method error.
The problem is that when requiring file in ruby, local variables are out of scope. Therefore any local variables defined in required file will be not available later on.
Solutions:
Make your program object oriented and add this hash as a field in some class
Make your variable constant (like this: NAME_HASH)
Make your variable global (like this: $name_hash)
Make your variable an instance variable (like this: #name_hash)
If program is very simple, I would choose option 3. However if it's suppose to grow, 1 is your best choice.
Requires in Ruby don't just inline the file like in PHP; each file has its own scope. That is, the code in the file will execute in a scope that means that all local variables are all constrained to that file's scope, and will not be visible outside of it.
In order to execute a file in the current scope, you're going to have to eval it with the current binding:
file = "file_with_hash.rb"
File.open(file) {|f| eval f.read, binding, file }
This is a bad idea and you shouldn't do it unless you know what you're doing and why. Instead, you should expose that hash as a member of a constant (which transcends the file scope):
# foo.rb
module Foo
PROGRAMMER_WORDS = {foo: 1, bar: 2, baz: 3}
...
end
# another file
require 'foo'
Foo::PROGRAMMER_WORDS # => {:foo=>1, :bar=>2, :baz=>3}
I have want to set this deferred variable in Capistrano which depends on some variable I set during calling the task
set(:installation_dir) do
if target == "staging"
"/some/path"
else
"/some/other/path"
end
end
task :foo do
p "INSTALLATION_DIR >>> #{installation_dir}"
end
If running the task this error happens
Hector:monitoring-agent robertj$ cap foo -s target=development
/Users/robertj/.rvm/gems/ruby-2.0.0-p0#pf/gems/capistrano-2.15.4/lib/capistrano/configuration/variables.rb:122:in
`method_missing_with_variables': undefined local variable or method `target' for
#<Capistrano::Configuration:0x007fd6a22f9100> (NameError)
This is making me mad. Why doesnt Capistrano 2.x have a simple way to access variables where ever I call.
Looks like, fetch do what you want
p "INSTALLATION_DIR >>> #{fetch(:installation_dir)}"
When you run it like that, you're setting an environment variable. To use it in your Capistrano script you need to set :target, ENV['target'].