Code & Clay – Notes to self. Mainly Ruby/Rails.

Use is_a? when checking class

Do not use == when checking that an object is of a given class.

Whilst it works here:

> Array.new.class == Array
=> true

…it won’t work with descendants of the class we’re checking against:

> class Thing < Array; end
=> nil
> Thing.new.class
=> Thing
> Thing.new.class == Array
=> false

Instead use is_a?:

> Array.new.is_a? Array
=> true
> Thing.new.is_a? Thing
=> true
> Thing.new.is_a? Array
=> true

Enhanced shell scripting with Ruby

From Dev_Dungeon:

Ruby is a better Perl and in my opinion is an essential language for system administrators. If you are still writing scripts in Bash, I hope this inspires you to start integrating Ruby in to your shell scripts. I will show you how you can ease in to it and make the transition very smoothly.

I’ve learnt a few things from the article:

Debug output, and anything that does not belong in the output of the application should go to STDERR and only data that should be piped to anothe application or stored should go to STDOUT

There’s ARGF in addition to ARGV.

I can ask the user for a password without the password being echoed back to the terminal:

#!/usr/bin/ruby
require 'io/console'

# The prompt is optional
password = IO::console.getpass "Enter Password: "
puts "Your password was #{password.length} characters long."

I can check for return codes and errors via the $? object.

I can trap interrupt signals with Signal.trap().

#!/usr/bin/ruby

Signal.trap("SIGINT") {
    puts 'Caught a CTRL-C / SIGINT. Shutting down cleanly.'
    exit(true)
}

puts 'Running forever until CTRL-C / SIGINT signal is recieved.'
while true do end

Understanding how to implement a feature that enables users to follow each other

Many sites enable users to follow and be followed by other users. It’s a common feature. Dev.to implements it. Twitter implements it.

Michael Hartl demonstrates an implementation in Chapter 14 of The Ruby on Rails Tutorial. I didn’t really understand what was going on at first. It wasn’t until I’d gained a better understanding of ActiveRecord that I fully comprehended what was going on.

Here, I will explain my understanding of how it works.

Most of my following example is identical to Michael Hartl’s implementation.

What am I trying to implement?

Any user can follow any other user. At the same time, any user can be followed by any other user. The relationship is not reciprocal. That is, if one user follows another user, it is not necessary that the other follows back.

Essentially though, whether or not a user follows or is followed, I am describing two sides of the same relationship – one user points to the other.

  • I will call a user that follows another user a follower.
  • I will call a user that is followed by another user a followee.
  • A user can have many followers. Likewise, a user can follow many followees.
  • I will describe the relationship between a follower and followee as a followship.

Followships

I now have three concepts: followers, followees and followships.

Followships sounds like the name of a table to me. This table will have two columns. One will point to a follower. The other will point to a followee. Each is referenced by its id.

Followships table

Knowing this, I can generate a model named Followship. I will add an id column for followers and followees.

(Followships is a special kind of table called a join table. A join table is used to establish many to many relationships – where models can have many of each other.)

I’ll add an index for both follower_id and followee_id since I will want to look up followships by both followers and followees. Also, I’ll place a unique constraint on the rows because it doesn’t make sense that a user follows another more than once.

class CreateFollowships < ActiveRecord::Migration[5.2]
  def change
    create_table :followships do |t|
      t.integer :follower_id
      t.integer :followee_id

      t.timestamps
    end

    add_index :followships, :follower_id
    add_index :followships, :followee_id
    add_index :followships, [:follower_id, :followee_id], unique: true
  end
end

Before establishing the relationship in the model, there’s a couple of things to be aware of.

Firstly, an association name does not have to share the name of the model it points to. Let’s say I have the classes User and Project. A user can have many projects:

class User < ApplicationRecord
  has_many :projects
end

Each Project belongs to a single User. I could establish the relationship like this:

class Project < ApplicationRecord
  belongs_to :user
end

