blackbeard 0.0.2.0 → 0.0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +9 -0
  3. data/Guardfile +8 -0
  4. data/README.md +162 -20
  5. data/Rakefile +6 -0
  6. data/TODO.md +13 -34
  7. data/blackbeard.gemspec +5 -1
  8. data/console.rb +3 -0
  9. data/dashboard/public/bootstrap3-editable/css/bootstrap-editable.css +663 -0
  10. data/dashboard/public/bootstrap3-editable/img/clear.png +0 -0
  11. data/dashboard/public/bootstrap3-editable/img/loading.gif +0 -0
  12. data/dashboard/public/bootstrap3-editable/js/bootstrap-editable.min.js +7 -0
  13. data/dashboard/public/fonts/glyphicons-halflings-regular.eot +0 -0
  14. data/dashboard/public/fonts/glyphicons-halflings-regular.svg +229 -0
  15. data/dashboard/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  16. data/dashboard/public/fonts/glyphicons-halflings-regular.woff +0 -0
  17. data/dashboard/public/javascripts/bootstrap.min.js +7 -0
  18. data/dashboard/public/javascripts/jquery-1.10.2.min.js +6 -0
  19. data/dashboard/public/stylesheets/application.css +28 -0
  20. data/dashboard/public/stylesheets/bootstrap-theme.css +7 -0
  21. data/dashboard/public/stylesheets/bootstrap.css +7 -0
  22. data/dashboard/routes/base.rb +19 -0
  23. data/dashboard/routes/groups.rb +22 -0
  24. data/dashboard/routes/home.rb +11 -0
  25. data/dashboard/routes/metrics.rb +30 -0
  26. data/dashboard/routes/tests.rb +23 -0
  27. data/dashboard/views/groups/index.erb +22 -0
  28. data/dashboard/views/groups/show.erb +58 -0
  29. data/dashboard/views/index.erb +4 -0
  30. data/dashboard/views/layout.erb +48 -0
  31. data/dashboard/views/metrics/_metric_data.erb +59 -0
  32. data/dashboard/views/metrics/index.erb +23 -0
  33. data/dashboard/views/metrics/show.erb +73 -0
  34. data/dashboard/views/tests/index.erb +21 -0
  35. data/dashboard/views/tests/show.erb +58 -0
  36. data/lib/blackbeard/configuration.rb +8 -1
  37. data/lib/blackbeard/configuration_methods.rb +24 -0
  38. data/lib/blackbeard/context.rb +33 -21
  39. data/lib/blackbeard/dashboard.rb +17 -21
  40. data/lib/blackbeard/dashboard_helpers.rb +29 -0
  41. data/lib/blackbeard/errors.rb +2 -2
  42. data/lib/blackbeard/group.rb +35 -0
  43. data/lib/blackbeard/metric.rb +34 -32
  44. data/lib/blackbeard/metric_data/base.rb +101 -0
  45. data/lib/blackbeard/metric_data/total.rb +39 -0
  46. data/lib/blackbeard/metric_data/unique.rb +58 -0
  47. data/lib/blackbeard/metric_date.rb +11 -0
  48. data/lib/blackbeard/metric_hour.rb +17 -0
  49. data/lib/blackbeard/pirate.rb +33 -22
  50. data/lib/blackbeard/redis_store.rb +46 -2
  51. data/lib/blackbeard/selected_variation.rb +13 -0
  52. data/lib/blackbeard/storable.rb +39 -27
  53. data/lib/blackbeard/storable_attributes.rb +54 -0
  54. data/lib/blackbeard/storable_has_many.rb +60 -0
  55. data/lib/blackbeard/storable_has_set.rb +59 -0
  56. data/lib/blackbeard/test.rb +21 -0
  57. data/lib/blackbeard/version.rb +1 -1
  58. data/lib/blackbeard.rb +0 -8
  59. data/spec/configuration_spec.rb +15 -0
  60. data/spec/context_spec.rb +94 -19
  61. data/spec/dashboard/groups_spec.rb +50 -0
  62. data/spec/dashboard/home_spec.rb +20 -0
  63. data/spec/dashboard/metrics_spec.rb +57 -0
  64. data/spec/dashboard/tests_spec.rb +43 -0
  65. data/spec/group_spec.rb +36 -0
  66. data/spec/metric_data/base_spec.rb +57 -0
  67. data/spec/metric_data/total_spec.rb +116 -0
  68. data/spec/metric_data/unique_spec.rb +91 -0
  69. data/spec/metric_spec.rb +52 -44
  70. data/spec/pirate_spec.rb +32 -15
  71. data/spec/redis_store_spec.rb +121 -0
  72. data/spec/spec_helper.rb +13 -1
  73. data/spec/storable_attributes_spec.rb +47 -0
  74. data/spec/storable_has_many_spec.rb +49 -0
  75. data/spec/storable_has_set_spec.rb +39 -0
  76. data/spec/storable_spec.rb +33 -0
  77. data/spec/test_spec.rb +25 -0
  78. metadata +133 -17
  79. data/lib/blackbeard/dashboard/helpers.rb +0 -8
  80. data/lib/blackbeard/dashboard/views/layout.erb +0 -15
  81. data/lib/blackbeard/dashboard/views/metrics/index.erb +0 -10
  82. data/lib/blackbeard/dashboard/views/metrics/show.erb +0 -16
  83. data/lib/blackbeard/feature.rb +0 -13
  84. data/lib/blackbeard/metric/total.rb +0 -17
  85. data/lib/blackbeard/metric/unique.rb +0 -18
  86. data/spec/dashboard_spec.rb +0 -38
  87. data/spec/total_metric_spec.rb +0 -65
  88. data/spec/unique_metric_spec.rb +0 -60
