blackbeard 0.0.4.0 → 0.0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +44 -16
  3. data/TODO.md +3 -1
  4. data/dashboard/routes/cohorts.rb +25 -0
  5. data/dashboard/routes/features.rb +7 -4
  6. data/dashboard/routes/groups.rb +6 -3
  7. data/dashboard/routes/metrics.rb +25 -5
  8. data/dashboard/routes/tests.rb +6 -3
  9. data/dashboard/views/cohorts/index.erb +22 -0
  10. data/dashboard/views/cohorts/show.erb +41 -0
  11. data/dashboard/views/groups/show.erb +2 -2
  12. data/dashboard/views/layout.erb +1 -0
  13. data/dashboard/views/metrics/show.erb +37 -10
  14. data/dashboard/views/shared/_charts.erb +20 -0
  15. data/lib/blackbeard/chart.rb +65 -0
  16. data/lib/blackbeard/chartable.rb +47 -0
  17. data/lib/blackbeard/cohort.rb +48 -0
  18. data/lib/blackbeard/cohort_data.rb +72 -0
  19. data/lib/blackbeard/cohort_metric.rb +47 -0
  20. data/lib/blackbeard/context.rb +11 -2
  21. data/lib/blackbeard/dashboard.rb +2 -3
  22. data/lib/blackbeard/dashboard_helpers.rb +0 -22
  23. data/lib/blackbeard/errors.rb +2 -0
  24. data/lib/blackbeard/group.rb +3 -0
  25. data/lib/blackbeard/group_metric.rb +39 -0
  26. data/lib/blackbeard/metric.rb +30 -14
  27. data/lib/blackbeard/metric_data/base.rb +18 -35
  28. data/lib/blackbeard/metric_data/total.rb +2 -1
  29. data/lib/blackbeard/metric_data/uid_generator.rb +38 -0
  30. data/lib/blackbeard/metric_data/unique.rb +1 -1
  31. data/lib/blackbeard/metric_date.rb +10 -0
  32. data/lib/blackbeard/metric_hour.rb +8 -0
  33. data/lib/blackbeard/pirate.rb +18 -0
  34. data/lib/blackbeard/redis_store.rb +10 -1
  35. data/lib/blackbeard/storable.rb +1 -0
  36. data/lib/blackbeard/version.rb +1 -1
  37. data/spec/chart_spec.rb +38 -0
  38. data/spec/chartable_spec.rb +56 -0
  39. data/spec/cohort_data_spec.rb +142 -0
  40. data/spec/cohort_metric_spec.rb +26 -0
  41. data/spec/cohort_spec.rb +31 -0
  42. data/spec/context_spec.rb +9 -1
  43. data/spec/dashboard/cohorts_spec.rb +43 -0
  44. data/spec/dashboard/groups_spec.rb +0 -7
  45. data/spec/dashboard/metrics_spec.rb +35 -0
  46. data/spec/group_metric_spec.rb +26 -0
  47. data/spec/metric_data/base_spec.rb +0 -16
  48. data/spec/metric_data/uid_generator_spec.rb +40 -0
  49. data/spec/metric_spec.rb +23 -12
  50. data/spec/pirate_spec.rb +22 -1
  51. data/spec/redis_store_spec.rb +8 -2
  52. data/spec/storable_spec.rb +3 -0
  53. metadata +29 -3
  54. data/dashboard/views/metrics/_metric_data.erb +0 -59
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ module Blackbeard
4
+ describe CohortMetric do
5
+ let(:metric) { Metric.create(:total, "one-total") }
6
+ let(:cohort) { Cohort.create(:example) }
7
+ let(:cohort_metric){ CohortMetric.new( cohort, metric) }
8
+ let(:metric_data) { cohort_metric.metric_data }
9
+
10
+ describe "add" do
11
+ let(:context) { double(:unique_identifier => 'uid', :controller => double, :user => double) }
12
+
13
+ it "should increment metric data" do
14
+ cohort.should_receive(:hour_id_for_participant).with('uid').and_return("2014010101")
15
+ metric_data.should_receive(:add_at).with("2014010101","uid",1)
16
+ cohort_metric.add(context, 1)
17
+ end
18
+
19
+ it "should not increment non participants" do
20
+ cohort.should_receive(:hour_id_for_participant).with('uid').and_return(nil)
21
+ metric_data.should_not_receive(:add_at).with("2014010101","uid",1)
22
+ cohort_metric.add(context, 1)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ module Blackbeard
4
+ describe Cohort do
5
+ let(:uid){ "an id" }
6
+ let(:hour){ Time.new(2014,3,5,1) }
7
+ let(:cohort) { Cohort.new(:happy) }
8
+ let(:cohort_data) { CohortData.new(cohort) }
9
+
10
+ describe "add" do
11
+ let(:context){ double :unique_identifier => uid }
12
+ before :each do
13
+ cohort.stub(:data).and_return(cohort_data)
14
+ end
15
+
16
+ it "should save the cohort if its new" do
17
+ cohort.should_receive(:save)
18
+ cohort.add(context)
19
+ end
20
+ it "should call add_with_force if forced used" do
21
+ cohort_data.should_receive(:add_with_force).with(uid, hour).and_return(true)
22
+ cohort.add(context, hour, true).should be_true
23
+ end
24
+ it "should call add_without_force if not forced used" do
25
+ cohort_data.should_receive(:add_without_force).with(uid, hour).and_return(true)
26
+ cohort.add(context, hour).should be_true
27
+ end
28
+ end
29
+
30
+ end
31
+ end
data/spec/context_spec.rb CHANGED
@@ -9,6 +9,7 @@ module Blackbeard
9
9
  let(:total_metric) { Metric.create(:total, :things) }
