Reordering associated "has_many" objects

Hi,

I have two objects, lets call them Collection and Foobar (see below). I want to be able to specify the order of foobars in a collection using e.g.

       @collection.foobar_order = [4, 2, 1, 0, 3]        @collection.save

In irb (script/console), stuff like "a = [:a,:b, :c] ; a = [1 2 0].collect {|x| a}" works fine. Also in irb, "self.foobar_order.collect { |x| self.foobars }" correctly returns the re-sorted elements. However, in Collection#do_reorder, "self.foobars = ..." does not update the object's "foobars".

Why?

Thank you! :slight_smile:

Code:

def Collection < AR::Base   has_many :foobars, :order => :position   before_validation :do_reorder private   def do_reorder     self.foobars = self.foobar_order.collect { |x| self.foobars }     self.foobars.each_with_index do |f,i|       f.position = i # save position in DB for later fetching in correct order     end   end end

def Foobar < AR::Base   belongs_to :collection end

Additional info (not strictly needed for my question):

I have looked at acts_as_list's features, but I do not need to move objects up and down, and acts_as_list also has the (big) downside of acting directly on the DB, and not updating the object instances. All objects get loaded once and all sorting is done use a Scriptaculous Sortable instance using pure JS, no AJAX (= no server load). Upon form submit, Sortable saves the current sorting order as an array in a hidden field called "foobar_order", which is then used by the above code.

Possibly related question: I have tried to implement Array#reorder and Array#reorder! (in-place) using the above code like

def Array   # e.g. " a=[:a,:b,:c] ; o=[1 2 0] ; a.reorder(o) #=> [:b, :c, :a]   def reorder(o)     o.collect {|x| self }   end   def reorder!(o)     self = self.reorder(o)   end end

reorder works, but reorder! does not work since you cannot assign something to "self". How would you implement in-place reordering of Arrays? Perhaps even without having to create copies (since it might be an Array of AR objects)?

Thank you!

Jens

Hi,

I have two objects, lets call them Collection and Foobar (see below). I want to be able to specify the order of foobars in a collection using e.g.

      @collection.foobar_order = [4, 2, 1, 0, 3]       @collection.save

In irb (script/console), stuff like "a = [:a,:b, :c] ; a = [1 2 0].collect {|x| a}" works fine. Also in irb, "self.foobar_order.collect { |x| self.foobars }" correctly returns the re-sorted elements. However, in Collection#do_reorder, "self.foobars = ..." does not update the object's "foobars".

Because it's not an actual array you're assigning to. It's futzing
with db stuff and actually doesn't touch objects that don't need to be
added/removed to the collection. The relevant bit of code is at http://github.com/rails/rails/tree/2-2-stable/activerecord/lib/active_record/associations/association_collection.rb#L309

Fred

Why?

Thank you! :slight_smile:

Code:

def Collection < AR::Base has_many :foobars, :order => :position before_validation :do_reorder private def do_reorder    self.foobars = self.foobar_order.collect { |x| self.foobars }    self.foobars.each_with_index do |f,i|      f.position = i # save position in DB for later fetching in correct order    end end end

def Foobar < AR::Base belongs_to :collection end

reorder works, but reorder! does not work since you cannot assign something to "self". How would you implement in-place reordering of Arrays? Perhaps even without having to create copies (since it might be an Array of AR objects)?

Look at Array#replace (and either way right now you're not copying AR
objects)