data/spec/metric_spec.rb CHANGED
@@ -1,50 +1,58 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- describe Blackbeard::Metric do
4
-
5
- describe "hour_keys" do
6
- before :each do
7
- @total_metric = Blackbeard::Metric::Total.new("one-total")
8
- end
9
-
10
- it "should return an empty array if no metrics" do
11
- @total_metric.send(:hour_keys).should == []
12
- end
13
-
14
- it "should return an array for each hour" do
15
- @total_metric.add('user1', 1)
16
- key = @total_metric.send(:key_for_hour, Blackbeard.tz.now)
17
- @total_metric.send(:hour_keys).should == [key]
3
+ module Blackbeard
4
+ describe Metric do
5
+ let(:metric) { Metric.new(:total, "one-total") }
6
+ let(:group) { Group.new(:example) }
7
+ let(:metric_data) { metric.metric_data }
8
+ let(:group_metric_data) { metric.metric_data(group) }
9
+
10
+ describe "self.all" do
11
+ before :each do
12
+ Metric.new(:total, "one-total")
13
+ Metric.new(:total, "two-total")
14
+ Metric.new(:unique, "one-unique")
15
+ end
16
+ it "should return a Metric Object for each Metric created" do
17
+ Metric.all.should have(3).metrics
18
+ end
19
+ end
20
+
21
+ describe "add" do
22
+ let(:context) { double(:unique_identifier => 'uid', :controller => double, :user => double) }
23
+ before :each do
24
+ metric.stub(:groups).and_return([group])
25
+ end
26
+
27
+ it "should increment metric data" do
28
+ metric_data.should_receive(:add).with("uid",1)
29
+ metric.add(context, 1)
30
+ end
31
+
32
+ it "should increment metric data for each group" do
33
+ group.stub(:segment_for).and_return("segment")
34
+ group_metric_data.should_receive(:add).with("uid", 1, "segment" )
35
+ metric.add(context, 1)
36
+ end
37
+
38
+ it "should not increment nil segments" do
39
+ group.stub(:segment_for).and_return(nil)
40
+ group_metric_data.should_not_receive(:add)
41
+ metric.add(context, 1)
42
+ end
43
+ end
44
+
45
+ describe "addable_groups" do
46
+ it "should include the groups not added" do
47
+ group # to initialize it
48
+ metric.addable_groups.map{|g| g.id }.should include(group.id)
49
+ end
50
+
51
+ it "should not include the group added" do
52
+ metric.add_group(group)
53
+ metric.addable_groups.map{|g| g.id }.should_not include(group.id)
54
+ end
18
55
  end
