Justin
Justin

Reputation: 4940

Convert a string into a models attribute

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

Answers (1)

Benjamin Bouchet
Benjamin Bouchet

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

Related Questions