Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Delegate With has_many In Rails?

We've got 2 models & a join model:

#app/models/message.rb
Class Message < ActiveRecord::Base
    has_many :image_messages
    has_many :images, through: :image_messages
end

#app/models/image.rb
Class Image < ActiveRecord::Base
    has_many :image_messages
    has_many :messages, through: :image_messages
end

#app/models/image_message.rb
Class ImageMessage < ActiveRecord::Base
    belongs_to :image
    belongs_to :message
end

Extra Attributes

We're looking to extract the extra attributes from the join model (ImageMessage) and have them accessible in the Message model:

@message.image_messages.first.caption # -> what happens now    
@message.images.first.caption #-> we want

We've already achieved this using the select method when declaring the association:

#app/models/message.rb
has_many :images, -> { select("#{Image.table_name}.*", "#{ImageMessage.table_name}.caption AS caption") }, class_name: 'Image', through: :image_messages, dependent: :destroy

Delegate

We've just found the delegate method, which does exactly what this needs. However, it only seems to work for has_one and belongs_to associations

We just got this working with a single association, but it seems it does not work for collections (just takes you to a public method)


Question

Do you know any way we could return the .caption attribute from the ImageMessage join model through the Image model?

We have this currently:

#app/models/image.rb
Class Message < ActiveRecord::Base
    has_many :image_messages
    has_many :messages, through: :image_messages

    delegate :caption, to: :image_messages, allow_nil: true
end

#app/models/image_message.rb
Class ImageMessage < ActiveRecord::Base
    belongs_to :image
    belongs_to :message

    def self.caption # -> only works with class method
        #what do we put here?
    end
end

Update

Thanks to Billy Chan (for the instance method idea), we have got it working very tentatively:

#app/models/image.rb
Class Image < ActiveRecord::Base
    #Caption
    def caption
        self.image_messages.to_a
    end
end

#app/views/messages/show.html.erb
<%= @message.images.each_with_index do |i, index| %>
    <%= i.caption[index][:caption] %> #-> works, but super sketchy
<% end %>

Any way to refactor, specifically to get it so that each time .caption is called, it returns the image_message.caption value for that particular record?

like image 707
Richard Peck Avatar asked Dec 28 '13 15:12

Richard Peck


1 Answers

delegate is just a shorthand as equivalent instance method. It's not a solution for all, and there are even some debate that it's not so explicit.

You can use an instance method when simple delegate can't fit.

I reviewed and found any association is unnecessary is this case. The ImageMessage's class method caption is more like a constant, you can refer it directly.

def image_message_caption
  ImageMessage.caption
end
like image 64
Billy Chan Avatar answered Sep 25 '22 13:09

Billy Chan