Here is some code:
class Person
def initialize(age)
#age = age
end
def age
#age
end
def age_difference_with(other_person)
(self.age - other_person.age).abs
end
protected :age
end
What I want to know is the difference between using #age and self.age in age_difference_with method.
Writing #age directly accesses the instance variable #age. Writing self.age tells the object to send itself the message age, which will usually return the instance variable #age — but could do any number of other things depending on how the age method is implemented in a given subclass. For example, you might have a MiddleAgedSocialite class that always reports its age 10 years younger than it actually is. Or more practically, a PersistentPerson class might lazily read that data from a persistent store, cache all its persistent data in a hash.
The difference is that it is isolating the use of the method from the implementation of it. If the implementation of the property were to change -- say to keep the birthdate and then calculate age based on the difference in time between now and the birthdate -- then the code depending on the method doesn't need to change. If it used the property directly, then the change would need to propagate to other areas of the code. In this sense, using the property directly is more fragile than using the class-provided interface to it.
Be warned when you inherit a class from Struct.new which is a neat way to generate an intializer (How to generate initializer in Ruby?)
class Node < Struct.new(:value)
def initialize(value)
#value = value
end
def show()
p #value
p self.value # or `p value`
end
end
n = Node.new(30)
n.show()
will return
30
nil
However, when you remove the initializer, it will return
nil
30
With the class definition
class Node2
attr_accessor :value
def initialize(value)
#value = value
end
def show()
p #value
p self.value
end
end
You should provide the constructor.
n2 = Node2.new(30)
n2.show()
will return
30
30
The first answer is entirely correct, but as a relative newbie it wasn't immediately clear to me what it implied (sending messages to self? uh huh...). I think that a short example will help:
class CrazyAccessors
def bar=(val)
#bar = val - 20 # sets #bar to (input - 20)
end
def bar
#bar
end
def baz=(value)
self.bar = value # goes through `bar=` method, so #bar = (50 - 20)
end
def quux=(value)
#bar = value # sets #bar directly to 50
end
end
obj = CrazyAccessors.new
obj.baz = 50
obj.bar # => 30
obj.quux = 50
obj.bar # => 50
There isn't any difference. I suspect that it was done just for the documentary value of seeing self.age and other_person.age near each other.
I suppose that use does allow for an actual getter to be written in the future, which might do something more complex than just return an instance variable, and in that case the method would not need to change.
But that's an unlikely abstraction to worry about, after all, if the implementation of the object changed it's reasonable to change other methods, at some point a simple reference within the object itself is perfectly reasonable.
In any case, abstraction of the age property still doesn't explain the explicit use of self, as just plain age would also have invoked the accessor.
#age - is definitely the instance variable age
self.age - refers to the instance property age.
Related
I need to save all instances of an object in a global variable, so I can access that instances from another object. There is no need to pass them like parameteres.
In my solution I have a mixin with a method that puts the instance in a variable, also I used an open class technique to include that mixin in Object, so other objects use that method (and not only ONE class).
class Object
include Favourite
end
module Favourite
def favourite_it
#if the variable its not initialized:
#favourites.class == Array.class ? #favourites.push(self) :
#favourites = [].push(self)
end
def get_favourites
#favourites
end
end
#this class is only an example
class Dog
def initialize age
#age = age
end
end
class Dog_T
#all instances of this class will be saved in the variable
def initialize age
#age = age
favourite_it
end
end
class Handler
def do_something
#here I need to access the variable with all the instances of favourites to do something to them
end
end
And here is a simple test
handler = Handler.new
d1 = Dog_T.new(10)
d2 = Dog_T.new(12)
all_f = Handler.get_favourites
expect(all_f[0].age).to eq 10
expect(all_f[1].age).to eq 12
d3 = Dog_T.new(15)
all_f = Handler.get_favourites
expect(all_f[3].age).to eq 15
I tried to do this, but only each instance save itself in a different list (it makes sense because I'm not using global variables yet).
How can I do to have only one list, add the instances when are created and have the ability to empty and manipulate that list from Handler?
Ruby supports using a class variable in a Module. You also need a reader method for the Dog_T object to be able to access the instance variable. Since Favourite doesn't know anything about the object, you will want to use respond_to? to guard against calling an non-existent method in the list. For example, if there were a Dog_R class that did not have a method age but did add itself, you would get a runtime error blindly calling the age method on members of the Favourite array.
module Favourite
##favourites = [] # you can use a class variable in module
def self.favourite_it(obj) # singleton method of the class
##favourites.push(obj)
end
def self.get_favourites # singleton method of the class, see below for usage example
##favourites
end
end
class Object
include Favourite
end
class Dog
def initialize age
#age = age
end
end
class Dog_T
attr_reader :age # you need a reader to able to access it
def initialize age
#age = age
Favourite.favourite_it(self)
end
end
d1 = Dog_T.new(10)
d2 = Dog_T.new(12)
all_f = Favourite.get_favourites
all_f.each do |obj|
puts "#{obj.class}: #{obj.age if obj.respond_to?(:age)}"
end
puts '-' * 20
d3 = Dog_T.new(15)
all_f = Favourite.get_favourites
all_f.each do |obj|
puts "#{obj.class}: #{obj.age if obj.respond_to?(:age)}"
end
The output of this program is:
Dog_T: 10
Dog_T: 12
--------------------
Dog_T: 10
Dog_T: 12
Dog_T: 15
I have a curiosity regarding which is the preferred way of accessing instance variables inside a class that has defined an accessor for that instance variable.
One way would be by referencing the instance variable directly:
class Example
attr_accessor :attribute
def meth
puts #attribute
end
end
The other way would be by calling the reader created by the accessor:
class Example
attr_accessor :attribute
def meth
puts attribute
end
end
It's a small difference, but I am curious which is the preferred approach and why. The only advantage I see on readers vs. direct instance variable access is that it is easier to stub a reader inside a test.
It's best to go through the accessors. For instance, if you access the instance variable directly, and then you later convert the value with the reader, then accessing the instance variable directly won't see that change.
Using the accessors rather than accessing the instance variables directly causes one small quirk when you want to use the accessor to set the value of the instance variable.
Normally when you write:
some_meth 10
...ruby will interpret that as:
self.some_meth(10)
But if you write:
age = 10
...ruby will not interpret that as:
self.age=(10)
Instead, ruby will create a local variable named age and set it to 10, which has no affect on an instance variable named #age.
In order to call the setter for #age, you have to explicitly write self:
self.age = 10
Here is a complete example:
class Dog
attr_reader :age
def age=(val)
#age = val * 7
end
def initialize val
self.age = val #age = val will not call the setter
end
end
d = Dog.new 10
puts d.age #=> 70
I'm trying to understand self in Ruby.
In the code pasted below, if I create a new instance of Animal with
fox = Animal.new.name("Fox").color("red").natural_habitat("forest").specie("mammal")
and then call
fox.to_s
It does not do anything if I do not return self in every method.
Why do I need self in every method? Isn't the variable already saved once I create a new Animal?
class Animal
def name(name)
#name = name
self
end
def specie(specie)
#specie = specie
self
end
def color(color)
#color = color
self
end
def natural_habitat(natural_habitat)
#natural_habitat = natural_habitat
self
end
def to_s
"Name: #{#name}, Specie: #{#specie}, Color: #{#color}, Natural Habitat: #{#natural_habitat}"
end
end
This pattern is used infrequently in Ruby, it's much more common in languages like Java and JavaScript, where it's notably rampant in jQuery. Part of the reason why is the verbosity you're describing here, and secondly because Ruby provides a convenient mutator generator in the form of attr_accessor or attr_writer.
One problem with these accessor/mutator dual purpose methods is ambiguity. The implementation you have is incomplete, you're unable to read from them. What you need is this:
def color(*color)
case (color.length)
when 1
#color = color
self
when 0
#color
else
raise ArgumentError, "wrong number of arguments (%d for 0)" % color.length
end
end
That's a whole lot of code to implement something that can be used in two ways:
animal.color('red')
animal_color = animal.color
If you want to use these, you'll need to write your own meta-programming method that can generate them, though I'd highly discourage going down that path in the first place. Use attr_accessor methods and an options Hash.
Here's the equivalent Ruby pattern:
# Up-front assignment via Hash
animal = Animal.new(
name: 'Fox',
color: 'red',
natural_habitat: 'forest',
specie: 'mammal'
)
# Assignment after the fact
animal.color = 'white'
In your example, using self as the return value is convenient. The methods return the instance itself, so that you can call:
fox = Animal.new
fox.name("Fox").color("red").natural_habitat("forest").specie("mammal")
The value of fox.name("Fox") is the instance itself, that's why you can call .color("red") on it.
if the #name method would be implemented without calling self, like so:
def name(name)
#name = name
end
This method would return a string when called.
Animal.new.name #=> returns "name of animal"
This means that
Animal.new.name.specie
would call #specie method on a string object (which probably raises NotImplemented error) instead of object of Animal class which implements the method.
Recently I've been reading "Practical Object Oriented Design in Ruby", and I noticed one of the best practices was to use accessor methods instead of directly grabbing the #instance_variable. For example:
class Foo
attr_accessor :bar
def initialize(my_argument)
#bar = my_argument
end
# bad
# def lorem_ipsum
# #bar * 999
# end
# good
def lorem_ipsum
bar * 999
end
end
It makes sense to keep things DRY, and, in case I need to process #bar somehow before actually grabbing its value. However, I noticed that the initialize method sets the value of the #bar instance variable directly:
class Foo
attr_accessor :bar
def initialize(my_argument)
#bar = my_argument #<-- why isn't self.bar = my_argument used here?
end
Is there a reason for this? Shouldn't the setter method be used instead of directly using the = operator to set the value of the instance variable?
You're right, it would make much more sense to do
class Foo
attr_accessor :bar
def initialize(my_argument)
self.bar = my_argument
end
end
Arguments differ as to whether you should respect encapsulation within the object itself or not, but if you believe in that, then, yes, you should do this.
The initializer SETS the value upon initialization. The accessor lets you access (read/write) via the symbol after the object is already instantiated.
This post might help you understand:
What is attr_accessor in Ruby?
Actually, the setter can be used in initialize the same as in other methods, but setter cannot be used without a receiver.
I think you can use a_foo.bar= or self.bar=, but cannot use bar= without a receiver, because, in the later case, bar will be treated as a local variable rather than a setter method:
class Song
attr_accessor :name
def initialize(name)
self.name = name
end
def test_setter1(value)
#name = value
end
def test_setter2(value)
name = value #name is local variable
end
end
s = Song.new("Mike")
p s
s.test_setter1("John")
p s
s.test_setter2("Rosy")
p s
This results in:
#<Song:0x23a50b8 #name="Mike">
#<Song:0x23a50b8 #name="John">
#<Song:0x23a50b8 #name="John">
While you can use the setter in the initialization as shown in #uncutstone's answer, you cannot use it as you've proposed in the comment in your code.
The problem is that Ruby would interpret:
bar = my_argument
as an assignment to the bar local variable rather than an invocation of the bar= method.
This is discussed rather extensively in "Why do Ruby setters need "self." qualification within the class?".
I have a class with several methods:
class Test
def initialize (age, height)
#age = age
#height = height
end
def older
#age = #age + 2
end
def shorter
#height = #height - 5
end
end
man = Test.new(40, 170)
man.older
man.shorter
[...]
I want to pass onto object man a custom method, that is, I want to write something like man.variablemethod and set .variablemethod to either .older or .shorter, depending on some other factors. How can I do that?
I figured out that I can call "if condition then man.older", but I do not want to use if, especially when I have twenty different methods to choose from.
Sounds like you need send:
man.send(method_name)
You can pass a string or symbol representing the name of the method you wish to call, even pass additional arguments too:
def man.older_by_increment(years)
#age += years
end
man.send(:older_by_increment, 7)
This also allows you to call private and protected methods from anywhere:
class Man
# ...
private
def weight
#weight
end
end
Man.new.weight # => private method `weight' called for #<Man:0x10bc956d8> (NoMethodError)
Man.new.send(:weight) # => #weight