Merge pull request #13 from georgmittendorfer/feature-unique

Unique impressions
This commit is contained in:
John McAliley 2011-11-15 18:43:51 -08:00
commit b6a001d6e9
11 changed files with 521 additions and 136 deletions

1
.gitignore vendored
View File

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

View File

@ -3,27 +3,23 @@ require 'digest/sha2'
module ImpressionistController module ImpressionistController
module ClassMethods module ClassMethods
def impressionist(opts={}) def impressionist(opts={})
before_filter { |c| c.impressionist_subapp_filter opts[:actions] } before_filter { |c| c.impressionist_subapp_filter(opts[:actions], opts[:unique])}
end end
end end
module InstanceMethods module InstanceMethods
def self.included(base) def self.included(base)
base.before_filter :impressionist_app_filter base.before_filter :impressionist_app_filter
end end
def impressionist(obj,message=nil) def impressionist(obj,message=nil,opts={})
unless bypass unless bypass
if obj.respond_to?("impressionable?") if obj.respond_to?("impressionable?")
obj.impressions.create(:message=> message, if unique_instance?(obj, opts[:unique])
:request_hash=> @impressionist_hash, obj.impressions.create(associative_create_statement({:message => message}))
:session_hash=> request.session_options[:id], end
:ip_address=> request.remote_ip,
:user_id=> user_id,
:controller_name=>controller_name,
:action_name=> action_name,
:referrer=>request.referer)
else else
# we could create an impression anyway. for classes, too. why not?
raise "#{obj.class.to_s} is not impressionable!" raise "#{obj.class.to_s} is not impressionable!"
end end
end end
@ -33,31 +29,73 @@ module ImpressionistController
@impressionist_hash = Digest::SHA2.hexdigest(Time.now.to_f.to_s+rand(10000).to_s) @impressionist_hash = Digest::SHA2.hexdigest(Time.now.to_f.to_s+rand(10000).to_s)
end end
def impressionist_subapp_filter(actions=nil) def impressionist_subapp_filter(actions=nil,unique_opts=nil)
unless bypass unless bypass
actions.collect!{|a|a.to_s} unless actions.blank? actions.collect!{|a|a.to_s} unless actions.blank?
if actions.blank? or actions.include?(action_name) if (actions.blank? || actions.include?(action_name)) && unique?(unique_opts)
Impression.create(:controller_name=> controller_name, Impression.create(direct_create_statement)
:action_name=> action_name,
:user_id=> user_id,
:request_hash=> @impressionist_hash,
:session_hash=> request.session_options[:id],
:ip_address=> request.remote_ip,
:impressionable_type=> controller_name.singularize.camelize,
:impressionable_id=> params[:id],
:referrer=>request.referer)
end end
end end
end end
private private
def bypass def bypass
Impressionist::Bots::WILD_CARDS.each do |wild_card| Impressionist::Bots::WILD_CARDS.each do |wild_card|
return true if request.user_agent and request.user_agent.downcase.include? wild_card return true if request.user_agent and request.user_agent.downcase.include? wild_card
end end
Impressionist::Bots::LIST.include? request.user_agent Impressionist::Bots::LIST.include? request.user_agent
end end
def unique_instance?(impressionable, unique_opts)
return unique_opts.blank? || impressionable.impressions.where(unique_query(unique_opts)).size == 0
end
def unique?(unique_opts)
return unique_opts.blank? || Impression.where(unique_query(unique_opts)).size == 0
end
# creates the query to check for uniqueness
def unique_query(unique_opts)
full_statement = direct_create_statement
# reduce the full statement to the params we need for the specified unique options
unique_opts.reduce({}) do |query, param|
query[param] = full_statement[param]
query
end
end
# creates a statment hash that contains default values for creating an impression via an AR relation.
def associative_create_statement(query_params={})
query_params.reverse_merge!(
:controller_name => controller_name,
:action_name => action_name,
:user_id => user_id,
:request_hash => @impressionist_hash,
:session_hash => session_hash,
:ip_address => request.remote_ip,
:referrer => request.referer
)
end
# creates a statment hash that contains default values for creating an impression.
def direct_create_statement(query_params={})
query_params.reverse_merge!(
:impressionable_type => controller_name.singularize.camelize,
:impressionable_id=> params[:id]
)
associative_create_statement(query_params)
end
def session_hash
# # careful: request.session_options[:id] encoding in rspec test was ASCII-8BIT
# # that broke the database query for uniqueness. not sure if this is a testing only issue.
# str = request.session_options[:id]
# logger.debug "Encoding: #{str.encoding.inspect}"
# # request.session_options[:id].encode("ISO-8859-1")
request.session_options[:id]
end
#use both @current_user and current_user helper #use both @current_user and current_user helper
def user_id def user_id
user_id = @current_user ? @current_user.id : nil rescue nil user_id = @current_user ? @current_user.id : nil rescue nil

