views:

21

answers:

2

Here's what I'm doing:

offers = v.offers.sort { |a,b| a.expires <=> b.expires }

This data is loaded via ActiveResource (so each set of instance attributes is defined by the data it contains). However, a recent change in the incoming data has made the "expires" attribute optional. Is there a class definition change that will cause the sort method to go grab a default value if the attribute is missing from an instance?

edit: @Nikita

It looks like it won't be that simple:

o.expires == nil?
NoMethodError: undefined method `expires' for #<Offer:0x00000100d3faa8>

o.expires?
=> nil

so I tried:

offers.sort{|a,b|
if a.expires?
    b.expires? ? 0 : -1
else
    b.expires? ? 1 : a.expires <=> b.expires
end
}

NoMethodError: undefined method `expires' for #<Offer:0x00000100d3faa8>

I was hoping to be able to update the class definition with something like:

expires ||= ""

... but I don't know if that's possible. I don't really follow how the sorting blocks work yet, though. I know I could just loop through the offers and assign the value, but it seems grossly inefficient.

update

offers.sort{|a,b|
if defined? a.expires == nil
    (defined? b.expires == nil) ? 0 : -1
else
    (defined? b.expires == nil) ? 1 : a.expires <=> b.expires
end
}

ArgumentError: comparison of Offer with Offer failed

from (irb):70:in `sort'
from (irb):70
from /usr/local/lib/ruby/gems/1.9.1/gems/railties-3.0.0.beta4/lib/rails/commands/console.rb:47:in `start'
from /usr/local/lib/ruby/gems/1.9.1/gems/railties-3.0.0.beta4/lib/rails/commands/console.rb:8:in `start'
from /usr/local/lib/ruby/gems/1.9.1/gems/railties-3.0.0.beta4/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'

Hooray for verbosity ;p

+1  A: 

I don't get what 'class definition' you want. Won't just adapting sort block solve the problem?

if a.expires == nil
    b.expires == nil ? 0 : -1
else
    b.expires == nil ? 1 : a.expires <=> b.expires
end

on update
I see. There's a defined? operator in ruby which might help. It returns nil or string representing type of element (like 'method').

if defined? a.expires == nil
    (defined? b.expires == nil) ? 0 : -1
else
    (defined? b.expires == nil) ? 1 : a.expires <=> b.expires
end

I don't really follow how the sorting blocks work yet, though.
That's easy. It should return -1, 0 or 1 depending on whether first element is smaller, equal or bigger than second.
<=> operator does more or less the same thing:

1 <=> 2 # result is -1
2 <=> 2 # result is 0
15 <=> 2 # result is 1

update again
Works for me

class Offer
  def initialize(e)
    @expires = e
  end

  def expires
    @expires
  end
end
offers = [Offer.new(3), Offer.new(1), Offer.new(2), Object.new]


offers.sort! { |a, b|
  if (defined? a.expires) == nil
    ((defined? b.expires) == nil) ? 0 : -1
  else
    ((defined? b.expires) == nil) ? 1 : a.expires <=> b.expires
  end
}
puts offers.inspect

Your error indicates that a.expires has type Offer, which can't be compared.

Hooray for verbosity ;p
Welcome to rails :)

Nikita Rybak
updated post with reply
Michael F
That did it! Thank you, Nikita. Super helpful.cheers!
Michael F
A: 

Whoa, no need for this to be ugly.

offers.sort_by {|o| o.expires || ""}
glenn mcdonald