Null And Void

Build Status Code Climate Code Climate

Create Null Objects with Ease

Using the Null Object pattern in Ruby is rather easy so why create a gem?

I found myself using the same implementation around the Null Object pattern over and over. Rather than duplicating the logic in every one of my projects, I decided to package it up as a gem.

In the process of doing research to make sure I was implementing this gem the best way possible, I came across an article by Avdi Grimm. In the article Avdi brings up a lot of the same ideas I was implementing in my gem. Additionally, he had come up with some other nicities that I hadn't thought of.








Installation

gem install null_and_void

Usage

Whenever you want to return a Null Object you can do something like:

def first_tps_line_item
  TpsReport.line_items.first || NullAndVoid::NullObject.instance
end

Alternatively, you can define your own Null Objects such as:

def first_tps_line_item
  TpsReport.line_items.first || NullTpsLineItem.instance
end

If you decide to use the mixin you can call the as_null_object method on the class and use convention to figure out which Null Object you want:

def first_tps_line_item
  TpsReport.line_items.first || TpsLineItem.as_null_object
end

In this case it will return a NullTpsLineItem if it's defined and otherwise will return a NullAndVoid::NullObject.

Implementation

Method Implementation

With the exception of those defined below, any calls to any methods on a Null Object that inherits from NullAndVoid::NullObject will result in itself.

Example:

null_object = NullAndVoid::NullObject.instance

null_object == null_object.foobar # => true

Helper Methods

Many common Ruby methods are implemented as 'noops' such as:

null_object = NullUser.instance

null_object.to_i      # => 0
null_object.to_int    # => 0
null_object.to_f      # => 0.0
null_object.to_r      # => (0/1)
null_object.to_a      # => []
null_object.to_ary    # => []
null_object.to_hash   # => {}
null_object.to_html   # => ''
null_object.to_json   # => 'null'
null_object.to_xml    # => ''
null_object.to_model  # => User.new
null_object.to_s      # => ''

Falsity

NullAndVoid::NullObject attempts to be falsey whenever possible however it's my opinion that this is an anti-pattern and only the basic attempts are made to make this work. For example:

null_object = NullAndVoid::NullObject.instance

!null_object          # => true
null_object.nil?      # => true
null_object.blank?    # => true
null_object.present?  # => false

Singleton Implementation

NullAndVoid::NullObject is a singleton. All calls to an implementation of it will result in the same object. Therefore:

NullUser.instance == User.new.as_null_object == User.as_null_object # => true

Define Your Own Null Objects

When implementing a Null Object for a specific class that you own, simply include NullAndVoid::Nullified like so:

require 'null_and_void/nullified'

class NullUser
  include NullAndVoid::Nullified

  def admin?
    false
  end
end

Maybies

Unfortunately when you're programming in the 'Real World ©" you don't always have control over the API you're using. And sometimes, those APIs, rather than doing the sane thing and always returning the same type no matter what, instead return nils.

In these situations, you could do this:

class Foo
  def bar
    call_some_method_that_could_return_nils || NullAndVoid::NullObject.instance
  end
end

Or instead, you can simply mixin NullAndVoid::Mayby and it will handle that for you. For example:

class Foo
  include NullAndVoid::Maybe

  def bar
    maybe(call_some_method_that_could_return_nils)
  end
end

If you'd rather not mix it into your class, you can also just call it directly:

class Foo
  def bar
    NullAndVoid.maybe(call_some_method_that_could_return_nils)
  end
end

Include Into Your Objects (Optional)

You can add Null Object creation to your class by including NullAndVoid::ModelSupport.

class User
  include NullAndVoid::ModelSupport

  def admin?
    type == 'admin'
  end
end

This will endow your class with two methods .as_null_object and #as_null_object. Both of which return either the Null Object for that class (eg NullUser) or the generic NullAndVoid::NullObject.

Now, when calling:

User.as_null_object

or:

User.new.as_null_object

Will return:

NullUser.instance

Other Approaches

If you'd like to try a more builder-centric approach to Null Objects, check out:

Naught

Issues

If you have problems, please create a Github issue.

Credits

thekompanee

null_and_void is maintained by The Kompanee, Ltd.

The names and logos for The Kompanee are trademarks of The Kompanee, Ltd.

License

null_and_void is Copyright © 2013 The Kompanee. It is free software, and may be redistributed under the terms specified in the LICENSE file.