0

Assume that I have the following class, how can I access the var variable:

class Test
  var = "value"

  def m1
    var
  end

  def self.m2
    var
  end
end

I attempted these ways and they were not successful:

Test.new.m1  #Error
Test.m2      #Error
Test.var     #Error
Test::var    #Error

NOTE: I don't mean to define an instance or a class variable, I'm just curious about this situation.

Ali Sepehri.Kh
  • 2,468
  • 2
  • 18
  • 27
  • I notice that you tagged this question with [tag:ruby-on-rails]. The behavior of variables in Ruby is, like in almost all programming languages, specified by the language specification and *cannot* be changed by libraries. So, it is *completely impossible* for Ruby on Rails to have influence on this whatsoever. – Jörg W Mittag May 01 '20 at 06:14

2 Answers2

1

Assume that I have the following class, how can I access the var variable

Variables whose identifier starts with a lowercase letter are local variables. Local variables are local to the scope they are defined in, that's why they are called "local" variables.

The variable you have defined is just a boring old standard local variable, just like any other local variable. There is nothing special or different about it. It can be accessed just like any other local variable: by simply writing out its name.

E.g.:

class Test
  var = "value"

  puts var # See? Nothing special, just like any other local variable.
end
# value

The last expression evaluated inside a module definition body is the value of the module definition expression, so you could extract the content of the variable like this:

another_local_variable = class Test
  var = "value"

  var
end

another_local_variable
#=> "value"

You can also access the local variable in nested scopes. Again, there is absolutely nothing special or different about this. It's just a local variable that behaves exactly like every other local variable you may have already used.

The only constructs in Ruby that create nested scopes are blocks and lambda literals. So, you could do something like this:

class Test
  var = "value"

  define_method(:m1)           do var end
  define_singleton_method(:m2) do var end
end

Test.new.m1 #=> "value"
Test.m2     #=> "value"

If you can get access to the Binding object (using the Kernel#binding method) for the lexical scope in which the local variable is defined, then you can retrieve its value using Binding#local_variable_get:

b = class Test
  var = "value"

  binding
end

b.local_variable_get(:var) #=> "value"

But again, I must repeat: nothing about this is in any way special. This local variable behaves 100% exactly like every other local variable. A local variable is simply a local variable.

Local variables are very useful in Ruby, because they are the only language construct that provides perfect encapsulation. All other means of encapsulation in Ruby (instance variables, private, etc.) can be circumvented by reflection (e.g. using Object#instance_variable_get or Object#send), and reflection is always available to everyone with no access controls.

But unless someone explicitly gives you a Binding object, local variables cannot be accessed outside of their local scope. It used to be very useful for implementing this pattern:

class Foo
  def bar
    'Hello'
  end
end 

class Foo
  old_bar = instance_method(:bar)

  define_method(:bar) do
    old_bar.bind(self).() + ' World'
  end
end

Foo.new.bar # => 'Hello World'

You can read more about this pattern in this answer of mine under the heading Method Wrapping: https://stackoverflow.com/a/4471202/2988

While this specific pattern has been made obsolete by the introduction of Module#prepend, there are other similar situations where it is still useful, e.g. for memoization.

Jörg W Mittag
  • 363,080
  • 75
  • 446
  • 653
-1

As far as I know, you can't do it the way you've suggested above.

But you can do some pretty crazy things like below to get a hold of those variables.

class Test

  # This can only be accessed within the class.
  var = "value"

  def initialize
    @hello = "hello world!"
  end

  def m1
    var # you won't be able to access var here
  end

  def self.m2
    var
  end

  private # note the following method is private

  def n1
    "hello n1"
  end

end

t = Test.new

# puts t.m1 # =>  `m1': undefined local variable or method `var' for #<Test:0x0000563c68688a58>

# t.send(:m1) # =>  `m1': undefined local variable or method `var' for #<Test:0x0000563c68688a58>

# we can still access the private method like this:

puts t.send(:n1) # => hello n1

puts t.instance_variable_get("@hello") # => "hello world"
BenKoshy
  • 33,477
  • 14
  • 111
  • 80