Why Can't I Access a Local Variable Inside a Method in Ruby

Why can't I access a local variable inside a method in Ruby?

The reason ff is inaccessible inside the test method definition is simply that methods (created with the def keyword) create a new scope. Same with defining classes and modules using the class and module keywords respectively.

The role of main (the top-level object) is almost completely irrelevant to the question of scope in this situation.

Note that, if you DO want your test method to have access to locals defined in the definition context, then use the define_method (or in your case, the define_singleton_method method), see here:

ff = "hi"
define_singleton_method("test") { ff }
test #=> "hi"

Unlike the def keyword, the define_method family of methods do not create new scopes but instead close over the current scope, capturing any local variables.

The reason using @ff worked in the next example given by @soup, is not that main is somehow a "special case" it's just that an ivar defined at top-level is an ivar of main and so is accessible to a method invoked on main.

What, however, is the relationship of the test method to main? It is not a method on just main itself - it is actually a private instance method defined on the Object class. This means that the test method would be available (as a private method) to nearly every object in your ruby program. All methods defined at top-level (main) are actually defined as private instance methods on the Object class.

For more information on the Ruby top-level, see this article: http://banisterfiend.wordpress.com/2010/11/23/what-is-the-ruby-top-level/

Can you access Ruby variables with outer scope inside of methods?

This is a duplicate of Ruby accessing outer variables in nested function.

You could make it an instance variable on the containing object by calling it @rotation, but why not just pass string and rotation into the encrypt method?

Access Local variable in another method Ruby

Your instincts are good - you don't want to repeat yourself, and there are better ways of structuring this code. But rather than sharing variables, you should think about small pieces, loosely joined. Write methods that do one thing well, and combine them together. For instance, we could write a get_client method that just returns a client for other methods to use:

protected
def get_client
client = Google::APIClient.new(
application_name: "Example Application",
application_version: "1.0")

client.authorization = Signet::OAuth2::Client.new(
:token_credential_uri => 'https://accounts.google.com/o/oauth2/token',
:audience => 'https://accounts.google.com/o/oauth2/token',
:scope => 'https://www.googleapis.com/auth/analytics.readonly',
:issuer => SERVICE_ACCOUNT_EMAIL_ADDRESS,
:signing_key => Google::APIClient::KeyUtils.load_from_pkcs12(PATH_TO_KEY_FILE, 'notasecret')).tap { |auth| auth.fetch_access_token! }
client
end

It's protected because external code - stuff outside your Analytic class - shouldn't work with it directly. They should use the methods we provide for them.


You could also provide a helper method for getting results from the API. I'm not familiar with the query API, but it looks like it's your metrics value that changes. So maybe something like this:

protected
def get_result(metrics)
client = self.get_client
api_method = client.discovered_api('analytics','v3').data.ga.get

result = client.execute(:api_method => api_method, :parameters => {
'ids' => PROFILE,
'start-date' => Date.new(2014,1,1).to_s,
'end-date' => Date.today.to_s,
'dimensions' => 'ga:pagePath',
'metrics' => metrics,
'filters' => 'ga:pagePath==/'
})
result
end

Now you can write simple methods that your external classes can use:

def new_users
get_result('ga:newUsers')
end

def total_visits
get_result('ga:pageViews')
end

If you can, try to return simple data from these methods. Maybe total_visits is going to return get_result('ga:pageViews')['totalsForAllResults']['ga:pageviews'] instead. Code outside your class shouldn't have to know about the GA data format to work with it. This is called encapsulation.

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.

Why can't we access local variable inside rescue?

You most certainly can access local variables defined in a begin, in the corresponding rescue block (assuming of course, the exception has been raised, after the variable was set).

What you can't do is access local variables that are defined inside a block, outside of the block. This has nothing to do with exceptions. See this simple example:

define transaction() yield end
transaction do
x = 42
end
puts x # This will cause an error because `x` is not defined here.

What you can do to fix this, is to define the variable before the block (you can just set it to nil) and then set it inside the block.

x = nil
transaction do
x = 42
end
puts x # Will print 42

So if you change your code like this, it will work:

