Ruby, How to Access Local Variables Outside the Do - End Loop

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 to puts, 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



Leave a reply



Submit