19
56
 
20
57
  end
21
-
22
- describe "hours" do
23
- before :each do
24
- @total_metric = Blackbeard::Metric::Total.new("one-total")
25
- end
26
-
27
- it "should return an array of hashes" do
28
- @total_metric.add('user1', 1)
29
- @total_metric.hours.should be_an(Array)
30
- @total_metric.hours.first.should be_a(Hash)
31
- end
32
- end
33
-
34
- describe "self.all" do
35
- before :each do
36
- Blackbeard::Metric::Total.new("one-total")
37
- Blackbeard::Metric::Total.new("two-total")
38
- Blackbeard::Metric::Unique.new("one-unique")
39
- end
40
- it "should return a Metric Object for each Metric created" do
41
- Blackbeard::Metric.all.should have(3).metrics
42
- end
43
-
44
- it "should instantiate each metric with the correct class" do
45
- Blackbeard::Metric.all.select{|m| m.name == "two-total"}.should have(1).metric
46
- Blackbeard::Metric.all.select{|m| m.name == "two-total"}.first.should be_a(Blackbeard::Metric::Total)
47
- end
48
- end
49
-
50
58
  end
data/spec/pirate_spec.rb CHANGED
@@ -6,20 +6,16 @@ describe Blackbeard::Pirate do
6
6
  describe "memoization" do
7
7
  let(:name){ "bond" }
8
8
 
9
- it "should get features once" do
10
- Blackbeard::Feature.should_receive(:new).with(name).once.and_return(true)
11
- 4.times{ pirate.feature(name) }
9
+ it "should get metrics once" do
10
+ Blackbeard::Metric.should_receive(:new).with(:total, name).once.and_return(double)
11
+ 4.times{ pirate.metric(:total, name) }
12
12
  end
13
13
 
14
- it "should get unique metrics once" do
15
- Blackbeard::Metric::Unique.should_receive(:new).with(name).once.and_return(true)
16
- 4.times{ pirate.unique_metric(name) }
14
+ it "should get test once" do
15
+ Blackbeard::Test.should_receive(:new).with(name).once.and_return(double)
16
+ 4.times{ pirate.test(name) }
17
17
  end
18
18
 
19
- it "should get total metrics once" do
20
- Blackbeard::Metric::Total.should_receive(:new).with(name).once.and_return(true)
21
- 4.times{ pirate.total_metric(name) }
22
- end
23
19
 
24
20
  end
25
21
 
@@ -33,15 +29,26 @@ describe Blackbeard::Pirate do
33
29
 
34
30
  describe "set context delegations" do
35
31
  context "with no set context" do
36
- it "should raise Blackbeard::MissingContextError" do
37
- expect{ pirate.add_unique(:exmaple) }.to raise_error( Blackbeard::MissingContextError )
32
+ it "add_unique should raise Blackbeard::MissingContextError" do
33
+ expect{ pirate.add_unique(:example) }.to_not raise_error
34
+ end
35
+
36
+ it "add_total should raise Blackbeard::MissingContextError" do
37
+ expect{ pirate.add_total(:example, 1) }.to_not raise_error
38
38
  end
39
- it "should raise Blackbeard::MissingContextError" do
40
- expect{ pirate.add_total(:exmaple, 1) }.to raise_error( Blackbeard::MissingContextError )
39
+
40
+ it "ab_test should raise Blackbeard::MissingContextError" do
41
+ expect{ pirate.ab_test(:example, :on => 1, :off => 2) }.to_not raise_error
42
+ end
43
+
44
+ it "active? should raise Blackbeard::MissingContextError" do
45
+ expect{ pirate.active?(:example) }.to_not raise_error
41
46
  end
47
+
42
48
  end
43
49
  context "with context set" do
