In Ruby, Foo.Inspect Can Print Out All Instance Variables -- How to Print Out an Individual One If There Is No Accessor

In Ruby, foo.inspect can print out all instance variables -- can we print out an individual one if there is no accessor?

In Ruby, all access protection can be circumvented using reflection:

@bar.instance_variable_get(:@wah)

Access nested properties using inspect in ruby

You can fetch instance variables for introspection using instance_variable_get:

seach_form.instance_variable_get(:@field_options)

ruby style and instance variables

Accessing the attribute through the getter has the advantage of providing encapsulation. The use of an instance variable to store the value is an implementation detail in some respects. Whether that's appropriate is, of course, situational. I don't recall reading anything explicit this style issue, however.

Found https://softwareengineering.stackexchange.com/questions/181567/should-the-methods-of-a-class-call-its-own-getters-and-setters, which discusses the issue from a language-independent point of view. Also found https://www.ruby-forum.com/topic/141107, which is ruby-specific, although it doesn't break any new ground, let alone imply a Ruby standard.

Update: Just came across the following statement on page 24 of http://www.amazon.com/Practical-Object-Oriented-Design-Ruby-Addison-Wesley/dp/0321721330/ref=sr_1_1?s=books&ie=UTF8&qid=1376760915&sr=1-1, a well-respected book on Ruby: "Hide the variables, even from the class that defines them, by wrapping them in methods." (emphasis added). It goes on to give examples of methods in the class using the accessor methods for access.

How do I access class variable?

In Ruby, reading and writing to @instance variables (and @@class variables) of an object must be done through a method on that object. For example:

class TestClass
@@variable = "var"
def self.variable
# Return the value of this variable
@@variable
end
end

p TestClass.variable #=> "var"

Ruby has some built-in methods to create simple accessor methods for you. If you will use an instance variable on the class (instead of a class variable):

class TestClass
@variable = "var"
class << self
attr_accessor :variable
end
end

Ruby on Rails offers a convenience method specifically for class variables:

class TestClass
mattr_accessor :variable
end

When do Ruby instance variables get set?

Instance variables in ruby may be a bit confusing when first learning Ruby, especially if you are accustomed to another OO language like Java.

You cannot simply declare an instance variable.

One of the most important things to know about instance variables in ruby, apart from the notation with an @ sign prefix, is that they spring into life the first time they are assigned to.

class Hello
def create_some_state
@hello = "hello"
end
end

h = Hello.new
p h.instance_variables

h.create_some_state
p h.instance_variables

# Output
[]
["@hello"]

You can use the method Object#instance_variables to list all instance variables of an object.

You normally “declare” and initialize all the instance variables in the initialize method. Another way to clearly document which instance variables that should be publicly available is to use the Module methods attr_accessor (read/write), attr_writer (write) and attr_reader (read). These methods will synthesize different accessor methods for the listed instance variable.

class Hello
attr_accessor :hello
end

h = Hello.new
p h.instance_variables

h.hello = "hello"
p h.instance_variables

# Output
[]
["@hello"]

The instance variable still isn’t created until it’s assigned to using the synthesized Hello#hello= method.

Another important issue, like kch described, is that you need to be aware of the different contexts active when declaring a class. When declaring a class the default receiver (self) in the outermost scope will be the object that represents the class itself. Hence your code will first create a class instance variable when assigning to @hello on the class level.

Inside methods self will be the object on which the method is invoked, hence you are trying to print the value of an instance variable with the name @hello in the object, which doesn’t exists (note that it’s perfectly legal to read a non existing instance variable).

Ruby Rspec : Testing instance variables without adding an accessor to source

You can use Object#instance_variable_get method to get value of any instance variable of the object like that:

class Foo 
def initialize
@foo = 5 # no accessor for that variable
end
end

foo = Foo.new
puts foo.instance_variable_get(:@foo)
#=> 5

And Object#instance_variable_set can be used to set instance variable values:

foo.instance_variable_set(:@foo, 12) 
puts foo.instance_variable_get(:@foo)
#=> 12

Why are symbols in Ruby not thought of as a type of variable?

Symbols used in accessor methods are not variables. They are just representing the name of a variable. Variables hold some reference, so you cannot use a variable itself in defining accessor methods. For example, suppose you wanted to define an accessor method for the variable @foo in a context where its value is "bar". What would happen if Ruby's syntax were to be like this:

attr_accessor @foo

This would be no different from writing:

attr_accessor "bar"

where you have no access to the name @foo that you are interested in. Therefore, such constructions have to be designed to refer to variable names at a meta level. Symbol is used for this reason. They are not variables themselves. They represent the name of a variable.

And the variable relevant to accessor methods are instance variables.

Adding an instance variable to a class in Ruby

You can use attribute accessors:

class Array
attr_accessor :var
end

Now you can access it via:

array = []
array.var = 123
puts array.var

Note that you can also use attr_reader or attr_writer to define just getters or setters or you can define them manually as such:

class Array
attr_reader :getter_only_method
attr_writer :setter_only_method

# Manual definitions equivalent to using attr_reader/writer/accessor
def var
@var
end

def var=(value)
@var = value
end
end

You can also use singleton methods if you just want it defined on a single instance:

array = []

def array.var
@var
end

def array.var=(value)
@var = value
end

array.var = 123
puts array.var

FYI, in response to the comment on this answer, the singleton method works fine, and the following is proof:

irb(main):001:0> class A
irb(main):002:1> attr_accessor :b
irb(main):003:1> end
=> nil
irb(main):004:0> a = A.new
=> #<A:0x7fbb4b0efe58>
irb(main):005:0> a.b = 1
=> 1
irb(main):006:0> a.b
=> 1
irb(main):007:0> def a.setit=(value)
irb(main):008:1> @b = value
irb(main):009:1> end
=> nil
irb(main):010:0> a.setit = 2
=> 2
irb(main):011:0> a.b
=> 2
irb(main):012:0>

As you can see, the singleton method setit will set the same field, @b, as the one defined using the attr_accessor... so a singleton method is a perfectly valid approach to this question.

What is attr_accessor in Ruby?

Let's say you have a class Person.

class Person
end

person = Person.new
person.name # => no method error

Obviously we never defined method name. Let's do that.

class Person
def name
@name # simply returning an instance variable @name
end
end

person = Person.new
person.name # => nil
person.name = "Dennis" # => no method error

Aha, we can read the name, but that doesn't mean we can assign the name. Those are two different methods. The former is called reader and latter is called writer. We didn't create the writer yet so let's do that.

class Person
def name
@name
end

def name=(str)
@name = str
end
end

person = Person.new
person.name = 'Dennis'
person.name # => "Dennis"

Awesome. Now we can write and read instance variable @name using reader and writer methods. Except, this is done so frequently, why waste time writing these methods every time? We can do it easier.

class Person
attr_reader :name
attr_writer :name
end

Even this can get repetitive. When you want both reader and writer just use accessor!

class Person
attr_accessor :name
end

person = Person.new
person.name = "Dennis"
person.name # => "Dennis"

Works the same way! And guess what: the instance variable @name in our person object will be set just like when we did it manually, so you can use it in other methods.

class Person
attr_accessor :name

def greeting
"Hello #{@name}"
end
end

person = Person.new
person.name = "Dennis"
person.greeting # => "Hello Dennis"

That's it. In order to understand how attr_reader, attr_writer, and attr_accessor methods actually generate methods for you, read other answers, books, ruby docs.



Related Topics



Leave a reply



Submit