Ruby, How can i access local variables outside the do - end loop
You can't. Local variables are local to their scope. That's why they are called local variables.
You could, however, use a variable from an outer scope:
communicator = nil
ssh.exec('...') do |ch, stream, data|
break unless stream =~ /vmupgrade/
puts "#{value_hosts} is #{data}", stream, data
communicator = {ch: ch, stream: stream, data: data}
end
puts communicator
BTW: there were several bugs in your code, that would have prevented it from working anyway regardless of your problem with variable scoping, because you used wrong syntax for dereferencing local variables: the syntax for dereferencing a variable is simply the name of the variable, e.g. foo
, and not #{foo}
(that's simply a syntax error).
Also, there are some other improvements:
- formatting: the standard for indentation in Ruby is 2 spaces, not 26
- formatting: the standard for indentation in Ruby is 2 spaces, not 0
- formatting: usually, the block arguments are separated from the
do
keyword with a space - guard clause: if you wrap the entire body of a block or method in a conditional, you can just replace that with a guard a the beginning of the block that skips the whole block if the condition is true
- string interpolation: adding strings together with
+
is unusual in Ruby; if you need to concatenate strings, you usually do it with<<
, but more often than not, string interpolation is preferred - multiple arguments to
puts
: if you pass multiple arguments toputs
, it will print all of them on a separate line, you don't need to call it multiple times
Local variable acessible outside block
In Ruby, a "block" is basically code that is enclosed either inside a do...end
or between curly braces. (There are other ways — sort of — to create blocks in Ruby, but this definition will hold you for quite a while.) if...end
isn't considered a block. Neither is while...end
. To further complicate the issue, the do
that you add after the while
doesn't make it a block, either; Ruby just helpfully ignores it.
Cary has already explained this in the comments, but I'll see if I can clarify it a bit for you. I'll simplify and clean up your code a bit:
a = 3
while a < 10
b = a * 3
a += 1
end
puts b
This will give 27. But now, if I do this:
a = 3
loop do
b = a * 3
a += 1
break if a == 10
end
puts b
I'll get this:
undefined local variable or method `b' for main:Object (NameError)
which is what you were expecting. This is because while
is a statement. while...end
may enclose several lines of code, but it doesn't count as a block. (Ditto for if
.) But loop
is actually a method that takes a block as an argument, so the scoping rules that Cary describes in his comment apply.
Ruby forgets local variables during a while loop?
I think this is because message is defined inside the loop. At the end of the loop iteration "message" goes out of scope. Defining "message" outside of the loop stops the variable from going out of scope at the end of each loop iteration. So I think you have the right answer.
You could output the value of message at the beginning of each loop iteration to test whether my suggestion is correct.
Ruby can not access variable outside the method?
The result
and template
variables inside the generateMethods
function are different from the ones declared outside and are local to that function. You could declare them as global variables with $
:
$template=<<MTEMP
#methodName#:function(){},
MTEMP
$result="";
def generateMethods(mds)
mds.each do |md|
$result+=$template.gsub(/#methodName#/,md).to_s+"\n";
end
$result;
end
puts generateMethods(['getName','getAge','setName','setAge'])
But what's your purpose with this function? I think there's a cleaner way to do this if you can explain your question more.
Ruby loop local variables and inmutability
each
loops often mutate the object. Each has to do something.
Because each
doesn't return anything useful - it returns the array itself, It won't mutate the object if it sends every element somewhere, like to be printed on screen.
foo.each do |bar|
# do something with element like
# print it, change it, save it
end
Functional alterantive is map
foo.map { |bar| bar.something }
It returns new array which is original array processed in immutable way. Obviously you have to be careful to use immutable methods. This would not be immutable:
foo.map { |bar| bar.something! }
Here something!
does something destructive to the element of array.
However I have never seen map
used like that. Use each
for something destructive.
Accessing objects/methods outside of a loop in Ruby
The example code you provided works fine. I was able to run the server, connect through telnet localhost 2000
and receive the "Hello, world!" message before the connection closed.
There are times when a block won't have access to the local scope surrounding it. That isn't the case in your example, but if you are encountering it elsewhere there are ways around it.
Constants and globals are visible globally. You could assign your Hello
instance variable to a global: $hi = Hello.new
, though most developers will tell you using globals is "bad". You could modify your Hello
class so that it contains a registry of instances:
require 'socket' # Get sockets from stdlib
class Hello
def initialize(name)
self.class.registry[name] = self
end
def say_hi
"Hello, world!"
end
def self.registry
@@registry ||= {}
end
end
Hello.new(:hi)
server = TCPServer.open(2000) # Socket to listen on port 2000
loop { # Servers run forever
Thread.start(server.accept) do |client|
client.puts(Hello.registry[:hi].say_hi)
client.close # Disconnect from the client
end
}
There are other ways to overcome scope issues as well, including passing the hi
instance to Thread.start
, using thread local variables, and others. Hard to suggest a specific when the supplied code already works.
Related Topics
How to Optimize Graphviz Output Width
Ruby Class Object Garbage Collection
How to Mock Super in Ruby Using Rspec
Webmock Simulate Failing API (No Internet, Timeout ++)
What's Does the [5.0] in Rails 5's Activerecord::Migration Mean
Ruby Outputting to the Same Line as the Previous Output
Require Ruby File Without .Rb Extension
Ruby - Naming Convention - Letter Case for Acronyms in Class/Module Names
Do I Need to Indent My Code in Ruby
When to Use Keyword Arguments Aka Named Parameters in Ruby
Ruby on Rails: Creating a Model Entry with a Belongs_To Association
Flash Message with HTML_Safe from the Controller in Rails 4 (Safe Version)
Is There an Elegant Way to Exclude the First Value of a Range
Unexpected Value of _Callee_ When Including a Module - Is This a Ruby Bug