View File

@ -1,9 +1,9 @@
source 'http://rubygems.org' source 'http://rubygems.org'
gem 'rails', '3.1.0.rc1' gem 'rails', '3.1'
gem 'sqlite3-ruby', :require => 'sqlite3' gem 'sqlite3-ruby', :require => 'sqlite3'
gem 'impressionist', :path=>"#{File.dirname(__FILE__)}/../" gem 'impressionist', :path=>"#{File.dirname(__FILE__)}/../"
gem "pg" #gem "pg"
group :development do group :development do
gem 'ZenTest' gem 'ZenTest'

View File

@ -1,155 +1,156 @@
PATH PATH
remote: /rails_plugins/mine/impressionist remote: /home/mio/prog/projects/impressionist
specs: specs:
impressionist (0.3.2) impressionist (0.4.0)
GEM GEM
remote: http://rubygems.org/ remote: http://rubygems.org/
specs: specs:
ZenTest (4.5.0) ZenTest (4.6.2)
actionmailer (3.1.0.rc1) actionmailer (3.1.0)
actionpack (= 3.1.0.rc1) actionpack (= 3.1.0)
mail (~> 2.3.0) mail (~> 2.3.0)
actionpack (3.1.0.rc1) actionpack (3.1.0)
activemodel (= 3.1.0.rc1) activemodel (= 3.1.0)
activesupport (= 3.1.0.rc1) activesupport (= 3.1.0)
builder (~> 3.0.0) builder (~> 3.0.0)
erubis (~> 2.7.0) erubis (~> 2.7.0)
i18n (~> 0.6.0beta1) i18n (~> 0.6)
rack (~> 1.3.0.beta2) rack (~> 1.3.2)
rack-cache (~> 1.0.1) rack-cache (~> 1.0.3)
rack-mount (~> 0.8.1) rack-mount (~> 0.8.2)
rack-test (~> 0.6.0) rack-test (~> 0.6.1)
sprockets (~> 2.0.0.beta.5) sprockets (~> 2.0.0)
tzinfo (~> 0.3.27) activemodel (3.1.0)
activemodel (3.1.0.rc1) activesupport (= 3.1.0)
activesupport (= 3.1.0.rc1) bcrypt-ruby (~> 3.0.0)
bcrypt-ruby (~> 2.1.4)
builder (~> 3.0.0) builder (~> 3.0.0)
i18n (~> 0.6.0beta1) i18n (~> 0.6)
activerecord (3.1.0.rc1) activerecord (3.1.0)
activemodel (= 3.1.0.rc1) activemodel (= 3.1.0)
activesupport (= 3.1.0.rc1) activesupport (= 3.1.0)
arel (~> 2.1.1) arel (~> 2.2.1)
tzinfo (~> 0.3.27) tzinfo (~> 0.3.29)
activeresource (3.1.0.rc1) activeresource (3.1.0)
activemodel (= 3.1.0.rc1) activemodel (= 3.1.0)
activesupport (= 3.1.0.rc1) activesupport (= 3.1.0)
activesupport (3.1.0.rc1) activesupport (3.1.0)
multi_json (~> 1.0) multi_json (~> 1.0)
arel (2.1.1) addressable (2.2.6)
arel (2.2.1)
autotest (4.4.6) autotest (4.4.6)
ZenTest (>= 4.4.1) ZenTest (>= 4.4.1)
autotest-notification (2.3.1) autotest-notification (2.3.3)
autotest (~> 4.3) autotest-standalone (~> 4.5)
bcrypt-ruby (2.1.4) autotest-standalone (4.5.8)
bcrypt-ruby (3.0.1)
builder (3.0.0) builder (3.0.0)
capybara (1.0.0.beta1) capybara (1.1.1)
mime-types (>= 1.16) mime-types (>= 1.16)
nokogiri (>= 1.3.3) nokogiri (>= 1.3.3)
rack (>= 1.0.0) rack (>= 1.0.0)
rack-test (>= 0.5.4) rack-test (>= 0.5.4)
selenium-webdriver (>= 0.0.27) selenium-webdriver (~> 2.0)
xpath (~> 0.1.4) xpath (~> 0.1.4)
childprocess (0.1.9) childprocess (0.2.2)
ffi (~> 1.0.6) ffi (~> 1.0.6)
configuration (1.2.0) cucumber (1.1.1)
cucumber (0.10.3)
builder (>= 2.1.2) builder (>= 2.1.2)
diff-lcs (>= 1.1.2) diff-lcs (>= 1.1.2)
gherkin (>= 2.3.8) gherkin (~> 2.6.0)
json (>= 1.4.6) json (>= 1.4.6)
term-ansicolor (>= 1.0.5) term-ansicolor (>= 1.0.6)
cucumber-rails (0.5.1) cucumber-rails (1.2.0)
capybara (>= 1.0.0.beta1) capybara (>= 1.1.1)
cucumber (>= 0.10.3) cucumber (>= 1.1.1)
nokogiri (>= 1.4.4) nokogiri (>= 1.5.0)
rack-test (>= 0.5.7)
daemons (1.0.10) daemons (1.0.10)
database_cleaner (0.6.7) database_cleaner (0.6.7)
diff-lcs (1.1.2) diff-lcs (1.1.3)
erubis (2.7.0) erubis (2.7.0)
ffi (1.0.9) ffi (1.0.9)
gem_plugin (0.2.3) gem_plugin (0.2.3)
gherkin (2.3.10) gherkin (2.6.2)
json (>= 1.4.6) json (>= 1.4.6)
hike (1.0.0) hike (1.2.1)
i18n (0.6.0) i18n (0.6.0)
json (1.5.1) json (1.6.1)
json_pure (1.5.1) json_pure (1.6.1)
launchy (0.4.0) launchy (2.0.5)
configuration (>= 0.0.5) addressable (~> 2.2.6)
rake (>= 0.8.1)
mail (2.3.0) mail (2.3.0)
i18n (>= 0.4.0) i18n (>= 0.4.0)
mime-types (~> 1.16) mime-types (~> 1.16)
treetop (~> 1.4.8) treetop (~> 1.4.8)
mime-types (1.16) mime-types (1.17.2)
mongrel (1.2.0.pre2) mongrel (1.2.0.pre2)
daemons (~> 1.0.10) daemons (~> 1.0.10)
gem_plugin (~> 0.2.3) gem_plugin (~> 0.2.3)
multi_json (1.0.3) multi_json (1.0.3)
nokogiri (1.4.4) nokogiri (1.5.0)
pg (0.11.0) polyglot (0.3.3)
polyglot (0.3.1) rack (1.3.5)
rack (1.3.0) rack-cache (1.0.3)
rack-cache (1.0.2)
rack (>= 0.4) rack (>= 0.4)
rack-mount (0.8.1) rack-mount (0.8.3)
rack (>= 1.0.0) rack (>= 1.0.0)
rack-ssl (1.3.2) rack-ssl (1.3.2)
rack rack
rack-test (0.6.0) rack-test (0.6.1)
rack (>= 1.0) rack (>= 1.0)
rails (3.1.0.rc1) rails (3.1.0)
actionmailer (= 3.1.0.rc1) actionmailer (= 3.1.0)
actionpack (= 3.1.0.rc1) actionpack (= 3.1.0)
activerecord (= 3.1.0.rc1) activerecord (= 3.1.0)
activeresource (= 3.1.0.rc1) activeresource (= 3.1.0)
activesupport (= 3.1.0.rc1) activesupport (= 3.1.0)
bundler (~> 1.0) bundler (~> 1.0)
railties (= 3.1.0.rc1) railties (= 3.1.0)
railties (3.1.0.rc1) railties (3.1.0)
actionpack (= 3.1.0.rc1) actionpack (= 3.1.0)
activesupport (= 3.1.0.rc1) activesupport (= 3.1.0)
rack-ssl (~> 1.3.2) rack-ssl (~> 1.3.2)
rake (>= 0.8.7) rake (>= 0.8.7)
rdoc (~> 3.4)
thor (~> 0.14.6) thor (~> 0.14.6)
rake (0.9.1) rake (0.9.2.2)
rspec (2.6.0) rdoc (3.11)
rspec-core (~> 2.6.0) json (~> 1.4)
rspec-expectations (~> 2.6.0) rspec (2.7.0)
rspec-mocks (~> 2.6.0) rspec-core (~> 2.7.0)
rspec-core (2.6.3) rspec-expectations (~> 2.7.0)
rspec-expectations (2.6.0) rspec-mocks (~> 2.7.0)
rspec-core (2.7.1)
rspec-expectations (2.7.0)
diff-lcs (~> 1.1.2) diff-lcs (~> 1.1.2)
rspec-mocks (2.6.0) rspec-mocks (2.7.0)
rspec-rails (2.6.1) rspec-rails (2.7.0)
actionpack (~> 3.0) actionpack (~> 3.0)
activesupport (~> 3.0) activesupport (~> 3.0)
railties (~> 3.0) railties (~> 3.0)
rspec (~> 2.6.0) rspec (~> 2.7.0)
rubyzip (0.9.4) rubyzip (0.9.4)
selenium-webdriver (0.2.1) selenium-webdriver (2.10.0)
childprocess (>= 0.1.7) childprocess (>= 0.2.1)
ffi (>= 1.0.7) ffi (= 1.0.9)
json_pure json_pure
rubyzip rubyzip
spork (0.8.5) spork (0.8.5)
sprockets (2.0.0.beta.9) sprockets (2.0.3)
hike (~> 1.0) hike (~> 1.2)
rack (~> 1.0) rack (~> 1.0)
tilt (~> 1.1, != 1.3.0) tilt (~> 1.1, != 1.3.0)
sqlite3 (1.3.3) sqlite3 (1.3.4)
sqlite3-ruby (1.3.3) sqlite3-ruby (1.3.3)
sqlite3 (>= 1.3.3) sqlite3 (>= 1.3.3)
systemu (2.2.0) systemu (2.4.1)
term-ansicolor (1.0.5) term-ansicolor (1.0.7)
thor (0.14.6) thor (0.14.6)
tilt (1.3.2) tilt (1.3.3)
treetop (1.4.9) treetop (1.4.10)
polyglot
polyglot (>= 0.3.1) polyglot (>= 0.3.1)
tzinfo (0.3.27) tzinfo (0.3.31)
xpath (0.1.4) xpath (0.1.4)
nokogiri (~> 1.3) nokogiri (~> 1.3)
@ -167,8 +168,7 @@ DEPENDENCIES
impressionist! impressionist!
launchy launchy
mongrel (= 1.2.0.pre2) mongrel (= 1.2.0.pre2)
pg rails (= 3.1)
rails (= 3.1.0.rc1)
rspec rspec
rspec-rails rspec-rails
spork spork

