Merge pull request #12 from coryschires/master

Merged in the counter caching code. Added docs for previous pull request concerning recording unique impressions
This commit is contained in:
Cory Schires 2011-11-27 11:44:22 -08:00
commit 85a714ff21
16 changed files with 275 additions and 103 deletions

1
.gitignore vendored
View File

@ -1,5 +1,4 @@
Gemfile.lock Gemfile.lock
/test_app/db/migrate/20*
/test_app/db/schema.rb /test_app/db/schema.rb
/pkg /pkg
*~ *~

View File

@ -5,20 +5,14 @@ impressionist
A lightweight plugin that logs impressions per action or manually per model A lightweight plugin that logs impressions per action or manually per model
I would not call this a stable plugin yet, although I have been running it in prod with no problems. Use at your own risk ;-)
------------------------------------------------------------------------------------------------------------------------------ ------------------------------------------------------------------------------------------------------------------------------
NOTE: If you are upgrading from a version prior to 0.4.0, you will need to run this migration after the upgrade:
https://github.com/charlotte-ruby/impressionist/blob/master/upgrade_migrations/version_0_4_0.rb
If you don't run this migration you will receive this error: Unknown attribute : referrer
What does this thing do? What does this thing do?
------------------------ ------------------------
Logs an impression... and I use that term loosely. It can log page impressions (technically action impressions), but it is not limited to that. Logs an impression... and I use that term loosely. It can log page impressions (technically action impressions), but it is not limited to that.
You can log impressions multiple times per request. And you can also attach it to a model. The goal of this project is to provide customizable You can log impressions multiple times per request. And you can also attach it to a model. The goal of this project is to provide customizable
stats that are immediately accessible in your application as opposed to using G Analytics and pulling data using their API. You can attach custom stats that are immediately accessible in your application as opposed to using G Analytics and pulling data using their API. You can attach custom
messages to impressions. No reporting yet.. this thingy just creates the data. messages to impressions. No reporting yet.. this thingy just creates the data.
What about bots? What about bots?
@ -28,7 +22,7 @@ http://www.user-agents.org/allagents.xml
Which versions of Rails and Ruby is this compatible with? Which versions of Rails and Ruby is this compatible with?
--------------------------------------------------------- ---------------------------------------------------------
Rails 3.0.x and Ruby 1.9.2 (also tested on REE 1.8.7) - Sorry, but you need to upgrade if you are using Rails 2. You know you want to anyways.. all the cool kids are doing it ;-) Rails 3.0.4 and Ruby 1.9.2 (also tested on REE 1.8.7) - Sorry, but you need to upgrade if you are using Rails 2. You know you want to anyways.. all the cool kids are doing it ;-)
Installation Installation
------------ ------------
@ -101,17 +95,49 @@ Usage
6. Get the unique impression count from a model filtered by IP address. This in turn will give you impressions with unique request_hash, since rows with the same request_hash will have the same IP address. 6. Get the unique impression count from a model filtered by IP address. This in turn will give you impressions with unique request_hash, since rows with the same request_hash will have the same IP address.
@widget.impressionist_count(:filter=>:ip_address) @widget.impressionist_count(:filter=>:ip_address)
7. Get the unique impression count from a model filtered by session hash. Same as #6 regarding request hash. This may be more desirable than filtering by IP address depending on your situation, since filtering by IP may ignore visitors that use the same IP. The downside to this filtering is that a user could clear session data in their browser and skew the results. 7. Get the unique impression count from a model filtered by session hash. Same as #6 regarding request hash. This may be more desirable than filtering by IP address depending on your situation, since filtering by IP may ignore visitors that use the same IP. The downside to this filtering is that a user could clear session data in their browser and skew the results.
@widget.impressionist_count(:filter=>:session_hash) @widget.impressionist_count(:filter=>:session_hash)
8. Get total impression count. This may return more than 1 impression per http request, depending on how you are logging impressions 8. Get total impression count. This may return more than 1 impression per http request, depending on how you are logging impressions
@widget.impressionist_count(:filter=>:all) @widget.impressionist_count(:filter=>:all)
Logging impressions for authenticated users happens automatically. If you have a current_user helper or use @current_user in your before_filter to set your authenticated user, current_user.id will be written to the user_id field in the impressions table. Logging impressions for authenticated users happens automatically. If you have a current_user helper or use @current_user in your before_filter to set your authenticated user, current_user.id will be written to the user_id field in the impressions table.
Adding a counter cache
----------------------
Impressionist makes it easy to add a `counter_cache` column to your model. The most basic configuration looks like:
is_impressionable :counter_cache => true
This will automatically increment the `impressions_count` column in the included model. Note: You'll need to add that column to your model. If you'd like specific a different column name, you can:
is_impressionable :counter_cache => { :column_name => :my_column }
If you'd like to include only unique impressions in your count:
is_impressionable :counter_cache => { :column_name => :my_column, :unique => true }
What if I only want to record unique impressions?
-------------------------------------------------
Maybe you only care about unique impressions and would like to eliminate unnecessary database calls. You can specify conditions for recording impressions in your controller:
# only record impression if the request has a unique combination of type, id, and session
impressionist :unique => [:impressionable_type, :impressionable_id, :session_hash]
# only record impression if the request has a unique combination of controller, action, and session
impressionist :unique => [:controller_name, :action_name, :session_hash]
# only record impression if session is unique
impressionist :unique => [:session_hash]
Or you can use the `impressionist` method directly:
impressionist(impressionable, "some message", :unique => [:session_hash])
Development Roadmap Development Roadmap
------------------- -------------------
@ -121,14 +147,14 @@ Development Roadmap
* AB testing integration * AB testing integration
Contributing to impressionist Contributing to impressionist
----------------------------- -----------------------------
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
* Fork the project * Fork the project
* Start a feature/bugfix branch * Start a feature/bugfix branch
* Commit and push until you are happy with your contribution * Commit and push until you are happy with your contribution
* Make sure to add rpsec tests for it. Patches or features without tests will be ignored. Also, try to write better tests than I do ;-) * Make sure to add rpsec tests for it. Patches or features without tests will be ignored. Also, try to write better tests than I do ;-)
* If adding engine controller or view functionality, use HAML and Inherited Resources. * If adding engine controller or view functionality, use HAML and Inherited Resources.
* All testing is done inside a small Rails app (test_app). You will find specs within this app. * All testing is done inside a small Rails app (test_app). You will find specs within this app.
Copyright (c) 2011 John McAliley. See LICENSE.txt for further details. Copyright (c) 2011 John McAliley. See LICENSE.txt for further details.

