bandit 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. data/.gitignore +1 -0
  2. data/LICENSE +674 -0
  3. data/README.rdoc +70 -2
  4. data/Rakefile +32 -0
  5. data/bandit.gemspec +2 -0
  6. data/lib/bandit.rb +52 -1
  7. data/lib/bandit/config.rb +33 -0
  8. data/lib/bandit/date_hour.rb +82 -0
  9. data/lib/bandit/exceptions.rb +10 -0
  10. data/lib/bandit/experiment.rb +63 -0
  11. data/lib/bandit/extensions/controller_concerns.rb +24 -0
  12. data/lib/bandit/extensions/string.rb +5 -0
  13. data/lib/bandit/extensions/time.rb +5 -0
  14. data/lib/bandit/extensions/view_concerns.rb +24 -0
  15. data/lib/bandit/memoizable.rb +15 -0
  16. data/lib/bandit/players/base.rb +37 -0
  17. data/lib/bandit/players/epsilon_greedy.rb +32 -0
  18. data/lib/bandit/players/round_robin.rb +7 -0
  19. data/lib/bandit/storage/base.rb +124 -0
  20. data/lib/bandit/storage/memcache.rb +34 -0
  21. data/lib/bandit/storage/memory.rb +31 -0
  22. data/lib/bandit/storage/redis.rb +37 -0
  23. data/lib/bandit/version.rb +1 -1
  24. data/lib/generators/bandit/USAGE +3 -0
  25. data/lib/generators/bandit/dashboard_generator.rb +31 -0
  26. data/lib/generators/bandit/install_generator.rb +22 -0
  27. data/lib/generators/bandit/templates/bandit.rake +20 -0
  28. data/lib/generators/bandit/templates/bandit.rb +18 -0
  29. data/lib/generators/bandit/templates/bandit.yml +16 -0
  30. data/lib/generators/bandit/templates/bandit_controller.rb +28 -0
  31. data/lib/generators/bandit/templates/dashboard/bandit.html.erb +43 -0
  32. data/lib/generators/bandit/templates/dashboard/css/application.css +7 -0
  33. data/lib/generators/bandit/templates/dashboard/css/base.css +22 -0
  34. data/lib/generators/bandit/templates/dashboard/css/toupee/buttons.css +101 -0
  35. data/lib/generators/bandit/templates/dashboard/css/toupee/forms.css +89 -0
  36. data/lib/generators/bandit/templates/dashboard/css/toupee/modules.css +30 -0
  37. data/lib/generators/bandit/templates/dashboard/css/toupee/reset.css +42 -0
  38. data/lib/generators/bandit/templates/dashboard/css/toupee/structure.css +124 -0
  39. data/lib/generators/bandit/templates/dashboard/css/toupee/typography.css +103 -0
  40. data/lib/generators/bandit/templates/dashboard/helpers/bandit_helper.rb +19 -0
  41. data/lib/generators/bandit/templates/dashboard/js/bandit.js +34 -0
  42. data/lib/generators/bandit/templates/dashboard/js/highstock.js +215 -0
  43. data/lib/generators/bandit/templates/dashboard/js/jquery.min.js +154 -0
  44. data/lib/generators/bandit/templates/dashboard/view/index.html.erb +21 -0
  45. data/lib/generators/bandit/templates/dashboard/view/show.html.erb +24 -0
  46. data/players.rdoc +22 -0
  47. data/test/config.yml +7 -0
  48. data/test/helper.rb +18 -0
  49. data/test/memcache_storage_test.rb +17 -0
  50. data/test/memory_storage_test.rb +16 -0
  51. data/test/redis_storage_test.rb +17 -0
  52. data/test/storage_test_base.rb +54 -0
  53. metadata +88 -8