44
- let!(:set_context){ pirate.set_context(:user_id => 1) }
50
+ let(:user){ double }
51
+ let!(:set_context){ pirate.set_context(user) }
45
52
 
46
53
  it "should delegate #add_unique" do
47
54
  set_context.should_receive(:add_unique).with(:example_metric).and_return(set_context)
@@ -52,6 +59,16 @@ describe Blackbeard::Pirate do
52
59
  set_context.should_receive(:add_total).with(:example_metric, 1).and_return(set_context)
53
60
  pirate.add_total(:example_metric, 1)
54
61
  end
62
+
63
+ it "should delegate #ab_test" do
64
+ set_context.should_receive(:ab_test).with(:example_metric, :on => 1, :off => 2).and_return(set_context)
65
+ pirate.ab_test(:example_metric, :on => 1, :off => 2)
66
+ end
67
+
68
+ it "should delegate #active?" do
69
+ set_context.should_receive(:active?).with(:example_metric).and_return(false)
70
+ pirate.active?(:example_metric)
71
+ end
55
72
  end
56
73
  end
57
74
 
@@ -0,0 +1,121 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ module Blackbeard
4
+ describe RedisStore do
5
+
6
+ it "should keep keys" do
7
+ db.set("hello", "world")
8
+ db.keys.should == ["hello"]
9
+ end
10
+
11
+ describe "hashes" do
12
+ it "should set and get" do
13
+ db.hash_set('a_hash', 'hello', 'world')
14
+ db.hash_get('a_hash', 'hello').should == 'world'
15
+ end
16
+
17
+ it "should multi set" do
18
+ db.hash_multi_set('a_hash', {:one => 'two', :three => 'four'})
19
+ db.hash_get('a_hash','three').should == 'four'
20
+ end
21
+
22
+ it "should not raise error on multi set with empty hash" do
23
+ expect{
24
+ db.hash_multi_set('a_hash', {})
25
+ }.to_not raise_error
26
+ end
27
+
28
+ it "should set if field does not exist" do
29
+ db.hash_set('a_hash', 'hello', 'world')
30
+ db.hash_key_set_if_not_exists('a_hash', 'hello', 'bar')
31
+ db.hash_get('a_hash', 'hello').should == 'world'
32
+ db.hash_key_set_if_not_exists('a_hash', 'foo', 'bar')
33
+ db.hash_get('a_hash', 'foo').should == 'bar'
34
+ end
35
+
36
+ it "should return lenth and keys" do
37
+ db.hash_set('a_hash', 'foo', 'bar')
38
+ db.hash_set('a_hash', 'hello', 'world')
39
+ db.hash_length('a_hash').should eq(2)
40
+ db.hash_keys('a_hash').should include('foo', 'hello')
41
+ end
42
+
43
+ it "should get all" do
44
+ db.hash_set('a_hash', 'foo', 'bar')
45
+ db.hash_set('a_hash', 'hello', 'world')
46
+ db.hash_get_all('a_hash').should include('foo' => 'bar', 'hello' => 'world')
47
+ end
48
+
49
+ it "should increment by float" do
50
+ db.hash_increment_by_float('a_hash', 'field', 1)
51
+ db.hash_increment_by_float('a_hash', 'field', 2.5)
52
+ db.hash_get('a_hash', 'field').should == "3.5"
53
+ end
54
+ end
55
+
56
+ describe "sets" do
57
+ it "should add and remove" do
58
+ db.set_add_member('a_set', 'foo')
59
+ db.set_add_member('a_set', 'bar')
60
+ db.set_members('a_set').should include('foo', 'bar')
61
+ db.set_remove_member('a_set', 'bar')
62
+ db.set_members('a_set').should_not include('bar')
63
+ end
64
+
65
+ it "should return true if added" do
66
+ db.set_add_member('a_set', 'foo').should be(true)
67
+ db.set_add_member('a_set', 'foo').should be(false)
68
+ end
69
+
70
+ it "should return all the members" do
71
+ db.set_add_member('a_set', 'foo')
72
+ db.set_add_member('a_set', 'bar')
73
+ db.set_members('a_set').should include('foo', 'bar')
74
+ end
75
+
76
+ it "should set multiple members at once" do
77
+ db.set_add_members('a_set', 'foo', ['bar', 'shag'])
78
+ db.set_members('a_set').should include('foo', 'bar', 'shag')
79
+ end
80
+
81
+ it "should count the members" do
82
+ db.set_add_members('a_set', 'foo', 'bar')
83
+ db.set_count('a_set').should eq(2)
84
+ end
85
+
86
+ it "should count the union" do
87
+ db.set_add_members('a_set', 'foo', 'bar')
88
+ db.set_add_members('x_set', 'bar', 'world')
89
+ db.set_union_count('a_set','x_set').should eq(3)
90
+ end
91
+
92
+ end
93
+
94
+ describe "strings" do
95
+ it "should get and set" do
96
+ db.set('hello','world')
97
+ db.get('hello').should eq('world')
98
+ end
99
+
100
+ it "should delete" do
101
+ db.set('hello','world')
102
+ expect{
103
+ db.del('hello')
104
+ }.to change{ db.get('hello') }.from('world').to(nil)
105
+ end
106
+
107
+ it "should get many at once" do
108
+ db.set('hello','world')
109
+ db.set('foo','bar')
110
+ db.multi_get('hello','herp', 'foo').should == ['world',nil,'bar']
111
+ end
112
+
113
+ it "should increment" do
114
+ db.increment('count')
115
+ db.get('count').should eq('1')
116
+ db.increment('count')
117
+ db.get('count').should eq('2')
118
+ end
119
+ end
120
+ end
121
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,22 @@
1
1
  require 'blackbeard'
