Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Renamed the project to Split

  • Loading branch information...
commit d97fb57f2140ff8fd6ddd969e0a49f55e2ee1004 1 parent a36f935
@andrew andrew authored
View
1  Gemfile
@@ -1,4 +1,3 @@
source "http://rubygems.org"
-# Specify your gem's dependencies in multivariate.gemspec
gemspec
View
65 README.mdown
@@ -1,13 +1,13 @@
-# Multivariate
-## Rack based multivariate testing framework
+# Split
+## Rack based split testing framework
-Multivariate is a rack based ab testing framework designed to work with Rails, Sinatra or any other rack based app.
+Split is a rack based ab testing framework designed to work with Rails, Sinatra or any other rack based app.
-Multivariate is heavily inspired by the Abingo and Vanity rails ab testing plugins and Resque in its use of Redis.
+Split is heavily inspired by the Abingo and Vanity rails ab testing plugins and Resque in its use of Redis.
## Requirements
-Multivariate uses redis as a datastore.
+Split uses redis as a datastore.
If you're on OS X, Homebrew is the simplest way to install Redis:
@@ -18,9 +18,9 @@ You now have a Redis daemon running on 6379.
## Setup
-If you are using bundler add multivariate to your Gemfile:
+If you are using bundler add split to your Gemfile:
- gem 'multivariate'
+ gem 'split'
Then run:
@@ -28,23 +28,23 @@ Then run:
Otherwise install the gem:
- gem install multivariate
+ gem install split
and require it in your project:
- require 'multivariate'
+ require 'split'
### Rails
-Multivariate is autoloaded when rails starts up, as long as you've configured redis it will 'just work'.
+Split is autoloaded when rails starts up, as long as you've configured redis it will 'just work'.
### Sinatra
-To configure sinatra with Multivariate you need to enable sessions and mix in the helper methods. Add the following lines at the top of your sinatra app:
+To configure sinatra with Split you need to enable sessions and mix in the helper methods. Add the following lines at the top of your sinatra app:
class MySinatraApp < Sinatra::Base
enable :sessions
- helpers Multivariate::Helper
+ helpers Split::Helper
get '/' do
...
@@ -86,40 +86,40 @@ Example: Conversion tracking (in a view)
## Web Interface
-Multivariate comes with a Sinatra-based front end to get an overview of how your experiments are doing.
+Split comes with a Sinatra-based front end to get an overview of how your experiments are doing.
You can mount this inside your app using Rack::URLMap in your `config.ru`
- require 'multivariate/dashboard'
+ require 'split/dashboard'
run Rack::URLMap.new \
"/" => Your::App.new,
- "/multivariate" => Multivariate::Dashboard.new
+ "/split" => Split::Dashboard.new
You may want to password protect that page, you can do so with `Rack::Auth::Basic`
- Multivariate::Dashboard.use Rack::Auth::Basic do |username, password|
+ Split::Dashboard.use Rack::Auth::Basic do |username, password|
username == 'admin' && password == 'p4s5w0rd'
end
## Configuration
-You may want to change the Redis host and port Multivariate connects to, or
+You may want to change the Redis host and port Split connects to, or
set various other options at startup.
-Multivariate has a `redis` setter which can be given a string or a Redis
-object. This means if you're already using Redis in your app, Multivariate
+Split has a `redis` setter which can be given a string or a Redis
+object. This means if you're already using Redis in your app, Split
can re-use the existing connection.
-String: `Multivariate.redis = 'localhost:6379'`
+String: `Split.redis = 'localhost:6379'`
-Redis: `Multivariate.redis = $redis`
+Redis: `Split.redis = $redis`
-For our rails app we have a `config/initializers/multivariate.rb` file where
-we load `config/multivariate.yml` by hand and set the Redis information
+For our rails app we have a `config/initializers/split.rb` file where
+we load `config/split.yml` by hand and set the Redis information
appropriately.
-Here's our `config/multivariate.yml`:
+Here's our `config/split.yml`:
development: localhost:6379
test: localhost:6379
@@ -132,22 +132,22 @@ And our initializer:
rails_root = ENV['RAILS_ROOT'] || File.dirname(__FILE__) + '/../..'
rails_env = ENV['RAILS_ENV'] || 'development'
- multivariate_config = YAML.load_file(rails_root + '/config/multivariate.yml')
- Multivariate.redis = multivariate_config[rails_env]
+ split_config = YAML.load_file(rails_root + '/config/split.yml')
+ Split.redis = split_config[rails_env]
## Namespaces
-If you're running multiple, separate instances of Multivariate you may want
+If you're running multiple, separate instances of Split you may want
to namespace the keyspaces so they do not overlap. This is not unlike
the approach taken by many memcached clients.
This feature is provided by the [redis-namespace][rs] library, which
-Multivariate uses by default to separate the keys it manages from other keys
+Split uses by default to separate the keys it manages from other keys
in your Redis server.
-Simply use the `Multivariate.redis.namespace` accessor:
+Simply use the `Split.redis.namespace` accessor:
- Multivariate.redis.namespace = "resque:GitHub"
+ Split.redis.namespace = "resque:GitHub"
We recommend sticking this in your initializer somewhere after Redis
is configured.
@@ -171,4 +171,7 @@ Special thanks to the following people for submitting patches:
## Copyright
-Copyright (c) 2011 Andrew Nesbitt. See LICENSE for details.
+Copyright (c) 2011 Andrew Nesbitt. See LICENSE for details.
+
+
+n.b don't pass the same alternative twice!
View
16 lib/multivariate.rb → lib/split.rb
@@ -1,10 +1,10 @@
require 'rubygems'
-require 'multivariate/experiment'
-require 'multivariate/alternative'
-require 'multivariate/helper'
+require 'split/experiment'
+require 'split/alternative'
+require 'split/helper'
require 'redis/namespace'
-module Multivariate
+module Split
extend self
# Accepts:
# 1. A 'hostname:port' string
@@ -23,13 +23,13 @@ def redis=(server)
redis = Redis.new(:host => host, :port => port,
:thread_safe => true, :db => db)
end
- namespace ||= :multivariate
+ namespace ||= :split
@redis = Redis::Namespace.new(namespace, :redis => redis)
elsif server.respond_to? :namespace=
@redis = server
else
- @redis = Redis::Namespace.new(:multivariate, :redis => server)
+ @redis = Redis::Namespace.new(:split, :redis => server)
end
end
@@ -44,7 +44,7 @@ def redis
if defined?(Rails)
class ActionController::Base
- ActionController::Base.send :include, Multivariate::Helper
- ActionController::Base.helper Multivariate::Helper
+ ActionController::Base.send :include, Split::Helper
+ ActionController::Base.helper Split::Helper
end
end
View
14 lib/multivariate/alternative.rb → lib/split/alternative.rb
@@ -1,4 +1,4 @@
-module Multivariate
+module Split
class Alternative
attr_accessor :name
attr_accessor :participant_count
@@ -33,7 +33,7 @@ def z_score
# E = the number of impressions within the experiment split
# C = the number of impressions within the control split
- experiment = Multivariate::Experiment.find(@experiment_name)
+ experiment = Split::Experiment.find(@experiment_name)
control = experiment.alternatives[0]
alternative = self
@@ -51,16 +51,16 @@ def z_score
end
def save
- if Multivariate.redis.hgetall("#{experiment_name}:#{name}")
- Multivariate.redis.hset "#{experiment_name}:#{name}", 'participant_count', @participant_count
- Multivariate.redis.hset "#{experiment_name}:#{name}", 'completed_count', @completed_count
+ if Split.redis.hgetall("#{experiment_name}:#{name}")
+ Split.redis.hset "#{experiment_name}:#{name}", 'participant_count', @participant_count
+ Split.redis.hset "#{experiment_name}:#{name}", 'completed_count', @completed_count
else
- Multivariate.redis.hmset "#{experiment_name}:#{name}", 'participant_count', 'completed_count', @participant_count, @completed_count
+ Split.redis.hmset "#{experiment_name}:#{name}", 'participant_count', 'completed_count', @participant_count, @completed_count
end
end
def self.find(name, experiment_name)
- counters = Multivariate.redis.hgetall "#{experiment_name}:#{name}"
+ counters = Split.redis.hgetall "#{experiment_name}:#{name}"
self.new(name, experiment_name, counters)
end
View
10 lib/multivariate/dashboard.rb → lib/split/dashboard.rb
@@ -1,7 +1,7 @@
require 'sinatra/base'
-require 'multivariate'
+require 'split'
-module Multivariate
+module Split
class Dashboard < Sinatra::Base
dir = File.dirname(File.expand_path(__FILE__))
@@ -20,13 +20,13 @@ def path_prefix
end
get '/' do
- @experiments = Multivariate::Experiment.all
+ @experiments = Split::Experiment.all
erb :index
end
post '/:experiment' do
- @experiment = Multivariate::Experiment.find(params[:experiment])
- @alternative = Multivariate::Alternative.find(params[:alternative], params[:experiment])
+ @experiment = Split::Experiment.find(params[:experiment])
+ @alternative = Split::Alternative.find(params[:alternative], params[:experiment])
@experiment.winner = @alternative.name
@experiment.save
redirect url('/')
View
0  lib/multivariate/dashboard/public/reset.css → lib/split/dashboard/public/reset.css
File renamed without changes
View
0  lib/multivariate/dashboard/public/style.css → lib/split/dashboard/public/style.css
File renamed without changes
View
2  lib/multivariate/dashboard/views/index.erb → lib/split/dashboard/views/index.erb
@@ -1,4 +1,4 @@
-<h1>Multivariate Dashboard</h1>
+<h1>Split Dashboard</h1>
<p class="intro">The list below contains all the registered experiments along with the number of test participants, completed and conversion rate currently in the system.</p>
<% @experiments.each do |experiment| %>
View
4 lib/multivariate/dashboard/views/layout.erb → lib/split/dashboard/views/layout.erb
@@ -5,7 +5,7 @@
<link href="<%= url 'reset.css' %>" media="screen" rel="stylesheet" type="text/css">
<link href="<%= url 'style.css' %>" media="screen" rel="stylesheet" type="text/css">
-<title>Multivariate</title>
+<title>Split</title>
</head>
<body>
@@ -16,7 +16,7 @@
</div>
<div id="footer">
- <p>Powered by <a href="http://github.com/andrew/multivariate">Multivariate</a> v<%=Multivariate::VERSION %></p>
+ <p>Powered by <a href="http://github.com/andrew/split">Split</a> v<%=Split::VERSION %></p>
</div>
</body>
</html>
View
24 lib/multivariate/experiment.rb → lib/split/experiment.rb
@@ -1,4 +1,4 @@
-module Multivariate
+module Split
class Experiment
attr_accessor :name
attr_accessor :alternatives
@@ -10,19 +10,19 @@ def initialize(name, *alternatives)
end
def winner
- if w = Multivariate.redis.hget(:experiment_winner, name)
- return Multivariate::Alternative.find(w, name)
+ if w = Split.redis.hget(:experiment_winner, name)
+ return Split::Alternative.find(w, name)
else
nil
end
end
def winner=(winner_name)
- Multivariate.redis.hset(:experiment_winner, name, winner_name.to_s)
+ Split.redis.hset(:experiment_winner, name, winner_name.to_s)
end
def alternatives
- @alternatives.map {|a| Multivariate::Alternative.find_or_create(a, name)}
+ @alternatives.map {|a| Split::Alternative.find_or_create(a, name)}
end
def next_alternative
@@ -30,25 +30,25 @@ def next_alternative
end
def save
- Multivariate.redis.sadd(:experiments, name)
- @alternatives.each {|a| Multivariate.redis.sadd(name, a) }
+ Split.redis.sadd(:experiments, name)
+ @alternatives.each {|a| Split.redis.sadd(name, a) }
end
def self.all
- Array(Multivariate.redis.smembers(:experiments)).map {|e| find(e)}
+ Array(Split.redis.smembers(:experiments)).map {|e| find(e)}
end
def self.find(name)
- if Multivariate.redis.exists(name)
- self.new(name, *Multivariate.redis.smembers(name))
+ if Split.redis.exists(name)
+ self.new(name, *Split.redis.smembers(name))
else
raise 'Experiment not found'
end
end
def self.find_or_create(name, *alternatives)
- if Multivariate.redis.exists(name)
- return self.new(name, *Multivariate.redis.smembers(name))
+ if Split.redis.exists(name)
+ return self.new(name, *Split.redis.smembers(name))
else
experiment = self.new(name, *alternatives)
experiment.save
View
10 lib/multivariate/helper.rb → lib/split/helper.rb
@@ -1,7 +1,7 @@
-module Multivariate
+module Split
module Helper
def ab_test(experiment_name, *alternatives)
- experiment = Multivariate::Experiment.find_or_create(experiment_name, *alternatives)
+ experiment = Split::Experiment.find_or_create(experiment_name, *alternatives)
return experiment.winner.name if experiment.winner
if ab_user[experiment_name]
@@ -16,13 +16,13 @@ def ab_test(experiment_name, *alternatives)
def finished(experiment_name)
alternative_name = ab_user[experiment_name]
- alternative = Multivariate::Alternative.find(alternative_name, experiment_name)
+ alternative = Split::Alternative.find(alternative_name, experiment_name)
alternative.increment_completion
- session[:multivariate].delete(experiment_name)
+ session[:split].delete(experiment_name)
end
def ab_user
- session[:multivariate] ||= {}
+ session[:split] ||= {}
end
end
end
View
2  lib/multivariate/version.rb → lib/split/version.rb
@@ -1,3 +1,3 @@
-module Multivariate
+module Split
VERSION = "0.1.0"
end
View
36 spec/experiment_spec.rb
@@ -1,65 +1,65 @@
require 'spec_helper'
-require 'multivariate/experiment'
+require 'split/experiment'
-describe Multivariate::Experiment do
- before(:each) { Multivariate.redis.flushall }
+describe Split::Experiment do
+ before(:each) { Split.redis.flushall }
it "should have a name" do
- experiment = Multivariate::Experiment.new('basket_text', 'Basket', "Cart")
+ experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
experiment.name.should eql('basket_text')
end
it "should have alternatives" do
- experiment = Multivariate::Experiment.new('basket_text', 'Basket', "Cart")
+ experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
experiment.alternatives.length.should be 2
end
it "should save to redis" do
- experiment = Multivariate::Experiment.new('basket_text', 'Basket', "Cart")
+ experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
experiment.save
- Multivariate.redis.exists('basket_text').should be true
+ Split.redis.exists('basket_text').should be true
end
it "should return an existing experiment" do
- experiment = Multivariate::Experiment.new('basket_text', 'Basket', "Cart")
+ experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
experiment.save
- Multivariate::Experiment.find('basket_text').name.should eql('basket_text')
+ Split::Experiment.find('basket_text').name.should eql('basket_text')
end
describe 'winner' do
it "should have no winner initially" do
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
experiment.winner.should be_nil
end
it "should allow you to specify a winner" do
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
experiment.winner = 'red'
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
experiment.winner.name.should == 'red'
end
end
describe 'next_alternative' do
it "should return a random alternative from those with the least participants" do
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
- Multivariate::Alternative.find('blue', 'link_color').increment_participation
- Multivariate::Alternative.find('red', 'link_color').increment_participation
+ Split::Alternative.find('blue', 'link_color').increment_participation
+ Split::Alternative.find('red', 'link_color').increment_participation
experiment.next_alternative.name.should == 'green'
end
it "should always return the winner if one exists" do
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
- green = Multivariate::Alternative.find('green', 'link_color')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
+ green = Split::Alternative.find('green', 'link_color')
experiment.winner = 'green'
experiment.next_alternative.name.should == 'green'
green.increment_participation
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
experiment.next_alternative.name.should == 'green'
end
end
View
40 spec/helper_spec.rb
@@ -1,10 +1,10 @@
require 'spec_helper'
-describe Multivariate::Helper do
- include Multivariate::Helper
+describe Split::Helper do
+ include Split::Helper
before(:each) do
- Multivariate.redis.flushall
+ Split.redis.flushall
@session = {}
end
@@ -15,28 +15,28 @@
end
it "should increment the participation counter after assignment to a new user" do
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
- previous_red_count = Multivariate::Alternative.find('red', 'link_color').participant_count
- previous_blue_count = Multivariate::Alternative.find('blue', 'link_color').participant_count
+ previous_red_count = Split::Alternative.find('red', 'link_color').participant_count
+ previous_blue_count = Split::Alternative.find('blue', 'link_color').participant_count
ab_test('link_color', 'blue', 'red')
- new_red_count = Multivariate::Alternative.find('red', 'link_color').participant_count
- new_blue_count = Multivariate::Alternative.find('blue', 'link_color').participant_count
+ new_red_count = Split::Alternative.find('red', 'link_color').participant_count
+ new_blue_count = Split::Alternative.find('blue', 'link_color').participant_count
(new_red_count + new_blue_count).should eql(previous_red_count + previous_blue_count + 1)
end
it "should return the given alternative for an existing user" do
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
alternative = ab_test('link_color', 'blue', 'red')
repeat_alternative = ab_test('link_color', 'blue', 'red')
alternative.should eql repeat_alternative
end
it 'should always return the winner if one is present' do
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
experiment.winner = "orange"
ab_test('link_color', 'blue', 'red').should == 'orange'
@@ -45,41 +45,41 @@
describe 'finished' do
it 'should increment the counter for the completed alternative' do
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
alternative_name = ab_test('link_color', 'blue', 'red')
- previous_completion_count = Multivariate::Alternative.find(alternative_name, 'link_color').completed_count
+ previous_completion_count = Split::Alternative.find(alternative_name, 'link_color').completed_count
finished('link_color')
- new_completion_count = Multivariate::Alternative.find(alternative_name, 'link_color').completed_count
+ new_completion_count = Split::Alternative.find(alternative_name, 'link_color').completed_count
new_completion_count.should eql(previous_completion_count + 1)
end
it "should clear out the user's participation from their session" do
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
alternative_name = ab_test('link_color', 'blue', 'red')
- previous_completion_count = Multivariate::Alternative.find(alternative_name, 'link_color').completed_count
+ previous_completion_count = Split::Alternative.find(alternative_name, 'link_color').completed_count
- session[:multivariate].should == {"link_color" => alternative_name}
+ session[:split].should == {"link_color" => alternative_name}
finished('link_color')
- session[:multivariate].should == {}
+ session[:split].should == {}
end
end
describe 'conversions' do
it 'should return a conversion rate for an alternative' do
- experiment = Multivariate::Experiment.find_or_create('link_color', 'blue', 'red')
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
alternative_name = ab_test('link_color', 'blue', 'red')
- previous_convertion_rate = Multivariate::Alternative.find(alternative_name, 'link_color').conversion_rate
+ previous_convertion_rate = Split::Alternative.find(alternative_name, 'link_color').conversion_rate
previous_convertion_rate.should eql(0.0)
finished('link_color')
- new_convertion_rate = Multivariate::Alternative.find(alternative_name, 'link_color').conversion_rate
+ new_convertion_rate = Split::Alternative.find(alternative_name, 'link_color').conversion_rate
new_convertion_rate.should eql(1.0)
end
end
View
2  spec/spec_helper.rb
@@ -1,6 +1,6 @@
require 'rubygems'
require 'bundler/setup'
-require 'multivariate'
+require 'split'
def session
@session ||= {}
View
10 multivariate.gemspec → split.gemspec
@@ -1,17 +1,17 @@
# -*- encoding: utf-8 -*-
$:.push File.expand_path("../lib", __FILE__)
-require "multivariate/version"
+require "split/version"
Gem::Specification.new do |s|
- s.name = "multivariate"
- s.version = Multivariate::VERSION
+ s.name = "split"
+ s.version = Split::VERSION
s.platform = Gem::Platform::RUBY
s.authors = ["Andrew Nesbitt"]
s.email = ["andrewnez@gmail.com"]
s.homepage = ""
- s.summary = %q{Rack based multivariate testing framework}
+ s.summary = %q{Rack based split testing framework}
- s.rubyforge_project = "multivariate"
+ s.rubyforge_project = "split"
s.files = `git ls-files`.split("\n")
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
Please sign in to comment.
Something went wrong with that request. Please try again.