10
10
  let(:unique_metric) { Metric.create(:unique, :things) }
11
11
  let(:test) { Test.create(:example_test) }
12
+ let(:cohort) { Cohort.create(:joined) }
12
13
 
13
14
  describe "#add_total" do
14
15
  it "should call add on the total metric" do
@@ -26,6 +27,14 @@ module Blackbeard
26
27
  end
27
28
  end
28
29
 
30
+ describe "#add_to_cohort" do
31
+ it "should call add on the cohort" do
32
+ pirate.should_receive(:cohort).with("joined"){ cohort }
33
+ cohort.should_receive(:add).with(context, nil, false)
34
+ context.add_to_cohort(:joined)
35
+ end
36
+ end
37
+
29
38
  describe "#ab_test" do
30
39
  before :each do
31
40
  pirate.should_receive(:test).with(test.id).and_return(test)
@@ -94,7 +103,6 @@ module Blackbeard
94
103
  inactive_feature.should_receive(:active_for?).with(context).and_return(false)
95
104
  context.feature_active?(:inactive_feature).should be_false
96
105
  end
97
-
98
106
  end
99
107
 
100
108
  end
@@ -0,0 +1,43 @@
1
+ require File.expand_path(File.dirname(__FILE__) + './../spec_helper')
2
+
3
+ require 'rack/test'
4
+ require 'blackbeard/dashboard'
5
+
6
+ module Blackbeard
7
+ describe Dashboard do
8
+ include Rack::Test::Methods
9
+
10
+ let(:app) { Dashboard }
11
+
12
+ describe "get /cohorts" do
13
+ it "should list all the groups" do
14
+ Cohort.create("jostling")
15
+ get "/cohorts"
16
+
17
+ last_response.should be_ok
18
+ last_response.body.should include('cohorts')
19
+ end
20
+ end
21
+
22
+ describe "get /cohorts/:id" do
23
+ it "should show a metric" do
24
+ cohort = Cohort.create("jostling")
25
+ get "/cohorts/#{cohort.id}"
26
+
27
+ last_response.should be_ok
28
+ last_response.body.should include("jostling")
29
+ end
30
+ end
31
+
32
+ describe "post /cohorts/:id" do
33
+ it "should update the cohort" do
34
+ cohort = Cohort.create("jostling")
35
+ post "/cohorts/#{cohort.id}", :name => 'hello'
36
+
37
+ last_response.should be_ok
38
+ Cohort.find("jostling").name.should == 'hello'
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -9,13 +9,6 @@ module Blackbeard
9
9
 
