Forum: Ruby on Rails Reordering associated "has_many" objects

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Jens (Guest)
on 2009-01-12 13:23
(Received via mailing list)
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[x]}" works fine.
Also in irb, "self.foobar_order.collect { |x| self.foobars[x] }"
correctly returns the re-sorted elements.
However, in Collection#do_reorder, "self.foobars = ..." does not
update the object's "foobars".

Why?

Thank you! :)



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[x] }
    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[x] }
  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
Frederick C. (Guest)
on 2009-01-12 13:59
(Received via mailing list)
On 12 Jan 2009, at 11:22, Jens wrote:

> In irb (script/console), stuff like "a = [:a,:b, :c] ; a = [1 2
> 0].collect {|x| a[x]}" works fine.
> Also in irb, "self.foobar_order.collect { |x| self.foobars[x] }"
> 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/acti...

Fred


>  before_validation :do_reorder
> 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)
This topic is locked and can not be replied to.