Class Methods and Variables in Ruby

Class methods are methods that apply to a particular class in Ruby. Rather than acting on a particular instance of a class, they act on the class blueprint itself, i.e. the instance of the Class object.

Class methods can’t access instance variables. But instance methods can access class variables.

The most common way to create a class method in Ruby is to use the self keyword. This refers back to the class object itself.

class IceCream
  @@total_scoops = 0

  def self.increment_scoops
    @@total_scoops +=1
  end

  def self.scoops_served
    @@total_scoops
  end
end

In this example, we use the class variable @@scoops to keep track of total ice cream scoops served. If you aren’t careful, class variables can cause you problems in Ruby. Consider the following example.

class FrozenYogurt < IceCream
  @@total_scoops = 0
end

What would you expect the output to be when you ran the following code?

puts "Serving ice cream."
IceCream.increment_scoops
puts "#{IceCream.scoops_served} scoops of ice cream served."

puts "Serving froyo."
FrozenYogurt.increment_scoops
puts "#{FrozenYogurt.scoops_served} scoops of froyo served."

The answer is…

Serving ice cream.
1 scoops of ice cream served.
Serving froyo.
2 scoops of froyo served.

In Ruby, classes share class variables with their ancestors. Both of these classes are operating on the same @@scoops variable. This is often not the desired behavior. When you don’t want this, you should use what are sometimes called “class instance variables”. These are really just instance variables, but they’re applied to the instance of the Class class, the blueprint object. You create one by defining an instance variable right in the class body. You can only refer to these variable within class methods. Let’s try it.

class IceCream
  @total_scoops = 0

  def self.increment_scoops
    @total_scoops +=1
  end

  def self.scoops_served
    @total_scoops
  end
end
class FrozenYogurt < IceCream
  @total_scoops = 0
end

puts "Serving ice cream."
IceCream.increment_scoops
puts "#{IceCream.scoops_served} scoops of ice cream served."

puts "Serving froyo."
FrozenYogurt.increment_scoops
puts "#{FrozenYogurt.scoops_served} scoops of froyo served."

This code outputs:

Serving ice cream.
1 scoops of ice cream served.
Serving froyo.
1 scoops of froyo served.

Now both of our class instances, IceCream and Froyo, each have their own @scoops variable. Remember, classes in Ruby are objects too, specifically instances of the Class class. It’s a little confusing but all we are doing here is adding instance variables to our class instance. This avoids the issue with class variables being inherited by child classes.

A Spooky Ruby Class

In Ruby, pretty much everything is an object. The number 5 is an object. The string “boo!” is an object. When we want to be able to create a lot of similar objects, we use classes. Classes are objects too.

A class is like a blueprint for something we call instances. Each object you create using a class is called an instance. Ruby has a lot of built-in classes. Let’s take a quick look at the String class. To create a new instance of any class, we call the new method. So to create instances of the String class we could type:

greeting = String.new("Hi!")
#=>"Hi!"
formal_greeting = String.new("Hello!")
#=>"Hello!"

These String instances are each their own objects but they also share a lot of methods that they get from the String class.

greeting.size
#=>3
formal_greeting.size
#=>6

The built-in classes are great and all, but let’s try making our own. Let’s make a class for spooky monsters.

class Monster
  def initialize(spookiness, spooky_grabber)
    @spookiness = spookiness
    @spooky_grabber = spooky_grabber
  end
end

A lot of stuff is actually going on here so let’s break it down a bit.

class Monster

The class keyword declares the beginning of a class. Our class can be referenced later by its class name, Monster.

``ruby def initialize(spookiness, spooky_grabber)


Here we declare the `initialize` method. `initialize` is a special method. Whenever a new instance is of a class created, that class's `initialize` method is automatically called on that instance. It also receives all the arguments that were passed to the new method. This is the place to set up your object.