But I would like to refer to the user as the owner of the project.

So how about this?

class Project < ApplicationRecord
  # This will not work!
  belongs_to :owner
end

Using the above, Rails believes that I want to reference a model name Owner. It infers the class name of the associated object from the name I’ve provided.

So I need to be more explicit.

class Project < ApplicationRecord
  belongs_to :owner, class_name: 'User'
end

Now that I know this technique, I can create belongs_to associations for a followship’s follower and followee. Despite their names, both columns point to User.

class Followship < ApplicationRecord
  belongs_to :follower, class_name: 'User'
  belongs_to :followee, class_name: 'User'

Note: because both of these are belongs_to associations, it is the responsibility of Followship to record the foreign ids of the rows to which it belongs.

From the Rails guides:

4.1 belongs_to Association Reference

The belongs_to association creates a one-to-one match with another model. In database terms, this association says that this class contains the foreign key. If the other class contains the foreign key, then you should use has_one instead.

One final step here. I want to ensure that both the follower_id and followee_id is present. There cannot be a relationship if one is without the other.

The class ends up looking like this:

class Followship < ApplicationRecord
  belongs_to :follower, class_name: 'User'
  belongs_to :followee, class_name: 'User'

  validates :follower_id, presence: true
  validates :followee_id, presence: true
end

Catching breath

So what have I done so far?

  • I have created a model named Followship.
  • Each row in followships points to a follower_id and a followee_id.
  • I’ve indexed by both columns and ensured each pairing is unique.
  • I’ve made sure both ids are present in every row.
  • I’ve established that Followship belongs to a follower and followee. (In doing so, I’ve had to tell Rails that in both cases I’m actually pointing to User.)

Connecting User to the Followship

From the point of view of User, followships come in two flavours.

Depending on which way you look at it, a followship can either be a relationship in which the user is a follower of another or it can be a relationship in which the user is a followee of the other.

In the first case, I want to establish that the user has many relationships in which it follows another user. I want to tell Rails that it should reference the user by follower_id in the followships table.

I want the User model to declare to Rails:

“I can have many followships where I am following another user.

“I will refer to these followships as follower_followships.

“In this case, I will refer to myself as a follower.

“Store my id in followship’s’ follower_id field.”

# app/models/user.rb
# ...
# A follower is a user than follows another user.
has_many :follower_followships,
  class_name: "Followship",
  foreign_key: "follower_id",
  dependent: :destroy

(Like the project/owner example above, I have given the association a name that is different from the class it points to. I also know that because the other class is on the belonging side of the relationship, it is the other class that stores the id. In this case, the user id is stored as the foreign key follower_id.

Notice that I have also made the relationship dependent on the user existing. If the user is destroyed, so too are any rows in followships that correspond to it.)

All good but I haven’t yet pointed to another user. So far, User points only to the join table.

Through

Join tables are called such because they join one table to another. They are the glue in a has_many/has_many relationship. (They provide the belongs_to association that stores the foreign keys). Relationships are made through the join table.

I know that each row has a follower and followee column. I know that if my user follows another, its id will be stored in the follower_id column. I also know that in each row my user is recorded as a follower, there will be the corresponding followee_id.

Now I want the User model to declare:

“I can have many followees.

“I can follow many users. Look up my follower_followships. If you see any, the corresponding followee_id in those rows is the id of the followee I am following.”

Like so:

Follower followships

I declare the has_many association with followees through the previously established follower_followships association:

# app/models/user.rb
# To see the users that the user follows, we reference them through the join
# table.
has_many :followees, through: :follower_followships

So far, the User looks like this:

class User < ApplicationRecord
  # A follower is a user than follows another user.
  has_many :follower_followships,
    class_name: "Followship",
    foreign_key: "follower_id",
    dependent: :destroy

  # We reference the users that the user follows through the join
  # table.
  has_many :followees, through: :follower_followships

Now, I need to do the reverse. I need to establish the other flavour of followship where the user is the followee. A user can be a follower but when other users point to it, it is also a followee.

# A followee is a user that is followed by another user.
has_many :followee_followships,
  class_name: "Followship",
  foreign_key: "followee_id",
  dependent: :destroy

And I can look up followers through the join table:

# To see the users that follow a user, we reference them through the join
# table.
has_many :followers, through: :followee_followships

And now, followees can look up followers through the join table.

Followee followships

A couple of convenience methods

I can now access the users which a user follows by calling self.followees.

Knowing that, I can create two methods that allow me to follow and unfollow users conveniently.

def follow(user)
  followees << user
end

def unfollow(followed_user)
  followees.delete followed_user
end

The final class looks like this:

class User < ApplicationRecord
  # A follower is a user than follows another user.
  has_many :follower_followships,
    class_name: "Followship",
    foreign_key: "follower_id",
    dependent: :destroy

  # To see the users that the user follows, we reference them through the join
  # table.
  has_many :followees, through: :follower_followships

  # A followee is a user that is followed by another user.
  has_many :followee_followships,
    class_name: "Followship",
    foreign_key: "followee_id",
    dependent: :destroy

  # To see the users that follow a user, we reference them through the join
  # table.
  has_many :followers, through: :followee_followships

  def follow(user)
    followees << user
  end

  def unfollow(followed_user)
    followees.delete followed_user
  end
end

In the wild

Dev.to’s implementation is more complicated as it deals with polymorphic associations. However, something similar can be seen in mentor_relationship.rb and in the relevant part of User.rb:

has_many :mentor_relationships_as_mentee,
       class_name: "MentorRelationship", foreign_key: "mentee_id", inverse_of: :mentee
has_many :mentor_relationships_as_mentor,
       class_name: "MentorRelationship", foreign_key: "mentor_id", inverse_of: :mentor
has_many :mentors,
       through: :mentor_relationships_as_mentee,
       source: :mentor
has_many :mentees,
       through: :mentor_relationships_as_mentor,
       source: :mentee

Wrapping up

What have I done?

  • I’ve created a model called Followship.
  • I’ve established the Followship model belongs to a follower and followee.
  • The corresponding table followships is a join table. Users will relate to each other as followers and followees through this table.
  • Each row in the followships table stores a follower_id and a followee_id.
  • I’ve established the a User can have many follower_followships and many followee_followships.
  • A follower_followship (a followship in which the user does the following) is where the user id is stored under followship’s follower_id column.
  • A followee_followhsip (a followship in which the user does is followed) is where the user’s id is stored as a foreign key under followship’s followee_id column.
  • A user can have many followees through follower_followships.
  • A user can have many followers through followee_followships.
  • I’ve added a couple of convenience methods to follow and unfollow users.

What did I need to understand to get here?

  • A model that belongs to another model is responsible for storing the other model’s id as a foreign key.
  • I can establish many to many relationships through a join table.
  • I can give a relation a name that is different from the name of the class it refers to.

Giving an association a custom name

Sometimes, it may be desirable to reference an association with a name that differs from the one generated from the table name.

I have a model named Steps. Each step can have many steps. Each step belongs to one other parent step.

class Step < ApplicationRecord
  belongs_to :step
  has_many :steps
end
a = Step.create
b = Step.create
c = Step.create

a.steps >> b
a.steps >> c

I could get the parent step of b like so:

b.step
# => #<Step:0x00007fdf24b916e8

I don’t think that’s intuitive enough though. Since I’m asking for the parent step, it should be more like:

b.parent
# => #<Step:0x00007fdf24b916e8

Setting up the custom name is simple. I can supply the name of my choosing. Then, all I need to do is specify the class name and foreign id in the options.

class Step < ApplicationRecord
  belongs_to :parent, class_name: 'Step', foreign_key: :step_id
  has_many :steps
end

Be careful when memoizing booleans!

(This post is also published on dev.to)