begin
object = nil
transaction do #Code inside transaction
object = Class.new attributes
raise unless object.save!
end
rescue
puts object.error.full_messages # Why can't we use local varible inside rescue ?
end

ruby: can a block affect local variables in a method?

First of all, block.call() is done with yield, and you don't need the &block parameter that way.

You can't normally do what you want, blocks are bound when they are created, and inside the block you can see the local variables defined at that moment; the easiest way to do what you want, which is not how you will use blocks normally, is this:

def test()
foo = yield if block_given?
puts "in test, foo is #{foo}"
end

test() {
foo="this is foo"
}

But that's only a side effect because foo is "returned" by the block. If you instead do this:

def test()
foo = yield if block_given?
puts "in test, foo is #{foo}"
end

test() {
foo="this is foo"
"ha ha, no foo for you"
}

You'll notice that it does something different.

Here's more magic:

def test(&block)
foo = eval "foo", block.binding
puts foo
block.call
foo = eval "foo", block.binding
puts foo
end

foo = "before test"
test() {
foo = "after test"
"ha ha, no foo for you"
}

That would kind of work, but it breaks if you remove foo = "before test" because foo becomes a local variable in the block and does not exist in the binding.

Summary: you can't access local variables from a block, just the locals where the block was defined and the return value of the block.

Even this won't work:

def test(&block)
eval "foo = 'go fish'", block.binding
block.call
bar = eval "foo", block.binding
puts bar
end

because the foo in the binding is different from the local in the block (I didn't know this, thanks).

How to access variables from one method into another method in ruby?

Local variables are scoped to the local context. You cannot access them from arbitrary other places in the code; that's the whole point of them being local.

If you want to access a variable globally across the whole program (which is, 99.9% of the time, very bad practice), then you need to declare it as a global variable.

In ruby, global variables are declared by naming them with a $ symbol, e.g. $num_1.


Alternatively, you could pass the variables into the other method. For example:

def request_op_num
puts "Please enter your first number"
num_1 = gets.to_f
puts "Please enter the operator you would like to use (+,-,*,/)"
op = gets
puts "Please enter your second number"
num_2 = gets.to_f

answer(num_1, num_2, op)
end

def answer(num_1, num_2, op)
if op == '+'
puts num_1 + num_2
elsif op == '-'
puts num_1 - num_2
elsif op == '*'
puts num_1 * num_2
elsif op == '/'
puts num_1 / num_2
else
puts "wrong operator"
end
end

request_op_num

Or:

def request_op_num
puts "Please enter your first number"
num_1 = gets.to_f
puts "Please enter the operator you would like to use (+,-,*,/)"
op = gets
puts "Please enter your second number"
num_2 = gets.to_f

[num_1, num_2, op]
end

def answer(num_1, num_2, op)
if op == '+'
puts num_1 + num_2
elsif op == '-'
puts num_1 - num_2
elsif op == '*'
puts num_1 * num_2
elsif op == '/'
puts num_1 / num_2
else
puts "wrong operator"
end
end

num_1, num_2, op = request_op_num

answer(num_1, num_2, op)

Why does this locals variable declaration not work?

The problem is that you are calling a partial named 'node' and so rails is expecting to define node and node_counter via other means. You can fix your code by doing the following:

<%= render partial: 'nodes/node', object: event.eventable.node, locals: { new_node_counter: index } %>

I believe you will need to use a different variable name for the node_counter. If you need to use the node_counter variable because other pages use this partial, you could always do something at the top of the partial 'nodes/node' like this:

<% node_counter = node_counter || new_node_counter %>

Here are some relevant excerpts from http://guides.rubyonrails.org/layouts_and_rendering.html:

Every partial also has a local variable with the same name as the partial (minus the underscore). You can pass an object in to this local variable via the :object option:

<%= render partial: "customer", object: @new_customer %>

...

Rails also makes a counter variable available within a partial called by the collection, named after the member of the collection followed by _counter. For example, if you're rendering @products, within the partial you can refer to product_counter to tell you how many times the partial has been rendered. This does not work in conjunction with the as: :value option.



Related Topics



Leave a reply



Submit