need a way to use sort_by with possible nil values

In my view, I do a

<%= render :partial => 'person', :collection => @household.people.sort_by(&:birthday) %>

household has a has_many relationship with person, and this works fine until...

an errant user creates a new person and tries to save without filling the fields(day, month, and year). After this the birthday field is created and fails horribly. Is there any way to delete blank records as part of the validation??

Thanks

Bob <bsm2th@gmail.com>

What do you mean "as part of the validation"? Which validation? What records do you want to delete? Why allow them to save if they're not valid?

There are lots of ways you can stop the errors though - it just depends which direction you want to approach it: * You could add validation to the Person model to make birthday mandatory. * You could add a method to Person called "birthday" which returns the birthday attribute or a dummy date (you could call this method "birthday_for_sort" - to make it clear what its purpose is, and to maintain the AR method for ) * you could remove records from the people collection before the sort_by:      @household.people.delete_if { |p| p.birthday.blank? }.sort_by(&:birthday) * you could add a fake dob value before the sort_by:      @household.people.each { |p| p.birthday = "1 Jan 1900" if p.birthday.bllank? }.sort_by(&:birthday)

and more ways to crack the nut... Some of them smell more than others - depends, as ever, on what you actually want to achieve... but maybe you can get some idea of how to sort your problem.

HTH

You could use 'accepts_nested_attributes_for' in your parent model:

accepts_nested_attributes_for :children_models,     :allow_destroy => true,     :reject_if => proc {|attributes| attributes['your_attribute'].blank?}

The 'reject_if' is what does the trick.

While I'm not contradicting the responses from Michael and pepe, I'm going to lead you a different way.

First, I'll suggest that you don't have this level of code in your view. Put a method on your Household model that returns the people in the proper order. Your view code then becomes:

<%= render :partial => 'person', :collection => @household.people_by_birthday %>

(Which in later version of Rails can be   just <%= render @household.people_by_birthday %> )

Second, use one of the methods given by Michael or pepe to keep persons having invalid (or missing) birthdays from being created.

Finally, as a possible implementation of the #people_by_birthday method (note that I'm assuming that there's a #name method on which to sort the persons having no birthday):

class Household    def people_by_birthday      list = self.people.partition {|person| person.birthday.blank?}      list[1].sort_by(&:birthday) + list[0].sort_by(&:name)    end end

-Rob

Rob Biedenharn Rob@AgileConsultingLLC.com http://AgileConsultingLLC.com/ rab@GaslightSoftware.com http://GaslightSoftware.com/

Michael Pavling wrote:

Is there any way to delete blank records as part of the validation??

What do you mean "as part of the validation"? Which validation? What records do you want to delete? Why allow them to save if they're not valid?

There are lots of ways you can stop the errors though - it just depends which direction you want to approach it: * You could add validation to the Person model to make birthday mandatory. * You could add a method to Person called "birthday" which returns the birthday attribute or a dummy date (you could call this method "birthday_for_sort" - to make it clear what its purpose is, and to maintain the AR method for ) * you could remove records from the people collection before the sort_by:      @household.people.delete_if { |p| p.birthday.blank? }.sort_by(&:birthday) * you could add a fake dob value before the sort_by:      @household.people.each { |p| p.birthday = "1 Jan 1900" if p.birthday.bllank? }.sort_by(&:birthday)

and more ways to crack the nut... Some of them smell more than others - depends, as ever, on what you actually want to achieve... but maybe you can get some idea of how to sort your problem.

HTH

Sorry, not enough information.. again..

My view has 4 fields for new records: sex month, day, and year. It also has a new person link that creates as many new empty records as needed. After the 'Save Changes' link is clicked, my controller then validates these fields and makes a birthday field using whatever is entered ( Sounds like I should put this action in the model ). The problem comes if a new person record is left without any data. It looks like the model isn't saving these empty records, but they are still in the @household.people collection, killing a call to sort_by. All this trouble to put the birthdays in order..

Thanks

Bob <bsm2th@gmail.com>

After the 'Save Changes' link is clicked, my controller then validates these fields and makes a birthday field using whatever is entered

Your controller shouldn't be doing any "validation" - the model should be doing that (but maybe you mean the controller is ensuring "Person.valid?" ?)

Sounds like I should put this action in the model

ah... yes, you should really

if a new person record is left without any data. It looks like the model isn't saving these empty records, but they are still in the @household.people collection

So try to see how my suggestion of using .delete_if (or .reject) on the collection to remove invalid items works for you:    @household.people.reject { |person| !person.valid? }

Rob Biedenharn wrote:

class Household    def people_by_birthday      list = self.people.partition {|person| person.birthday.blank?}      list[1].sort_by(&:birthday) + list[0].sort_by(&:name)    end end

-Rob

This is the method I ended up with, almost.. I did

  def people_by_birthday      list = self.people.partition {|person| person.birthday.blank?}      list[1].sort_by(&:birthday)# + list[0].sort_by(&:name)      return list[1]   end

I had no use for the empty part of the partition, do it left.. Works perfectly. Only problem now is that clearing empty dates trips the error message from failed validations.

Thanks a lot

Bob