View File

@ -1,3 +1,16 @@
class Impression < ActiveRecord::Base class Impression < ActiveRecord::Base
belongs_to :impressionable, :polymorphic=>true belongs_to :impressionable, :polymorphic=>true
after_save :update_impressions_counter_cache
private
def update_impressions_counter_cache
impressionable_class = self.impressionable_type.constantize
if impressionable_class.counter_cache_options
resouce = impressionable_class.find(self.impressionable_id)
resouce.try(:update_counter_cache)
end
end
end end

View File

@ -1,15 +1,38 @@
module Impressionist module Impressionist
module Impressionable module Impressionable
def is_impressionable
has_many :impressions, :as=>:impressionable def self.included(base)
include InstanceMethods base.extend ClassMethods
base.send(:include, InstanceMethods)
end end
module ClassMethods
attr_accessor :cache_options
@cache_options = nil
def is_impressionable(options={})
has_many :impressions, :as=>:impressionable
@cache_options = options[:counter_cache]
end
def counter_cache_options
if @cache_options
options = { :column_name => :impressions_count, :unique => false }
options.merge!(@cache_options) if @cache_options.is_a?(Hash)
options
end
end
def counter_caching?
counter_cache_options.present?
end
end
module InstanceMethods module InstanceMethods
def impressionable? def impressionable?
true true
end end
def impressionist_count(options={}) def impressionist_count(options={})
options.reverse_merge!(:filter=>:request_hash, :start_date=>nil, :end_date=>Time.now) options.reverse_merge!(:filter=>:request_hash, :start_date=>nil, :end_date=>Time.now)
imps = options[:start_date].blank? ? impressions : impressions.where("created_at>=? and created_at<=?",options[:start_date],options[:end_date]) imps = options[:start_date].blank? ? impressions : impressions.where("created_at>=? and created_at<=?",options[:start_date],options[:end_date])
@ -18,7 +41,14 @@ module Impressionist
end end
imps.all.size imps.all.size
end end
def update_counter_cache
cache_options = self.class.counter_cache_options
column_name = cache_options[:column_name].to_sym
count = cache_options[:unique] ? impressionist_count(:filter => :ip_address) : impressionist_count
update_attribute(column_name, count)
end
# OLD METHODS - DEPRECATE IN V0.5 # OLD METHODS - DEPRECATE IN V0.5
def impression_count(start_date=nil,end_date=Time.now) def impression_count(start_date=nil,end_date=Time.now)
impressionist_count({:start_date=>start_date, :end_date=>end_date, :filter=>:all}) impressionist_count({:start_date=>start_date, :end_date=>end_date, :filter=>:all})
@ -27,14 +57,15 @@ module Impressionist
def unique_impression_count(start_date=nil,end_date=Time.now) def unique_impression_count(start_date=nil,end_date=Time.now)
impressionist_count({:start_date=>start_date, :end_date=>end_date, :filter=> :request_hash}) impressionist_count({:start_date=>start_date, :end_date=>end_date, :filter=> :request_hash})
end end
def unique_impression_count_ip(start_date=nil,end_date=Time.now) def unique_impression_count_ip(start_date=nil,end_date=Time.now)
impressionist_count({:start_date=>start_date, :end_date=>end_date, :filter=> :ip_address}) impressionist_count({:start_date=>start_date, :end_date=>end_date, :filter=> :ip_address})
end end
def unique_impression_count_session(start_date=nil,end_date=Time.now) def unique_impression_count_session(start_date=nil,end_date=Time.now)
impressionist_count({:start_date=>start_date, :end_date=>end_date, :filter=> :session_hash}) impressionist_count({:start_date=>start_date, :end_date=>end_date, :filter=> :session_hash})
end end
end end
end end
end end

