Follow me on Twitter!

Dont Trust This Guy why not take my word for it? A blog by Jim Jeffers

Rails: What’s the best way to handle my lists?

Ok so I’m having a slight problem building my CMS for a client. I want to utilize the same model to handle the photos for their case studies as well as their services. Each has their own model. So the Photo model belongs to a Project or a Service. But in reality it belongs to a project OR a service. Thus I have a problem using acts_as_list.

class Photo < ActiveRecord::Base
  belongs_to :service
  belongs_to :project

  acts_as_taggable
  acts_as_list :scope => :project_id

  file_column :image, :magick => { :versions => { "thumb" => "50x50", "medium" => "640x480>" } }
end

If I could do something like:

acts_as_list :scope => :project_id || :service_id

Then this would be a piece of cake but it’s not that easy. How would you solve this? I know it’s not that challenging.

12 Responses to This Article.

  1. Josh Knowles Says:

    It’s late, heading to bed, figured I’d try and point you in right direction, not sure if this will make sense or work

    What if we change your domain model a tad… Instead of a Photo belonging to both Project or Service what if it belonged to an Album. You could then have a Project/Service belong_to an album (we use belong_to here so that the foreign key gets placed on the project table as opposed to having to have a project_id and service_id on each album). You then add an has_many :through to get direct access to the Photos.

    Example (note writing from memory check for typos/syntax errors):

    class Photo  :album
    end
    
    class Album  :album
    end
    
    class Service  :album
    end
    
  2. Josh Knowles Says:

    It’s late, heading to bed, figured I’d try and point you in right direction, not sure if this will make sense or work

    What if we change your domain model a tad… Instead of a Photo belonging to both Project or Service what if it belonged to an Album. You could then have a Project/Service belong_to an album (we use belong_to here so that the foreign key gets placed on the project table as opposed to having to have a project_id and service_id on each album). You then add an has_many :through to get direct access to the Photos.

    Example (note writing from memory check for typos/syntax errors):

    class Photo  :album
    end
    
    class Album  :album
    end
    
    class Service  :album
    end
    
  3. Josh Knowles Says:

    (note have attempted to add comment couple times but it isn’t displaying all the code… too tired to figure out so I butchered it a bit trying to make it submit correctly, you should still get the general idea)

    It’s late, heading to bed, figured I’d try and point you in right direction, not sure if this will make sense or work

    What if we change your domain model a tad… Instead of a Photo belonging to both Project or Service what if it belonged to an Album. You could then have a Project/Service belong_to an album (we use belong_to here so that the foreign key gets placed on the project table as opposed to having to have a project_id and service_id on each album). You then add an has_many :through to get direct access to the Photos.

    Example (note writing from memory check for typos/syntax errors):

    class Photo
      belongs_to :album
      acts_as_list :scope => :album
    end
    
    class Album
      has_many :photos
    end
    
    class Project
      belongs_to :album
      has_many :photos, :through => :album
    end
    
    class Service
      belongs_to :album
      has_many :photos, :through => :album
    end
    
  4. Bogdan Says:

    With polymorphic associations..

    class Project  :addressable
    end
    
    class Service  :addressable
    end
    
    class Image  true
        acts_as_list :scope =>
           '(addressable_id = #{addressable_id} AND addressable_type = "Project") OR (addressable_id = #{addressable_id} AND addressable_type = "Service")'
    end
    

    It is based on polymorphic relationships and this excerpt from the documentation: it’s also possible to give it an entire string that is interpolated if you need a tighter scope than just a foreign key. Example: acts_as_list :scope => @todo_list_id = #{todo_list_id} AND completed = “”.

    Hope it’s useful. Haven’t tested it though.

  5. Jim Says:

    Hey Josh,
    Sorry for the confusion if the comments weren’t showing up! My comments are all moderated.. my site gets a lot of spam comments like over a dozen a day. Once you’ve been approved you are free to comment from that point onward.

  6. Jim Says:

    Thanks Bogdan! Your response is definitely interesting but I believe Josh’s method is the more ‘rails’ way to solve the situation.

  7. Josh Knowles Says:

    While I like have the more explicit Domain Model myself, Bogdan’s solution is correct and may actually be simplier if you don’t need the concept of an Album object. See http://wiki.rubyonrails.org/rails/pages/SimplePolymorphicAssociation for some explanation of what a Polymorphic Association is.

    You don’t even need the fancy scope that he is using, you can just get away with doing :scope => ‘addressable_id’

    Josh

  8. Bogdan Says:

    Nice solution Josh. Definitely more simplistic, more like CRUD.

    (Sorry Josh, for the incomplete code, but it seems that the comment is somehow “escaped”)

  9. seasonfive Says:

    I had exactly the same problem.
    I used polymorphism which is a more generic way to manage than the Album solution (allow each entity to have one or severals picture without database modification) and that works well :

    class Picture  true
      file_column :file, :magick => {
                            :versions => { ...  }
        }
    
      acts_as_list  :scope => 'gallery_type = \'#{gallery_type}\' AND gallery_id = \'#{gallery_id}\''
    end
    
    Then in the Content model :
    
    class Content  :gallery
      ...
    end
    
    and in the Car model :
    
    class Car  :gallery
      ...
    end
    

    Hope this could help.

  10. seasonfive Says:

    Sorry for the double post but seems my Picture model wasn’t pasted correctly :-p

    class Picture  true
      file_column :file, :magick => {
                            :versions => {  ...  }
        }
    
      acts_as_list  :scope => 'gallery_type = \'#{gallery_type}\' AND gallery_id = \'#{gallery_id}\' AND session = \'#{session}\''
    
    end
    

    (Hope this will work this time …)

  11. seasonfive Says:

    Arghhh none of the class pasted well … Sorry to poluate your blog man. I would be able to edit my post but i can’t. Here the Content and Car model which could have Picture :

    class Car  :gallery
      ....
    end
    
    class Content  :gallery
      ....
    end
    
  12. seasonfive Says:

    Last try …

    class Content < ActiveRecord::Base
      has_one     :picture, :as => :gallery
    end
    
    class Picture < ActiveRecord::Base
      belongs_to  :gallery, :polymorphic => true
    end
    

Leave a Reply

Meta Information

This post was filed under code and tagged with: , , .

This Post as a Feed

The content of this post and it's comments can be subscribed to as an RSS feed.

DontTrustThisGuy.com and all contents copyright 2003-2007 by Jim Jeffers, unless otherwise noted.Written in valid XHTML and a participant of XFN while being powered by WordPress
Contents under Creative Commons License. Visual design, layout and Cascading Style Sheets may not be reused without permission.
Entries (RSS) and Comments (RSS)