It took me an hour or so of frustration to figure out why a method was being called multiple times despite my attempt at memoizing its return value.

The problem

My problem looked a bit like this.

def happy?
  @happy ||= post_complete?
end

My intention was that value of post_complete? would be stored as @happy so that post_complete? would be fired only once.

However, that’s not guaranteed to happen here. post_complete? might be evaluated and its value assigned to @happy every time I call happy?.

Can you see why?

  @happy ||= post_complete?

What’s going on?

The question mark denotes that post_complete? is expected to return a boolean value. But, what if that value is always false?

Another way of writing the statement is:

@happy || @happy = post_complete?

In the above example, I want to know if at least one of the sides is true.

Remember that if the left-hand side of an || statement is false, then the right-hand side is evaluated. If the left side is truthy, there’s no need to evaluate the right side – the statement has already been proven to be true – and so the statement short circuits.

If I replace post_complete? with boolean values, it’s easier to see what is happening.

In this example, @happy becomes true:

def happy?
  @happy || @happy = true
  # @happy == true
end

However, in this example, @happy becomes false:

def happy?
  @happy || @happy = false
  # @happy == false
end

In the former, @happy is falsey the first time the method is called, then true on subsequent calls. In that example, the right-hand side is evaluated once only. In the latter, @happy is always false and so both sides are always evaluated.

When using the ||= style of memoization, only truthy values will be memoized.

So the problem is that if post_complete? returns false the first time happy? is called, it will be evaluated until it returns true.

A solution

So how do I go about memoizing a false value?

Instead of testing the truthiness of @happy, I could check whether or not it has a value assigned to it. If it has, I can return @happy. It if hasn’t, then I will assign one. I will use Object#defined?.

The documentation states:

defined? expression tests whether or not expression refers to anything recognizable (literal object, local variable that has been initialized, method name visible from the current scope, etc.). The return value is nil if the expression cannot be resolved. Otherwise, the return value provides information about the expression.

Note that the expression is not executed.

I use it like so:

def happy?
  return @happy if defined? @happy
  @happy = false
end

Referring back to the documentation, there’s one thing I need to be aware of. This isn’t the same as checking for nil or false. It’s a bit counterintuitive, but defined? doesn’t return a boolean. Instead, it returns information about the argument object in the form of a string:

> @a, $a, a = 1,2,3
> defined? @a
#=> "instance-variable"
> defined? $a
#=> "global-variable"
> defined? a
#=> "local-variable"
> defined? puts
#=> "method"

If I assign nil to a variable, what do you think the return value will be when I call defined? with that variable?

> defined? @b
#=> nil
> @b = nil
#=> nil
> defined? @b
#=> "instance-variable"

So, as long as the variable has been assigned with something (even nil), then defined? will be truthy. Only if the variable is uninitialized, it returns nil.

Of course, you can guess what happens when we set the variable’s value to false.

> @c = false
#=> false
> defined? @c
=> "instance-variable"

Update: An improved solution

Prompted by Valentin Baca’s comment, I’ve reassessed my original solution. Do I really need to check whether or not the variable is initialised or is checking for nil enough?

@happy.nil? should suffice as I’m only interested in knowing that the variable is nil rather than false. (false and nil are the only falsey values in Ruby.)

I think this version is more readable:

def happy?
  @happy = post_complete? if @happy.nil?
  @happy
end

Wrapping up

I now know that the ||= style of memoization utilizes short-circuiting. If the left-hand side variable is false, then the right-hand part of the statement will be evaluated. If that’s an expensive method call which always returns false, then the performance of my program would be impacted. So instead of ||= I can check if the variable is initialized or I can check if it’s nil.

And now I’m happy.

def happy?
  @happy = post_complete? if @happy.nil?
  @happy
end

Value Objects In Ruby

(This post is also published on dev.to)

What is a value object?

A small simple object, like money or a date range, whose equality isn’t based on identity. Martin Fowler