View File

@ -0,0 +1,6 @@
# This controller imports the impressionist module to make the modules methods available for testing
class DummyController < ActionController::Base
impressionist
end

View File

@ -1,5 +1,6 @@
class WidgetsController < ApplicationController class WidgetsController < ApplicationController
impressionist :actions=>[:show,:index] impressionist :actions=>[:show,:index], :unique => [:controller_name,:action_name,:impressionable_id]
def show def show
end end
@ -8,4 +9,5 @@ class WidgetsController < ApplicationController
def new def new
end end
end end

View File

@ -6,28 +6,19 @@ development:
pool: 5 pool: 5
timeout: 5000 timeout: 5000
# Warning: The database defined as "test" will be erased and test: &test
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
#test: &test
# adapter: sqlite3
# database: db/test.sqlite3
# pool: 5
# timeout: 5000
test:
adapter: sqlite3 adapter: sqlite3
database: db/test.sqlite3 database: db/test.sqlite3
pool: 5 pool: 5
timeout: 5000 timeout: 5000
pg_test: #pg_test:
adapter: postgresql # adapter: postgresql
database: impressionist_test # database: impressionist_test
username: johnmcaliley # username: johnmcaliley
password: # password:
host: localhost # host: localhost
encoding: UTF8 # encoding: UTF8
production: production:
adapter: sqlite3 adapter: sqlite3