```ruby
    @spookiness = spookiness
    @spooky_grabber = spooky_grabber

Just assigning the parameters to some variables. But what are those @ signs? Those @ signs indicate that these are instance variables. Each instance keeps track of it’s own instance variables. Each monster will have it’s own individual spookiness and spooky grabber.

Now we can create some instances!

ghost = Monster.new(1, "a chilling presence")
skeleton = Monster.new(2, "a bony claw")
fishman = Monster.new(2, "a damp webbed hand")
cthulu = Monster.new(3, "several eldritch tentacles")

The good news is we have our instances. The bad news is, they don’t really do much yet. Let’s add some methods to our class so we can at least inspect the values we set up.

class Monster
  def initialize(spookiness, spooky_grabber)
    @spookiness = spookiness
    @spooky_grabber = spooky_grabber
  end

  def spookiness
    @spookiness
  end

  def spooky_grabber
    @spooky_grabber
  end
end

We can use these methods to see the values of our instance variables.

ghost.spookiness
#=>1

That seems like a lot of code to write just to read each variable. I know what you’re thinking. This is Ruby. There’s gotta be a shorter way. That’s where the attr_reader keyword come into play.

class Monster
  attr_reader "spookiness"
end

is the same as

class Monster
  def spookiness
    @spookiness
  end
end

Great let’s add it to our code.

class Monster
  attr_reader "spookiness", "spooky_grabber"

And let’s add some methods to print spooky text to the console.

class Monster
  attr_reader "spookiness", "spooky_grabber"

  def initialize(spookiness, spooky_grabber)
    @spookiness = spookiness
    @spooky_grabber = spooky_grabber
  end

  def spook
    print "You suddenly feel #{@spooky_grabber} brush your shoulder"
    puts  "." * spookiness
  end

  def scare
    print "You suddenly feel #{@spooky_grabber} wrap around your neck"
  puts  "!" * spookiness
  end
end

The spook and scare methods are called ‘instance methods’. That’s because each instance can use them. And when you call an instance method it has access to its caller’s instance variables.

ghost.spook
#  You suddenly feel a chilling presence brush your shoulder.
#=>nil
skeleton.scare
#  You suddenly feel a bony claw wrap around your neck!!
#=>nil

Sometimes you have a method that’s related to a class, but doesn’t really apply to individual instances. You can create ‘class methods’ by using the self keyword. Here’s an example. Pay attention to how self is used.

class Monster
  def self.merge (monster_a, monster_b)
    spookiness = monster_a.spookiness + monster_b.spookiness
    grabber = monster_a.spooky_grabber + " and " +  monster_b.spooky_grabber
    Monster.new(spookiness, grabber)
  end
end

You call class methods like this:

spook_team = Monster.merge(fishman, cthulu)

This method combines the properties of two objects then creates and returns a new instance. The created instance also has all the same methods as the other instances.

spook_team.scare
#  You suddenly feel a damp webbed hand and several eldritch tentacles wrap around your neck!!!!!
#=>nil

Great, nice and spooky!

And here’s the full code for our class.

class Monster
  attr_reader "spookiness", "spooky_grabber"

  def initialize(spookiness, spooky_grabber)
    @spookiness = spookiness
    @spooky_grabber = spooky_grabber
  end

  def spook
    print "You suddenly feel #{@spooky_grabber} brush your shoulder"
    puts  "." * spookiness
  end

  def scare
    print "You suddenly feel #{@spooky_grabber} wrap around your neck"
    puts  "!" * spookiness
  end

  def self.merge (monster_a, monster_b)
    spookiness = monster_a.spookiness + monster_b.spookiness
    grabber = monster_a.spooky_grabber + " and " +  monster_b.spooky_grabber
    Monster.new(spookiness, grabber)
  end
end

Why Accessibility?

When designing for the web, it’s important to always keep accessibility in mind. Accessibility means making sure your website or application is accessible to everyone. For example, audio content isn’t accessible for people with hearing difficulties, but if you add a transcript it can be. Text with low contrast might be less accessible for people with vision trouble, but by increasing the contrast you can make it easier for them read. And so on. The “how” of making accessible web pages is a incredibly broad topic, but what I want to touch on here is the “why”.

It’s easy to make excuses for not designing with accessibility in mind. “How is anyone supposed to remember all these guidelines. It seems like a disproportionate amount of work for such a small number of people.” Now, it’s true that designing an accessible site takes some additional effort. But you might be surprised to find out just how many users have some sort of accessibility issue.

If you haven’t read it before, I encourage you to check out the great article, An Alphabet of Accessibility Issues, by Anne Gibson right now. The article illustrates the huge variety of forms that accessibility issues can take. Accessibility affects the lives of all sorts of people.

And that’s not all. The fact is, making a site accessible improves the experience for everyone. Just going back to the earlier examples, adding transcripts for audio doesn’t just help people with hearing difficulties. Now your content is easier to access for people who can’t turn on their speakers where they are, people who don’t have time for audio, and people who would just prefer to read. And improving the contrast of your text can make the site more comfortable to read even for users with perfect vision.

Fun with Enumerable#map

Ruby’s map function is great when you want to transform every item of a list in the same way. Here’s the syntax from the Ruby docs:

map { |obj| block } → array

map iterates through an enumerable object, and returns an array using the return value of the code in block for each item in the enumberable. The obj is whatever the enumerable object supplies for iteration. For an array it will be a value in the array. For a hash it will be a key-value pair.

map is a non-destructive function. It leaves your original object alone and returns a new transformed object. You can transform your items in all sorts of ways. It’s great when you have a set of data that’s just a step or two away from what you want.

Add one to every item.

[0, 5, 9].map {|value| value + 1}
#=> [1, 6, 10]

Get the first ten square numbers.

(1..10).map {|n| n * n}
#=> [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Capitalize every string.

["hello", "world"].map {|word| word.to_s.upcase}
#=> ["HELLO", "WORLD"]

Simple Pig Latin with map.

["ruby", "is", "fun"].map {|word| 
    vowels = ["a","e","i","o","u"]
    if vowels.include? word[0]
        word + "yay"
    else
        word[1..-1] + word[0] + "ay"
    end
}
#=> ["ubyray", "isyay", "unfay"]

You can also use map on hashes. Just remember map always returns an array, so you have to do a little bit of extra work to change a hash to a new hash with map.

Map always returns an array, even when applied to a hash.

balances = {"Amy" => 20000.0, "Bill" => 4000.0, "Carol" => 6000.0}
interest_rate = 1.01
balances.map {|_, value| value * interest_rate]}
#=> [20200.0, 4040.0, 6060.0]

When you return key-value pairs, you still get an array.

balances.map {|key, value| [key, value * interest_rate]}
#=> [["Amy", 20200.0], ["Bill", 4040.0], ["Carol", 6060.0]]

You can convert an array of key-value pair arrays to a hash using .to_h

balances.map {|key, value| [key, value * interest_rate]}.to_h
#=> {"Amy"=>20200.0, "Bill"=>4040.0, "Carol"=>6060.0}

I hope these examples helped you to better understand what the map function does and how to use it.