Objects in Ruby are usually considered to be entity objects. Two objects may have matching attribute values but we do not consider them equal because they are distinct objects.

In this example a and c are not equal:

class Panserbjorn
  def initialize(name)
    @name = name
  end  
end

a = Panserbjorn.new('Iorek')
b = Panserbjorn.new('Iofur')
c = Panserbjorn.new('Iorek')

a == c #=> => false

# Three distinct objects:
a.object_id #=> 70165973839880
b.object_id #=> 70165971554200
c.object_id #=> 70165971965460

Value objects on the other hand, are compared by value. Two different value objects are considered equal when their attribute values match.

Symbol, String, Integer and Range are examples of value objects in Ruby.

Here, a and c are considered equal despite being distinct objects:

a = 'Iorek'
b = 'Iofur'
c = 'Iorek'

a == b #=> false
a == c #=> true

# Three distinct objects:
a.object_id #=> 70300461022500
b.object_id #=> 70300453210700
c.object_id #=> 70300461053840

How can I create a value object?

Say I want a class to represent the days of the week and I also want instances of that class to be considered equal if they represent the same day. A Sunday object should equal another Sunday object. A Monday object should equal another Monday object, etc…

I might begin with the following class:

class DayOfWeek
  DAYS = {
    1 => 'Sunday',
    2 => 'Monday',
    3 => 'Tuesday',
    4 => 'Wednesday',
    5 => 'Thursday',
    6 => 'Friday',
    7 => 'Saturday'
  }.freeze

  def initialize(day)
    raise ArgumentError, 'Day outside range' unless (1..7).cover?(day)

    @day = day
  end

  def to_i
    day
  end

  def to_s
    DAYS[day]
  end

  private

  attr_accessor :day
end

Now, I am going to instantiate three objects to represent the days of the week on which I eat pizza, pay the milk man, and put out the recycling for collection:

pizza_day = DayOfWeek.new(5)
milk_money_day = DayOfWeek.new(2)
recycling_collection_day = DayOfWeek.new(5)

I know that I eat pizza for dinner the same day that I put out the recycling. I consider these objects to represent the same thing: Thursday. They should be equivalent. But they’re not:

pizza_day == recycling_collection_day #=> false

That’s because they’re not yet value objects. #== compares the identities of the objects.

I should override #==. I will use pry to find out where the method comes from so we can see how it derives its current behaviour.

pizza_day.method(:==).owner #=> BasicObject

DayOfWeek inherits #== from BasicObject.

The page for BasicObject#== states:

== returns true only if obj and other are the same object. Typically, this method is overridden in descendant classes to provide class-specific meaning.

Aha! The class specific meaning in this case is I want to compare its instances by value.

I know that these objects expose an integer. It makes sense to compare against that but I don’t want to compare a day with an actual integer. Thursday should not be equivalent to the number 5.

I also know that a DayOfWeek exposes a string as well. It follows that any equivalent days would return matching string and integer values:

class DayOfWeek
  # ...

  def ==(other)
    to_i == other.to_i &&
    to_s == other.to_s
  end

  alias eql? ==

  # ...
end

I have aliased #eql? to #==. The BasicObject documentation explains:

For objects of class Object, eql? is synonymous with ==. Subclasses normally continue this tradition by aliasing eql? to their overridden ==

Bingo! We have value objects. pizza_day and recycling_collection_day are considered equivalent:

pizza_day == recycling_collection_day #=> true

I could override other comparison methods, #<==>. <=, <, ==, >=, > and between? as it makes sense to say that Monday is less than Tuesday or Friday is greater than Thursday but I have decided that’s not needed for now.

There is however, one more important step that I need to implement. These objects are equivalent, so when used as a hash key I would expect them to point to the same bucket.

The Hash documentation suggests:

Two objects refer to the same hash key when their hash value is identical and the two objects are eql? to each other.

A user-defined class may be used as a hash key if the hash and eql? methods are overridden to provide meaningful behavior. By default, separate instances refer to separate hash keys.