@@ -0,0 +1,21 @@
1
+ <section class='span-20 colborder'>
2
+ <div class='title'><h1>Status</h1></div>
3
+ <p>
4
+ Using <%= storage_name %> storage <%= storage_config %> and a <%= player_name %> player <%= player_config %> for <%= Bandit.experiments.length %> active experiment(s).
5
+ </p>
6
+ <div class='title'><h1>Running Experiments</h1></div>
7
+ <% @experiments.each { |experiment| %>
8
+ <h2><%= link_to experiment.name.to_s.titleize, url_for(:action => 'show', :id => experiment.name) %></h2>
9
+ <p>
10
+ <%= experiment.description %>
11
+ </p>
12
+ <% experiment.alternatives.each { |alt| %>
13
+ <h3>Alternative: <%= alt %></h3>
14
+ <ul>
15
+ <li>Participants: <%= experiment.participant_count(alt) %></li>
16
+ <li>Conversions: <%= experiment.conversion_count(alt) %></li>
17
+ <li>Conversion rate: <%= experiment.conversion_rate(alt) %> %</li>
18
+ </ul>
19
+ <% } %>
20
+ <% } %>
21
+ </section>
@@ -0,0 +1,24 @@
1
+ <section class='span-20 colborder'>
2
+ <div class='title'><h1><%= @experiment.name.to_s.titleize %></h1></div>
3
+ <p>
4
+ <%= @experiment.description %>
5
+ </p>
6
+
7
+ <div id="gcontainer"></div>
8
+
9
+ <% @experiment.alternatives.each { |alt| %>
10
+ <h2>Alternative: <%= alt %></h2>
11
+ <ul>
12
+ <li>Participants: <%= @experiment.participant_count(alt) %></li>
13
+ <li>Conversions: <%= @experiment.conversion_count(alt) %></li>
14
+ <li>Conversion rate: <%= @experiment.conversion_rate(alt) %> %</li>
15
+ </ul>
16
+ <% } %>
17
+
18
+ </section>
19
+
20
+ <script type="text/javascript">
21
+ var url = "<%= url_for(:controller => 'bandit', :action => 'show', :id => @experiment.name, :format => 'csv') %>";
22
+ var title = "<%= @experiment.name.to_s.titleize %>";
23
+ show_chart(title, url);
24
+ </script>
@@ -0,0 +1,22 @@
1
+ = Bandit Players
2
+ There are a number of different possible players that each seek to explore/exploit using different methods.
3
+
4
+ == Epsilon Greedy
5
+ The epsilon greedy player selects the best alternative with a probability of 1 - epsilon, and selects uniformly among the other alternatives with a probability of epsilon. You can set the value of epsilon in the config file like this:
6
+
7
+ production:
8
+ ...
9
+ player: epsilon_greedy
10
+ player_config:
11
+ epsilon: 0.1
12
+ ...
13
+
14
+
15
+ == Round Robin
16
+ This is included for testing purposes only. It will not optimize. There are no config options.
17
+
18
+
19
+ production:
20
+ ...
21
+ player: round_robin
22
+ ...
@@ -0,0 +1,7 @@
1
+ redis_storage_config:
2
+ host: localhost
3
+ port: 6379
4
+
5
+ memcache_storage_config:
6
+ host: localhost
7
+
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'yaml'
4
+
5
+ $:.unshift(File.join File.dirname(__FILE__), '..', 'lib')
6
+ require 'bandit'
7
+
8
+ CONFIG = YAML.load_file File.join(File.dirname(__FILE__), "config.yml")
9
+
10
+ module SetupHelper
11
+ def new_experiment
12
+ Bandit::Experiment.create(:exp_test) { |exp|
13
+ exp.alternatives = [10,20,30,40]
14
+ exp.title = "A Test Exp"
15
+ exp.description = "Desc"
16
+ }
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ require File.join File.dirname(__FILE__), 'helper'
2
+ require File.join File.dirname(__FILE__), 'storage_test_base'
3
+
4
+ class MemCacheStorageTest < Test::Unit::TestCase
5
+ include StorageTestBase
6
+ include SetupHelper
7
+
8
+ def setup
9
+ Bandit.setup do |config|
10
+ config.player = "round_robin"
11
+ config.storage = 'memcache'
12
+ config.storage_config = CONFIG['memcache_storage_config']
13
+ end
14
+
15
+ @storage = Bandit.storage
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ require File.join File.dirname(__FILE__), 'helper'
2
+ require File.join File.dirname(__FILE__), 'storage_test_base'
3
+
4
+ class MemoryStorageTest < Test::Unit::TestCase
5
+ include StorageTestBase
6
+ include SetupHelper
7
+
8
+ def setup
9
+ Bandit.setup do |config|
10
+ config.player = "round_robin"
11
+ config.storage = 'memory'
12
+ end
13
+
14
+ @storage = Bandit.storage
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ require File.join File.dirname(__FILE__), 'helper'
2
+ require File.join File.dirname(__FILE__), 'storage_test_base'
3
+
4
+ class MemCacheStorageTest < Test::Unit::TestCase
5
+ include StorageTestBase
6
+ include SetupHelper
7
+
8
+ def setup
9
+ Bandit.setup do |config|
10
+ config.player = "round_robin"
11
+ config.storage = 'redis'
12
+ config.storage_config = CONFIG['redis_storage_config']
13
+ end
14
+
15
+ @storage = Bandit.storage
16
+ end
17
+ end
@@ -0,0 +1,54 @@
1
+ require 'date'
2
+
3
+ module StorageTestBase
4
+ def teardown
5
+ @storage.clear!
6
+ end
7
+
8
+ def test_participant_storage
9
+ exp = new_experiment
10
+ alt = exp.alternatives.first
11
+
12
+ start = Bandit::DateHour.now
13
+ @storage.incr_participants(exp, alt)
14
+ assert_equal @storage.participant_count(exp, alt), 1
15
+ @storage.incr_participants(exp, alt, 2)
16
+ assert_equal @storage.participant_count(exp, alt), 3
17
+ assert_equal @storage.participant_count(exp, alt, Bandit::DateHour.now), 3
18
+
19
+ assert_equal @storage.alternative_start_time(exp, alt), start
20
+
21
+ future = Bandit::DateHour.new(Date.today, Time.now.hour+3)
22
+ @storage.incr_participants(exp, alt, 123, future)
23
+ assert_equal @storage.participant_count(exp, alt, future), 123
24
+
25
+ assert_equal @storage.participant_count(exp, "nonexistant"), 0
26
+ end
27
+
28
+ def test_conversion_storage
29
+ exp = new_experiment
30
+ alt = exp.alternatives.first
31
+
32
+ @storage.incr_conversions(exp, alt)
33
+ assert_equal @storage.conversion_count(exp, alt), 1
34
+ @storage.incr_conversions(exp, alt, 2)
35
+ assert_equal @storage.conversion_count(exp, alt), 3
36
+ assert_equal @storage.conversion_count(exp, alt, Bandit::DateHour.now), 3
37
+
38
+ future = Bandit::DateHour.new(Date.today, Time.now.hour+3)
39
+ @storage.incr_conversions(exp, alt, 123, future)
40
+ assert_equal @storage.conversion_count(exp, alt, future), 123
41
+
42
+ assert_equal @storage.conversion_count(exp, "nonexistant"), 0
43
+ end
44
+
45
+ def test_player_storage
46
+ player = Bandit.player
47
+ exp = new_experiment
48
+
49
+ @storage.player_state_set(exp, player, "something", 123)
50
+ assert_equal @storage.player_state_get(exp, player, "something"), 123
51
+
52
+ assert_equal @storage.player_state_get(exp, player, "nonexistant"), nil
53
+ end
54
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bandit
3
3
  version: !ruby/object:Gem::Version