10
10
  let(:app) { Dashboard }
11
11
 
12
- describe "get /" do
13
- it "should redirect" do
14
- get "/"
15
- last_response.should be_ok
16
- end
17
- end
18
-
19
12
  describe "get /groups" do
20
13
  it "should list all the groups" do
21
14
  Group.create("jostling")
@@ -27,6 +27,27 @@ module Blackbeard
27
27
  last_response.should be_ok
28
28
  last_response.body.should include("jostling")
29
29
  end
30
+
31
+ it "should show a metric with a group" do
32
+ metric = Metric.create("total", "jostling")
33
+ group = Group.create(:example)
34
+ metric.add_group(group)
35
+ get "/metrics/#{metric.type}/#{metric.type_id}", :group_id => group.id
36
+
37
+ last_response.should be_ok
38
+ last_response.body.should include("jostling")
39
+ end
40
+
41
+ it "should show a metric with a cohort" do
42
+ metric = Metric.create("total", "jostling")
43
+ cohort = Cohort.create(:example)
44
+ metric.add_cohort(cohort)
45
+ get "/metrics/#{metric.type}/#{metric.type_id}", :cohort_id => cohort.id
46
+
47
+ last_response.should be_ok
48
+ last_response.body.should include("jostling")
49
+ end
50
+
30
51
  end
31
52
 
32
53
  describe "post /metrics/:type/:type_id" do
@@ -53,5 +74,19 @@ module Blackbeard
53
74
  end
54
75
 
55
76
 
77
+ describe "post /metrics/:type/:type_id/cohorts" do
78
+ it "should add a cohort to the metric" do
79
+ metric = Metric.create("total", "jostling")
80
+ cohort = Cohort.create("admin")
81
+ post "/metrics/#{metric.type}/#{metric.type_id}/cohorts", :cohort_id => cohort.id
82
+
83
+ last_response.should be_redirect
84
+ follow_redirect!
85
+ last_request.url.should == "http://example.org/metrics/#{metric.type}/#{metric.type_id}?cohort_id=#{cohort.id}"
86
+ metric.has_cohort?(cohort).should be_true
87
+ end
88
+ end
89
+
90
+
56
91
  end
57
92
  end
@@ -0,0 +1,26 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ module Blackbeard
4
+ describe GroupMetric do
5
+ let(:metric) { Metric.create(:total, "one-total") }
6
+ let(:group) { Group.create(:example) }
7
+ let(:metric_data) { group_metric.metric_data }
8
+ # let(:group_metric_data) { metric.metric_data(group) }
9
+ let(:group_metric){ GroupMetric.new( group, metric) }
10
+ describe "add" do
11
+ let(:context) { double(:unique_identifier => 'uid', :controller => double, :user => double) }
12
+
13
+ it "should increment metric data" do
14
+ group.stub(:segment_for).and_return("segment")
15
+ metric_data.should_receive(:add).with("uid",1, "segment")
16
+ group_metric.add(context, 1)
17
+ end
18
+
19
+ it "should not increment nil segments" do
20
+ group.stub(:segment_for).and_return(nil)
21
+ metric_data.should_not_receive(:add).with("uid",1, "segment")
22
+ group_metric.add(context, 1)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -27,22 +27,6 @@ module Blackbeard
27
27
  end
28
28
  end
29
29
 
30
- describe "recent_hours" do
31
- let(:start_at) { Time.new(2014,1,1,12,0,0) }
32
-
33
- it "should return results for recent hours" do
34
- metric_data.recent_hours(3, start_at).should have(3).metric_hours
35
- end
36
- end
37
-
38
- describe "recent_days" do
39
- let(:start_on) { Date.new(2014,1,3) }
40
-
41
- it "should return results for recent days" do
42
- metric_data.recent_days(3, start_on).should have(3).metric_days
43
- end
44
- end
45
-
46
30
  describe "hour_keys_for_day" do