View File

@ -2,11 +2,11 @@ require "impressionist"
require "rails" require "rails"
module Impressionist module Impressionist
class Engine < Rails::Engine class Engine < Rails::Engine
initializer 'impressionist.extend_ar' do |app| initializer 'impressionist.extend_ar' do |app|
ActiveRecord::Base.extend Impressionist::Impressionable ActiveRecord::Base.send(:include, Impressionist::Impressionable)
end end
initializer 'impressionist.controller' do initializer 'impressionist.controller' do
ActiveSupport.on_load(:action_controller) do ActiveSupport.on_load(:action_controller) do
include ImpressionistController::InstanceMethods include ImpressionistController::InstanceMethods

View File

@ -1,5 +1,5 @@
PATH PATH
remote: /home/mio/prog/projects/impressionist remote: /Users/coryschires/Desktop/impressionist
specs: specs:
impressionist (0.4.0) impressionist (0.4.0)
@ -45,7 +45,7 @@ GEM
autotest-standalone (4.5.8) autotest-standalone (4.5.8)
bcrypt-ruby (3.0.1) bcrypt-ruby (3.0.1)
builder (3.0.0) builder (3.0.0)
capybara (1.1.1) capybara (1.1.2)
mime-types (>= 1.16) mime-types (>= 1.16)
nokogiri (>= 1.3.3) nokogiri (>= 1.3.3)
rack (>= 1.0.0) rack (>= 1.0.0)
@ -54,10 +54,10 @@ GEM
xpath (~> 0.1.4) xpath (~> 0.1.4)
childprocess (0.2.2) childprocess (0.2.2)
ffi (~> 1.0.6) ffi (~> 1.0.6)
cucumber (1.1.1) cucumber (1.1.3)
builder (>= 2.1.2) builder (>= 2.1.2)
diff-lcs (>= 1.1.2) diff-lcs (>= 1.1.2)
gherkin (~> 2.6.0) gherkin (~> 2.6.7)
json (>= 1.4.6) json (>= 1.4.6)
term-ansicolor (>= 1.0.6) term-ansicolor (>= 1.0.6)
cucumber-rails (1.2.0) cucumber-rails (1.2.0)
@ -65,12 +65,12 @@ GEM
cucumber (>= 1.1.1) cucumber (>= 1.1.1)
nokogiri (>= 1.5.0) nokogiri (>= 1.5.0)
daemons (1.0.10) daemons (1.0.10)
database_cleaner (0.6.7) database_cleaner (0.7.0)
diff-lcs (1.1.3) diff-lcs (1.1.3)
erubis (2.7.0) erubis (2.7.0)
ffi (1.0.9) ffi (1.0.11)
gem_plugin (0.2.3) gem_plugin (0.2.3)
gherkin (2.6.2) gherkin (2.6.8)
json (>= 1.4.6) json (>= 1.4.6)
hike (1.2.1) hike (1.2.1)
i18n (0.6.0) i18n (0.6.0)
@ -129,10 +129,10 @@ GEM
activesupport (~> 3.0) activesupport (~> 3.0)
railties (~> 3.0) railties (~> 3.0)
rspec (~> 2.7.0) rspec (~> 2.7.0)
rubyzip (0.9.4) rubyzip (0.9.5)
selenium-webdriver (2.10.0) selenium-webdriver (2.13.0)
childprocess (>= 0.2.1) childprocess (>= 0.2.1)
ffi (= 1.0.9) ffi (~> 1.0.9)
json_pure json_pure
rubyzip rubyzip
spork (0.8.5) spork (0.8.5)

View File

@ -2,5 +2,5 @@
class DummyController < ActionController::Base class DummyController < ActionController::Base
impressionist impressionist
end end

View File

@ -0,0 +1,7 @@
# We don't really care about this model. It's just being used to test the uniqueness controller
# specs. Nevertheless, we need a model because the counter caching functionality expects it.
#
class Dummy < ActiveRecord::Base
self.abstract_class = true # doesn't need to be backed by an actual table
is_impressionable
end

View File

@ -0,0 +1,3 @@
class Widget < ActiveRecord::Base
is_impressionable :counter_cache => true
end

View File