4
- hash: 27
4
+ hash: 25
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 2
10
- version: 0.0.2
9
+ - 3
10
+ version: 0.0.3
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brian Muller
@@ -15,10 +15,39 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-08-05 00:00:00 -04:00
18
+ date: 2011-08-13 00:00:00 -04:00
19
19
  default_executable:
20
- dependencies: []
21
-
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rails
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 3
32
+ - 0
33
+ - 5
34
+ version: 3.0.5
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: rdoc
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 3
46
+ segments:
47
+ - 0
48
+ version: "0"
49
+ type: :runtime
50
+ version_requirements: *id002
22
51
  description: Bandit provides a way to do multi-armed bandit optimization of alternatives in a rails website
23
52
  email:
24
53
  - brian.muller@livingsocial.com
@@ -31,11 +60,57 @@ extra_rdoc_files: []
31
60
  files:
32
61
  - .gitignore
33
62
  - Gemfile
63
+ - LICENSE
34
64
  - README.rdoc
35
65
  - Rakefile
36
66
  - bandit.gemspec
37
67
  - lib/bandit.rb
68
+ - lib/bandit/config.rb
69
+ - lib/bandit/date_hour.rb
70
+ - lib/bandit/exceptions.rb
71
+ - lib/bandit/experiment.rb
72
+ - lib/bandit/extensions/controller_concerns.rb
73
+ - lib/bandit/extensions/string.rb
74
+ - lib/bandit/extensions/time.rb
75
+ - lib/bandit/extensions/view_concerns.rb
76
+ - lib/bandit/memoizable.rb
77
+ - lib/bandit/players/base.rb
78
+ - lib/bandit/players/epsilon_greedy.rb
79
+ - lib/bandit/players/round_robin.rb
80
+ - lib/bandit/storage/base.rb
81
+ - lib/bandit/storage/memcache.rb
82
+ - lib/bandit/storage/memory.rb
83
+ - lib/bandit/storage/redis.rb
38
84
  - lib/bandit/version.rb
