Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

-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?

like image 857
Andrew Avatar asked Jan 16 '11 02:01

Andrew


People also ask

How do you delegate a method in Ruby?

In Ruby, every object inherits from the Object class by default. That's why you get access to methods like puts , class & object_id . With composition a class creates (or is given) objects from another class… then it uses these objects to delegate work to them.

What is delegate method in Rails?

On the Profile side we use the delegate method to pass any class methods to the User model that we want access from our User model inside our Profile model. The delegate method allows you to optionally pass allow_nil and a prefix as well. Ultimately this allows us to query for data in custom ways.


3 Answers

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.

like image 110
nathanvda Avatar answered Oct 02 '22 10:10

nathanvda


You can use Rails 'delegate' method. Have a look at my answer for this question:

What is a more Ruby-like way of doing this command?

like image 23
monocle Avatar answered Oct 02 '22 09:10

monocle


The simplest way that comes to mind is to delegate url method in attachment to file:

class Attachment < ActiveRecord::Base
  delegate :url, :to => :file
end

This way you can call photo.attachment.url, photo.attachment.width, photo.attachment.height, which for me seems pretty consistent. You could optionally alias attachment to file - this way you'd get the exact method names you asked for (photo.file.width, photo.file.url), but I would not recommend that, because it seems confusing (calling an attachment "file").

class Photo < ActiveRecord::Base
  def file
    attachment
  end
end
like image 35
psyho Avatar answered Oct 02 '22 08:10

psyho