Learn Ruby and rails from rails guru
Posts tagged intialize with parameters
Quick Ruby Tutorials–2
Mar 24th
This is a continuation of the post Quick Ruby Tutorials -1
In the last post we created a Dog class with the ability to eat and bark. Lets continue to add more features to our class.
Each dog has a name, weight, height, color etc.
Let us add the property “weight” to the Dog class.
For this we need to
1) Store the weight of the dog. For this purpose we use an instance variable. Instance variables are explained later.
2) Add a method to access the weight
3) Add a method to set the weight.
def get_weight
@weight
end
def set_weight(w)
@weight = w
end
end
dog1 = Dog.new
dog1.set_weight(4)
dog1.get_weight #4
dog2 = Dog.new
dog2.get_weight # nil
dog2.set_weight(10)
dog2.get_weight # 10
dog1.get_weight # 4 , weight of dog1 still remains the same
@weight is an instance variable.
Instance variable start with @ sign.
Instance variable can be defined in any instance method. In this case it is defined in set_weight. In get weight it is being accessed, so if get_weight is called before set_weight then it returns a nil.
Note that, changing the weight of dog2 does not effect the weight of dog1.
So there is different copy of instance variable(@weight) for every instance(object). That is why we call it a instance variable.
Let us try accessing our instance variable directly from the object.
dog.@weight #error : syntax error
dog.weight #error: undefined method weight.
So we see that the instance variables are not accessible outside the object. That is the reason we needed to define the the instance methods get_weight and set_weight.
Let us change the names of the methods get_weight and set_weight
def weight
return @weight
end
def weight= new_weight
puts "in method weight="
# @weight is an instance variable. Instance variables start with @ sign.
@weight = weight
end
end
Now we can do
dog.weight #nil
dog.weight= 10 #in method weight=
dog.weight # 10
dog.weight = 20 #in method weight=
dog.weight # 20
Supper!!!
Note the special type of name we have given to method “weight=”.
This method is called when we do weight= or weight =(note the space between “weight” and “=”)
All these are same
dog.weight= 10
dog.weight = 10
Due to these type of methods and the possibility of eliminating parenthesis,
it does not look like we are accessing a method. It is as if we are accessing a variable “weight” inside the dog object.
When the name of the method is of the format method_name=, then ruby expects the method to accept only a single parameter. If you define a method with this format which accepts no parameters or accepts more than one parameters, then there would be a run time error. Try doing this
Hold to your seats tight, you may fly away after reading the next example
attr_accessor :weight
end
dog = Dog.new
dog.weight #nil
dog.weight = 10
dog.weight # 10
How did this happen, with just one line of code in the Dog class??
attr_accessor is a method which generates the getter method(to get the value of the instance variable),
and setter method(to set the value of the instance variable)
:weight: this is a data type symbol in ruby. attr_accessor creates instance variable @weight when it is passed the symbol :weight
More about symbols
Symbol. A symbol is an instance of the class Symbol and is defined by prefixing a colon with an identifier eg :name, :weight, :height etc. .
symbols are data types in ruby which are identified by a unique value property ie you cannot create 2 separate objects with same values.
y = :test
x.object_id # 79858
y.object_id# 79858
a = "test"
b = "test"
a.object_id # 22528570
b.object_id # 22520730
object id for both x and y are same. Object id is an internal id unique to the variable.(the values may be different for you)
object id for the strings are different. ie 2 different objects are created
Symbols are lighter version of their string counter parts
Symbols are much lighter on memory than strings
Most operations which can be performed on strings cannot be done on symbols.
Operations like read a part of the string, append something, delete some part of string etc
symbols with space are defined like :”ruby on rails”
symbols comparison is faster than string comparison as symbol comparison is just one comparison of hash value, but string comparison involves multiple comparison character by character(till there is no match)
When to use a symbol/ when to use a string
Whenever you want to name some thing: some property, key/value of a hash, but no string operations are required , use a symbol.
Getting back to our example,
Here attr_accessor generates for us the 2 mehtods weight and weight=.
We can pass multiple parameters to the attr_accessor method
attr_accessor :weight, :height, :name, :color
end
This creates the setter and getter methods for instance variables @weight, @height, @name and @color. ie it will create weigh,weight=, height and height= etc methods.
So now we can do
d.weight = 10
d.weight #10
d.height = 1
d.height #1
d.name = "Tim"
d.name #"Tim"
d.color = "brown"
d.name # "brown"
Similar to attr_accessor there attr_reader and attr_writer methods.
attr_reader only generate the reader(weight/height).
attr_writer only generates the writer(weight=/height=).
The properties of dog such as height/weight/color etc cannot me modified externally. ie they can only be read, not written. They may get modified due some internal processes. Like the weight and height of the dog grows due to metabolic activities internally.
The instance methods we define should only expose things which are possible.
So modification of weight, height, color should not be possible directly .
Of course we should be able to feed the dog more to increase its weight
attr_reader :weight, :height, :color
attr_accessor :name
end
d = Dog.new
d.weight = 1 # error undefined method weight= for the instance
d.height = 2 # error undefined method height= for the instance
d.color = "brown" # error undefined method color= for the instance
puts d.weight #nil
puts d.height #nil
puts d.color #nil
Not that you would need to comment out the line with errors for the program execution to complete
So it is clear from the above example that only setter/writer methods are not defined, only getter/reader methods are defined
Now our dog is born with no values set for weight, height, color. This is not correct.
Whenever a dog is born(a new object is created), the dog should have some weight, height, color etc.
Lets say when a dog is born it has a weight of 1 kg, height 0.5 feet and color “brown”.
We can use constructors for this purpose, similar to those in C++.
A constructor is a method which is called immediately after a new object is created.
In ruby the constructor method is named “initialize”.
So if you define a method with name “initialize” in your class, it will be called after a object of that class is created.
attr_reader :weight, :height, :color
attr_accessor: name
def initialize
puts "in initialize" #This is used to demonstrate the flow of control
@weight =1
@height = 0.5
@color = "brown"
end
end
dog = Dog.new #in initialize
puts dog.weight # 1
puts dog.height #0.5
dog.color # brown
Let us give our dog a name
puts dog.name # Tim
Note that a dog when born does not have a name. It is given to it later.
That is the reason we did not assign the name in the initialize method.
As per the above code, all the dogs born will have a weight of 1kg, height of 0.5 feet and white color.
If god would have written such a program, then there would be no diversity on this planet. That would be very boring, right?
We should have a option of selecting the weight, height and color. If we can pass these parameters to the new method that would be perfect.
That can be done making our initialize method accept some parameters.
attr_reader :weight, :height, :color
attr_accessor: name
def initialize(weight, height, color)
puts "in initialize"
@weight, @height, @color = weight, height, color #note the parallel assignment of variables
end
end
dog1 = Dog.new(2,1,"white") # in initialize
puts dog1.weight # 2
puts dog1.height # 1
puts dog1.color # white
dog2 = Dog.new # error: wrong number of arguments 0 for 3
It would be great if there are some defaults for the weight, color and heigth.
ie, if the value is not passed then use a default value
attr_reader :weight, :height, :color
attr_accessor: name
def initialize(weight = 1,height = 0.5, color = "brown")
puts "in initialize"
@weight, @height, @color = weight, height, color
end
end
d = Dog.new #in initialize
puts d.weight # 1
puts d.height # 0.5
puts d.color # white
d2 = Dog.new(4,2) #in initialize
puts d2.weight # 4
puts d.height # 2
puts d.color # brown
d3 = Dog.new
puts d2.weight # 1
puts d.height # .5
puts d.color # brown
So now God has lot more flexibility in creating new dogs
Add other abilities to our Dog :sleep, eat, run
attr_reader :weight, :height, :color
attr_accessor: name
def initialize(weight = 1,height = 0.5, color = "white")
puts "in initialize"
@weight, @height, @color = weight, height, color
end
def bark
puts "bhow bhow!!!"
end
def eat(food="bone")
puts "I am eating #{food}"
end
def sleep
puts "bbye, I am going to sleep"
end
def shit
puts "aah! now I am hungry again"
end
end
dog = Dog.new
dog.bark # bhow bhow!!!
dog.eat # I am eating bone
dog.sleep # bbye, I am going to sleep
dog.shit #aah! now I am hungry again
Now our dog can eat, bark, sleep and shit.
Good enough, we will enhance our dog in the next post.
Share

