How to convert a Ruby primitive to a custom domain object in two minutes

You already know why you want to use domain objects instead of Ruby’s built-in primitives… but what do you do if you’ve already used strings and integers all over the place? Did you code yourself into a corner? Are you going to have to change lots of code, and potentially break things? Of course not! You can convert a Ruby primitive into a custom domain object in just a few minutes… here’s how you do it:

  1. Wrap the primitive with a new custom object, and define ==
  2. Replace equality comparisons with domain methods

Example: Convert a Ruby primitive into a custom domain object

We’ll start with a simple class that fetches an HTTP page:

require 'net/http'

class PageFetcher
  def fetch(url)
    @response = Net::HTTP.get_response URI(url)
    @response.code
  end
end

describe PageFetcher do
  describe '#fetch(url)' do
    let(:response) { double('response', code: '200') }

    before do
      allow(Net::HTTP).to receive(:get_response)
        .and_return(response)
    end

    it 'returns the status code' do
      expect(PageFetcher.new.fetch('http://example.com')).to eq('200')
    end
  end
end

It just fetches the response using Net::HTTP and returns the status code. Now, let’s refactor the String primitive to a custom domain object!

Step 1: Wrap the primitive with a new custom object, and define ==

Simply add a new class, and use it to wrap the return value. You won’t even have to change any tests! Check it out…

require 'net/http'

class PageFetcher
  def fetch(url)
    @response = Net::HTTP.get_response URI(url)
    Response.new @response
  end

  class Response
    def initialize(response)
      @response = response
    end

    def ==(other)
      @response.code == other
    end
  end
end

describe PageFetcher do
  describe '#fetch(url)' do
    let(:response) { double('response', code: '200') }

    before do
      allow(Net::HTTP).to receive(:get_response)
        .and_return(response)
    end

    it 'returns the status code' do
      expect(PageFetcher.new.fetch('http://example.com')).to eq('200')
    end
  end
end

Step 2. Replace equality comparisons with domain methods

Now that you’re returning a custom object, you can introduce domain methods. You just encapsulate the original comparison in a method. Easy!

require 'net/http'

class PageFetcher
  def fetch(url)
    @response = Net::HTTP.get_response URI(url)
    Response.new @response
  end

  class Response
    def initialize(response)
      @response = response
    end

    def success?
      @response.code == '200'
    end
  end
end

describe PageFetcher do
  describe '#fetch(url)' do
    let(:response) { double('response', code: '200') }

    before do
      allow(Net::HTTP).to receive(:get_response)
        .and_return(response)
    end

    it 'is successful' do
      expect(PageFetcher.new.fetch('http://example.com')).to be_success
    end
  end
end

Let your new object attract behavior

Now that you have a new domain object, you’ve got a great place to put related behavior! Try it and see what happens…

"How do I become a better Ruby developer?"

Blogs, books, and bootcamps all promise to make you a better Ruby developer, but end up confusing you more.

What if you had step-by-step instructions on how to become a better Ruby developer?

Enter your name and email below and I’ll show you how to...

  • get better at Ruby in just five minutes each day
  • use testing, OOP, and refactoring to write professional-level Ruby
  • identify and learn new programming skills quickly