View File

@ -89,5 +89,32 @@ describe WidgetsController do
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
it "should log unique impressions at the per action level" do
get "show", :id=> 1
Impression.all.size.should eq 12
get "show", :id=> 2
Impression.all.size.should eq 13
get "show", :id => 2
Impression.all.size.should eq 13
get "index"
Impression.all.size.should eq 14
end
it "should log unique impressions only once per id" do
get "show", :id=> 1
Impression.all.size.should eq 12
get "show", :id=> 2
Impression.all.size.should eq 13
get "show", :id => 2
Impression.all.size.should eq 13
get "index"
Impression.all.size.should eq 14
end
end
end end

View File

@ -0,0 +1,312 @@
require "spec_helper.rb"
# we use the posts controller as it uses the impressionsist module. any such controller would do.
describe DummyController do
before do
@impression_count = Impression.all.size
end
describe "impressionist filter uniqueness" do
it "should ignore uniqueness if not requested" do
controller.impressionist_subapp_filter(nil, nil)
controller.impressionist_subapp_filter(nil, nil)
Impression.should have(@impression_count + 2).records
end
it "should recognize unique session" do
# 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.impressionist_subapp_filter(nil, [:session_hash])
controller.impressionist_subapp_filter(nil, [:session_hash])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique ip" do
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])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique request" do
controller.impressionist_subapp_filter(nil, [:request_hash])
controller.impressionist_subapp_filter(nil, [:request_hash])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique action" do
controller.stub!(:action_name).and_return("test_action")
controller.impressionist_subapp_filter(nil, [:action_name])
controller.impressionist_subapp_filter(nil, [:action_name])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique controller" do
controller.stub!(:controller_name).and_return("test_controller")
controller.impressionist_subapp_filter(nil, [:controller_name])
controller.impressionist_subapp_filter(nil, [:controller_name])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique user" do
controller.stub!(:user_id).and_return(42)
controller.impressionist_subapp_filter(nil, [:user_id])
controller.impressionist_subapp_filter(nil, [:user_id])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique referer" do
controller.request.stub!(:referer).and_return("http://foo/bar")
controller.impressionist_subapp_filter(nil, [:referrer])
controller.impressionist_subapp_filter(nil, [:referrer])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique id" do
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])
Impression.should have(@impression_count + 1).records
end
# extra redundant test for important controller and action combination.
it "should recognize different controller and action" do
controller.stub!(:controller_name).and_return("test_controller")
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])
Impression.should have(@impression_count + 1).records
controller.stub!(:action_name).and_return("another_action")
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
controller.stub!(:controller_name).and_return("another_controller")
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
end
it "should recognize different action" do
controller.stub!(:action_name).and_return("test_action")
controller.impressionist_subapp_filter(nil, [:action_name])
controller.impressionist_subapp_filter(nil, [:action_name])
Impression.should have(@impression_count + 1).records
controller.stub!(:action_name).and_return("another_action")
controller.impressionist_subapp_filter(nil, [:action_name])
controller.impressionist_subapp_filter(nil, [:action_name])
Impression.should have(@impression_count + 2).records
end
it "should recognize different controller" do
controller.stub!(:controller_name).and_return("test_controller")
controller.impressionist_subapp_filter(nil, [:controller_name])
controller.impressionist_subapp_filter(nil, [:controller_name])
Impression.should have(@impression_count + 1).records
controller.stub!(:controller_name).and_return("another_controller")
controller.impressionist_subapp_filter(nil, [:controller_name])
controller.impressionist_subapp_filter(nil, [:controller_name])
Impression.should have(@impression_count + 2).records
end
it "should recognize different session" do
controller.stub!(:session_hash).and_return("foo")
controller.impressionist_subapp_filter(nil, [:session_hash])
controller.impressionist_subapp_filter(nil, [:session_hash])
Impression.should have(@impression_count + 1).records
controller.stub!(:session_hash).and_return("bar")
controller.impressionist_subapp_filter(nil, [:session_hash])
controller.impressionist_subapp_filter(nil, [:session_hash])
Impression.should have(@impression_count + 2).records
end
it "should recognize different ip" do
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])
Impression.should have(@impression_count + 1).records
controller.request.stub!(:remote_ip).and_return("5.6.7.8")
controller.impressionist_subapp_filter(nil, [:ip_address])
controller.impressionist_subapp_filter(nil, [:ip_address])
Impression.should have(@impression_count + 2).records
end
it "should recognize different referer" do
controller.request.stub!(:referer).and_return("http://foo/bar")
controller.impressionist_subapp_filter(nil, [:referrer])
controller.impressionist_subapp_filter(nil, [:referrer])
Impression.should have(@impression_count + 1).records
controller.request.stub!(:referer).and_return("http://bar/fo")
controller.impressionist_subapp_filter(nil, [:referrer])
controller.impressionist_subapp_filter(nil, [:referrer])
Impression.should have(@impression_count + 2).records
end
it "should recognize different id" do
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])
controller.stub!(:params).and_return({:id => "42"}) # for correct impressionable id in filter
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
end
it "should recognize combined uniqueness" do
controller.stub!(:action_name).and_return("test_action")
controller.impressionist_subapp_filter(nil, [:ip_address, :request_hash, :action_name])
controller.impressionist_subapp_filter(nil, [:request_hash, :ip_address, :action_name])
controller.impressionist_subapp_filter(nil, [:request_hash, :action_name])
controller.impressionist_subapp_filter(nil, [:ip_address, :action_name])
controller.impressionist_subapp_filter(nil, [:ip_address, :request_hash])
controller.impressionist_subapp_filter(nil, [:action_name])
controller.impressionist_subapp_filter(nil, [:ip_address])
controller.impressionist_subapp_filter(nil, [:request_hash])
Impression.should have(@impression_count + 1).records
end
it "should recognize combined non-uniqueness" do
controller.stub!(:action_name).and_return(nil)
controller.impressionist_subapp_filter(nil, [:ip_address, :action_name])
controller.stub!(:action_name).and_return("test_action")
controller.impressionist_subapp_filter(nil, [:ip_address, :action_name])
controller.stub!(:action_name).and_return("another_action")
controller.impressionist_subapp_filter(nil, [:ip_address, :action_name])
Impression.should have(@impression_count + 3).records
end
end
describe "impressionist method uniqueness for impressionables" do
# in this test we reuse the post model. might break if model changes.
it "should ignore uniqueness if not requested" do
impressionable = Post.create
controller.impressionist impressionable
controller.impressionist impressionable
Impression.should have(@impression_count + 2).records
end
it "should recognize unique session" do
# 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"))
impressionable = Post.create
controller.impressionist(impressionable, nil, :unique => [:session_hash])
controller.impressionist(impressionable, nil, :unique => [:session_hash])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique ip" do
controller.request.stub!(:remote_ip).and_return("1.2.3.4")
impressionable = Post.create
controller.impressionist(impressionable, nil, :unique => [:ip_address])
controller.impressionist(impressionable, nil, :unique => [:ip_address])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique request" do
impressionable = Post.create
controller.impressionist(impressionable, nil, :unique => [:request_hash])
controller.impressionist(impressionable, nil, :unique => [:request_hash])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique user" do
controller.stub!(:user_id).and_return(666)
impressionable = Post.create
controller.impressionist(impressionable, nil, :unique => [:user_id])
controller.impressionist(impressionable, nil, :unique => [:user_id])
Impression.should have(@impression_count + 1).records
end
it "should recognize unique referer" do
controller.request.stub!(:referer).and_return("http://foo/bar")
impressionable = Post.create
controller.impressionist(impressionable, nil, :unique => [:referrer])
controller.impressionist(impressionable, nil, :unique => [:referrer])
Impression.should have(@impression_count + 1).records
end
it "should recognize different session" do
impressionable = Post.create
controller.stub!(:session_hash).and_return("foo")
controller.impressionist(impressionable, nil, :unique => [:session_hash])
controller.impressionist(impressionable, nil, :unique => [:session_hash])
Impression.should have(@impression_count + 1).records
controller.stub!(:session_hash).and_return("bar")
controller.impressionist(impressionable, nil, :unique => [:session_hash])
controller.impressionist(impressionable, nil, :unique => [:session_hash])
Impression.should have(@impression_count + 2).records
end
it "should recognize different ip" do
controller.request.stub!(:remote_ip).and_return("1.2.3.4")
impressionable = Post.create
controller.impressionist(impressionable, nil, :unique => [:ip_address])
controller.impressionist(impressionable, nil, :unique => [:ip_address])
Impression.should have(@impression_count + 1).records
controller.request.stub!(:remote_ip).and_return("5.6.7.8")
controller.impressionist(impressionable, nil, :unique => [:ip_address])
controller.impressionist(impressionable, nil, :unique => [:ip_address])
Impression.should have(@impression_count + 2).records
end
it "should recognize different user" do
impressionable = Post.create
controller.stub!(:user_id).and_return(666)
controller.impressionist(impressionable, nil, :unique => [:user_id])
controller.impressionist(impressionable, nil, :unique => [:user_id])
Impression.should have(@impression_count + 1).records
controller.stub!(:user_id).and_return(42)
controller.impressionist(impressionable, nil, :unique => [:user_id])
controller.impressionist(impressionable, nil, :unique => [:user_id])
Impression.should have(@impression_count + 2).records
end
it "should recognize combined uniqueness" do
impressionable = Post.create
controller.stub!(:session_hash).and_return("foo")
controller.impressionist(impressionable, nil, :unique => [:ip_address, :request_hash, :session_hash])
controller.impressionist(impressionable, nil, :unique => [:request_hash, :ip_address, :session_hash])
controller.impressionist(impressionable, nil, :unique => [:request_hash, :session_hash])
controller.impressionist(impressionable, nil, :unique => [:ip_address, :session_hash])
controller.impressionist(impressionable, nil, :unique => [:ip_address, :request_hash])
controller.impressionist(impressionable, nil, :unique => [:session_hash])
controller.impressionist(impressionable, nil, :unique => [:ip_address])
controller.impressionist(impressionable, nil, :unique => [:request_hash])
Impression.should have(@impression_count + 1).records
end
it "should recognize combined non-uniqueness" do
impressionable = Post.create
controller.stub!(:session_hash).and_return(nil)
controller.impressionist(impressionable, nil, :unique => [:ip_address, :session_hash])
controller.stub!(:session_hash).and_return("foo")
controller.impressionist(impressionable, nil, :unique => [:ip_address, :session_hash])
controller.stub!(:session_hash).and_return("bar")
controller.impressionist(impressionable, nil, :unique => [:ip_address, :session_hash])
Impression.should have(@impression_count + 3).records
end
end
describe "impressionist filter and method uniqueness" do
it "should recognize uniqueness" do
impressionable = Post.create
controller.stub!(:controller_name).and_return("posts") # for correct impressionable type in filter
controller.stub!(:params).and_return({:id => impressionable.id.to_s}) # for correct impressionable id in filter
controller.stub!(:session_hash).and_return("foo")
controller.request.stub!(:remote_ip).and_return("1.2.3.4")
# order of the following methods is important for the test!
controller.impressionist_subapp_filter(nil, [:ip_address, :request_hash, :session_hash])
controller.impressionist(impressionable, nil, :unique => [:ip_address, :request_hash, :session_hash])
Impression.should have(@impression_count + 1).records
end
end
end

View File

@ -1,6 +1,8 @@
require 'spec_helper' require 'spec_helper'
require 'systemu' require 'systemu'
# FIXME this test might break the others if run before them
#
describe Impressionist do describe Impressionist do
fixtures :articles,:impressions,:posts fixtures :articles,:impressions,:posts
it "should delete existing migration and generate the migration file" do it "should delete existing migration and generate the migration file" do

View File

@ -24,4 +24,10 @@ RSpec.configure do |config|
# examples within a transaction, remove the following line or assign false # examples within a transaction, remove the following line or assign false
# instead of true. # instead of true.
config.use_transactional_fixtures = true config.use_transactional_fixtures = true
# make the rails logger usable in the tests as logger.xxx "..."
def logger
Rails.logger
end
end end