Michael Karavaev
Michael Karavaev

Reputation: 1527

add array to active record object

Suppose, I have Campaign#show action which looks like this:

 def show
   @campaign = Campaign.find(params[:id])
 end

Suppose, I want show in my view some info that I get from YouTube API, wrote in variable, so Campaign#show looks like this now:

def show
  @campaign = Campaign.find(params[:id])
  @views = Campaign.youtube_info(@campaign.url) # this class method of Campaign
end

But for the sake of convenience I want to merge @campaign and @views and have something like this @campaign[:views][:view_count] in @campaign.

As I understood @campaing is ActiveRecord object and @views is Hash and it not allowed to make such merge.

What is the best approach in this situation?

Thanks in advance.

Upvotes: 1

Views: 1037

Answers (1)

user229044
user229044

Reputation: 239250

Firstly, you will pass an arbitrary number of objects to the view as part of a normal Rails app. There's nothing wrong at all with using @views and @campaign.

However, in your case, things are written very weirdly.

For one, views should be a method on Campaign, and it should just invoke youtube_info.

It is strange that youtube_info is a class-level method, since it requires the input of a property from an instance of the class. It should be an instance method, and you should simply invoke that method in your view.

If you want to prevent additional calls to the API for each invocation, then cache the result of the first invocation. The process of "caching" a method's return value is called memoization, you can find a lot written about it in the Ruby world. The following is an extremely common pattern in Ruby:

class Campaign < ActiveRecord::Base
  def youtube_info
    @youtube_info ||= perform_some_api_request(@url)
  end

  def views
    youtube_info['views']
  end
end

The method youtube_info will only invoke the perform_some_api_request method when it is first run. The results will be assigned to @youtube_info, and then returned. Each subsequent call will test @youtube_info and return it if it is set to a non-falsy value. Each call to views will simply invoke youtube_info, and only the first invocation performs an API call.

You could use the same technique and leave your code exactly as written, invoking the class level method instead, but I wouldn't recommend it:

class Campaign < ActiveRecord::Base
  def self.youtube_info(url)
    # ...
  end

  def views
    @views ||= Campaign.youtube_info(@url)['views']
  end
end

But, the first example is better. There's really no reason for youtube_info to be a class-level method.

Upvotes: 2

Related Questions