Dynamic Dispatching Basics

Reading time ~4 minutes

Besides the added benefit of writing more concise and considerably less code, metaprogramming has many other advantages as well. Possibly the most appealing feature of metaprogramming is it allows your code to become much more flexible and easier to edit. Although there are many aspects of metaprogramming, I would like to focus on one concept in particular: dynamic dispatching. Dynamic dispatching is a valuable tool for any programmer because you can call methods throughout your program at run time.

In Ruby, we rely on the send method to dynamically dispatch. To illustrate how the send method works I’ll use the below example:

1
"Hello, World".send("reverse")

*The arguments passed into the send method could be a String or a Symbol.

Here, the send method, or the message, converts the "reverse" argument into a method and determines whether it’s a viable method to be called upon the receiver, the String object "Hello, World". Since it is, Ruby evaluates it and outputs the following:

1
"Hello, World".send("reverse") #=> "dlroW ,olleH"

However, if the method cannot be called upon the object then an error message is displayed. For example, if we tried to add the integer 1 to the String object "Hello, World", we would get the following Type Error:

1
2
"Hello, World".send(:+, 1)
 #=> TypeError: no implicit conversion of Fixnum into String

Updating a Hash using Dynamic Dispatching

Another opportune time to utilize dynamic dispatching is when you want to update a hash. Let’s say you’re creating an application that tracks a users fitness goal for the week. At the beginning of the week the user designates how many minutes they plan on working out each muscle group and throughout the week they’re able to update their progress and monitor their results. The users weekly goal is stored in a hash like the one below where the key is the muscle group and the value is the amount of time they plan on working out that muscle:

1
2
3
4
5
6
7
8
"goal"=> {
  "chest"=> 45,
  "back"=> 45,
  "legs"=> 45,
  "shoulders"=> 30,
  "arms"=> 30,
  "cardio"=> 120
}

Our intention is to have the goal hash reflect the users progress after they conclude a workout. Let’s say the user worked out their chest for 40 minutes as well as doing about 20 minutes of cardio. We could expect to receive this data in a hash where the key is the muscle group and the value is the duration of the workout corresponding to that muscle.

1
updated_muscles = {"chest"=> 40,  "cardio"=> 20}

*We’ll assign the hash to a variable called updated_muscles so we can use this data later on in our dynamic dispatching function.

Although it may not be the most eloquent code, we could use a simple if/else statement to update our goal hash:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
updated_muscles.each do |muscle, updated_t|
  original_t = goal.send("#{muscle}")
  if muscle == "chest"
    goal[:chest] = (original_t - updated_t)
  elsif muscle == "back"
    goal[:back] = (original_t - updated_t)
  elsif muscle == "legs"
    goal[:legs] = (original_t - updated_t)
  elsif muscle == "shoulders"
    goal[:shoulders] = (original_t - updated_t)
  elsif muscle == "arms"
    goal[:arms] = (original_t - updated_t)
  elsif muscle == "cardio"
    goal[:cardio] = (original_t - updated_t)
  end
end

Is it just me or does there seem to be a lot of repetitious code in the above function? Let’s see if we can refactor this function using dynamic dispatching.

1
2
3
4
updated_muscles.each do |muscle, updated_t|
  original_t = goal.send("#{muscle}")
  goal.send(("#{muscle}="), (original_t - updated_t))
end

That’s better. These two functions perform the same task, just with different syntax. Now let’s examine the latter function to fully understand the functionality behind it.

To correctly update the goal hash we have to subtract the users inputted workout duration from their original goal. As the function iterates over each key-value pair stored inside the updated_muscles variable, the time that is associated with the muscle group that the current iteration is being called upon is stored in a local variable called original_t. In our example, the first iteration being called is the chest key-value pair, therefore original_t is set equal to 45, the value of the chest key from the original goal hash.

Now we can use the value of original_t, subtract it from the value of updated_t, and assign the difference to the original goal hash, thereby successfully updating our hash like we intended. On the current iteration, we access the chest key from our goal hash and assign it the value of 5 (45 – 40). This process would be repeated until each key-value pair in updated_muscles is iterated over. The updated goal hash would become:

1
2
3
4
5
6
7
8
"goal"=> {
  "chest"=> 5,
  "back"=> 45,
  "legs"=> 45,
  "shoulders"=> 30,
  "arms"=> 30,
  "cardio"=> 100
}

By using dynamic dispatching you can significantly reduce the lines of code you need to express a solution. In addition, your code will be able to handle future edits with ease. What’s not to like?

What Is Idempotency and Why Is It Useful?

Just the other day I was on a phone interview when I was asked to explain the benefits of idempotency. At first, this term bewildered me,...… Continue reading

Behind the Scenes of Website Hosting

Published on September 30, 2016

Factories vs Services

Published on August 31, 2016