How to implement a viewing system in Rails
02:49 PMDevelopment, RubyStefano
Hello everyone.
Today I will show how to implement a viewing system for any model of your Rails application.
Suppose we have a model News and we would like to keep track of how many times a single news has been displayed, in order to implement box like “the most ‘seen’” etc..
Suppose we have a model News created in this way:
myapp/db/migrate/001_create_news.rb:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | class CreateNews < ActiveRecord::Migration def self.up create_table :news do |t| t.string :title t.text :content t.date :online_date_start, :null => true t.date :online_date_end, :null => true t.boolean :online, :null => false, :default => true t.timestamps end end def self.down drop_table :news end end |
myapp/app/models/news.rb:
1 2 3 4 | class News < ActiveRecord::Base validates_length_of :title, :within => 2..255 validates_presence_of :title, :content, :online_date_start end |
The basic idea is to increment a counter displayed every time a user views the news.
Some considerations:
an implementation of this type would allow a user to press the refresh button and constantly increase the counter.
So the goal is to have a clever mechanism that increases the counter:
- 1 once for each registered user
- 1 only once per IP in the case of guest
We would also like that this mechanism could be applied not only to model News, but to any model of our application.
Then we create the migration of our model Viewing:
myapp/db/migrate/002_create_viewings.rb:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class CreateViewings < ActiveRecord::Migration def self.up create_table :viewings do |t| t.string :ip t.string :viewable_type t.integer :viewable_id t.references :person t.datetime :created_at end end def self.down drop_table :viewings end end |
and the corresponding model:
myapp/app/models/viewing.rb:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class Viewing < ActiveRecord::Base # RELATIONSHIPS belongs_to :viewable, :polymorphic => true, :counter_cache => :popularity belongs_to :viewer, :class_name => "Person", :foreign_key => "person_id" # VALIDATIONS validates_uniqueness_of :ip, :scope => [:viewable_id, :viewable_type, :person_id] validates_uniqueness_of :person_id, :allow_nil => true # OTHER def viewable_type=(sType) super(sType.to_s.classify.constantize.base_class.to_s) end end |
As you can see, the model Viewing is not closely tied to model News, but given its polimorphic nature, it may be associated with any model.
At this point, we can associate the model Viewing to the News :
myapp/app/models/news.rb:
1 2 3 4 5 | class News < ActiveRecord::Base validates_length_of :title, :within => 2..255 validates_presence_of :title, :content, :online_date_start has_many :viewings, :as => :viewable end |
and add the column “counter cache to the table News:
myapp/db/migrate/003_add_popularity_to_news.rb:
1 | Specified file is not in uploads folder, does not exists, or not a file. |
Now we have everything necessary to implement the mechanism for viewing in our model News.
Let’s show how to increment the counter to “show” of the news.
myapp/app/controllers/news_controller.rb:
1 | Specified file is not in uploads folder, does not exists, or not a file. |
Tags: ruby, ruby on rails

















Social comments and analytics for this post…
This post was mentioned on Twitter by devinterface: New blog post, “How to implement a viewing system in Rails” – http://tr.im/D1n4...
[...] http://www.devinterface.com/2009/10/how-to-implement-a-viewing-system-in-rails/ [...]
[...] How to implement a viewing system in Rails http://www.devinterface.com/2009/10/how-to-implement-a-viewing-system-in-rails/ [...]