47
31
  it "should return 1 key for every hour from morning to night" do
48
32
  keys_for_day = metric_data.hour_keys_for_day(Date.new(2014,1,1))
@@ -0,0 +1,40 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ module Blackbeard
4
+ module MetricData
5
+
6
+ describe UidGenerator do
7
+ let(:metric) { Blackbeard::Metric.new(:total, "one-total") }
8
+ let(:metric2) { Blackbeard::Metric.new(:total, "two-total") }
9
+ let(:metric_data) { metric.metric_data }
10
+ let(:metric_data2) { metric2.metric_data }
11
+
12
+ context "already existing uid" do
13
+ it "should return the existing uid" do
14
+ UidGenerator.new(metric_data).uid.should == UidGenerator.new(metric_data).uid
15
+ end
16
+ end
17
+ context "new metric_data" do
18
+ it "should increment to the next uid" do
19
+ uid = UidGenerator.new(metric_data).uid
20
+ UidGenerator.new(metric_data2).uid.to_i.should == uid.to_i + 1
21
+ end
22
+ end
23
+
24
+ describe "lookup field" do
25
+ context "with a cohort" do
26
+ it "should return the field unique to cohort" do
27
+ metric.save
28
+ cohort = Cohort.create(:example_cohort)
29
+ cohort_metric = CohortMetric.new(cohort, metric)
30
+ metric.add_cohort(cohort)
31
+ gen1 = UidGenerator.new(cohort_metric.metric_data)
32
+ gen2 = UidGenerator.new(metric.metric_data)
33
+ gen1.send(:lookup_field).should_not == gen2.send(:lookup_field)
34
+ end
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
data/spec/metric_spec.rb CHANGED
@@ -3,9 +3,7 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
  module Blackbeard
4
4
  describe Metric do
5
5
  let(:metric) { Metric.create(:total, "one-total") }
6
- let(:group) { Group.create(:example) }
7
6
  let(:metric_data) { metric.metric_data }
8
- let(:group_metric_data) { metric.metric_data(group) }
9
7
 
10
8
  describe "self.all" do
11
9
  before :each do
@@ -20,31 +18,31 @@ module Blackbeard
20
18
 
21
19
  describe "add" do
22
20
  let(:context) { double(:unique_identifier => 'uid', :controller => double, :user => double) }
23
- before :each do
24
- metric.stub(:groups).and_return([group])
25
- end
26
21
 
27
22
  it "should increment metric data" do
28
23
  metric_data.should_receive(:add).with("uid",1)
29
24
  metric.add(context, 1)
30
25
  end
31
26
 
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" )
27
+ it "should call add on all group metrics" do
28
+ group_metric = double
29
+ metric.should_receive(:group_metrics).and_return([group_metric])
30
+ group_metric.should_receive(:add).with(context, 1)
35
31
  metric.add(context, 1)
36
32
  end
37
33
 
38
- it "should not increment nil segments" do
39
- group.stub(:segment_for).and_return(nil)
40
- group_metric_data.should_not_receive(:add)
34
+ it "should call add on all cohort metrics" do
35
+ cohort_metric = double
36
+ metric.should_receive(:cohort_metrics).and_return([cohort_metric])
37
+ cohort_metric.should_receive(:add).with(context, 1)
41
38
  metric.add(context, 1)
42
39
  end
40
+
43
41
  end
44
42
 
45
43
  describe "addable_groups" do
44
+ let!(:group) { Group.create(:example) }
46
45
  it "should include the groups not added" do
47
- group # to initialize it
48
46
  metric.addable_groups.map{|g| g.id }.should include(group.id)
49
47
  end
50
48
 
@@ -54,5 +52,18 @@ module Blackbeard
54
52
  end
55
53
  end
56
54
 