@ -0,0 +1,15 @@
class CreateWidgets < ActiveRecord::Migration
def self.up
create_table :widgets do |t|
t.string :name
t.integer :impressions_count
t.timestamps
end
end
def self.down
drop_table :widgets
end
end

View File

@ -0,0 +1,37 @@
class CreateImpressionsTable < ActiveRecord::Migration
def self.up
create_table :impressions, :force => true do |t|
t.string :impressionable_type
t.integer :impressionable_id
t.integer :user_id
t.string :controller_name
t.string :action_name
t.string :view_name
t.string :request_hash
t.string :session_hash
t.string :ip_address
t.string :message
t.string :referrer
t.timestamps
end
add_index :impressions, [:impressionable_type, :impressionable_id, :request_hash], :name => "poly_request_index", :unique => false
add_index :impressions, [:impressionable_type, :impressionable_id, :ip_address], :name => "poly_ip_index", :unique => false
add_index :impressions, [:impressionable_type, :impressionable_id, :session_hash], :name => "poly_session_index", :unique => false
add_index :impressions, [:controller_name,:action_name,:request_hash], :name => "controlleraction_request_index", :unique => false
add_index :impressions, [:controller_name,:action_name,:ip_address], :name => "controlleraction_ip_index", :unique => false
add_index :impressions, [:controller_name,:action_name,:session_hash], :name => "controlleraction_session_index", :unique => false
add_index :impressions, :user_id
end
def self.down
remove_index :impressions, :name => :poly_request_index
remove_index :impressions, :name => :poly_ip_index
remove_index :impressions, :name => :poly_session_index
remove_index :impressions, :name => :controlleraction_request_index
remove_index :impressions, :name => :controlleraction_ip_index
remove_index :impressions, :name => :controlleraction_session_index
remove_index :impressions, :user_id
drop_table :impressions
end
end

View File

@ -1,14 +1,14 @@
require 'spec_helper.rb' require 'spec_helper.rb'
describe ArticlesController do describe ArticlesController do
fixtures :articles,:impressions,:posts fixtures :articles,:impressions,:posts,:widgets
render_views render_views
it "should make the impressionable_hash available" do it "should make the impressionable_hash available" do
get "index" get "index"
response.body.include?("false").should eq true response.body.include?("false").should eq true
end end
it "should log an impression with a message" do it "should log an impression with a message" do
get "index" get "index"
Impression.all.size.should eq 12 Impression.all.size.should eq 12
@ -16,7 +16,7 @@ describe ArticlesController do
Article.first.impressions.last.controller_name.should eq "articles" Article.first.impressions.last.controller_name.should eq "articles"
Article.first.impressions.last.action_name.should eq "index" Article.first.impressions.last.action_name.should eq "index"
end end
it "should log an impression without a message" do it "should log an impression without a message" do
get "show", :id=> 1 get "show", :id=> 1
Impression.all.size.should eq 12 Impression.all.size.should eq 12
@ -24,18 +24,18 @@ describe ArticlesController do
Article.first.impressions.last.controller_name.should eq "articles" Article.first.impressions.last.controller_name.should eq "articles"
Article.first.impressions.last.action_name.should eq "show" Article.first.impressions.last.action_name.should eq "show"
end end
it "should log the user_id if user is authenticated (@current_user before_filter method)" do it "should log the user_id if user is authenticated (@current_user before_filter method)" do
session[:user_id] = 123 session[:user_id] = 123
get "show", :id=> 1 get "show", :id=> 1
Article.first.impressions.last.user_id.should eq 123 Article.first.impressions.last.user_id.should eq 123
end end
it "should not log the user_id if user is authenticated" do it "should not log the user_id if user is authenticated" do
get "show", :id=> 1 get "show", :id=> 1
Article.first.impressions.last.user_id.should eq nil Article.first.impressions.last.user_id.should eq nil
end end
it "should log the request_hash, ip_address, referrer and session_hash" do it "should log the request_hash, ip_address, referrer and session_hash" do
get "show", :id=> 1 get "show", :id=> 1
Impression.last.request_hash.size.should eq 64 Impression.last.request_hash.size.should eq 64
@ -43,13 +43,13 @@ describe ArticlesController do
Impression.last.session_hash.size.should eq 32 Impression.last.session_hash.size.should eq 32
Impression.last.referrer.should eq nil Impression.last.referrer.should eq nil
end end
it "should log the referrer when you click a link" do it "should log the referrer when you click a link" do
visit article_url(Article.first) visit article_url(Article.first)
click_link "Same Page" click_link "Same Page"
Impression.last.referrer.should eq "http://test.host/articles/1" Impression.last.referrer.should eq "http://test.host/articles/1"
end end
end end
describe PostsController do describe PostsController do
it "should log impression at the action level" do it "should log impression at the action level" do
@ -60,7 +60,7 @@ describe PostsController do
Impression.last.impressionable_type.should eq "Post" Impression.last.impressionable_type.should eq "Post"
Impression.last.impressionable_id.should eq 1 Impression.last.impressionable_id.should eq 1
end end
it "should log the user_id if user is authenticated (current_user helper method)" do it "should log the user_id if user is authenticated (current_user helper method)" do
session[:user_id] = 123 session[:user_id] = 123
get "show", :id=> 1 get "show", :id=> 1
@ -69,6 +69,12 @@ describe PostsController do
end end
describe WidgetsController do describe WidgetsController do
before(:each) do
@widget = Widget.find(1)
Widget.stub(:find).and_return(@widget)
end
it "should log impression at the per action level" do it "should log impression at the per action level" do
get "show", :id=> 1 get "show", :id=> 1
Impression.all.size.should eq 12 Impression.all.size.should eq 12
@ -77,17 +83,17 @@ describe WidgetsController do
get "new" get "new"
Impression.all.size.should eq 13 Impression.all.size.should eq 13
end end
it "should not log impression when user-agent is in wildcard list" do it "should not log impression when user-agent is in wildcard list" do
request.stub!(:user_agent).and_return('somebot') request.stub!(:user_agent).and_return('somebot')
get "show", :id=> 1 get "show", :id=> 1
Impression.all.size.should eq 11 Impression.all.size.should eq 11
end end
it "should not log impression when user-agent is in the bot list" do it "should not log impression when user-agent is in the bot list" do
request.stub!(:user_agent).and_return('Acoon Robot v1.50.001') request.stub!(:user_agent).and_return('Acoon Robot v1.50.001')
get "show", :id=> 1 get "show", :id=> 1
Impression.all.size.should eq 11 Impression.all.size.should eq 11
end end
describe "impressionist unique options" do describe "impressionist unique options" do
@ -117,4 +123,3 @@ describe WidgetsController do
end end
end end

