Nothing and Ducks



Let's talk about nothing

Types of Nothing in Code


Numbers

0.0 / 0.0 # => NaN
Strings

""
Collections

[]

{}
Objects

nil


Let's talk about ducks

NaN is a Duck




def math_it_up!(number)
  number * 100 / 10 + 5 - 3
end

math_it_up!(2)       # => 22
math_it_up!(0.0/0.0) # => NaN

"" is a Duck




def string_it_up!(string)
  string.upcase.capitalize
end

string_it_up!("ducks") # => "Ducks"
string_it_up!("")      # => ""

[] and {} are Ducks




def array_it_up!(array)
  array.map(&:to_s)
end

array_it_up!([1, 2]) # => ["1", "2"]
array_it_up!([])     # => []

def hash_it_up!(hash)
  hash.group_by(&:last).map{|num, pairs| [num, pairs.map(&:first)]}
end

hash_it_up!(:one => 1, :uno => 1, :dos => 2, :two => 2)
  # => [[1, [:uno, :one]], [2, [:dos, :two]]]
hash_it_up!({}) # => []

nil is a Goose




class Report
  def initialize(data)
    @data = data
  end 
def generate @data.each do |row| puts row end @generated = true end end report = Report.new([1, 3, 6]) report.generate nil.generate # => NoMethodError: undefined method `generate' for nil:NilClass

Overcoming Object Nothingness







The Null Object Pattern

Real World Example


Before

module Emails
  class EmailDirector
    class << self
      def get_email_for(email_type)
        "Emails::EmailTypes::#{email_type.classify}".constantize.new 
      rescue NameError
        nil
      end
    end
  end
end

Real World Example


Before

class MessageDispatcher
  class << self
    def method_missing(method, *args)
      email = EmailDirector.get_email_for(method, *args)
      
      if email && email.use_exact_target?
        email.push_email_message_to_bus
      else
        CustomerNotifier.send(method, *args)
      end
    end
  end
end

Real World Example




class Emails::EmailTypes::Base
  def initialize(*args); raise "Me wants initialization"; end
  def use_exact_target?; raise "Me wants definition"; end
      
  def push_email_message_to_bus
    xml = build_message
    push_message_to_bus(xml)
  end

private

  def build_message; raise "Me wants definition"; end
      
  def push_message_to_bus(xml)
    ESB::MessageBridge.transaction do
      ESB::MessageBridge.push(xml, :mail_message)
    end
  end   
end

Real World Example


After

module Emails
  module EmailTypes
    class NilType < Emails::EmailTypes::Base
      def initialize(*args)        
      end
      
      def use_exact_target?
        false
      end
            
    private  
      
      def build_message
      end
    end
  end
end

Real World Example


After

module Emails
  class EmailDirector
    class << self
      def get_email_for(email_type, *args)
        "Emails::EmailTypes::#{email_type.camelize}".constantize.new(*args)
      rescue NameError
        Emails::EmailTypes::NilType.new
      end
    end
  end
end

Real World Example


After

class MessageDispatcher
  class << self
    def method_missing(method, *args)
      email = EmailDirector.get_email_for(method, *args)
      
      if email.use_exact_target?
        email.push_email_message_to_bus
      else
        CustomerNotifier.send(method, *args)
      end
    end
  end
end

Generalizing the Null Object Pattern






Query methods are hard

Generalizing the Null Object Pattern






"Tell, don't ask" makes it easy

Generalizing the Null Object Pattern



class NullObject
  def initialize(*args)
  end
  
  def method_missing(*args)
  end
end

object = NullObject.new
object.do_important_stuff!

Questions?

nothing and ducks

By blatyo

nothing and ducks

  • 795