Permalink
Browse files

Refine visitor strategy

  • Loading branch information...
1 parent cc3266c commit ebcb3dc37907df9e1798c3f89d061f5563300940 Tobi Knaup committed Feb 24, 2012
Showing with 78 additions and 36 deletions.
  1. +14 −0 README.md
  2. +14 −0 lib/trebuchet.rb
  3. +7 −9 lib/trebuchet/strategy/visitor_percent.rb
  4. +43 −27 spec/visitor_percent_strategy_spec.rb
View
14 README.md
@@ -76,3 +76,17 @@ Like parameters for builtin strategies, these can be changed while the applicati
Trebuchet.aim('time_machine', :markets, ['San Francisco', 'New York City'])
When using Trebuchet together with Rails, a good place to define custom strategies is in an initializer.
+
+
+Visitor Strategy
+----------------
+
+Trebuchet can be used to launch to visitors (no user object present).
+First, set the visitor id either directly (in a before filter) or as a proc:
+
+ Trebuchet.visitor_id = 123
+
+ Trebuchet.visitor_id = proc { |request| request && request.cookies[:visitor] && request.cookies[:visitor].hash }
+
+If you're using a proc, Trebuchet passes in the request object. It expects that the proc returns an integer.
+If it returns anything else, Trebuchet will not launch.
View
14 lib/trebuchet.rb
@@ -26,6 +26,20 @@ def self.define_strategy(name, &block)
Strategy::Custom.define(name, block)
end
+ def self.visitor_id=(id_or_proc)
+ if id_or_proc.is_a?(Proc)
+ @@visitor_proc = id_or_proc
+ elsif id_or_proc.is_a?(Integer)
+ @@visitor_proc = proc { |request| id_or_proc }
+ else
+ @@visitor_proc = nil
+ end
+ end
+
+ def self.visitor_id_proc
+ @@visitor_proc
+ end
+
def self.feature(name)
Feature.find(name)
end
View
16 lib/trebuchet/strategy/visitor_percent.rb
@@ -5,22 +5,20 @@ class Trebuchet::Strategy::VisitorPercent < Trebuchet::Strategy::Base
def initialize(percent)
@percent = percent
end
-
+
def offset
feature_id % 100
end
def launch_at?(user, request = nil)
- session_id = request.respond_to?(:session_options) &&
- request.session_options[:id]
-
- if !session_id || session_id == ''
- false
+ if Trebuchet.visitor_id_proc.respond_to?(:call)
+ visitor_id = Trebuchet.visitor_id_proc.call(request)
else
- session_id_int = session_id.hex
- session_id_int > 0 &&
- (session_id_int + offset) % 100 < percent
+ visitor_id = nil
end
+
+ return false if visitor_id.nil?
+ (visitor_id + offset) % 100 < percent
end
end
View
70 spec/visitor_percent_strategy_spec.rb
@@ -2,43 +2,59 @@
describe Trebuchet::Strategy::VisitorPercent do
- # Implements Rack's #session_options and sets #session_options[:id] to the
- # ID sent at initialization
- class MockRackRequest
- attr_accessor :session_options
+ describe 'visitor id integer' do
+ before do
+ Trebuchet.visitor_id = 123
+ end
+
+ it 'should launch' do
+ # offset of some_feature is 33
+ Trebuchet.aim('some_feature', :visitor_percent, 100)
+ t = Trebuchet.new(User.new(0))
+ t.launch?('some_feature').should == true
+
+ Trebuchet.aim('some_feature', :visitor_percent, 57)
+ t.launch?('some_feature').should == true
- def initialize session_id
- @session_options = {:id => session_id}
+ Trebuchet.aim('some_feature', :visitor_percent, 56)
+ t.launch?('some_feature').should == false
end
end
- it 'should not launch if no request is present' do
- Trebuchet.aim('some_feature', :visitor_percent, 100)
- should_not_launch('some_feature', [1000])
- end
+ describe 'visitor id proc' do
+ before do
+ Trebuchet.visitor_id = proc { |request| request && request.cookies[:visitor] && request.cookies[:visitor].hash }
+ end
- it 'should launch to a valid session' do
- Trebuchet.aim('some_feature', :visitor_percent, 100)
- t = Trebuchet.new(User.new(0), MockRackRequest.new('12345'))
- t.launch?('some_feature').should === true
- end
+ it 'should not launch if no request is present' do
+ Trebuchet.aim('some_feature', :visitor_percent, 100)
+ should_not_launch('some_feature', [1000])
+ end
+
+ it 'should launch to a valid session' do
+ Trebuchet.aim('some_feature', :visitor_percent, 100)
+ t = Trebuchet.new(User.new(0), mock_request('abcdef'))
+ t.launch?('some_feature').should == true
+ end
- it 'should not launch to a nil session ID' do
- Trebuchet.aim('some_feature', :visitor_percent, 100)
- t = Trebuchet.new(User.new(0), MockRackRequest.new(nil))
- t.launch?('some_feature').should === false
+ it 'should not launch to a nil session ID' do
+ Trebuchet.aim('some_feature', :visitor_percent, 100)
+ t = Trebuchet.new(User.new(0), mock_request(nil))
+ t.launch?('some_feature').should == false
+ end
end
- it 'should not launch to an empty session ID' do
- Trebuchet.aim('some_feature', :visitor_percent, 100)
- t = Trebuchet.new(User.new(0), MockRackRequest.new(''))
- t.launch?('some_feature').should === false
+ describe 'visitor id invalid' do
+ it "should handle nil" do
+ Trebuchet.visitor_id = nil
+ Trebuchet.aim('some_feature', :visitor_percent, 100)
+ t = Trebuchet.new(User.new(0), mock_request('abcdef'))
+ t.launch?('some_feature').should == false
+ end
end
- it 'should not launch to a bogus, non-hex session ID' do
- Trebuchet.aim('some_feature', :visitor_percent, 100)
- t = Trebuchet.new(User.new(0), MockRackRequest.new('My Name is Bob'))
- t.launch?('some_feature').should === false
+ def mock_request(cookie = nil)
+ mock 'Request', :cookies => {:visitor => cookie}
end
end

0 comments on commit ebcb3dc

Please sign in to comment.