View File

@ -2,19 +2,19 @@ require "spec_helper.rb"
# we use the posts controller as it uses the impressionsist module. any such controller would do. # we use the posts controller as it uses the impressionsist module. any such controller would do.
describe DummyController do describe DummyController do
before do before do
@impression_count = Impression.all.size @impression_count = Impression.all.size
end end
describe "impressionist filter uniqueness" do describe "impressionist filter uniqueness" do
it "should ignore uniqueness if not requested" do it "should ignore uniqueness if not requested" do
controller.impressionist_subapp_filter(nil, nil) controller.impressionist_subapp_filter(nil, nil)
controller.impressionist_subapp_filter(nil, nil) controller.impressionist_subapp_filter(nil, nil)
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize unique session" do it "should recognize unique session" do
# the following line was necessary as session hash returned a binary string (ASCII-8BIT encoded) # the following line was necessary as session hash returned a binary string (ASCII-8BIT encoded)
controller.stub!(:session_hash).and_return(request.session_options[:id].encode("ISO-8859-1")) controller.stub!(:session_hash).and_return(request.session_options[:id].encode("ISO-8859-1"))
@ -22,58 +22,58 @@ describe DummyController do
controller.impressionist_subapp_filter(nil, [:session_hash]) controller.impressionist_subapp_filter(nil, [:session_hash])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique ip" do it "should recognize unique ip" do
controller.request.stub!(:remote_ip).and_return("1.2.3.4") controller.request.stub!(:remote_ip).and_return("1.2.3.4")
controller.impressionist_subapp_filter(nil, [:ip_address]) controller.impressionist_subapp_filter(nil, [:ip_address])
controller.impressionist_subapp_filter(nil, [:ip_address]) controller.impressionist_subapp_filter(nil, [:ip_address])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique request" do it "should recognize unique request" do
controller.impressionist_subapp_filter(nil, [:request_hash]) controller.impressionist_subapp_filter(nil, [:request_hash])
controller.impressionist_subapp_filter(nil, [:request_hash]) controller.impressionist_subapp_filter(nil, [:request_hash])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique action" do it "should recognize unique action" do
controller.stub!(:action_name).and_return("test_action") controller.stub!(:action_name).and_return("test_action")
controller.impressionist_subapp_filter(nil, [:action_name]) controller.impressionist_subapp_filter(nil, [:action_name])
controller.impressionist_subapp_filter(nil, [:action_name]) controller.impressionist_subapp_filter(nil, [:action_name])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique controller" do it "should recognize unique controller" do
controller.stub!(:controller_name).and_return("test_controller") controller.stub!(:controller_name).and_return("post")
controller.impressionist_subapp_filter(nil, [:controller_name]) controller.impressionist_subapp_filter(nil, [:controller_name])
controller.impressionist_subapp_filter(nil, [:controller_name]) controller.impressionist_subapp_filter(nil, [:controller_name])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique user" do it "should recognize unique user" do
controller.stub!(:user_id).and_return(42) controller.stub!(:user_id).and_return(42)
controller.impressionist_subapp_filter(nil, [:user_id]) controller.impressionist_subapp_filter(nil, [:user_id])
controller.impressionist_subapp_filter(nil, [:user_id]) controller.impressionist_subapp_filter(nil, [:user_id])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique referer" do it "should recognize unique referer" do
controller.request.stub!(:referer).and_return("http://foo/bar") controller.request.stub!(:referer).and_return("http://foo/bar")
controller.impressionist_subapp_filter(nil, [:referrer]) controller.impressionist_subapp_filter(nil, [:referrer])
controller.impressionist_subapp_filter(nil, [:referrer]) controller.impressionist_subapp_filter(nil, [:referrer])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique id" do it "should recognize unique id" do
controller.stub!(:params).and_return({:id => "666"}) # for correct impressionable id in filter controller.stub!(:params).and_return({:id => "666"}) # for correct impressionable id in filter
controller.impressionist_subapp_filter(nil, [:impressionable_id]) controller.impressionist_subapp_filter(nil, [:impressionable_id])
controller.impressionist_subapp_filter(nil, [:impressionable_id]) controller.impressionist_subapp_filter(nil, [:impressionable_id])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
# extra redundant test for important controller and action combination. # extra redundant test for important controller and action combination.
it "should recognize different controller and action" do it "should recognize different controller and action" do
controller.stub!(:controller_name).and_return("test_controller") controller.stub!(:controller_name).and_return("post")
controller.stub!(:action_name).and_return("test_action") controller.stub!(:action_name).and_return("test_action")
controller.impressionist_subapp_filter(nil, [:controller_name, :action_name]) controller.impressionist_subapp_filter(nil, [:controller_name, :action_name])
controller.impressionist_subapp_filter(nil, [:controller_name, :action_name]) controller.impressionist_subapp_filter(nil, [:controller_name, :action_name])
@ -82,12 +82,12 @@ describe DummyController do
controller.impressionist_subapp_filter(nil, [:controller_name, :action_name]) controller.impressionist_subapp_filter(nil, [:controller_name, :action_name])
controller.impressionist_subapp_filter(nil, [:controller_name, :action_name]) controller.impressionist_subapp_filter(nil, [:controller_name, :action_name])
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
controller.stub!(:controller_name).and_return("another_controller") controller.stub!(:controller_name).and_return("article")
controller.impressionist_subapp_filter(nil, [:controller_name, :action_name]) controller.impressionist_subapp_filter(nil, [:controller_name, :action_name])
controller.impressionist_subapp_filter(nil, [:controller_name, :action_name]) controller.impressionist_subapp_filter(nil, [:controller_name, :action_name])
Impression.should have(@impression_count + 3).records Impression.should have(@impression_count + 3).records
end end
it "should recognize different action" do it "should recognize different action" do
controller.stub!(:action_name).and_return("test_action") controller.stub!(:action_name).and_return("test_action")
controller.impressionist_subapp_filter(nil, [:action_name]) controller.impressionist_subapp_filter(nil, [:action_name])
@ -98,18 +98,18 @@ describe DummyController do
controller.impressionist_subapp_filter(nil, [:action_name]) controller.impressionist_subapp_filter(nil, [:action_name])
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize different controller" do it "should recognize different controller" do
controller.stub!(:controller_name).and_return("test_controller") controller.stub!(:controller_name).and_return("post")
controller.impressionist_subapp_filter(nil, [:controller_name]) controller.impressionist_subapp_filter(nil, [:controller_name])
controller.impressionist_subapp_filter(nil, [:controller_name]) controller.impressionist_subapp_filter(nil, [:controller_name])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
controller.stub!(:controller_name).and_return("another_controller") controller.stub!(:controller_name).and_return("article")
controller.impressionist_subapp_filter(nil, [:controller_name]) controller.impressionist_subapp_filter(nil, [:controller_name])
controller.impressionist_subapp_filter(nil, [:controller_name]) controller.impressionist_subapp_filter(nil, [:controller_name])
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize different session" do it "should recognize different session" do
controller.stub!(:session_hash).and_return("foo") controller.stub!(:session_hash).and_return("foo")
controller.impressionist_subapp_filter(nil, [:session_hash]) controller.impressionist_subapp_filter(nil, [:session_hash])
@ -120,7 +120,7 @@ describe DummyController do
controller.impressionist_subapp_filter(nil, [:session_hash]) controller.impressionist_subapp_filter(nil, [:session_hash])
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize different ip" do it "should recognize different ip" do
controller.request.stub!(:remote_ip).and_return("1.2.3.4") controller.request.stub!(:remote_ip).and_return("1.2.3.4")
controller.impressionist_subapp_filter(nil, [:ip_address]) controller.impressionist_subapp_filter(nil, [:ip_address])
@ -131,7 +131,7 @@ describe DummyController do
controller.impressionist_subapp_filter(nil, [:ip_address]) controller.impressionist_subapp_filter(nil, [:ip_address])
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize different referer" do it "should recognize different referer" do
controller.request.stub!(:referer).and_return("http://foo/bar") controller.request.stub!(:referer).and_return("http://foo/bar")
controller.impressionist_subapp_filter(nil, [:referrer]) controller.impressionist_subapp_filter(nil, [:referrer])
@ -142,7 +142,7 @@ describe DummyController do
controller.impressionist_subapp_filter(nil, [:referrer]) controller.impressionist_subapp_filter(nil, [:referrer])
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize different id" do it "should recognize different id" do
controller.stub!(:params).and_return({:id => "666"}) # for correct impressionable id in filter controller.stub!(:params).and_return({:id => "666"}) # for correct impressionable id in filter
controller.impressionist_subapp_filter(nil, [:impressionable_type, :impressionable_id]) controller.impressionist_subapp_filter(nil, [:impressionable_type, :impressionable_id])
@ -152,7 +152,7 @@ describe DummyController do
controller.impressionist_subapp_filter(nil, [:impressionable_type, :impressionable_id]) controller.impressionist_subapp_filter(nil, [:impressionable_type, :impressionable_id])
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize combined uniqueness" do it "should recognize combined uniqueness" do
controller.stub!(:action_name).and_return("test_action") controller.stub!(:action_name).and_return("test_action")
controller.impressionist_subapp_filter(nil, [:ip_address, :request_hash, :action_name]) controller.impressionist_subapp_filter(nil, [:ip_address, :request_hash, :action_name])
@ -165,7 +165,7 @@ describe DummyController do
controller.impressionist_subapp_filter(nil, [:request_hash]) controller.impressionist_subapp_filter(nil, [:request_hash])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize combined non-uniqueness" do it "should recognize combined non-uniqueness" do
controller.stub!(:action_name).and_return(nil) controller.stub!(:action_name).and_return(nil)
controller.impressionist_subapp_filter(nil, [:ip_address, :action_name]) controller.impressionist_subapp_filter(nil, [:ip_address, :action_name])
@ -174,21 +174,21 @@ describe DummyController do
controller.stub!(:action_name).and_return("another_action") controller.stub!(:action_name).and_return("another_action")
controller.impressionist_subapp_filter(nil, [:ip_address, :action_name]) controller.impressionist_subapp_filter(nil, [:ip_address, :action_name])
Impression.should have(@impression_count + 3).records Impression.should have(@impression_count + 3).records
end end
end end
describe "impressionist method uniqueness for impressionables" do describe "impressionist method uniqueness for impressionables" do
# in this test we reuse the post model. might break if model changes. # in this test we reuse the post model. might break if model changes.
it "should ignore uniqueness if not requested" do it "should ignore uniqueness if not requested" do
impressionable = Post.create impressionable = Post.create
controller.impressionist impressionable controller.impressionist impressionable
controller.impressionist impressionable controller.impressionist impressionable
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize unique session" do it "should recognize unique session" do
# the following line was necessary as session hash returned a binary string (ASCII-8BIT encoded) # the following line was necessary as session hash returned a binary string (ASCII-8BIT encoded)
controller.stub!(:session_hash).and_return(request.session_options[:id].encode("ISO-8859-1")) controller.stub!(:session_hash).and_return(request.session_options[:id].encode("ISO-8859-1"))
@ -197,7 +197,7 @@ describe DummyController do
controller.impressionist(impressionable, nil, :unique => [:session_hash]) controller.impressionist(impressionable, nil, :unique => [:session_hash])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique ip" do it "should recognize unique ip" do
controller.request.stub!(:remote_ip).and_return("1.2.3.4") controller.request.stub!(:remote_ip).and_return("1.2.3.4")
impressionable = Post.create impressionable = Post.create
@ -205,22 +205,22 @@ describe DummyController do
controller.impressionist(impressionable, nil, :unique => [:ip_address]) controller.impressionist(impressionable, nil, :unique => [:ip_address])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique request" do it "should recognize unique request" do
impressionable = Post.create impressionable = Post.create
controller.impressionist(impressionable, nil, :unique => [:request_hash]) controller.impressionist(impressionable, nil, :unique => [:request_hash])
controller.impressionist(impressionable, nil, :unique => [:request_hash]) controller.impressionist(impressionable, nil, :unique => [:request_hash])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique user" do it "should recognize unique user" do
controller.stub!(:user_id).and_return(666) controller.stub!(:user_id).and_return(666)
impressionable = Post.create impressionable = Post.create
controller.impressionist(impressionable, nil, :unique => [:user_id]) controller.impressionist(impressionable, nil, :unique => [:user_id])
controller.impressionist(impressionable, nil, :unique => [:user_id]) controller.impressionist(impressionable, nil, :unique => [:user_id])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize unique referer" do it "should recognize unique referer" do
controller.request.stub!(:referer).and_return("http://foo/bar") controller.request.stub!(:referer).and_return("http://foo/bar")
impressionable = Post.create impressionable = Post.create
@ -228,7 +228,7 @@ describe DummyController do
controller.impressionist(impressionable, nil, :unique => [:referrer]) controller.impressionist(impressionable, nil, :unique => [:referrer])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize different session" do it "should recognize different session" do
impressionable = Post.create impressionable = Post.create
controller.stub!(:session_hash).and_return("foo") controller.stub!(:session_hash).and_return("foo")
@ -240,7 +240,7 @@ describe DummyController do
controller.impressionist(impressionable, nil, :unique => [:session_hash]) controller.impressionist(impressionable, nil, :unique => [:session_hash])
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize different ip" do it "should recognize different ip" do
controller.request.stub!(:remote_ip).and_return("1.2.3.4") controller.request.stub!(:remote_ip).and_return("1.2.3.4")
impressionable = Post.create impressionable = Post.create
@ -252,7 +252,7 @@ describe DummyController do
controller.impressionist(impressionable, nil, :unique => [:ip_address]) controller.impressionist(impressionable, nil, :unique => [:ip_address])
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize different user" do it "should recognize different user" do
impressionable = Post.create impressionable = Post.create
controller.stub!(:user_id).and_return(666) controller.stub!(:user_id).and_return(666)
@ -264,7 +264,7 @@ describe DummyController do
controller.impressionist(impressionable, nil, :unique => [:user_id]) controller.impressionist(impressionable, nil, :unique => [:user_id])
Impression.should have(@impression_count + 2).records Impression.should have(@impression_count + 2).records
end end
it "should recognize combined uniqueness" do it "should recognize combined uniqueness" do
impressionable = Post.create impressionable = Post.create
controller.stub!(:session_hash).and_return("foo") controller.stub!(:session_hash).and_return("foo")
@ -278,7 +278,7 @@ describe DummyController do
controller.impressionist(impressionable, nil, :unique => [:request_hash]) controller.impressionist(impressionable, nil, :unique => [:request_hash])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
it "should recognize combined non-uniqueness" do it "should recognize combined non-uniqueness" do
impressionable = Post.create impressionable = Post.create
controller.stub!(:session_hash).and_return(nil) controller.stub!(:session_hash).and_return(nil)
@ -289,11 +289,11 @@ describe DummyController do
controller.impressionist(impressionable, nil, :unique => [:ip_address, :session_hash]) controller.impressionist(impressionable, nil, :unique => [:ip_address, :session_hash])
Impression.should have(@impression_count + 3).records Impression.should have(@impression_count + 3).records
end end
end end
describe "impressionist filter and method uniqueness" do describe "impressionist filter and method uniqueness" do
it "should recognize uniqueness" do it "should recognize uniqueness" do
impressionable = Post.create impressionable = Post.create
controller.stub!(:controller_name).and_return("posts") # for correct impressionable type in filter controller.stub!(:controller_name).and_return("posts") # for correct impressionable type in filter
@ -305,8 +305,8 @@ describe DummyController do
controller.impressionist(impressionable, nil, :unique => [:ip_address, :request_hash, :session_hash]) controller.impressionist(impressionable, nil, :unique => [:ip_address, :request_hash, :session_hash])
Impression.should have(@impression_count + 1).records Impression.should have(@impression_count + 1).records
end end
end end
end end

