Trying to Learn/Understand Ruby Setter and Getter Methods

Trying to learn / understand Ruby setter and getter methods

You can interact with that instance variable from other methods belonging to that instance, even if there is no getter:

def noise=(noise)
@noise = noise
end

def last_noise
@noise
end

There doesn't need to be a getter defined with the same name as the method; the two are not linked at all. The getter is needed to "get" the value of the instance variable, but only in a short syntax.

What's happening in your example is that you're initializing a new object (Human.new), and then using a method (noise=, yes the method name contains the = symbol) that just-so-happens to define an instance variable (that is, a variable just for that instance), and then finally retrieving that instance variable with another method call.

You can actually use instance_variable_get to get the instance variable without defining any getter at all:

man = Human.new
man.noise = "Howdie"
man.instance_variable_get("@noise")

This will return "Howdie", even though there is no getter defined.

And no, I don't think he's using an older version of Ruby.

Im trying to understand Getters and Setters in the ruby programming language

Understanding attr_accesor, attr_reader and attr_writer

These are Ruby's getters and setters shortcut. It works like C# properties, that injects the get_Prop (getter) and set_Prop (setter) methods.

  • attr_accessor: injects prop (getter) and prop= (setter) methods.
  • attr_reader: it's a shortcut for read-only properties. Injects prop method. The prop value can only be changed inside the class, manipulating the instance variable @prop.
  • attr_writer: it's a shortcut for write-only properties. Injects prop= method.

Ruby doesn't have methods called get_prop (getter) and set_prop (setter), instead, they're called prop (getter) and prop= (setter).

That being said, you can infer that

class Person
attr_accessor :name, :age
end

is the short version for

class Person
# getter
def name
return @name
end

# setter
def name=(value)
@name = value
end
end

You don't need to call return, Ruby methods returns the last executed statement.

If you are using Ruby on Rails gem, you can build model objects using new and passing properties values as arguments, just like:

p = Person.new(name: 'Vinicius', age: 18)
p.name
=> 'Vinicius'

That's possible because Rails injects something like this initialize method to ActiveRecord::Base and classes that includes ActiveModel::Model:

def initialize(params)
params.each do |key, value|
instance_variable_set("@#{key}", value)
end
end

Ruby/Rails: Understanding ruby getter-setter methods and instances

When you do

@dog = Dog.new

You do two spearate things

1) Create an instance variable @dog for whatever object your code is currently inside

2) Instantiate a new instance of Dog (with all its methods and attributes) and assign a reference to it to @dog

@dog is a variable, that just happens to point at the Dog instance ("instance of class" generally same meaning as "object") you created at that point. You can set other variables to point to the same instance, and in Ruby this is generally how you pass data around. Objects contain instance variables, and those instance variables point to yet more objects.

Using the assignment operator (i.e "=") you can point a variable at any other object.

To answer your questions in turn:

When I am in the owner class and I call @dog.popularity how does it
know the value of popularity for that instance?

You have to be careful in Ruby (and OO languages in general) to differentiate between class and object in your descriptions and questions. Ruby I'm assuming you are referring to a line of code in the Owner class, and that you intend it to work with an owner object. I'd also assume that @dog is an attribute you have added to Owner.

In which case, Ruby knows because @dog points to the Dog object that you added to owner. Each Dog object has its own copy of all of Dog's instance variables. You do need to take care in Ruby though, because variables point to objects, that you aren't simply passing in the same Dog object to all the owners (i.e. they all effectively share a single dog). So you need to understand when you are creating new instances (via new) and when you are simply handling existing references.

At runtime are all methods processed and then that instance just
always is tied to the value at the time?

No. At runtime, basic Ruby will only perform the assignments that you have coded. Instance variables may not even exist until the code that assigns them has been run. If you use attr_reader etc methods, then the variables will at least exist (but will be nil unless you assign something during initialize)

Calling setters and getters

In your example:

book01.isbn=("9876")
book01.isbn= "9876"
book01.isbn = "9876"

The last 2 examples are 'syntactic sugar', which are things that technically aren't proper syntactically but are kept in the language because they keep the code cleaner. The first example is the only way that would work if Ruby didn't support syntactic sugar. Why?

Because attr_acccessor :isbn behind the hood creates the following code for you:

def isbn
@isbn
end

def isbn=(new_isbn)
@isbn = new_isbn
end

These are 2 totally different methods, this might be confusing because the only difference in name is the = sign. But that doesn't mean anything, and doesn't change the fact they are totally different methods.
So with:

book01.isbn=("9876")

you're actually calling def isbn=(new_isbn) which is a method, nothing more, nothing else. And with:

book01.isbn= "9876"
book01.isbn = "9876"

you're just calling the SAME method, just using 'syntactic sugar'. Behind the hood, Ruby sees all of these 2 as:

book01.isbn=("9876")

Can you guess why this code will not work?

book01.isbn("9876")

Because, as we saw earlier, behind the hood Ruby creates 2 methods. The first method doesn't accept ANY arguments, therefore, you get the error you're getting (Ruby is just telling you, I expected 0 arguments, and you provided 1, therefore I raised ArgumentError).

In the latest version of Ruby, is having both getter and setter methods necessary to access instance variables?

puts noise = "roar" 

This line has nothing to do with the previous lines - not with the class Carnivore, not with the instance carnivore. It creates a local variable noise (and prints it). Start experimenting with

puts carnivore.noise

Do Ruby objects have a built in getter/setter methods?

You're looking for attr_accessor :color if you want just the run of the mill auto-generated getters/setters.

Defines a named attribute for this module, where the name is symbol.id2name, creating an instance variable (@name) and a corresponding access method to read it. Also creates a method called name= to set the attribute. String arguments are converted to symbols.

There's also just an attr_reader :color if you don't want to be able to set the value outside the class

Creates instance variables and corresponding methods that return the value of each instance variable.

And attr_writer :color if you want to set, but not read, outside of the class.

Creates an accessor method to allow assignment to the attribute.

class Obj
attr_accessor :color

def initialize(color)
@color = color
end
end

t = Obj.new("red")
t.color #=> "red"

Ruby square bracket setter/getter

Yes, it can be done. You need to define a []= and [] methods.

In this example, I am using a Hash as the internal data structure - you are free to use whatever you like.

class Word
attr_reader :meaning

def initialize
@meaning = Meaning.new
end
end

class Meaning
attr_reader :h

def initialize
@h = {}
end

def []=(key, value)
@h[key] = value
end

def [](key)
@h[key]
end
end

Example:

word = Word.new
word.meaning[:english] = 'Hello'
word.meaning[:english] # => 'Hello'


Related Topics



Leave a reply



Submit