split 0.3.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby-18mode # JRuby in 1.8 mode
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-18mode
9
+ - rbx-19mode
@@ -1,3 +1,20 @@
1
+ ## 0.4.0 (March 7, 2012)
2
+
3
+ **IMPORTANT**
4
+
5
+ If using ruby 1.8.x and weighted alternatives you should always pass the control alternative through as the second argument with any other alternatives as a third argument because the order of the hash is not preserved in ruby 1.8, ruby 1.9 users are not affected by this bug.
6
+
7
+ Features:
8
+
9
+ - Experiments now record when they were started (@vrish88, #35)
10
+ - Old versions of experiments in sessions are now cleaned up
11
+ - Avoid users participating in multiple experiments at once (#21)
12
+
13
+ Bugfixes:
14
+
15
+ - Overriding alternatives doesn't work for weighted alternatives (@layflags, #34)
16
+ - confidence_level helper should handle tiny z-scores (#23)
17
+
1
18
  ## 0.3.3 (February 16, 2012)
2
19
 
3
20
  Bugfixes:
@@ -105,12 +105,14 @@ Perhaps you only want to show an alternative to 10% of your visitors because it
105
105
 
106
106
  To do this you can pass a weight with each alternative in the following ways:
107
107
 
108
- ab_test('homepage design', 'Old' => 20, 'New' => 2)
108
+ ab_test('homepage design', {'Old' => 20}, {'New' => 2})
109
109
 
110
110
  ab_test('homepage design', 'Old', {'New' => 0.1})
111
111
 
112
112
  ab_test('homepage design', {'Old' => 10}, 'New')
113
113
 
114
+ Note: If using ruby 1.8.x and weighted alternatives you should always pass the control alternative through as the second argument with any other alternatives as a third argument because the order of the hash is not preserved in ruby 1.8, ruby 1.9.1+ users are not affected by this bug.
115
+
114
116
  This will only show the new alternative to visitors 1 in 10 times, the default weight for an alternative is 1.
115
117
 
116
118
  ### Overriding alternatives
@@ -134,6 +136,16 @@ To stop this behaviour you can pass the following option to the `finished` metho
134
136
 
135
137
  The user will then always see the alternative they started with.
136
138
 
139
+ ### Multiple experiments at once
140
+
141
+ By default Split will avoid users participating in multiple experiments at once. This means you are less likely to skew results by adding in more variation to your tests.
142
+
143
+ To stop this behaviour and allow users to participate in multiple experiments at once enable the `allow_multiple_experiments` config option like so:
144
+
145
+ Split.configure do |config|
146
+ config.allow_multiple_experiments = true
147
+ end
148
+
137
149
  ## Web Interface
138
150
 
139
151
  Split comes with a Sinatra-based front end to get an overview of how your experiments are doing.
@@ -169,6 +181,7 @@ You can override the default configuration options of Split like so:
169
181
  config.ignore_ip_addresses << '81.19.48.130'
170
182
  config.db_failover = true # handle redis errors gracefully
171
183
  config.db_failover_on_db_error = proc{|error| Rails.logger.error(error.message) }
184
+ config.allow_multiple_experiments = true
172
185
  end
173
186
 
174
187
  ### DB failover solution
@@ -251,6 +264,8 @@ Report Issues/Feature requests on [GitHub Issues](http://github.com/andrew/split
251
264
 
252
265
  Tests can be ran with `rake spec`
253
266
 
267
+ [![Build Status](https://secure.travis-ci.org/andrew/split.png?branch=master)](http://travis-ci.org/andrew/split)
268
+
254
269
  ### Note on Patches/Pull Requests
255
270
 
256
271
  * Fork the project.
@@ -4,12 +4,14 @@ module Split
4
4
  attr_accessor :ignore_ip_addresses
5
5
  attr_accessor :db_failover
6
6
  attr_accessor :db_failover_on_db_error
7
+ attr_accessor :allow_multiple_experiments
7
8
 
8
9
  def initialize
9
10
  @robot_regex = /\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)\b/i
10
11
  @ignore_ip_addresses = []
11
12
  @db_failover = false
12
13
  @db_failover_on_db_error = proc{|error|} # e.g. use Rails logger here
14
+ @allow_multiple_experiments = false
13
15
  end
14
16
  end
15
17
  end
@@ -1,6 +1,7 @@
1
1
  require 'sinatra/base'
2
2
  require 'split'
3
3
  require 'bigdecimal'
4
+ require 'split/dashboard/helpers'
4
5
 
5
6
  module Split
6
7
  class Dashboard < Sinatra::Base
@@ -11,50 +12,7 @@ module Split
11
12
  set :static, true
12
13
  set :method_override, true
13
14
 
14
- helpers do
15
- def url(*path_parts)
16
- [ path_prefix, path_parts ].join("/").squeeze('/')
17
- end
18
-
19
- def path_prefix
20
- request.env['SCRIPT_NAME']
21
- end
22
-
23
- def number_to_percentage(number, precision = 2)
24
- round(number * 100)
25
- end
26
-
27
- def round(number, precision = 2)
28
- BigDecimal.new(number.to_s).round(precision).to_f
29
- end
30
-
31
- def confidence_level(z_score)
32
- z = z_score.to_f
33
- if z > 0.0
34
- if z < 1.96
35
- 'no confidence'
36
- elsif z < 2.57
37
- '95% confidence'
38
- elsif z < 3.29
39
- '99% confidence'
40
- else
41
- '99.9% confidence'
42
- end
43
- elsif z < 0.0
44
- if z > -1.96
45
- 'no confidence'
46
- elsif z > -2.57
47
- '95% confidence'
48
- elsif z > -3.29
49
- '99% confidence'
50
- else
51
- '99.9% confidence'
52
- end
53
- else
54
- "No Change"
55
- end
56
- end
57
- end
15
+ helpers Split::DashboardHelpers
58
16
 
59
17
  get '/' do
60
18
  @experiments = Split::Experiment.all
@@ -0,0 +1,48 @@
1
+ module Split
2
+ module DashboardHelpers
3
+ def url(*path_parts)
4
+ [ path_prefix, path_parts ].join("/").squeeze('/')
5
+ end
6
+
7
+ def path_prefix
8
+ request.env['SCRIPT_NAME']
9
+ end
10
+
11
+ def number_to_percentage(number, precision = 2)
12
+ round(number * 100)
13
+ end
14
+
15
+ def round(number, precision = 2)
16
+ BigDecimal.new(number.to_s).round(precision).to_f
17
+ end
18
+
19
+ def confidence_level(z_score)
20
+ return z_score if z_score.is_a? String
21
+
22
+ z = round(z_score.to_f, 3)
23
+ if z > 0.0
24
+ if z < 1.96
25
+ 'no confidence'
26
+ elsif z < 2.57
27
+ '95% confidence'
28
+ elsif z < 3.29
29
+ '99% confidence'
30
+ else
31
+ '99.9% confidence'
32
+ end
33
+ elsif z < 0.0
34
+ if z > -1.96
35
+ 'no confidence'
36
+ elsif z > -2.57
37
+ '95% confidence'
38
+ elsif z > -3.29
39
+ '99% confidence'
40
+ else
41
+ '99.9% confidence'
42
+ end
43
+ else
44
+ "No Change"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -218,6 +218,11 @@ body {
218
218
  float:right;
219
219
  }
220
220
 
221
+ .inline-controls small {
222
+ color: #888;
223
+ font-size: 11px;
224
+ }
225
+
221
226
  .inline-controls form {
222
227
  display: inline-block;
223
228
  font-size: 10px;
@@ -1,7 +1,12 @@
1
1
  <div class="experiment">
2
2
  <div class="experiment-header">
3
- <h2>Experiment: <%= experiment.name %> <% if experiment.version > 1 %><span class='version'>v<%= experiment.version %></span><% end %></h2>
3
+ <h2>
4
+ Experiment: <%= experiment.name %>
5
+ <% if experiment.version > 1 %><span class='version'>v<%= experiment.version %></span><% end %>
6
+ </h2>
7
+
4
8
  <div class='inline-controls'>
9
+ <small><%= experiment.start_time ? experiment.start_time.strftime('%Y-%m-%d') : 'Unknown' %></small>
5
10
  <form action="<%= url "/reset/#{experiment.name}" %>" method='post' onclick="return confirmReset()">
6
11
  <input type="submit" value="Reset Data">
7
12
  </form>
@@ -26,11 +26,15 @@ module Split
26
26
  Split.redis.hdel(:experiment_winner, name)
27
27
  end
28
28
 
29
-
30
29
  def winner=(winner_name)
31
30
  Split.redis.hset(:experiment_winner, name, winner_name.to_s)
32
31
  end
33
32
 
33
+ def start_time
34
+ t = Split.redis.hget(:experiment_start_times, @name)
35
+ Time.parse(t) if t
36
+ end
37
+
34
38
  def alternatives
35
39
  @alternatives.dup
36
40
  end
@@ -92,6 +96,7 @@ module Split
92
96
  def save
93
97
  if new_record?
94
98
  Split.redis.sadd(:experiments, name)
99
+ Split.redis.hset(:experiment_start_times, @name, Time.now)
95
100
  @alternatives.reverse.each {|a| Split.redis.lpush(name, a.name) }
96
101
  end
97
102
  end
@@ -1,17 +1,19 @@
1
1
  module Split
2
2
  module Helper
3
- def ab_test(experiment_name, *alternatives)
3
+ def ab_test(experiment_name, control, *alternatives)
4
+ puts 'WARNING: You should always pass the control alternative through as the second argument with any other alternatives as the third because the order of the hash is not preserved in ruby 1.8' if RUBY_VERSION.match(/1\.8/) && alternatives.length.zero?
4
5
  begin
5
- experiment = Split::Experiment.find_or_create(experiment_name, *alternatives)
6
+ experiment = Split::Experiment.find_or_create(experiment_name, *([control] + alternatives))
6
7
  if experiment.winner
7
8
  ret = experiment.winner.name
8
9
  else
9
- if forced_alternative = override(experiment.name, alternatives)
10
+ if forced_alternative = override(experiment.name, experiment.alternative_names)
10
11
  ret = forced_alternative
11
12
  else
12
- begin_experiment(experiment, experiment.control.name) if exclude_visitor?
13
+ clean_old_versions(experiment)
14
+ begin_experiment(experiment) if exclude_visitor? or not_allowed_to_test?(experiment.key)
13
15
 
14
- if ab_user[experiment.key]
16
+ if ab_user[experiment.key]
15
17
  ret = ab_user[experiment.key]
16
18
  else
17
19
  alternative = experiment.next_alternative
@@ -24,7 +26,7 @@ module Split
24
26
  rescue Errno::ECONNREFUSED => e
25
27
  raise unless Split.configuration.db_failover
26
28
  Split.configuration.db_failover_on_db_error.call(e)
27
- ret = Hash === (a0 = alternatives.first) ? a0.keys.first : a0
29
+ ret = Hash === control ? control.keys.first : control
28
30
  end
29
31
  if block_given?
30
32
  if defined?(capture) # a block in a rails view
@@ -56,7 +58,8 @@ module Split
56
58
  params[experiment_name] if defined?(params) && alternatives.include?(params[experiment_name])
57
59
  end
58
60
 
59
- def begin_experiment(experiment, alternative_name)
61
+ def begin_experiment(experiment, alternative_name = nil)
62
+ alternative_name ||= experiment.control.name
60
63
  ab_user[experiment.key] = alternative_name
61
64
  end
62
65
 
@@ -68,6 +71,28 @@ module Split
68
71
  is_robot? or is_ignored_ip_address?
69
72
  end
70
73
 
74
+ def not_allowed_to_test?(experiment_key)
75
+ !Split.configuration.allow_multiple_experiments && doing_other_tests?(experiment_key)
76
+ end
77
+
78
+ def doing_other_tests?(experiment_key)
79
+ ab_user.keys.reject{|k| k == experiment_key}.length > 0
80
+ end
81
+
82
+ def clean_old_versions(experiment)
83
+ old_versions(experiment).each do |old_key|
84
+ ab_user.delete old_key
85
+ end
86
+ end
87
+
88
+ def old_versions(experiment)
89
+ if experiment.version > 0
90
+ ab_user.keys.select{|k| k.match(Regexp.new(experiment.name))}.reject{|k| k == experiment.key}
91
+ else
92
+ []
93
+ end
94
+ end
95
+
71
96
  def is_robot?
72
97
  request.user_agent =~ Split.configuration.robot_regex
73
98
  end
@@ -1,3 +1,3 @@
1
1
  module Split
2
- VERSION = "0.3.3"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -8,5 +8,6 @@ describe Split::Configuration do
8
8
  config.robot_regex.should eql(/\b(Baidu|Gigabot|Googlebot|libwww-perl|lwp-trivial|msnbot|SiteUptime|Slurp|WordPress|ZIBB|ZyBorg)\b/i)
9
9
  config.db_failover.should be_false
10
10
  config.db_failover_on_db_error.should be_a Proc
11
+ config.allow_multiple_experiments.should be_false
11
12
  end
12
13
  end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'split/dashboard/helpers'
3
+
4
+ include Split::DashboardHelpers
5
+
6
+ describe Split::DashboardHelpers do
7
+ describe 'confidence_level' do
8
+ it 'should handle very small numbers' do
9
+ confidence_level(0.000000000000006).should eql('No Change')
10
+ end
11
+ end
12
+ end
@@ -51,4 +51,26 @@ describe Split::Dashboard do
51
51
  last_response.should be_redirect
52
52
  experiment.winner.name.should eql('red')
53
53
  end
54
+
55
+ it "should display the start date" do
56
+ experiment_start_time = Time.parse('2011-07-07')
57
+ Time.stub(:now => experiment_start_time)
58
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
59
+
60
+ get '/'
61
+
62
+ last_response.body.should include('<small>2011-07-07</small>')
63
+ end
64
+
65
+ it "should handle experiments without a start date" do
66
+ experiment_start_time = Time.parse('2011-07-07')
67
+ Time.stub(:now => experiment_start_time)
68
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
69
+
70
+ Split.redis.hdel(:experiment_start_times, experiment.name)
71
+
72
+ get '/'
73
+
74
+ last_response.body.should include('<small>Unknown</small>')
75
+ end
54
76
  end
@@ -20,6 +20,26 @@ describe Split::Experiment do
20
20
  Split.redis.exists('basket_text').should be true
21
21
  end
22
22
 
23
+ it "should save the start time to redis" do
24
+ experiment_start_time = Time.parse("Sat Mar 03 14:01:03")
25
+ Time.stub(:now => experiment_start_time)
26
+ experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
27
+ experiment.save
28
+
29
+ Split::Experiment.find('basket_text').start_time.should == experiment_start_time
30
+ end
31
+
32
+ it "should handle not having a start time" do
33
+ experiment_start_time = Time.parse("Sat Mar 03 14:01:03")
34
+ Time.stub(:now => experiment_start_time)
35
+ experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
36
+ experiment.save
37
+
38
+ Split.redis.hdel(:experiment_start_times, experiment.name)
39
+
40
+ Split::Experiment.find('basket_text').start_time.should == nil
41
+ end
42
+
23
43
  it "should not create duplicates when saving multiple times" do
24
44
  experiment = Split::Experiment.new('basket_text', 'Basket', "Cart")
25
45
  experiment.save
@@ -50,6 +50,13 @@ describe Split::Helper do
50
50
  @params = {'link_color' => 'blue'}
51
51
  alternative = ab_test('link_color', 'blue', 'red')
52
52
  alternative.should eql('blue')
53
+ alternative = ab_test('link_color', {'blue' => 1}, 'red' => 5)
54
+ alternative.should eql('blue')
55
+ @params = {'link_color' => 'red'}
56
+ alternative = ab_test('link_color', 'blue', 'red')
57
+ alternative.should eql('red')
58
+ alternative = ab_test('link_color', {'blue' => 5}, 'red' => 1)
59
+ alternative.should eql('red')
53
60
  end
54
61
 
55
62
  it "should allow passing a block" do
@@ -58,17 +65,37 @@ describe Split::Helper do
58
65
  ret.should eql("shared/#{alt}")
59
66
  end
60
67
 
61
- it "should allow the share of visitors see an alternative to be specificed" do
68
+ it "should allow the share of visitors see an alternative to be specificed" do
62
69
  ab_test('link_color', {'blue' => 0.8}, {'red' => 20})
63
70
  ['red', 'blue'].should include(ab_user['link_color'])
64
71
  end
65
72
 
66
- it "should allow alternative weighting interface as a single hash" do
67
- ab_test('link_color', 'blue' => 0.01, 'red' => 0.2)
73
+ it "should allow alternative weighting interface as a single hash" do
74
+ ab_test('link_color', {'blue' => 0.01}, 'red' => 0.2)
68
75
  experiment = Split::Experiment.find('link_color')
69
76
  experiment.alternative_names.should eql(['blue', 'red'])
70
77
  end
71
78
 
79
+ it "should only let a user participate in one experiment at a time" do
80
+ ab_test('link_color', 'blue', 'red')
81
+ ab_test('button_size', 'small', 'big')
82
+ ab_user['button_size'].should eql('small')
83
+ big = Split::Alternative.new('big', 'button_size')
84
+ big.participant_count.should eql(0)
85
+ small = Split::Alternative.new('small', 'button_size')
86
+ small.participant_count.should eql(0)
87
+ end
88
+
89
+ it "should let a user participate in many experiment with allow_multiple_experiments option" do
90
+ Split.configure do |config|
91
+ config.allow_multiple_experiments = true
92
+ end
93
+ link_color = ab_test('link_color', 'blue', 'red')
94
+ button_size = ab_test('button_size', 'small', 'big')
95
+ ab_user['button_size'].should eql(button_size)
96
+ button_size_alt = Split::Alternative.new(button_size, 'button_size')
97
+ button_size_alt.participant_count.should eql(1)
98
+ end
72
99
  end
73
100
 
74
101
  describe 'finished' do
@@ -258,6 +285,22 @@ describe Split::Helper do
258
285
  new_alternative.participant_count.should eql(1)
259
286
  end
260
287
 
288
+ it "should cleanup old versions of experiments from the session" do
289
+ experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
290
+ alternative_name = ab_test('link_color', 'blue', 'red')
291
+ session[:split].should eql({'link_color' => alternative_name})
292
+ alternative = Split::Alternative.new(alternative_name, 'link_color')
293
+ alternative.participant_count.should eql(1)
294
+
295
+ experiment.reset
296
+ experiment.version.should eql(1)
297
+ alternative = Split::Alternative.new(alternative_name, 'link_color')
298
+ alternative.participant_count.should eql(0)
299
+
300
+ new_alternative_name = ab_test('link_color', 'blue', 'red')
301
+ session[:split].should eql({'link_color:1' => new_alternative_name})
302
+ end
303
+
261
304
  it "should only count completion of users on the current version" do
262
305
  experiment = Split::Experiment.find_or_create('link_color', 'blue', 'red')
263
306
  alternative_name = ab_test('link_color', 'blue', 'red')
@@ -329,14 +372,12 @@ describe Split::Helper do
329
372
  ab_test('link_color', 'blue', 'red')
330
373
  end
331
374
  it 'should always use first alternative' do
332
- 10.times do
333
- ab_test('link_color', 'blue', 'red').should eq('blue')
334
- ab_test('link_color', 'blue' => 0.01, 'red' => 0.2).should eq('blue')
335
- ab_test('link_color', {'blue' => 0.8}, {'red' => 20}).should eq('blue')
336
- ab_test('link_color', 'blue', 'red') do |alternative|
337
- "shared/#{alternative}"
338
- end.should eq('shared/blue')
339
- end
375
+ ab_test('link_color', 'blue', 'red').should eq('blue')
376
+ ab_test('link_color', {'blue' => 0.01}, 'red' => 0.2).should eq('blue')
377
+ ab_test('link_color', {'blue' => 0.8}, {'red' => 20}).should eq('blue')
378
+ ab_test('link_color', 'blue', 'red') do |alternative|
379
+ "shared/#{alternative}"
380
+ end.should eq('shared/blue')
340
381
  end
341
382
  end
342
383
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: split
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.3
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-02-16 00:00:00.000000000Z
12
+ date: 2012-03-07 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: redis
16
- requirement: &70173932070820 !ruby/object:Gem::Requirement
16
+ requirement: &70291376942100 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '2.1'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70173932070820
24
+ version_requirements: *70291376942100
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: redis-namespace
27
- requirement: &70173932070060 !ruby/object:Gem::Requirement
27
+ requirement: &70291376938960 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.0.3
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70173932070060
35
+ version_requirements: *70291376938960
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: sinatra
38
- requirement: &70173932069160 !ruby/object:Gem::Requirement
38
+ requirement: &70291376938260 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.2.6
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70173932069160
46
+ version_requirements: *70291376938260
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rake
49
- requirement: &70173932068040 !ruby/object:Gem::Requirement
49
+ requirement: &70291376937280 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70173932068040
57
+ version_requirements: *70291376937280
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: bundler
60
- requirement: &70173932066460 !ruby/object:Gem::Requirement
60
+ requirement: &70291376930680 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '1.0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70173932066460
68
+ version_requirements: *70291376930680
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rspec
71
- requirement: &70173932065340 !ruby/object:Gem::Requirement
71
+ requirement: &70291376929580 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '2.6'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70173932065340
79
+ version_requirements: *70291376929580
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rack-test
82
- requirement: &70173932064500 !ruby/object:Gem::Requirement
82
+ requirement: &70291376928420 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0.6'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *70173932064500
90
+ version_requirements: *70291376928420
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: guard-rspec
93
- requirement: &70173932062500 !ruby/object:Gem::Requirement
93
+ requirement: &70291376927240 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ~>
@@ -98,7 +98,7 @@ dependencies:
98
98
  version: '0.4'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *70173932062500
101
+ version_requirements: *70291376927240
102
102
  description:
103
103
  email:
104
104
  - andrewnez@gmail.com
@@ -107,6 +107,7 @@ extensions: []
107
107
  extra_rdoc_files: []
108
108
  files:
109
109
  - .gitignore
110
+ - .travis.yml
110
111
  - CHANGELOG.mdown
111
112
  - Gemfile
112
113
  - Guardfile
@@ -117,6 +118,7 @@ files:
117
118
  - lib/split/alternative.rb
118
119
  - lib/split/configuration.rb
119
120
  - lib/split/dashboard.rb
121
+ - lib/split/dashboard/helpers.rb
120
122
  - lib/split/dashboard/public/dashboard.js
121
123
  - lib/split/dashboard/public/reset.css
122
124
  - lib/split/dashboard/public/style.css
@@ -128,6 +130,7 @@ files:
128
130
  - lib/split/version.rb
129
131
  - spec/alternative_spec.rb
130
132
  - spec/configuration_spec.rb
133
+ - spec/dashboard_helpers_spec.rb
131
134
  - spec/dashboard_spec.rb
132
135
  - spec/experiment_spec.rb
133
136
  - spec/helper_spec.rb
@@ -153,13 +156,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
156
  version: '0'
154
157
  requirements: []
155
158
  rubyforge_project: split
156
- rubygems_version: 1.8.6
159
+ rubygems_version: 1.8.11
157
160
  signing_key:
158
161
  specification_version: 3
159
162
  summary: Rack based split testing framework
160
163
  test_files:
161
164
  - spec/alternative_spec.rb
162
165
  - spec/configuration_spec.rb
166
+ - spec/dashboard_helpers_spec.rb
163
167
  - spec/dashboard_spec.rb
164
168
  - spec/experiment_spec.rb
165
169
  - spec/helper_spec.rb