TRACKS_VISITS

Rails: Track unique and total visits/viewings of an ActiveRecord resource based on user/account or IP.

Installation

sudo gem install grimen-tracks_visits

Usage

1. Generate migration:


$ ./script/generate tracks_visits_migration

Generates db/migrations/{timestamp}_tracks_visits_migration with:


class TracksVisitsMigration < ActiveRecord::Migration
  def self.up
    create_table :visits do |t|
      t.references  :visitable,     :polymorphic => true
      
      t.references  :visitor,       :polymorphic => true
      t.string      :ip,            :limit => 24
      
      t.integer     :visits,        :default => 0
      
      # created_at <=> first_visited_at
      # updated_at <=> last_visited_at
      t.timestamps
    end
    
    add_index :visits, [:visitor_id, :visitor_type]
    add_index :visits, [:visitable_id, :visitable_type]
  end
  
  def self.down
    drop_table :visits
  end
end

2. Make your model count visits:


class Post < ActiveRecord::Base
  tracks_visits
end

or


class Post < ActiveRecord::Base
  tracks_visits :by => :user  # Setup associations for the visitor class automatically
end

Note: :by is optional if you choose any of User or Account as visitor classes.

3. …and here we go:


@post = Post.create

@post.visited?          # => false
@post.unique_visits     # => 0
@post.total_visits      # => 0

@post.visit!(:ip => '128.0.0.0')
@post.visit!(:visitor => @user)      # aliases: :user, :account

@post.visited?          # => true
@post.unique_visits     # => 2
@post.total_visits      # => 2

@post.visit!(:ip => '128.0.0.0')
@post.visit!(:visitor => @user)
@post.visit!(:ip => '128.0.0.1')

@post.unique_visits     # => 3
@post.total_visits      # => 5

@post.visted_by?('128.0.0.0')     # => true
@post.visted_by?(@user)           # => true
@post.visted_by?('128.0.0.2')     # => false
@post.visted_by?(@another_user)   # => false

@post.reset_visits!
@post.unique_visits     # => 0
@post.total_visits      # => 0

# Note: See documentation for more info.

Caching

If the visitable class table – in the sample above Post – contains a columns total_visits_count and unique_visits_count, then a cached value will be maintained within it for the number of unique and total visits the object have got. This will save a database query for counting the number of visits, which is a common task.

Additional caching fields:


class AddTrackVisitsCachingToPostsMigration < ActiveRecord::Migration
  def self.up
    # Enable tracks_visits-caching.
    add_column :posts, :unique_visits_count, :integer
    add_column :posts, :total_visits_count, :integer
  end
  
  def self.down
    remove_column :posts, :unique_visits_count
    remove_column :posts, :total_visits_count
  end
end

Dependencies

Basic usage:

For running tests:

Notes

  • Tested with Ruby 1.9+. =)
  • Let me know if you find any bugs; not used in production yet so consider this a concept version.

TODO

  • More thorough tests for more complex scenarios.
  • ActiveModel finder helpers.

License

Copyright © Jonas Grimfelt, released under the MIT-license.