I'm applying a web-based application shop with Rails. Its data model is proven the following:

class User < ActiveRecord::Base
  has_many purchase_records
  has_many items, :through => purchase_records
end

class Item < ActiveRecord::Base
  has_many purchase_records
  has_many users, :through => purchase_records
end

class PurchaseRecord < ActiveRecord::Base
  belongs_to :user
  belongs_to :item
end

It features a page showing available products there prices, and when the consumer has bought the product, the cost is a download link, just like Application Store does. A view assistant is written to assist generate such links:

def download_link(item)
  # generate a download link
end

def item_link(item)
  if current_user and current_user.items.where(:id => item.id).first != nil
    # User already purchased it
    download_link(item, 'book-price')
  else
    # Not purchased yet, show price and link to its details
    link_to item.price, item
  end
end

current_user is determined by devise. It really works fine except for this costs 20 extra database queries for any page with 20 products, since it must see if the consumer has bought the product or otherwise for every item. I'm wondering if it may be enhanced, for instance, to pre-load bought products of current user, however i have no clue crafting it inside a view assistant.

Well, you do not write that inside a view assistant. You are making a scope around the user model known as bought_products in which you would check all the products a person has bought.

Because you did not set up the origin code for User, Item and whatever their relationship is, I'm able to only provide you with that general hint.

I simply implemented downloadable content for any client.

Things I did was write a case method around the user class that retrieves anyone's bought products, e.g.:

class User < ActiveRecord::Base
  ...
  def downloads
    self.orders.collect { |o| o.products }.flatten
  end
end

You could utilize the include? method to see if the consumer bought the product, e.g.:

def item_link(item)
  if current_user && current_user.downloads.include?(item)
    download_link(item, 'book-price')
  else
    link_to item.price, item
  end
end    

Regrettably, although this is a little more explicit, it'll still loop with the user's orders each time item_link is hit. I recommend optimizing this with Rails low-level caching in which you may obvious the cache each time the consumer logs in or completes an order.

A Rails low-level cache may seem like this:

def downoads
  Rails.cache.fetch("user-downloads-#{self.id}") do
    self.orders.collect { |o| o.products }.flatten
  end
end

And call the next to obvious a cache:

Rails.cache.delete("user-downloads-#{self.id}")

You can set anyone's bought products for an instance variable within the controller. Then you are only striking the database once:

# app/controllers/items_controller.rb
def index
  @purchased_items = current_user.items
end

# app/helpers/items_helper.rb
def item_link(item)
  if @purchased_items.include?(item)
    download_link(...)
  else
    link_to ...
  end
end