Ruby (Rails) Delegate attributes to another model's method?

Andrew picture Andrew · Jan 16, 2011 · Viewed 25.2k times · Source

-EDIT-

After reading about the Delegate method from the first answer, my question is this, is it possible to delegate two different methods to another single method.

IE: I currently have: @photo.attachment.file.url, and @photo.attachment.height, and @photo.attachment.width

I'd like to be able to access all of these via @photo.file.url, @photo.file.height, @photo.file.width.

The reason for the syntax is Attachment is a model that uses Paperclip to manage files, Paperclip is generating the .file method (the model is called Attachment, the model uses Paperclip's has_attached_file :file).

-ORIGINAL QUESTION-

I was wondering about aliasing methods and attributes in Ruby (I think this is a general ruby question, although my application is in Rails 3):

I have two models: Photo has_one Attachment.

Attachment has "height" and "width" attributes, and a "file" method (from Paperclip).

So by default I can access bits of the Attachment model like so:

photo.attachment.width # returns width in px
photo.attachment.height # returns height in px
photo.attachment.file # returns file path
photo.attachment.file.url #returns url for the default style variant of the image
photo.attachment.file.url(:style) #returns the url for a given style variant of the image

Now, in my photo class I have created this method:

def file(*args)
    attachment.file(*args)
end

So, now I can simply use:

photo.file # returns file path
photo.file.url # returns file url (or variant url if you pass a style symbol)

My question is, I was able to direct photo.attachment.file to just photo.file, but can I also map height and width to photo.file, so that, for the sake of consistency, I could access the height and width attributes through photo.file.height and photo.file.width?

Is such a thing possible, and if so what does it look like?

Answer

nathanvda picture nathanvda · Jan 20, 2011

So what you are asking is that

photo.file       --> photo.attachment.file
photo.file.url   --> photo.attachment.file.url
photo.file.width --> photo.attachment.width

You can't solve this with delegates, because you want that file to mean different things based on what follows next. To achieve this you would need to reopen paperclip, which i would not recommend (because i believe the api is good the way it is).

The only way i can think of to solve this, is to add eliminate the file level too. Like so:

photo.width      --> photo.attachment.width
photo.file       --> photo.attachment.file
photo.url        --> photo.attachment.file.url

This you could then solve by using a delegate for each of the wanted methods.

So you write

class Photo
  delegate :width, :height, :file, :to => :attachment
  delegate :url,   :to => :'attachment.file'
end

Hope this helps.