split 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .bundle
3
3
  Gemfile.lock
4
4
  pkg/*
5
+ *.rbc
@@ -1,3 +1,18 @@
1
+ ## 0.2.2 (June 11, 2011)
2
+
3
+ Features:
4
+
5
+ - Updated redis-namespace requirement to 1.0.3
6
+ - Added a configuration object for changing options
7
+ - Robot regex can now be changed via a configuration options
8
+ - Added ability to ignore visits from specified IP addresses
9
+ - Dashboard now shows percentage improvement of alternatives compared to the control
10
+ - If the alternatives of an experiment are changed it resets the experiment and uses the new alternatives
11
+
12
+ Bugfixes:
13
+
14
+ - Saving an experiment multiple times no longer creates duplicate alternatives
15
+
1
16
  ## 0.2.1 (May 29, 2011)
2
17
 
3
18
  Bugfixes:
@@ -8,6 +8,8 @@ Split is heavily inspired by the Abingo and Vanity rails ab testing plugins and
8
8
 
9
9
  Split uses redis as a datastore.
10
10
 
11
+ Split only supports redis 2.0 or greater.
12
+
11
13
  If you're on OS X, Homebrew is the simplest way to install Redis:
12
14
 
13
15
  $ brew install redis
@@ -33,6 +35,14 @@ and require it in your project:
33
35
 
34
36
  require 'split'
35
37
 
38
+ ### SystemTimer
39
+
40
+ If you are using Redis on Ruby 1.8.x then you will likely want to also use the SystemTimer gem if you want to make sure the Redis client will not hang.
41
+
42
+ Put the following in your gemfile as well:
43
+
44
+ gem 'SystemTimer'
45
+
36
46
  ### Rails
37
47
 
38
48
  Split is autoloaded when rails starts up, as long as you've configured redis it will 'just work'.
@@ -114,6 +124,15 @@ You may want to password protect that page, you can do so with `Rack::Auth::Basi
114
124
 
115
125
  ## Configuration
116
126
 
127
+ You can override the default configuration options of Split like so:
128
+
129
+ Split.configure do |config|
130
+ config.robot_regex = /my_custom_robot_regex/
131
+ config.ignore_ip_addresses << '81.19.48.130'
132
+ end
133
+
134
+ ### Redis
135
+
117
136
  You may want to change the Redis host and port Split connects to, or
118
137
  set various other options at startup.
119
138
 
@@ -157,7 +176,7 @@ in your Redis server.
157
176
 
158
177
  Simply use the `Split.redis.namespace` accessor:
159
178
 
160
- Split.redis.namespace = "resque:GitHub"
179
+ Split.redis.namespace = "split:blog"
161
180
 
162
181
  We recommend sticking this in your initializer somewhere after Redis
163
182
  is configured.
@@ -169,7 +188,14 @@ Special thanks to the following people for submitting patches:
169
188
  * Lloyd Pick
170
189
  * Jeffery Chupp
171
190
 
172
- ## Note on Patches/Pull Requests
191
+ ## Development
192
+
193
+ Source hosted at [GitHub](http://github.com/andrew/split).
194
+ Report Issues/Feature requests on [GitHub Issues](http://github.com/andrew/split/issues).
195
+
196
+ Tests can be ran with `rake spec`
197
+
198
+ ### Note on Patches/Pull Requests
173
199
 
174
200
  * Fork the project.
175
201
  * Make your feature addition or bug fix.
@@ -3,10 +3,13 @@ require 'split/experiment'
3
3
  require 'split/alternative'
4
4
  require 'split/helper'
5
5
  require 'split/version'
6
+ require 'split/configuration'
6
7
  require 'redis/namespace'
7
8
 
8
9
  module Split
9
10
  extend self
11
+ attr_accessor :configuration
12
+
10
13
  # Accepts:
11
14
  # 1. A 'hostname:port' string
12
15
  # 2. A 'hostname:port:db' string (to select the Redis db)
@@ -41,8 +44,21 @@ module Split
41
44
  self.redis = 'localhost:6379'
42
45
  self.redis
43
46
  end
47
+
48
+ # Call this method to modify defaults in your initializers.
49
+ #
50
+ # @example
51
+ # Split.configure do |config|
52
+ # config.ignore_ips = '192.168.2.1'
53
+ # end
54
+ def configure
55
+ self.configuration ||= Configuration.new
56
+ yield(configuration)
57
+ end
44
58
  end
45
59
 
60
+ Split.configure {}
61
+
46
62
  if defined?(Rails)
47
63
  class ActionController::Base
48
64
  ActionController::Base.send :include, Split::Helper
@@ -12,6 +12,10 @@ module Split
12
12
  @completed_count = counters['completed_count'].to_i
13
13
  end
14
14
 
15
+ def to_s
16
+ name
17
+ end
18
+
15
19
  def increment_participation
16
20
  @participant_count +=1
17
21
  self.save
@@ -22,6 +26,10 @@ module Split
22
26
  self.save
23
27
  end
24
28
 
29
+ def control?
30
+ experiment.control.name == self.name
31
+ end
32
+
25
33
  def conversion_rate
26
34
  return 0 if participant_count.zero?
27
35
  (completed_count.to_f/participant_count.to_f)
@@ -71,6 +79,10 @@ module Split
71
79
  save
72
80
  end
73
81
 
82
+ def delete
83
+ Split.redis.del("#{experiment_name}:#{name}")
84
+ end
85
+
74
86
  def self.find(name, experiment_name)
75
87
  counters = Split.redis.hgetall "#{experiment_name}:#{name}"
76
88
  self.new(name, experiment_name, counters)
@@ -0,0 +1,11 @@
1
+ module Split
2
+ class Configuration
3
+ attr_accessor :robot_regex
4
+ attr_accessor :ignore_ip_addresses
5
+
6
+ def initialize()
7
+ @robot_regex = /\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)\b/i
8
+ @ignore_ip_addresses = []
9
+ end
10
+ end
11
+ end
@@ -20,7 +20,11 @@ module Split
20
20
  end
21
21
 
22
22
  def number_to_percentage(number, precision = 2)
23
- BigDecimal.new((number * 100).to_s).round(precision).to_f
23
+ round(number * 100)
24
+ end
25
+
26
+ def round(number, precision = 2)
27
+ BigDecimal.new(number.to_s).round(precision).to_f
24
28
  end
25
29
  end
26
30
 
@@ -33,7 +37,6 @@ module Split
33
37
  @experiment = Split::Experiment.find(params[:experiment])
34
38
  @alternative = Split::Alternative.find(params[:alternative], params[:experiment])
35
39
  @experiment.winner = @alternative.name
36
- @experiment.save
37
40
  redirect url('/')
38
41
  end
39
42
 
@@ -5,3 +5,11 @@ function confirmReset() {
5
5
  else
6
6
  return false;
7
7
  }
8
+
9
+ function confirmWinner() {
10
+ var agree=confirm("This will now be returned for all users. Are you sure?");
11
+ if (agree)
12
+ return true;
13
+ else
14
+ return false;
15
+ }
@@ -35,6 +35,7 @@ h2{
35
35
  }
36
36
 
37
37
  .reset{
38
+ display:inline-block;
38
39
  font-size:10px;
39
40
  line-height:38px;
40
41
  }
@@ -47,4 +48,11 @@ h2{
47
48
  clear:both;
48
49
  }
49
50
 
51
+ .worse, .better{
52
+ color:#F00;
53
+ font-size:10px;
54
+ }
50
55
 
56
+ .better{
57
+ color:#00D500;
58
+ }
@@ -25,8 +25,21 @@
25
25
  <td><%= alternative.participant_count %></td>
26
26
  <td><%= alternative.participant_count - alternative.completed_count %></td>
27
27
  <td><%= alternative.completed_count %></td>
28
- <td><%= number_to_percentage(alternative.conversion_rate) %>%</td>
29
- <td><%= alternative.z_score %></td>
28
+ <td>
29
+ <%= number_to_percentage(alternative.conversion_rate) %>%
30
+ <% if experiment.control.conversion_rate > 0 && !alternative.control? %>
31
+ <% if alternative.conversion_rate > experiment.control.conversion_rate %>
32
+ <span class='better'>
33
+ +<%= number_to_percentage((alternative.conversion_rate/experiment.control.conversion_rate)-1) %>%
34
+ </span>
35
+ <% elsif alternative.conversion_rate < experiment.control.conversion_rate %>
36
+ <span class='worse'>
37
+ <%= number_to_percentage((alternative.conversion_rate/experiment.control.conversion_rate)-1) %>%
38
+ </span>
39
+ <% end %>
40
+ <% end %>
41
+ </td>
42
+ <td><%= round(alternative.z_score, 3) %></td>
30
43
  <td>
31
44
  <% if experiment.winner %>
32
45
  <% if experiment.winner.name == alternative.name %>
@@ -35,7 +48,7 @@
35
48
  Loser
36
49
  <% end %>
37
50
  <% else %>
38
- <form action="<%= url experiment.name %>" method='post'>
51
+ <form action="<%= url experiment.name %>" method='post' onclick="return confirmWinner()">
39
52
  <input type='hidden' name='alternative' value='<%= alternative.name %>'>
40
53
  <input type="submit" value="Use this">
41
54
  </form>
@@ -38,15 +38,19 @@ module Split
38
38
  end
39
39
 
40
40
  def reset
41
- alternatives.each do |alternative|
42
- alternative.reset
43
- end
41
+ alternatives.each(&:reset)
44
42
  reset_winner
45
43
  end
46
44
 
45
+ def new_record?
46
+ !Split.redis.exists(name)
47
+ end
48
+
47
49
  def save
48
- Split.redis.sadd(:experiments, name)
49
- @alternative_names.reverse.each {|a| Split.redis.lpush(name, a) }
50
+ if new_record?
51
+ Split.redis.sadd(:experiments, name)
52
+ @alternative_names.reverse.each {|a| Split.redis.lpush(name, a) }
53
+ end
50
54
  end
51
55
 
52
56
  def self.load_alternatives_for(name)
@@ -75,12 +79,20 @@ module Split
75
79
 
76
80
  def self.find_or_create(name, *alternatives)
77
81
  if Split.redis.exists(name)
78
- return self.new(name, *load_alternatives_for(name))
82
+ if load_alternatives_for(name) == alternatives
83
+ experiment = self.new(name, *load_alternatives_for(name))
84
+ else
85
+ exp = self.new(name, *load_alternatives_for(name))
86
+ exp.reset
87
+ exp.alternatives.each(&:delete)
88
+ experiment = self.new(name, *alternatives)
89
+ experiment.save
90
+ end
79
91
  else
80
92
  experiment = self.new(name, *alternatives)
81
93
  experiment.save
82
- return experiment
83
94
  end
95
+ return experiment
84
96
  end
85
97
  end
86
98
  end
@@ -8,7 +8,7 @@ module Split
8
8
  return forced_alternative
9
9
  end
10
10
 
11
- ab_user[experiment_name] = experiment.control.name if is_robot?
11
+ ab_user[experiment_name] = experiment.control.name if exclude_visitor?
12
12
 
13
13
  if ab_user[experiment_name]
14
14
  return ab_user[experiment_name]
@@ -21,7 +21,7 @@ module Split
21
21
  end
22
22
 
23
23
  def finished(experiment_name)
24
- return if is_robot?
24
+ return if exclude_visitor?
25
25
  alternative_name = ab_user[experiment_name]
26
26
  alternative = Split::Alternative.find(alternative_name, experiment_name)
27
27
  alternative.increment_completion
@@ -36,8 +36,20 @@ module Split
36
36
  session[:split] ||= {}
37
37
  end
38
38
 
39
+ def exclude_visitor?
40
+ is_robot? or is_ignored_ip_address?
41
+ end
42
+
39
43
  def is_robot?
40
- request.user_agent =~ /\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)\b/i
44
+ request.user_agent =~ Split.configuration.robot_regex
45
+ end
46
+
47
+ def is_ignored_ip_address?
48
+ if Split.configuration.ignore_ip_addresses.any?
49
+ Split.configuration.ignore_ip_addresses.include?(request.ip)
50
+ else
51
+ false
52
+ end
41
53
  end
42
54
  end
43
55
  end
@@ -1,3 +1,3 @@
1
1
  module Split
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
@@ -50,7 +50,7 @@ describe Split::Alternative do
50
50
  alternative.increment_completion
51
51
  alternative.completed_count.should eql(old_completed_count+1)
52
52
  end
53
-
53
+
54
54
  it "can be reset" do
55
55
  alternative = Split::Alternative.new('Basket', 'basket_text', {'participant_count' => 10, 'completed_count' =>4})
56
56
  alternative.save
@@ -59,6 +59,15 @@ describe Split::Alternative do
59
59
  alternative.completed_count.should eql(0)
60
60
  end
61
61
 
62
+ it "should know if it is the control of an experiment" do
63
+ experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
64
+ experiment.save
65
+ alternative = Split::Alternative.find('Basket', 'basket_text')
66
+ alternative.control?.should be_true
67
+ alternative = Split::Alternative.find('Cart', 'basket_text')
68
+ alternative.control?.should be_false
69
+ end
70
+
62
71
  describe 'conversion rate' do
63
72
  it "should be 0 if there are no conversions" do
64
73
  alternative = Split::Alternative.new('Basket', 'basket_text')
@@ -84,7 +93,7 @@ describe Split::Alternative do
84
93
  alternative = Split::Alternative.find('red', 'link_color')
85
94
  alternative.z_score.should eql(0)
86
95
  end
87
-
96
+
88
97
  it "should be N/A for the control" do
89
98
  experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
90
99
 
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Split::Configuration do
4
+ it "should provide default values" do
5
+ config = Split::Configuration.new
6
+
7
+ config.ignore_ip_addresses.should eql([])
8
+ config.robot_regex.should eql(/\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)\b/i)
9
+ end
10
+ end
@@ -0,0 +1,48 @@
1
+ require 'spec_helper'
2
+ require 'rack/test'
3
+ require 'split/dashboard'
4
+
5
+ describe Split::Dashboard do
6
+ include Rack::Test::Methods
7
+
8
+ def app
9
+ @app ||= Split::Dashboard
10
+ end
11
+
12
+ it "should respond to /" do
13
+ get '/'
14
+ last_response.should be_ok
15
+ end
16
+
17
+ it "should reset an experiment" do
18
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
19
+ red = Split::Alternative.find('red', 'link_color').participant_count
20
+
21
+ red = Split::Alternative.find('red', 'link_color')
22
+ blue = Split::Alternative.find('blue', 'link_color')
23
+ red.participant_count = 5
24
+ red.save
25
+ blue.participant_count = 6
26
+ blue.save
27
+
28
+ post '/reset/link_color'
29
+
30
+ last_response.should be_redirect
31
+
32
+ new_red_count = Split::Alternative.find('red', 'link_color').participant_count
33
+ new_blue_count = Split::Alternative.find('blue', 'link_color').participant_count
34
+
35
+ new_blue_count.should eql(0)
36
+ new_red_count.should eql(0)
37
+ end
38
+
39
+ it "should mark an alternative as the winner" do
40
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
41
+ experiment.winner.should be_nil
42
+
43
+ post '/link_color', :alternative => 'red'
44
+
45
+ last_response.should be_redirect
46
+ experiment.winner.name.should eql('red')
47
+ end
48
+ end
@@ -8,18 +8,39 @@ describe Split::Experiment do
8
8
  experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
9
9
  experiment.name.should eql('basket_text')
10
10
  end
11
-
11
+
12
12
  it "should have alternatives" do
13
13
  experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
14
14
  experiment.alternatives.length.should be 2
15
15
  end
16
-
16
+
17
17
  it "should save to redis" do
18
18
  experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
19
19
  experiment.save
20
20
  Split.redis.exists('basket_text').should be true
21
21
  end
22
-
22
+
23
+ it "should not create duplicates when saving multiple times" do
24
+ experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
25
+ experiment.save
26
+ experiment.save
27
+ Split.redis.exists('basket_text').should be true
28
+ Split.redis.lrange('basket_text', 0, -1).should eql(['Basket', "Cart"])
29
+ end
30
+
31
+ describe 'new record?' do
32
+ it "should know if it hasn't been saved yet" do
33
+ experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
34
+ experiment.new_record?.should be_true
35
+ end
36
+
37
+ it "should know if it has been saved yet" do
38
+ experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
39
+ experiment.save
40
+ experiment.new_record?.should be_false
41
+ end
42
+ end
43
+
23
44
  it "should return an existing experiment" do
24
45
  experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
25
46
  experiment.save
@@ -101,5 +122,17 @@ describe Split::Experiment do
101
122
  experiment.next_alternative.name.should eql('green')
102
123
  end
103
124
  end
104
- end
105
125
 
126
+ describe 'changing an existing experiment' do
127
+ it "should reset an experiment if it is loaded with different alternatives" do
128
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red', 'green')
129
+ blue = Split::Alternative.find('blue', 'link_color')
130
+ blue.participant_count = 5
131
+ blue.save
132
+ same_experiment = Split::Experiment.find_or_create('link_color', 'blue', 'yellow', 'orange')
133
+ same_experiment.alternatives.map(&:name).should eql(['blue', 'yellow', 'orange'])
134
+ new_blue = Split::Alternative.find('blue', 'link_color')
135
+ new_blue.participant_count.should eql(0)
136
+ end
137
+ end
138
+ end
@@ -1,5 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
+ # TODO change some of these tests to use Rack::Test
4
+
3
5
  describe Split::Helper do
4
6
  include Split::Helper
5
7
 
@@ -97,6 +99,50 @@ describe Split::Helper do
97
99
  @request = OpenStruct.new(:user_agent => 'Googlebot/2.1 (+http://www.google.com/bot.html)')
98
100
  end
99
101
 
102
+ describe 'ab_test' do
103
+ it 'should return the control' do
104
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
105
+ alternative = ab_test('link_color', 'blue', 'red')
106
+ alternative.should eql experiment.control.name
107
+ end
108
+
109
+ it "should not increment the participation count" do
110
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
111
+
112
+ previous_red_count = Split::Alternative.find('red', 'link_color').participant_count
113
+ previous_blue_count = Split::Alternative.find('blue', 'link_color').participant_count
114
+
115
+ ab_test('link_color', 'blue', 'red')
116
+
117
+ new_red_count = Split::Alternative.find('red', 'link_color').participant_count
118
+ new_blue_count = Split::Alternative.find('blue', 'link_color').participant_count
119
+
120
+ (new_red_count + new_blue_count).should eql(previous_red_count + previous_blue_count)
121
+ end
122
+ end
123
+ describe 'finished' do
124
+ it "should not increment the completed count" do
125
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
126
+ alternative_name = ab_test('link_color', 'blue', 'red')
127
+
128
+ previous_completion_count = Split::Alternative.find(alternative_name, 'link_color').completed_count
129
+
130
+ finished('link_color')
131
+
132
+ new_completion_count = Split::Alternative.find(alternative_name, 'link_color').completed_count
133
+
134
+ new_completion_count.should eql(previous_completion_count)
135
+ end
136
+ end
137
+ end
138
+ describe 'when ip address is ignored' do
139
+ before(:each) do
140
+ @request = OpenStruct.new(:ip => '81.19.48.130')
141
+ Split.configure do |c|
142
+ c.ignore_ip_addresses << '81.19.48.130'
143
+ end
144
+ end
145
+
100
146
  describe 'ab_test' do
101
147
  it 'should return the control' do
102
148
  experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
@@ -14,5 +14,6 @@ end
14
14
  def request(ua = 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_6; de-de) AppleWebKit/533.20.25 (KHTML, like Gecko) Version/5.0.4 Safari/533.20.27')
15
15
  r = OpenStruct.new
16
16
  r.user_agent = ua
17
+ r.ip = '192.168.1.1'
17
18
  @request ||= r
18
19
  end
@@ -19,9 +19,10 @@ Gem::Specification.new do |s|
19
19
  s.require_paths = ["lib"]
20
20
 
21
21
  s.add_dependency(%q<redis>, ["~> 2.1"])
22
- s.add_dependency(%q<redis-namespace>, ["~> 0.10.0"])
22
+ s.add_dependency(%q<redis-namespace>, ["~> 1.0.3"])
23
23
  s.add_dependency(%q<sinatra>, ["~> 1.2.6"])
24
24
 
25
25
  # Development Dependencies
26
26
  s.add_development_dependency(%q<rspec>, ["~> 2.6"])
27
+ s.add_development_dependency(%q<rack-test>, ["~> 0.6"])
27
28
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: split
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 2
9
- - 1
10
- version: 0.2.1
9
+ - 2
10
+ version: 0.2.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Andrew Nesbitt
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-05-29 00:00:00 +01:00
18
+ date: 2011-06-12 00:00:00 +01:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -41,12 +41,12 @@ dependencies:
41
41
  requirements:
42
42
  - - ~>
43
43
  - !ruby/object:Gem::Version
44
- hash: 55
44
+ hash: 17
45
45
  segments:
46
+ - 1
46
47
  - 0
47
- - 10
48
- - 0
49
- version: 0.10.0
48
+ - 3
49
+ version: 1.0.3
50
50
  type: :runtime
51
51
  version_requirements: *id002
52
52
  - !ruby/object:Gem::Dependency
@@ -80,6 +80,21 @@ dependencies:
80
80
  version: "2.6"
81
81
  type: :development
82
82
  version_requirements: *id004
83
+ - !ruby/object:Gem::Dependency
84
+ name: rack-test
85
+ prerelease: false
86
+ requirement: &id005 !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ~>
90
+ - !ruby/object:Gem::Version
91
+ hash: 7
92
+ segments:
93
+ - 0
94
+ - 6
95
+ version: "0.6"
96
+ type: :development
97
+ version_requirements: *id005
83
98
  description:
84
99
  email:
85
100
  - andrewnez@gmail.com
@@ -98,6 +113,7 @@ files:
98
113
  - Rakefile
99
114
  - lib/split.rb
100
115
  - lib/split/alternative.rb
116
+ - lib/split/configuration.rb
101
117
  - lib/split/dashboard.rb
102
118
  - lib/split/dashboard/public/dashboard.js
103
119
  - lib/split/dashboard/public/reset.css
@@ -108,6 +124,8 @@ files:
108
124
  - lib/split/helper.rb
109
125
  - lib/split/version.rb
110
126
  - spec/alternative_spec.rb
127
+ - spec/configuration_spec.rb
128
+ - spec/dashboard_spec.rb
111
129
  - spec/experiment_spec.rb
112
130
  - spec/helper_spec.rb
113
131
  - spec/spec_helper.rb
@@ -148,6 +166,8 @@ specification_version: 3
148
166
  summary: Rack based split testing framework
149
167
  test_files:
150
168
  - spec/alternative_spec.rb
169
+ - spec/configuration_spec.rb
170
+ - spec/dashboard_spec.rb
151
171
  - spec/experiment_spec.rb
152
172
  - spec/helper_spec.rb
153
173
  - spec/spec_helper.rb