What Would a Default Getter and Setter Look Like in Rails

What would a default getter and setter look like in rails?

attr_accessor is a built-in Ruby method and has no special meaning in the context ActiveRecord. attr_accessor :tag_list is basically equivalent to this code:

# getter
def tag_list
@tag_list
end

# setter
def tag_list=(val)
@tag_list = val
end

In ActiveRecord models, however, it could be that you want something like this:

def tag_list
self[:tag_list]
end

def tag_list=(val)
self[:tag_list] = val
end

There is a slight difference: With the first method, obj[:tag_list] doesn't use the same storage as your getter and setter. With the latter, it does.

Explanation of the getter/setter concept

In Ruby, the following two lines of code are equivalent

thing.blabla
thing.blabla()

Both call the method blabla of the object thing and evaluate to the last expression evaluated within that method. This means, you also don't need a return statement in the case of the above getter method, because the method simply returns the last expression in the method (@tag_list, the value of the instance variable).

Also, those two lines of code are equivalent:

thing.blabla=("abc")
thing.blabla = "abc"

Both call the method blabla= of the object thing. The special name with the = character can be used like any other method name.

The fact that attributes, as they are sometimes called, are in fact plain methods, you can also use some special logic transformed on the values before returning or accepting them. Example:

def price_in_dollar
@price_in_euro * 0.78597815
end

def price_in_dollar=(val)
@price_in_euro = val / 0.78597815
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).

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 add getters and setters without def?

Ruby let you define simple getters/setters using the methods attr_accessor, attr_reader, and attr_writer. In your example you can use attr_accessor to define both a getter and a setter for your instance variables:

class Book

attr_accessor :title, :author, :pages, :year

def initialize (title, author, pages, year)
# same as before...
end
end

book = Book.new('foo', 'bar', 100, 2010)
book.title
# => "foo"

book.title = 'baz'
book.title
# => "baz"

Calling attr_accessor :title is equivalent to define a couple of methods like the following:

def title
@title
end

def title=(new_title)
@title = new_title
end

You can find more informations about assignment methods (i.e. methods whose name end in =) on the Ruby documentation.

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 get and set in one method

The trickier case is if you want to set duration to nil. I can think of two ways of doing this

def duration(*args)
@duration = args.first unless args.empty?
@duration
end

Allow people to pass any number of args and decide what to do based on the number. You could also raise an exception if more than one argument is passed.

Another way is

def duration(value = (getter=true;nil))
@duration = value unless getter
@duration
end

This exploits default arguments a little: they can be pretty much any expression.

When called with no arguments getter is set to true, but when an argument is supplied (even if it is nil) the default value is not evaluated. Because of how local variable scope works getter ends up nil.

Possibly a little too clever, but the method body itself is cleaner.

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.



Related Topics



Leave a reply



Submit