55
+ describe "addable_cohorts" do
56
+ let!(:cohort){ Cohort.create(:example)}
57
+
58
+ it "should include the groups not added" do
59
+ metric.addable_cohorts.map{|g| g.id }.should include(cohort.id)
60
+ end
61
+
62
+ it "should not include the group added" do
63
+ metric.add_cohort(cohort)
64
+ metric.addable_cohorts.map{|c| c.id }.should_not include(cohort.id)
65
+ end
66
+ end
67
+
57
68
  end
58
69
  end
data/spec/pirate_spec.rb CHANGED
@@ -21,6 +21,11 @@ describe Blackbeard::Pirate do
21
21
  4.times{ pirate.feature(name) }
22
22
  end
23
23
 
24
+ it "should get cohorts once" do
25
+ Blackbeard::Cohort.should_receive(:find_or_create).with(name).once.and_return(double)
26
+ 4.times{ pirate.cohort(name) }
27
+ end
28
+
24
29
  end
25
30
 
26
31
  describe "#context" do
@@ -45,10 +50,14 @@ describe Blackbeard::Pirate do
45
50
  expect{ pirate.ab_test(:example, :on => 1, :off => 2) }.to_not raise_error
46
51
  end
47
52
 
48
- it "active? should not raise error" do
53
+ it "feature_active? should not raise error" do
49
54
  expect{ pirate.feature_active?(:example) }.to_not raise_error
50
55
  end
51
56
 
57
+ it "add_to_cohort should not raise error" do
58
+ expect{ pirate.add_to_cohort(:example) }.to_not raise_error
59
+ end
60
+
52
61
  end
53
62
  context "with context set" do
54
63
  let(:user){ double }
@@ -73,6 +82,18 @@ describe Blackbeard::Pirate do
73
82
  set_context.should_receive(:feature_active?).with(:example_feature).and_return(false)
74
83
  pirate.feature_active?(:example_feature)
75
84
  end
85
+
86
+ it "should delegate add_to_cohort" do
87
+ timestamp = double
88
+ set_context.should_receive(:add_to_cohort).with(:example, timestamp).and_return(true)
89
+ pirate.add_to_cohort(:example, timestamp)
90
+ end
91
+
92
+ it "should delegate add_to_cohort!" do
93
+ timestamp = double
94
+ set_context.should_receive(:add_to_cohort!).with(:example, timestamp).and_return(true)
95
+ pirate.add_to_cohort!(:example, timestamp)
96
+ end
76
97
  end
77
98
  end
78
99
 
@@ -14,9 +14,9 @@ module Blackbeard
14
14
  db.hash_get('a_hash', 'hello').should == 'world'
15
15
  end
16
16
 
17
- it "should multi set" do
17
+ it "should multi set and get" do
18
18
  db.hash_multi_set('a_hash', {:one => 'two', :three => 'four'})
19
- db.hash_get('a_hash','three').should == 'four'
19
+ db.hash_multi_get('a_hash',['three','one']).should == ['four','two']
20
20
  end
21
21
 
22
22
  it "should not raise error on multi set with empty hash" do
@@ -52,6 +52,12 @@ module Blackbeard
52
52
  db.hash_get('a_hash', 'field').should == "3.5"
53
53
  end
54
54
 
55
+ it "should increment by int" do
56
+ db.hash_increment_by('a_hash', 'field', 1)
57
+ db.hash_increment_by('a_hash', 'field', 2.5)
58
+ db.hash_get('a_hash', 'field').should == "3"
59
+ end
60
+
55
61
  it "should determine if key exists" do
56
62
  expect{
57
63
  db.hash_set('a_hash', 'field', 'exists')
@@ -108,6 +108,9 @@ module Blackbeard
108
108
  new_record.save
109
109
  }.to change{ new_record.new_record }.from(true).to(false)
110
110
  end
111
+ it "should return true" do
112
+ new_record.save.should be_true
113
+ end
111
114
  end
112
115
 
113
116
  describe "master_key" do