85
+ - lib/generators/bandit/USAGE
86
+ - lib/generators/bandit/dashboard_generator.rb
87
+ - lib/generators/bandit/install_generator.rb
88
+ - lib/generators/bandit/templates/bandit.rake
89
+ - lib/generators/bandit/templates/bandit.rb
90
+ - lib/generators/bandit/templates/bandit.yml
91
+ - lib/generators/bandit/templates/bandit_controller.rb
92
+ - lib/generators/bandit/templates/dashboard/bandit.html.erb
93
+ - lib/generators/bandit/templates/dashboard/css/application.css
94
+ - lib/generators/bandit/templates/dashboard/css/base.css
95
+ - lib/generators/bandit/templates/dashboard/css/toupee/buttons.css
96
+ - lib/generators/bandit/templates/dashboard/css/toupee/forms.css
97
+ - lib/generators/bandit/templates/dashboard/css/toupee/modules.css
98
+ - lib/generators/bandit/templates/dashboard/css/toupee/reset.css
99
+ - lib/generators/bandit/templates/dashboard/css/toupee/structure.css
100
+ - lib/generators/bandit/templates/dashboard/css/toupee/typography.css
101
+ - lib/generators/bandit/templates/dashboard/helpers/bandit_helper.rb
102
+ - lib/generators/bandit/templates/dashboard/js/bandit.js
103
+ - lib/generators/bandit/templates/dashboard/js/highstock.js
104
+ - lib/generators/bandit/templates/dashboard/js/jquery.min.js
105
+ - lib/generators/bandit/templates/dashboard/view/index.html.erb
106
+ - lib/generators/bandit/templates/dashboard/view/show.html.erb
107
+ - players.rdoc
108
+ - test/config.yml
109
+ - test/helper.rb
110
+ - test/memcache_storage_test.rb
111
+ - test/memory_storage_test.rb
112
+ - test/redis_storage_test.rb
113
+ - test/storage_test_base.rb
39
114
  has_rdoc: true
40
115
  homepage: https://github.com/bmuller/bandit
41
116
  licenses: []
@@ -70,5 +145,10 @@ rubygems_version: 1.3.7
70
145
  signing_key:
71
146
  specification_version: 3
72
147
  summary: Multi-armed bandit testing in rails
73
- test_files: []
74
-
148
+ test_files:
149
+ - test/config.yml
150
+ - test/helper.rb
151
+ - test/memcache_storage_test.rb
152
+ - test/memory_storage_test.rb
153
+ - test/redis_storage_test.rb
154
+ - test/storage_test_base.rb