Reputation: 4940
I have a model, Avatar
, which has a few attributes, including original_url
, large_url
, and thumb_url
. I am using transloadit for file uploads, which is why I am storing these images like this.
I also have a method that takes in a few arguments, one of which is a string. Instead of creating multiple methods for each different avatar size, I'd rather create one with an argument of the which attribute to use (ie: original_url
, large_url
, etc).
def avatar_for(user, img_size, img_url)
url = user.avatar ? user.avatar.img_url : (Identicon.data_url_for user.username, img_size, [238, 238, 238])
image_tag(......)
end
The above img_url
, is where I would pass in the specific attribute I wanted to use like this:
= avatar_for(current_user, 250, 'original_url')
However, that returns the error
undefined method `img_url' for #<Avatar:0x007fdc98217948>
How can I take this string and convert it to act as a attribute on an object.
Upvotes: 0
Views: 1653
Reputation: 13181
You are looking at the power of Ruby messaging system
Every object in ruby can receive message with the send
method. Example when you do 'hello'.upcase
the output is 'HELLO'. With the send
method you write 'hello'.send(:upcase)
for the same output. Note that I used a symbol that represent the name of the method to call.
Whats's the advantage? In my example :upcase
can come from a variable (and that's what you want to do) but also send
allow to call private methods on other objects.
In your case, the correct code to use is the following:
def avatar_for(user, img_size, img_url)
url = user.avatar ? user.avatar.send(img_url) : (Identicon.data_url_for user.username, img_size, [238, 238, 238])
image_tag(......)
end
Then you will call your method like this:
= avatar_for(current_user, 250, :original_url) # Using symbol instead of string
Alternate answer
In Rails all model (i.e. objects that derivate from ActiveRecord::Base
) store the attribute in an array-like storage. Example if you have a User
model with name
attribute, you can access it with user[:name]
So in your case if you need an alternative to the send
method, given that user.avatar
is an active record model you can write the following:
def avatar_for(user, img_size, img_url)
url = user.avatar ? user.avatar[img_url] : (Identicon.data_url_for user.username, img_size, [238, 238, 238])
image_tag(......)
end
Upvotes: 2