2
2
  require 'redis'
3
+ require "codeclimate-test-reporter"
4
+
5
+ CodeClimate::TestReporter.start
3
6
 
4
7
  RSpec.configure do |config|
5
8
  config.before do
6
- redis = Blackbeard.db
9
+ redis = Blackbeard.config.db
10
+ namespace = "BlackbeardTests"
7
11
  keys = redis.keys
8
12
  redis.del(keys) if keys.any?
9
13
  end
10
14
  end
15
+
16
+ def tz
17
+ Blackbeard.config.tz
18
+ end
19
+
20
+ def db
21
+ Blackbeard.config.db
22
+ end
@@ -0,0 +1,47 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Blackbeard::Storable do
4
+ class ExampleStorableAttrBase < Blackbeard::Storable
5
+ string_attributes :name
6
+ end
7
+
8
+ class ExampleStorableAttr < ExampleStorableAttrBase
9
+ set_master_key :example
10
+ end
11
+
12
+ describe "string_attributes" do
13
+ it "should be read and write" do
14
+ example = ExampleStorableAttr.new("id")
15
+ example.name = "Some name"
16
+ example.name.should == "Some name"
17
+ end
18
+
19
+ it "should persist" do
20
+ example = ExampleStorableAttr.new("id")
21
+ example.name = "Some name"
22
+
23
+ example_reloaded = ExampleStorableAttr.new("id")
24
+ example_reloaded.name.should == "Some name"
25
+ end
26
+ end
27
+
28
+ describe "update_attributes" do
29
+ let(:storable){ ExampleStorableAttr.new("id") }
30
+ it "should not raise raise_error with non-attributes" do
31
+ expect{
32
+ storable.update_attributes(:name => 'hello')
33
+ }.to_not raise_error
34
+ end
35
+ it "should update the attribute for known attributes" do
36
+ expect{
37
+ storable.update_attributes(:name => 'hello')
38
+ }.to change{ storable.name }.from(nil).to('hello')
39
+ end
40
+
41
+ it "should update the attribute for known attributes even when strings" do
42
+ expect{
43
+ storable.update_attributes("name" => 'hello')
44
+ }.to change{ storable.name }.from(nil).to('hello')
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,49 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ module Blackbeard
4
+
5
+ class Thing < Storable
6
+ set_master_key :things
7
+ end
8
+
9
+ class HasManyExample < Storable
10
+ set_master_key :examples
11
+ has_many :things => Thing
12
+ end
13
+
14
+ describe StorableHasMany do
15
+ let(:example){ HasManyExample.new(:example) }
16
+ let(:thing){ Thing.new(:foo) }
17
+
18
+ it "should add and remove things" do
19
+ example.add_thing(thing)
20
+ expect{
21
+ example.remove_thing(thing)
22
+ }.to change{ example.has_thing?(thing) }.from(true).to(false)
23
+ end
24
+
25
+ it "should has_thing?" do
26
+ example.add_thing(thing)
27
+ example.has_thing?(thing).should be_true
28
+ end
29
+
30
+ it "should list things" do
31
+ expect{
32
+ example.add_thing(thing)
33
+ }.to change{ example.things.count}.from(0).to(1)
34
+ end
35
+
36
+ it "should list thing_ids" do
37
+ example.thing_ids.should == []
38
+ example.add_thing(thing)
39
+ example.thing_ids.should == [thing.id]
40
+ end
41
+
42
+ it "should list the keys" do
43
+ example.thing_keys.should == []
44
+ example.add_thing(thing)
45
+ example.thing_keys.should == [thing.key]
46
+ end
47
+ end
48
+
49
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ module Blackbeard
4
+
5
+ class HasSetExample < Storable
6
+ set_master_key :examples
7
+ has_set :things => :thing
8
+ end
9
+
10
+ describe StorableHasMany do
11
+ let(:example){ HasSetExample.new(:example) }
12
+ let(:thing) { "foo" }
13
+
14
+ it "should add and remove things" do
15
+ example.add_thing(thing)
16
+ expect{
17
+ example.remove_thing(thing)
18
+ }.to change{ example.has_thing?(thing) }.from(true).to(false)
19
+ end
20
+
21
+ it "should add many things at once" do
22
+ expect{
23
+ example.add_things("thing1", "thing2")
24
+ }.to change{ example.things.count }.by(2)
25
+ end
26
+
27
+ it "should has_thing?" do
28
+ example.add_thing(thing)
29
+ example.has_thing?(thing).should be_true
30
+ end
31
+
32
+ it "should list things" do
33
+ expect{
34
+ example.add_thing(thing)
35
+ }.to change{ example.things.count}.from(0).to(1)
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,33 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Blackbeard::Storable do
4
+ class ExampleStorable < Blackbeard::Storable
5
+ set_master_key :example
6
+ end
7
+
8
+ class AnotherStorable < Blackbeard::Storable
9
+ set_master_key :another
10
+ end
11
+
12
+ it "should be able to instantiate" do
13
+ expect{
14
+ ExampleStorable.new(:some_id)
15
+ }.to_not raise_error
16
+ end
17
+
18
+ describe "master_key" do
19
+ it "should be by class" do
20
+ ExampleStorable.master_key.should == 'example'
21
+ AnotherStorable.master_key.should == 'another'
22
+ end
23
+ end
24
+
25
+ describe "==" do
26
+ it "should match class and id" do
27
+ (ExampleStorable.new("thing") == ExampleStorable.new("thing")).should be_true
28
+ (ExampleStorable.new("thing") == ExampleStorable.new("thing2")).should be_false
29
+ (ExampleStorable.new("thing") == AnotherStorable.new("thing")).should be_false
30
+ end
31
+ end
32
+
33
+ end
data/spec/test_spec.rb ADDED
@@ -0,0 +1,25 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Blackbeard::Test do
4
+ let(:test){ Blackbeard::Test.new('example') }
5
+
6
+ describe "#select_variation" do
7
+ # '*' - experimenting
8
+ # :variation
9
+
10
+ context "feature is static variation" do
11
+ it "should return the static variation"
12
+ end
13
+
14
+ context "experimenting" do
15
+ context "unique_identifier has already seen the variation" do
16
+ it "should return the same variation"
17
+ end
18
+
19
+ context "unique_identifier has not already seen the variation" do
20
+ it "should employ a strategy to select a variation"
21
+ end
22
+ end
23
+
24
+ end
25
+ end