Ruby mixins explained

Hi Devs,

In this post i will try to explain how to use ruby mixins for DRYing up your code. Let’s say I have Car which can drive and honk. It can find a car with vin number or return count.

class Car
  def self.find(vin)
    puts "return car with VIN number #{vin}"
  end

  def self.count
    puts "return number of cars"
  end
  def honk
    puts "I like to yell at people"
  end

  def drive
    puts "will try to avoid speed tickets"
  end
end

# A Car can drive, honk

car = Car.new
car.drive
car.honk

# Find car by its VIN number
Car.find("123")
# Get total number of cars
Car.count

Now we get a requirement to add Trucks to our inventory which do most of the things aCar does. Let’s start DRYing the code.

First I will extract instance methods using a module and use include to make them available. In ruby we use include to add instance level methods from a module.

module VehicleBehavior
  def honk
    puts "I like to yell at people"
  end

  def drive
    puts "will try to avoid speed tickets"
  end
end

class Car
  include VehicleBehavior

  def self.find(vin)
    puts "return car with VIN number #{vin}"
  end

  def self.count
    puts "return number of cars"
  end
end

Making progress. Now i will extract the class level methods into a module and use theextend keyword to make them available. In ruby we use extend to add class level methods from a module.

module VehicleBehavior
  def honk
    puts "I like to yell at people"
  end

  def drive
    puts "will try to avoid speed tickets"
  end
end

module VehicleData
  def self.find(vin)
    puts "return car with VIN number #{vin}"
  end

  def self.count
    puts "return number of cars"
  end
end

class Car
  include VehicleBehavior
  extend VehicleData
end

Good. But is there a way to combine both modules into one ? yes.

Every time a class includes module – Ruby will trigger the self.included method on that module. It will also pass class as a parameter. In our case the Car class will be the klass argument.

module Vehicle
  def self.included(klass)
    klass.extend(ClassMethods)
  end

  module ClassMethods
    def find(vin)
      puts "return car with VIN number #{vin}"
    end

    def count
      puts "return number of cars"
    end
  end

  def honk
    puts "I like to yell at people"
  end

  def drive
    puts "will try to avoid speed tickets"
  end
end

class Car
  include Vehicle
end

Looking good. But is there is a cleaner way to do this YesActiveSupport::Concern

require 'active_support/concern'

module Vehicle
  extend ActiveSupport::Concern

  def honk
    puts "I like to yell at people"
  end

  def drive
    puts "will try to avoid speed tickets"
  end

  class_methods do
    def find(vin)
      puts "return car with VIN number #{vin}"
    end

    def count
      puts "return number of cars"
    end
  end
end

class Car
  include Vehicle
end

car = Car.new

Car.find("blah")
car.drive
car.honk

ActiveSupport::Concern makes the syntax better and also has the advantage of  gracefully handling module dependencies.

Now our Truck class is as simple as this

class Truck
  include Vehicle
end

Pretty cool right? Less code! Less bugs!! YaY.

Java vs Ruby

Runtime differences:

In Java the program always starts with a main method. Ruby program by default creates a main Object which is an instance of class class and then starts execution on it(weird!). It always starts reading from the first line of the file, there is no specific starting point.

So when you create a ruby test file HelloWorld.rb with below statement

puts "HelloWorld"

execute it in terminal with ruby HellowWorld.rb . Ruby interpreter creates main object and then starts to execute puts.(puts is an instance method from kernel module. These methods are called without a receiver).

To test this go to terminal irb and do the following.

ruby main object

Execution differences:

Java is a compiled language so the execution is a 2 step process. compile + run.

Ruby is an interpreting language.

Both Java compiler and Ruby interpreter are written in C.

Programatic differences:

Ruby is completely object oriented what does that mean from a java perspective ?

– In java we have primitive data types such as int, float etc along with Classes and Objects. In Ruby Integer, Float etc are also Classes inherited from Numeric class which intern inherited from Object class. here is a detailed image taken from rubylearning.com which explains this.

class

So when you create an Integer in ruby you are creating a object for Integer class.

One more important ruby feature to discuss is duck typing.

Java is strictly or strongly typed language where we need to declare the type of variable.

Ruby has implicit and explicit type conversions.

Lets see what implicit conversion means.

duck typing

Ruby is loosely typed language where it tries to define the type of a variable implicitly as shown above. If it can not infer it it throws an error as below.

undefined local variable or method

Explicit conversion is kind of ruby’s way to type safety. I can try to convert into my expected type. see below.

ruby convert to string

Happy coding!