Lesson Wednesday

In object-oriented programming languages, every class has a method called a constructor which "constructs" new objects following the rules provided for new instances. We’ve already worked with Ruby’s initialize() method extensively, especially with assigning values to instance variables. For instance, here’s our Album class with its initialize() method:

class Album
  def initialize(name, id)
    @name = name
    @id = id || @@total_rows += 1
  end
end

However, we're probably going to want to add more properties to our classes. For instance, we might want to add artist_name, year, genre, description, and so on:

class Album
  def initialize(name, artist, year, genre, description, id)
    @name = name
    @artist = artist
    @year = year
    @genre = genre
    @length = length
    @id = id || @@total_rows += 1
  end
end

However, what if we are deep in a project that already has dozens or even hundreds of specs? Each time we add a new property to a class, we need to change all our specs. We'd also need to change all our production-ready code.

Here’s an example of how we might need to change a test:

describe("#name") do
  it("returns the name of an album") do
    album = Album.new("In Rainbows", "Radiohead", 2007, "Rock", "42:39")
    expect(album.name()).to(eq("In Rainbows"))
  end
end

Let's say the record store owner wants to add a description property to an Album. Now we have to add that property to all of our specs like this:

describe("#name") do
  it("returns the name of an album") do
    album = Album.new("In Rainbows", "Radiohead", 2007, "Rock", "42:39", "Radiohead's seventh studio album")
    expect(album.name()).to(eq("In Rainbows"))
  end
end

This will get tedious fast. Because of this issue, our testing and production code will be brittle and break easily.

As the number of attributes to assign an object grows, we can use a single attributes hash to pass all of the attributes in one parameter instead of individually in multiple arguments. This will allow our classes to grow more easily without having to update as much code.

The keys of the attributes hash will represent the names of the attributes and the values will represent the values to be set for the new object.

Let's write a spec for initializing new Albums with a single attributes parameter:

describe("#name") do
  it("returns the name of an album") do
    album = Album.new({:name => "In Rainbows", :artist => "Radiohead", :year => 2007, :genre => "Rock", :length => "42:39"})
    expect(album.name()).to(eq("In Rainbows"))
  end
end

As expected, this will fail until we update our constructor method to expect the new attributes hash:

class Album
  def initialize(attributes)
    @name = attributes.fetch(:name)
    @artist = attributes.fetch(:artist)
    @year = attributes.fetch(:year)
    @genre = attributes.fetch(:genre)
    @length = attributes.fetch(:length)
  end
end

The attributes hash contains all of the values we need to create a new Album instance. We use the fetch method and the key for the attribute's name to get those values and assign them to instance variables for each new vehicle.

Adding new properties will be much easier in both our specs and constructor method because we won't have to change the details of the parameters. We'll just add the new property to the hash and fetch it in the constructor method.

This is how we’ll create and test new objects moving forward. Make sure to update your existing classes in the Record Store project to use the attributes hash. To do this successfully, you'll have to review all the files in both the spec and lib folders and update the following:

  • All initialize() methods to use the attributes hash
  • All .new() method calls on Song or Album to use the attributes hash

Using an Attributes Hash


Here's an example initialize method with an attributes hash:

class Album
  def initialize(attributes)
    @name = attributes.fetch(:name)
    @artist = attributes.fetch(:artist)
    @year = attributes.fetch(:year)
    @genre = attributes.fetch(:genre)
    @length = attributes.fetch(:length)
  end
end

We can create a new Album by doing the following:

Album.new({:name => "In Rainbows", :artist => "Radiohead", :year => 2007, :genre => "Rock", :length => "42:39"})

Lesson 32 of 37
Last updated August 7, 2022