https://github.com/grimen/is_visitable
Rails: Track unique and total visits/viewings of an ActiveRecord resource based on user/account or IP.
https://github.com/grimen/is_visitable
Last synced: 22 days ago
JSON representation
Rails: Track unique and total visits/viewings of an ActiveRecord resource based on user/account or IP.
- Host: GitHub
- URL: https://github.com/grimen/is_visitable
- Owner: grimen
- License: mit
- Created: 2009-08-31T19:45:43.000Z (over 15 years ago)
- Default Branch: master
- Last Pushed: 2009-11-20T00:12:42.000Z (over 15 years ago)
- Last Synced: 2025-04-15T20:20:26.955Z (about 1 month ago)
- Language: Ruby
- Homepage: http://rdoc.info/projects/grimen/tracks_visits
- Size: 107 KB
- Stars: 13
- Watchers: 1
- Forks: 4
- Open Issues: 0
-
Metadata Files:
- Readme: README.textile
- License: MIT-LICENSE
Awesome Lists containing this project
README
h1. IS_VISITABLE ~ concept version ~
_Rails: Track unique and total visits/viewings of an ActiveRecord resource based on user/account or IP._
h2. Installation
*Gem:*
sudo gem install is_visitableand in @config/environment.rb@:
config.gem 'is_visitable'*Plugin:*
./script/plugin install git://github.com/grimen/is_visitable.gith2. Usage
h3. 1. Generate migration:
$ ./script/generate is_visitable_migrationGenerates @db/migrations/{timestamp}_is_visitable_migration@ with:
class IsVisitableMigration < 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 => 1
# 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
endh3. 2. Make your model count visits:
class Post < ActiveRecord::Base
is_visitable
endor, with explicit visitor (or visitors):
class Post < ActiveRecord::Base
# Setup associations for the visitor class(es) automatically.
is_visitable :by => [:users, :ducks]
endh3. 3. ...and here we go:
Examples:
@post = Post.create@post.visited? # => false
@post.unique_visits # => 0
@post.total_visits # => 0@post.visit!(:by => '128.0.0.0')
@post.visit!(:by => @user) # aliases: :user, :account@post.visited? # => true
@post.unique_visits # => 2
@post.total_visits # => 2@post.visit!(:by => '128.0.0.0')
@post.visit!(:by => @user)
@post.visit!(:by => '128.0.0.1')@post.unique_visits # => 3
@post.total_visits # => 5@post.visited_by?('128.0.0.0') # => true
@post.visited_by?(@user) # => true
@post.visited_by?('128.0.0.2') # => false
@post.visited_by?(@another_user) # => false@post.reset_visits!
@post.unique_visits # => 0
@post.total_visits # => 0# Note: See documentation for more info.
h2. Mixin Arguments
The @is_visitable@ mixin takes some hash arguments for customization:
* @:by@ - the visitor model(s), e.g. User, Account, etc. (accepts either symbol or class, i.e. @User@ <=> @:user@ <=> @:users@, or an array of suchif there are more than one visitor model). The visitor model will be setup for you. Note: Polymorhic, so it accepts any model. Default: @nil@.
* @:accept_ip@ - accept anonymous users uniquely identified by IP (well...you handle the bots =D). See examples below how to use this as your visitor object. Default: @false@.h2. Aliases
To make the usage of IsVistable a bit more generic (similar to other plugins you may use), there are two useful aliases for this purpose:
* @Visit#owner@ <=> @Visit#visitor@
* @Visit#object@ <=> @Visit#visitable@Example:
@post.visits.first.owner == post.visits.first.visitor # => true
@post.visits.first.object == post.visits.first.visitable # => trueh2. Finders (Named Scopes)
IsVisitable has plenty of useful finders implemented using named scopes. Here they are:
h3. @Visit@
*Order:*
* @in_order@ - most recent visits last (order by creation date).
* @most_recent@ - most recent visits first (opposite of @in_order@ above).
* @least_visits@ - visits with least total visits first.
* @most_rating@ - visits with most total visits first.*Filter:*
* @limit()@ - maximum @@ visits.
* @since()@ - visits since @@.
* @recent()@ - if DateTime: visits since @@, else if Fixnum: pick last @@ number of visits.
* @between_dates(, to_date)@ - visits between two datetimes.
* @with_visits()@ - visits with(in) visits value (or range) @@.
* @of_visitable_type()@ - visits of @@ type of visitable models.
* @by_visitor_type()@ - visits of @@ type of visitor models.
* @on()@ - visits on the visitable object @@ .
* @by()@ - visits by the @@ type of visitor models.h3. @Visitable@
_TODO: Documentation on named scopes for Visitable._
h3. @Visitor@
_TODO: Documentation on named scopes for Visitor._
h3. Examples using finders:
@user = User.first
@post = Post.first@post.visits.recent(10) # => [10 most recent visits]
@post.visits.recent(1.week.ago) # => [visits since 1 week ago]@post.visits.with_visits(100..500) # => [all visits on @post with total visits between 100 and 500]
@post.visits.by_visitor_type(:user) # => [all visits on @post by User-objects]
# ...or:
@post.visits.by_visitor_type(:users) # => [all visits on @post by User-objects]
# ...or:
@post.visits.by_visitor_type(User) # => [all visits on @post by User-objects]@user.visits.on(@post) # => [all visits by @user on @post]
@post.visits.by(@user) # => [all visits by @user on @post] (equivalent with above)Visit.on(@post) # => [all visits on @user] <=> @post.visits
Visit.by(@user) # => [all visits by @user] <=> @user.visits# etc, etc. It's all named scopes, so it's really no new hokus-pokus you have to learn.
h2. Additional Methods
*Note:* See documentation (RDoc).
h2. Extend the Visit model
This is optional, but if you wanna be in control of your models (in this case @Visit@) you can take control like this:
class Visit < IsVisitable::Visit
# Do what you do best here... (stating the obvious: core IsVisitable associations, named scopes, etc. will be inherited)
endh2. Caching
If the visitable class table - in the sample above @Post@ - contains a columns @cached_total_visits_count@ and @cached_unique_visits@, 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 is_visitable-caching.
add_column :posts, :cached_unique_visits, :integer
add_column :posts, :cached_total_visits, :integer
end
def self.down
remove_column :posts, :cached_unique_visits
remove_column :posts, :cached_total_visits
end
endh2. Example
h3. In your "visitable resource" controller:
Example: @app/controllers/posts_controller.rb@:
class PostsController < ApplicationController
def show
...
@post.visit!(:by => (current_user.present? ? current_user : request.try(:remote_ip)))
...
end
endh2. Dependencies
For testing: "shoulda":http://github.com/thoughtbot/shoulda, "redgreen":http://gemcutter.org/gems/redgreen, "acts_as_fu":http://github.com/nakajima/acts_as_fu, and "sqlite3-ruby":http://gemcutter.org/gems/sqlite3-ruby.
h2. Notes
* Tested with Ruby 1.8.6 - 1.9.1 and Rails 2.3.2 - 2.3.4.
* Let me know if you find any bugs; not used in production yet so consider this a concept version.h2. TODO
* documentation: A few more README-examples.
* helper: Controller helper taking arguments for DRYer controller code. Example (in controller): handle_visits :by => current_user
* feature: Useful finders for @Visitable@.
* feature: Useful finders for @Visitor@.
* testing: More thorough tests.
* refactor: Refactor generic stuff to new gem, @is_base@, and add as gem dependency. Reason: Share the same patterns for my very similar ActiveRecord plugins: is_reviewable, is_visitable, is_commentable, and future additions.h2. License
Released under the MIT license.
Copyright (c) "Jonas Grimfelt":http://github.com/grimen