Following that advice, I need to change the default behaviour of #hash. I already know that integers in Ruby are value objects. I can see that equivalent integers always return the same #hash.

a = 1
b = 1

a.object_id #=> 3
b.object_id #=> 3
1.object_id #=> 3

1.hash == 2.hash #=> false
[a,b,1].map(&:hash).uniq.count #=> 1
101.hash == (100 + 1).hash #=> true

The same goes for strings:

a = 'Svalbard'
b = 'Svalbard'

# Note the different object ids:
a.object_id #=> 70253833847520
b.object_id #=> 70253847146940
'Svalbard'.object_id #=> 70253847210020

# The hash values of equivalent strings match:
'Svalbard'.hash == 'Bolvanger'.hash #=> false
[a,b,"Svalbard"].map(&:hash).uniq.count #=> 1
'Svalbard'.hash == ('Sval' + 'bard').hash #=> true

I will generate the hash using its string and integer properties.

  def ==(other)
    to_i == other.to_i
  end

  alias eql? ==

  def hash
    to_i.hash ^ to_s.hash
  end

Per the example in the documentation, I’ve used the XOR operator (^) to derive the new hash value.

Now that I have overridden #hash, I can see that equivalent DayOfWeek instances point to the same bucket:

day_1 = DayOfWeek.new(1)
day_2 = DayOfWeek.new(1)

day_1 == day_2 #=> true

notes = {} #=> {}
notes[day_1] = 'Rest'
notes[day_2] = 'Party'

notes.length #=> 1
notes #=> {#<DayOfWeek:0x00007fa193e44170 @day=1>=>"Party"}

Structs

If I want multiple value objects, I might have to override #hash and #== for each class.

I could decide to use structs instead.

A Struct is a convenient way to bundle a number of attributes together, using accessor methods, without having to write an explicit class.

Structs are value objects by default. Of course we now have an idea of how this works. The documentation explains:

Equality—Returns true if other has the same struct subclass and has equal member values (according to Object#==).

Just as we thought!

DayOfWeek = Struct.new(:day) do
  DAYS = {
    1 => 'Sunday',
    2 => 'Monday',
    3 => 'Tuesday',
    4 => 'Wednesday',
    5 => 'Thursday',
    6 => 'Friday',
    7 => 'Saturday'
  }.freeze

  def to_s
    DAYS[day]
  end

  def to_i
    day
  end
end

a = DayOfWeek.new(1)
b = DayOfWeek.new(2)
c = DayOfWeek.new(1)

a == c #=> true
a == b #=> false

Summary

We now know the difference between an entity object and a value object. We have learned that we need to override both #hash and #== if our value objects are to be used as hash keys. And, we have learned that structs provide value object behaviour straight out of the box.

Override `#hash` when overriding `#eql?`

In my notes here, I’ve written that #hash must be overridden when overriding #eql?. I was unsure why this was the case.

What is #hash?

I see in pry that all objects respond to #hash:

> 1.hash
=> 3748939910403886956
> 'a'.hash
=> 1677925148165319732
> [1,2,3].hash
=> -2230614089907012012
> Time.now.hash
=> -2249667312364590389

Equal objects return the same value:

> 1 == 1
=> true
> 1.hash
=> 3748939910403886956
> 1.hash
=> 3748939910403886956

Hash comes from the Kernel module:

> 1.method(:hash).owner
=> Kernel

The Kernel module is included by class Object, so its methods are available in every Ruby object.

It’s basically a bunch of helper methods made available to every class.

Kernel#hash seems to be undocumented though. It’s not in in the docs for the Kernel module. Though, that page does point towards the Object class page for information on Kernel instance methods. But there’s nothing on it there either.

The explanation to why #hash needs to be overridden when #eql is changed is in the Hash docs.

Two objects refer to the same hash key when their hash value is identical and the two objects are eql? to each other.

Two objects can have the same hash value but be unequal however this would be detriment to the speed of the hash.

So when keying by multiple objects, so long as their hash and eql? values match, they will point to the same bucket.

The naked asterisk

Andrew Berls writes about the naked asterisk in this post. I too stumbled across it whilst looking through Rails source.

Andrew says:

So what’s the deal with the unnamed asterisk? Well, this is still the same splat operator at work, the difference being that it does not name the argument array. This may not seem useful, but it actually comes into play when you’re not referring to the arguments inside the function, and instead calling super. To experiment, I put together this quick snippet:

class Bar
  def save(*args)
    puts "Args passed to superclass Bar#save: #{args.inspect}"
  end
end

class Foo < Bar
  def save(*)
    puts "Entered Foo#save"
    super
  end
end

Foo.new.save("one", "two")

which will print out the following:

Entered Foo#save
Args passed to superclass Bar#save: ["one", "two"]

The globbed arguments are automatically passed up to the superclass method. Some might be of the opinion that it’s better to be explicit and define methods with def method(*args), but in any case it’s a neat trick worth knowing!

Value objects

A small simple object, like money or a date range, whose equality isn’t based on identity. Martin Fowler

Symbol, String, Integer and Range are example of value objects.

Value Objects in Ruby on Rails

Notes to this presentation.

March is not 3. 3 is not March. March is March.

March is a range of days, 1st – 31st.

Examples of value objects:

  • Date, Time
  • Weight, Height
  • Duration
  • Temperature
  • Address
  • Money

A value object has:

  • Identity based on state
    • a dollar is a dollar
    • Nov 5 is Nov 5
    • 98.6F is 98.6F
    • 10 Downing Street is 10 Downing Street
  • Equality based on type and state
    • 98.6F != $98.60

They are immutable – or should be treated as such – and typically do not have setters.

Scalar values example

class Patient
  attr_accessor :height, :weight
end

patient.height #=> 65
patient.weight #=> 90

What do these values represent? What are their units?

class Height
  def initialize(inches)
    @inches = inches
  end

  def inches
    @inches
  end

  def centimetres
    inches * 2.54
  end
end

class Weight
  def initialize(pounds)
    @pounds = pounds
  end

  def pounds
    @pounds
  end

  def kilograms
    pounds / 2.2
  end
end

class Patient
  attr_accessor :height_inches, :weight_pounds

  def height
    Height.new(@height_inches)
  end

  def weight
    Weight.new(@weight_pounds)
  end
end

patient.weight.pounds #=> 100
patient.weight.kilograms #=> 45.4545454545

patient.height.inches #=> 65
patient.height.centimetres #=> 165.1

Equality

a = Weight.new(100)
b = Weight.new(110)
c = Weight.new(100)

a == b #=> false
a == c #=> false

a and c are not equal because they are different objects.

class Weight
  # ...

  def hash
    pounds.hash
  end

  def eql?(other)
    self.class == other.class &&
      self.pounds == other.pounds
  end
  alias :== eql?
end

We need to override #hash when we override #==.

The weight class now looks like:

  attr_reader :pounds

  def initialize(pounds)
    @pounds = pounds
  end

  def kilograms
    pounds / 2.2
  end

  def hash
    pounds.hash
  end

  def eql?(other)
    self.class == other.class &&
      self.pounds == other.pounds
  end
  alias :== eql?
end

Refactor to a struct

Weight = Struct.new(:pounds) do
  def kilograms
    pounts/2.2
  end
end

a = Weight.new(100)
b = Weight.new(110)
c = Weight.new(100)

a == b #=> false
a == c #=> true

But structs are mutable.

Value object gem

You can use value_object gem.

class Timespan < ValueObject::Base
  has_fields :start_time, :end_time

  def duration
    Duration.new(end_time - start_time)
  end

  def contains?(time)
    (start_time..end_time).cover?(time)
  end

  def overlays?(other)
    contains?(other.start_time) || contains?(other.end_time)
  end
end