How do I call a custom method for an object? - ruby

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

Related

Ruby class methods

Can some one please help me with this problem.
I try this code and i keep getting a method error as seen in the bottom
class Song
##play = 0; #class variable must be define before use
def initialize(name, artist, duration)
#name = name
#artist = artist
#duration = duration
#plays = 0
end
def play
#plays += 1 #same as #plays = #plays + 1
##play += 1
"This song: ##plays plays. Total ###play plays"
end
end
class SongList
Max_Time = 5*60
def self.is_too_long(song)
return song.duration > Max_Time
end
end
song1 = Song.new("Bicyclops", "Fleck", 260)
p song1.duration
p SongList.is_too_long(song1)
ruby_ex03.rb:49:in <main>': undefined method duration' for #Song:0x000000000605f338 (NoMethodError)
In Ruby, instance variables and methods are distinct things, and there is no way* to access instance variables from outside the class they belong to. If you want to make your instance variables accessible, you need to define accessors in your class
def duration
#duration
end
Fortunately, this pattern is so common that Ruby provides a shortcut for us. Simply put this in your class body
attr_reader :duration
*Technically, we can use reflection to access instance variables from anywhere, but conceptually you should think of them as inaccessible. Those techniques are advanced and useful only in special circumstances.

save instances in global variable

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

Understanding self with method chaining

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.

What is the difference between def func(var) and def func=(var)?

In a class definition, what is the difference between these two methods?
def func(var)
...
end
def func=(var)
...
end
Is there any, or is one of them not valid?
Both of them are valid method definitions. But the second one is defining a 'setter' method - you can call this method with the following syntax:
obj.func = 123
This statement will be translated into
obj.func=(123)
You can take a look at this answer where I explain this syntax in a bit more detail.
To explain some things about reader/writer AKA getter/setter methods in Ruby:
Ruby doesn't force us to use = in the method definition for a setter. We get to choose whether the method has one.
Consider this:
class Foo
# automagically creates:
# .v
# .v=
attr_accessor :v
def initialize(v)
puts "inside initialize(#{ v })"
#v = v
end
def setter(v)
puts "inside setter(#{ v })"
#v = v
end
def setter=(v)
puts "inside setter=(#{ v })"
#v = v
end
end
f = Foo.new(1)
puts f.v
f.setter(2)
puts f.v
f.setter = 3
puts f.v
f.setter=(4)
puts f.v
f.v = 5
puts f.v
f.v=(6)
puts f.v
Running the code outputs:
inside initialize(1)
1
inside setter(2)
2
inside setter=(3)
3
inside setter=(4)
4
5
6
The = is simply another letter in the method name because Ruby is smart enough to know if it sees f.setter = 3 it should use the setter=(v) method.
Ruby doesn't force using = to set a variable, you can decide if it makes more sense to you when you define the method. It is idiomatic that we use = because it helps make a setter look like an assignment, removing the urge to name all the setters something like set_v(v).
These are defining the getter and setter methods if you will. Say you have a Person class with a phone attribute.
class Person
def phone
#phone
end
def phone=(number)
#phone = number
end
end
Now you could change the phone attribute (managed internally in the #phone) by simply setting the property which will invoke the phone= method.
john = Person.new
john.phone = "123-456-7890"
It looks like a property assignment on the outside. Other characters that you can stack at the end of a method name are ? for boolean getters, ! for destructive operations. Again, these are just conventions and you're free to use these three characters as you want. However, code simply looks more natural with these symbols around. For example,
question.closed?
document.destroy!

Instance variable: self vs #

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.

Resources