Question

Preamble:

Don't be scared off by the fact My Rails 3 app uses the has_many_polymorphs gem as I don't think you need to be familiar with the gem in order to help me here :)

My code:

I have a Post model which has many Snippets. There are four additional models which are snippetable i.e. of type Snippet:

class Post < ActiveRecord::Base
  has_many_polymorphs :snippets, 
                      :from => [:texts, :videos, :images, :codes], 
                      :through => :snippets
end

class Snippet < ActiveRecord::Base
  belongs_to :post
  belongs_to :snippetable, :polymorphic => true      
  attr_accessible :post_id, :snippetable_type, :snippetable_id
end

# There following four models are snippetable:

class Code < ActiveRecord::Base
  # note that the attribute name is the same as the Class name
  attr_accessible :code
end

class Text < ActiveRecord::Base
  # note that the attribute name is the same as the Class name
  attr_accessible :text
end

class Image < ActiveRecord::Base
  # note that the attribute name is the same as the Class name
  attr_accessible :image
end

class Video < ActiveRecord::Base
  # note that the attribute name is the same as the Class name
  attr_accessible :video
end

Adding snippets to a Post

Now if I want to add two text snippets and two image snippets to a post I can do this:

# find the first post
p = Post.first

p.texts << Text.create(:text => "This is the first sentence")
p.images << Image.create(:image => "first_image.jpg")
p.texts << Text.create(:text => "This is the second sentence")
p.images << Image.create(:image => "second_image.jpg")

The result is a blog post that looks like this:

  • text snippet
  • image snippet
  • text snippet
  • image snippet

My Problem

I'm having some trouble displaying the content of each snippet in my view.

I could do the following in my view:

- for text in @post.texts
  = text.text
- for image in @post.images
  = image.image
- for code in @post.codes
  = code.code
- for video in @post.videos
  = video.video

BUT this will result in a blog post that looks like this:

  • text snippet
  • text snippet
  • image snippet
  • image snippet

I don't want snippets to be grouped by Class in this way.

How can I solve this?

Well I've a look at the problem. I know that I CAN do the following:

- for snippet in @post.snippets
  = snippet.snippetable_type.downcase

and this will output the Class name of each snippet as shown:

  • text
  • image
  • text
  • image

BUT I want the content of each snippet.

Expanding on what we have above, since each type of snippet has one attribute with the same name as the Class itself, I can also do this:

- for snippet in @post.snippets
  = "#{snippet.snippetable_type.downcase}.#{snippet.snippetable_type.downcase}"

And this will output the classname AND the attribute name of each snippet:

  • text.text
  • image.image
  • text.text
  • image.image

If I could just find a way of getting to the content and not the Classnames then I'd be ok. Anyone got any clue?

I will be absolutely amazed if anyone gets this one. Thanks if you have read this far.

Was it helpful?

Solution

So you want it in the order that for snippet in @post.snippets gives you, but you need it to send text, image, code or video depending on the snippetable_type, or am I misreading you?

- for snippet in @post.snippets
  = snippet.snippetable.send(snippet.snippetable_type.downcase)

OTHER TIPS

I'm wondering if either (a) there's more complexity to this app and these snippets than you're showing, or (b) you're using has_many_polymorphs in a situation that doesn't warrant the overhead.

What I mean is: if these snippet types are really all identical except for the class name and accessor name, then you don't need the subclasses at all: one generic Post class will do.

On the other hand, if the Post/Snippet classes do have different behavior depending on type, then a better solution would be to use duck typing to get the output you want. (And this may actually be the entire purpose of has_many_polymorphs if I understand it from your code.) For example: Each snippetable type could implement a method (the duck type) .to_html() in which each one would create a bare-bones html presentation most appropriate to it. And then this is the method you'd call inside your loop.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top