- ga-wdi-boston/js-objects-intro
- ga-wdi-boston/js-objects-intro-constructors
- ga-wdi-boston/ruby-basics
- ga-wdi-boston/ruby-arrays
- ga-wdi-boston/ruby-hashes
- ga-wdi-boston/ruby-methods
In JavaScript (JS), there's no distinction between Objects and key-value pairs (a.k.a. hashes, a.k.a. associative arrays), and in fact, JS objects look and behave similarly to Ruby hashes. However, objects in Ruby behave differently from objects in JS.
By the end of this lesson, students should be able to:
- Give one example of each of the following differences between JS hashes and objects within Ruby:
- Instantiating new objects.
- Creating new properties.
- Accessing properties.
- Define a class for an arbitrary object and instantiate it.
Why does the word 'object' refer to two kinds of different things, depending on whether we're talking about Ruby or JavaScript? The answer is that 'object' is actually a much more generic term. In pure computer science, an object is simply a location in memory that holds a particular value. In the case of object-oriented programming languages, like JavaScript and Ruby, 'object' means a self-contained collection of properties and methods.
At the end of the day, the idea of an object is just a conceit. The physical world is composed of objects (e.g. cars, buildings, people) which each have their own attributes and behaviors, so having the ability to model things in this way is very useful for solving problems.
As far as objects go, probably the biggest difference between Ruby and JavaScript is that Ruby is a 'classical' language; this means that Ruby uses things called Classes to define and instantiate objects (as compared to JavaScript,)
In JavaScript, a totally vanilla object can be created by simply typing {}
, also known as an object literal, or by using new
plus a constructor function, as follows:
var x = new Object();
To create an object that has a given set of properties, just write a different constructor function.
var Dog = function(name, breed) {
this.name = name;
this.breed = breed;
}
var rover = new Dog("Rover","greyhound");
In Ruby, there is no such thing as Object Literal syntax; instead objects must be created with a constructor, using the syntax below.
x = Object.new
Remember, in Ruby, it's not necessary to write parentheses to invoke a method (though it's common to do so if a method has arguments).
How do we define an object so that it has a given set of properties? In Ruby, this is accomplished using a type of object called a class. A class can be thought of as a template or definition for the object, but separate and distinct from the object itself; the class contains a constructor function, which specifies the properties that the object will have.
Here's how we might create the above 'Dog' object in Ruby:
class Dog
def initialize(name, breed)
@name = name
@breed = breed
end
end
The @
means that this property belongs individually to each specific instance of Person - in Ruby, this is called an instance variable.
Methods for a given object also get specified inside the class.
class Dog
def initialize(name, breed)
@name = name
@breed = breed
end
def bark
puts "WOOF"
end
end
Fork and clone this repo. Inside the folder lib
, you'll find a file called person.rb
. Inside this file, create a class for a Person object that has the following properties: name, age, and dare of birth.
Then, in the root directory of this repo, open up a ruby interpreter using the command pry
. In the first line, run the command require_relative 'lib/person.rb'
; this will load the contents of the entire person.rb
file, as if we had entered them manually into the console.
Finally, instantiate a person object using .new
and store it inside a variable. What do you see in the console?
In JavaScript, once we'd created an object, we could dynamically add properties and methods to it simply by calling their names, like so:
var x = {};
x.name = "Matt";
x.favoriteFood = "blueberries";
In Ruby, it's possible to add new properties or methods to an existing object, but it's not very common. Generally, all the properties and methods that an object will have will be laid out in the class.
In JavaScript, all properties and methods on an object are (by default) both publicly readable and writeable. This means that we can do things like this:
var phil = {
name: "Phil",
occupation: "researcher",
bestFriend: "Lem",
doScience: function() {
console.log("SCIENCE!!");
}
}
console.log(phil.name); // prints "Phil"
phil.doScience(); // "SCIENCE!!"
phil.bestFriend = "Ted"; // Changes the value of phil.bestFriend
In Ruby, by default, all instance variables are private - they can only be accessed or modified within the object - while methods are all public. In order to retrieve manipulate the properties of a Ruby object from the outside, we therefore need to create methods that can either retrieve ('getter') or change ('setter') the value of a property.
class Country
def initialize(name)
@name = name
@language
end
def get_language
@language
end
def set_language(lang)
@language = lang
end
end
england = Country.new("England")
england.set_language("english")
puts england.get_language
Above, the value of the @language
instance variable was not set when we created the new Country object. However, because we had a 'setter' method (in the form of set_language
), we were able to set its value after the object was created.
However, creating two methods (a 'setter' and a 'getter') for every property of an object is pretty tedious, especially since the methods are all essentially the same for every variable. Fortunately, Ruby gives us two handy shortcuts to help keep our code DRY.
class Country
attr_accessor :language
def initialize(name)
@name = name
end
end
england = Country.new("England")
england.language = "english"
puts england.language
The Ruby method attr_accessor
will create our setters and getters for us. We didn't even need to specify that language
was a property of a Country object - Ruby will automatically add language
as a property if it doesn't exist already. We can also create read-only properties using the attr_reader
keyword, like so:
class Country
attr_accessor :language
attr_reader :name
def initialize(name)
@name = name
end
end
england = Country.new("England")
puts england.name # prints out "England"
england.name = "France" # NoMethodError: undefined method `name=' for #<Country:0x__________________ @name="England">
Inside the lib
directory, create a new file called animal.rb
. Inside it, create a class called Animal (which, naturally, will create Animal objects). Each Animal object should have the following properties:
name
age
favorite_food
Set name
and age
in the constructor. Make name
'read-only', and make age
totally private. Make favorite_food
both readable and writeable.
Create another file in the lib
directory called shape.rb
. Inside it, create a Shape class, with the following properties:
name
(set in constructor, private)num_sides
(set in constructor, readable)color
(NOT set in constructor, readable and writeable)side_length
(set in constructor, but both readable and writeable)
It should also have a method called calculate_area
, which calculates the area of a 'regular' shape (all sides equal) for the given side length. The mathematical formula for this is
A = n * s * s / (4 * tangent(PI/n))
where n
is the number of sides, and s
is the length of the side.
HINT: Ruby has a module for performing mathematics called Math
; it has a lot of useful methods and properties that can help you out here. The Math
module is one of Ruby's default modules, so Ruby already knows how to find it; to add it to your Shape object, and gain access to those methods and properties, use the include
keyword.
Source code distributed under the MIT license. Text and other assets copyright General Assembly, Inc., all rights reserved.