4
test_app/spec/fixtures/widgets.yml vendored Normal file
View File

@ -0,0 +1,4 @@
one:
id: 1
name: A Widget
impressions_count: 0

View File

@ -0,0 +1,30 @@
require 'spec_helper'
describe Impression do
fixtures :widgets
before(:each) do
@widget = Widget.find(1)
Impression.destroy_all
end
describe "self#counter_caching?" do
it "should know when counter caching is enabled" do
Widget.should be_counter_caching
end
it "should know when counter caching is disabled" do
Article.should_not be_counter_caching
end
end
describe "#update_counter_cache" do
it "should update the counter cache column to reflect the correct number of impressions" do
lambda {
Impression.create(:impressionable_type => @widget.class.name, :impressionable_id => @widget.id)
@widget.reload
}.should change(@widget, :impressions_count).from(0).to(1)
end
end
end

View File

@ -49,6 +49,8 @@ describe Impression do
@article.impressionist_count(:filter=>:session_hash).should eq 7 @article.impressionist_count(:filter=>:session_hash).should eq 7
end end
#OLD COUNT METHODS. DEPRECATE SOON #OLD COUNT METHODS. DEPRECATE SOON
it "should return the impression count with no date range specified" do it "should return the impression count with no date range specified" do
@article.impression_count.should eq 11 @article.impression_count.should eq 11