Using the spaceship operator to compare nil?

Hey folks,

I have a test helper in a Rails app called assert_ordered_by

def assert_ordered_by(attribute, collection, direction)
  collection = collection.to_s
  return true if assigns["#{collection}"].length < 2
  option_one = assigns["#{collection}"][0].send(attribute)
  option_two = assigns["#{collection}"][1].send(attribute)
  assert option_one.send(direction, option_two), "#{option_one} is not
#{direction} #{option_two}"
end

It works well in all the cases I've tried thus far except when dealing
with nil. I want to teach the method to treat nil the same way SQL
treats NULL because the collections will often contain ActiveRecord
objects.

I have the idea in my head that I need to use the spaceship operator
(<=>) to do this, but am unsure about how...

Any ideas?

Thanks,
Dan Croak
http://thoughtbot.com/

I forgot to mention that this is called, for example, with:

assert_ordered_by :last_name, :users, "<="

I'm also moving away from using the spaceship operator into
conditional logic land because I was having difficulty dealing
(mentally) with comparing nil to various types (dates, strings,
integers, etc.). Here's the latest non-working code... :confused:

  def assert_ordered_by(attribute, collection, direction)
    collection = assigns["#{collection.to_s}"]
    return true if collection.length < 2
    collection.sort do |a, b|
      if a.nil? && !b.nil?
        return false if direction.include?(">") # nil cannot be
greater than anything except another nil
      elsif b.nil? && !a.nil?
        return false if direction.include?("<") # nil cannot be
greater than anything except another nil
      elsif !a.nil? && !b.nil?
        assert a.send(attribute).send(direction, b.send(attribute))
      elsif a.nil? && b.nil?
        return true
      end
    end
  end

Try this:

has_nil, doesnt = collection.partition { |r| r.send(attribute).nil? }

you can then sort only the ones that have a meaningful sort value and
then rejoin as needed based on the direction, e.g. for one directed
you want:

doesnt.sort + has_nil

and for another you want

has_nil + doesn't.sort

hope that helps. Excuse the incomplete code :slight_smile:

···

On 8/9/07, Dan Croak <dan.croak@nationalgazette.org> wrote:

I forgot to mention that this is called, for example, with:

assert_ordered_by :last_name, :users, "<="

I'm also moving away from using the spaceship operator into
conditional logic land because I was having difficulty dealing
(mentally) with comparing nil to various types (dates, strings,
integers, etc.). Here's the latest non-working code... :confused:

Thanks, Gregory. This is what I ended up with (works well) ~

  def assert_ordered_by(attribute, collection, direction)
    collection = assigns["#{collection.to_s}"]
    return true if collection.length < 2
    has_nil, has_no_nil = collection.partition { |r|
r.send(attribute).nil? }
    has_no_nil = has_no_nil.sort_by(&:"#{attribute}")
    expected = has_nil + has_no_nil
    expected.reverse! if direction.include?(">")
    assert_equal expected.map(&:"#{attribute}"),
collection.map(&:"#{attribute}")
  end

···

On Aug 9, 5:39 pm, "Gregory Brown" <gregory.t.br...@gmail.com> wrote:

Try this:

has_nil, doesnt = collection.partition { |r| r.send(attribute).nil? }

you can then sort only the ones that have a meaningful sort value and
then rejoin as needed based on the direction, e.g. for one directed
you want:

doesnt.sort + has_nil

and for another you want

has_nil + doesn't.sort

hope that helps. Excuse the incomplete code :slight_smile: