blackbeard 0.0.2.0 → 0.0.3.1

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 (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/context_spec.rb CHANGED
@@ -1,26 +1,101 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
- describe Blackbeard::Context do
4
- let(:pirate) { Blackbeard::Pirate.new }
5
- let(:context) { Blackbeard::Context.new(pirate, :user_id => 9) }
6
- let(:uid) { context.unique_identifier }
7
- let(:total_metric) { Blackbeard::Metric::Total.new(:total_things) }
8
- let(:unique_metric) { Blackbeard::Metric::Unique.new(:unique_things) }
9
-
10
- describe "#add_total" do
11
- it "should call add on the total metric" do
12
- pirate.should_receive(:total_metric).with(total_metric.name){ total_metric }
13
- total_metric.should_receive(:add).with(uid, 3)
14
- context.add_total( total_metric.name, 3 )
3
+ module Blackbeard
4
+ describe Context do
5
+ let(:pirate) { Pirate.new }
6
+ let(:user) { double(:id => 1) }
7
+ let(:context) { Context.new(pirate, user) }
8
+ let(:uid) { context.unique_identifier }
9
+ let(:total_metric) { Metric.new(:total, :things) }
10
+ let(:unique_metric) { Metric.new(:unique, :things) }
11
+ let(:test) { Test.new(:example_test) }
12
+
13
+ describe "#add_total" do
14
+ it "should call add on the total metric" do
15
+ pirate.should_receive(:metric).with(:total, total_metric.id){ total_metric }
16
+ total_metric.should_receive(:add).with(context, 3)
17
+ context.add_total( total_metric.id, 3 )
18
+ end
15
19
  end
16
- end
17
20
 
18
- describe "#add_unique" do
19
- it "should call add on the unique metric" do
20
- pirate.should_receive(:unique_metric).with(unique_metric.name){ unique_metric }
21
- unique_metric.should_receive(:add).with(uid)
22
- context.add_unique( unique_metric.name )
21
+ describe "#add_unique" do
22
+ it "should call add on the unique metric" do
23
+ pirate.should_receive(:metric).with(:unique, unique_metric.id){ unique_metric }
24
+ unique_metric.should_receive(:add).with(context, 1)
25
+ context.add_unique( unique_metric.id )
26
+ end
23
27
  end
24
- end
25
28
 
29
+ describe "#ab_test" do
30
+ before :each do
31
+ pirate.should_receive(:test).with(test.id).and_return(test)
32
+ end
33
+
34
+ it "should call select_variation on the test" do
35
+ test.should_receive(:add_variations).with([:on, :off]).and_return(test)
36
+ test.should_receive(:select_variation).and_return(:off)
37
+ context.ab_test(:example_test, :on => 1, :off => 2)
38
+ end
39
+
40
+ context "when passed options" do
41
+ before :each do
42
+ test.should_receive(:select_variation).and_return('double')
43
+ end
44
+
45
+ it "should return the value of selected option" do
46
+ context.ab_test(:example_test, :on => 1, :off => 2, :double => 'trouble').should == 'trouble'
47
+ end
48
+
49
+ it "should return nil when the selected variation is not an option" do
50
+ context.ab_test(:example_test, :on => 1, :off => 2).should be_nil
51
+ end
52
+ end
53
+
54
+ context "when not passed options" do
55
+ before :each do
56
+ test.should_receive(:select_variation).and_return('double')
57
+ end
58
+
59
+ it "should return a select_variation obj equal to the selected variation" do
60
+ test_result = context.ab_test(:example_test)
61
+ test_result.should be_a(Blackbeard::SelectedVariation)
62
+ test_result.should == :double
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ describe "unique_identifier" do
69
+ it "should work with users" do
70
+ Context.new(pirate, user, nil).unique_identifier.should == "a1"
71
+ end
72
+ it "should work without users" do
73
+ request = double(:cookies => {})
74
+ controller = double(:request => request)
75
+ Context.new(pirate, nil, controller).unique_identifier.should == "b1"
76
+ end
77
+ end
78
+
79
+ describe "#active?" do
80
+ let(:inactive_test) { Blackbeard::Test.new(:inactive_test) }
81
+ let(:active_test) { Blackbeard::Test.new(:active_test) }
82
+
83
+ before :each do
84
+ pirate.stub(:test).with(active_test.id).and_return(active_test)
85
+ pirate.stub(:test).with(inactive_test.id).and_return(inactive_test)
86
+ end
87
+
88
+ it "should return true when active" do
89
+ active_test.should_receive(:select_variation).and_return('active')
90
+ context.active?(:active_test).should be_true
91
+ end
92
+
93
+ it "should return true when active" do
94
+ inactive_test.should_receive(:select_variation).and_return('inactive')
95
+ context.active?(:inactive_test).should be_false
96
+ end
97
+
98
+ end
99
+
100
+ end
26
101
  end
@@ -0,0 +1,50 @@
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 /" do
13
+ it "should redirect" do
14
+ get "/"
15
+ last_response.should be_ok
16
+ end
17
+ end
18
+
19
+ describe "get /groups" do
20
+ it "should list all the groups" do
21
+ Group.new("jostling")
22
+ get "/groups"
23
+
24
+ last_response.should be_ok
25
+ last_response.body.should include('jostling')
26
+ end
27
+ end
28
+
29
+ describe "get /groups/:id" do
30
+ it "should show a metric" do
31
+ group = Group.new("jostling")
32
+ get "/groups/#{group.id}"
33
+
34
+ last_response.should be_ok
35
+ last_response.body.should include("jostling")
36
+ end
37
+ end
38
+
39
+ describe "post /groups/:id" do
40
+ it "should update the group" do
41
+ group = Group.new("jostling")
42
+ post "/groups/#{group.id}", :name => 'hello'
43
+
44
+ last_response.should be_ok
45
+ Group.new("jostling").name.should == 'hello'
46
+ end
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,20 @@
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 /" do
13
+ it "should redirect" do
14
+ get "/"
15
+ last_response.should be_ok
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,57 @@
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 /metrics" do
13
+ it "should list all the metrics" do
14
+ Metric.new("total", "jostling")
15
+ get "/metrics"
16
+
17
+ last_response.should be_ok
18
+ last_response.body.should include('jostling')
19
+ end
20
+ end
21
+
22
+ describe "get /metrics/:type/:type_id" do
23
+ it "should show a metric" do
24
+ metric = Metric.new("total", "jostling")
25
+ get "/metrics/#{metric.type}/#{metric.type_id}"
26
+
27
+ last_response.should be_ok
28
+ last_response.body.should include("jostling")
29
+ end
30
+ end
31
+
32
+ describe "post /metrics/:type/:type_id" do
33
+ it "should update the metric" do
34
+ metric = Metric.new("total", "jostling")
35
+ post "/metrics/#{metric.type}/#{metric.type_id}", :name => 'hello'
36
+
37
+ last_response.should be_ok
38
+ Metric.new(:total, "jostling").name.should == 'hello'
39
+ end
40
+ end
41
+
42
+ describe "post /metrics/:type/:type_id/groups" do
43
+ it "should add a group to the metric" do
44
+ metric = Metric.new("total", "jostling")
45
+ group = Group.new("admin")
46
+ post "/metrics/#{metric.type}/#{metric.type_id}/groups", :group_id => group.id
47
+
48
+ last_response.should be_redirect
49
+ follow_redirect!
50
+ last_request.url.should == "http://example.org/metrics/#{metric.type}/#{metric.type_id}?group_id=#{group.id}"
51
+ metric.has_group?(group).should be_true
52
+ end
53
+ end
54
+
55
+
56
+ end
57
+ 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 /tests" do
13
+ it "should list all the test" do
14
+ Test.new("jostling")
15
+ get "/tests"
16
+
17
+ last_response.should be_ok
18
+ last_response.body.should include('jostling')
19
+ end
20
+ end
21
+
22
+ describe "get /tests/:id" do
23
+ it "should show a test" do
24
+ test = Test.new("jostling")
25
+ get "/tests/#{test.id}"
26
+
27
+ last_response.should be_ok
28
+ last_response.body.should include("jostling")
29
+ end
30
+ end
31
+
32
+ describe "post /tests/:id" do
33
+ it "should update the test" do
34
+ test = Test.new("jostling")
35
+ post "/tests/#{test.id}", :name => 'hello'
36
+
37
+ last_response.should be_ok
38
+ Test.new("jostling").name.should == 'hello'
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,36 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe Blackbeard::Group do
4
+ let(:group){ Blackbeard::Group.new('example') }
5
+ let(:context){ double(:controller => double, :user => double) }
6
+ describe "segment" do
7
+ context "with no code defined" do
8
+ it "should return nil" do
9
+ group.segment_for(context).should be(nil)
10
+ end
11
+ end
12
+ context "with code defined" do
13
+ it "return nil for nil" do
14
+ Blackbeard.config.define_group(:example){ |r,u| nil }
15
+ group.segment_for(context).should be(nil)
16
+ end
17
+ it "return nil for false" do
18
+ Blackbeard.config.define_group(:example){ |r,u| false }
19
+ group.segment_for(context).should be(nil)
20
+ end
21
+ it "return string for true" do
22
+ Blackbeard.config.define_group(:example){ |r,u| true }
23
+ group.segment_for(context).should eq('example')
24
+ end
25
+ it "return string for string" do
26
+ Blackbeard.config.define_group(:example){ |r,u| 'foo' }
27
+ group.segment_for(context).should eq('foo')
28
+ end
29
+ it "return string for int" do
30
+ Blackbeard.config.define_group(:example){ |r,u| 12 }
31
+ group.segment_for(context).should eq('12')
32
+ end
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,57 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ module Blackbeard
4
+ module MetricData
5
+ describe Base do
6
+ let(:metric) { Blackbeard::Metric.new(:total, "one-total") }
7
+ let(:metric2) { Blackbeard::Metric.new(:total, "two-total") }
8
+ let(:metric_data) { metric.metric_data }
9
+
10
+ describe "key" do
11
+ it "should auto increment" do
12
+ metric_data.key.should == "data::1"
13
+ metric2.metric_data.key.should == "data::2"
14
+ end
15
+ end
16
+
17
+ describe "hour_keys" do
18
+
19
+ it "should return an empty array if no metrics" do
20
+ metric_data.send(:hour_keys).should == []
21
+ end
22
+
23
+ it "should return an array for each hour" do
24
+ metric_data.add('user1', 1)
25
+ key = metric_data.send(:key_for_hour, tz.now)
26
+ metric_data.send(:hour_keys).should have(1).key
27
+ end
28
+ end
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
+ describe "hour_keys_for_day" do
47
+ it "should return 1 key for every hour from morning to night" do
48
+ keys_for_day = metric_data.hour_keys_for_day(Date.new(2014,1,1))
49
+ keys_for_day.should have(24).keys
50
+ keys_for_day.first.should == metric_data.send(:key_for_hour, Time.new(2014,1,1,0,0,0))
51
+ keys_for_day.last.should == metric_data.send(:key_for_hour, Time.new(2014,1,1,23,0,0))
52
+ end
53
+ end
54
+
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,116 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ module Blackbeard
4
+ module MetricData
5
+
6
+ describe Total do
7
+
8
+ let(:metric) { Blackbeard::Metric.new(:total, "page views") }
9
+ let(:metric_data) { metric.metric_data }
10
+ let(:uid) { "unique identifier" }
11
+ let(:ouid) { "other unique identifier" }
12
+
13
+ describe "add" do
14
+ it "should increment the metric by the amount" do
15
+ expect{
16
+ metric_data.add(uid, 2)
17
+ }.to change{ metric_data.result_for_hour(tz.now)["total"] }.from(nil).to(2)
18
+ end
19
+
20
+ it "should increment an existing amount" do
21
+ metric_data.add(uid, 1)
22
+ expect{
23
+ metric_data.add(uid, 2)
24
+ }.to change{ metric_data.result_for_hour(tz.now)["total"] }.from(1).to(3)
25
+ end
26
+
27
+ it "should handle negatives ok" do
28
+ metric_data.add(uid, 2)
29
+ expect{
30
+ metric_data.add(uid, -1)
31
+ }.to change{ metric_data.result_for_hour(tz.now)["total"] }.from(2).to(1)
32
+ end
33
+
34
+ it "should handle floats" do
35
+ metric_data.add(uid, 2.5)
36
+ expect{
37
+ metric_data.add(uid, 1.25)
38
+ }.to change{ metric_data.result_for_hour(tz.now)["total"] }.to(3.75)
39
+ end
40
+
41
+ context "with segment" do
42
+ it "should increment the segment" do
43
+ expect{
44
+ metric_data.add(uid, 1, "segment")
45
+ }.to change{ metric_data.result_for_hour(tz.now)["segment"] }.from(nil).to(1)
46
+ end
47
+ end
48
+
49
+
50
+ end
51
+
52
+
53
+
54
+ describe "result_for_hour" do
55
+ it "should return 0 if no metric has been recorded" do
56
+ metric_data.result_for_hour(tz.now).should be_empty
57
+ end
58
+
59
+ it "should return sum if metric called more than once" do
60
+ metric_data.add(uid, 2)
61
+ metric_data.add(uid, 4)
62
+ metric_data.result_for_hour(tz.now).should == {"total"=>6.0}
63
+ end
64
+ end
65
+
66
+ describe "result_for_day" do
67
+ let(:date) { Date.new(2014,1,1) }
68
+ context "result in db" do
69
+ before :each do
70
+ day_key = metric_data.send(:key_for_date, date)
71
+ db.hash_set(day_key, "total", 4)
72
+ end
73
+ it "should return the result from db" do
74
+ metric_data.result_for_day(date).should == {"total"=>4.0}
75
+ end
76
+ it "should not remerge the results" do
77
+ metric_data.should_not_receive(:generate_result_for_day).with(date)
78
+ metric_data.result_for_day(date)
79
+ end
80
+ end
81
+
82
+ context "result not in db" do
83
+ it "should merge hours" do
84
+ metric_data.should_receive(:generate_result_for_day).with(date).and_return({"total" => "2"})
85
+ metric_data.result_for_day(date).should == {"total" => 2.0 }
86
+ end
87
+ end
88
+ end
89
+
90
+
91
+ describe "generate_result_for_day" do
92
+ let(:date) { Date.new(2014,1,1) }
93
+ before :each do
94
+ at_1am = Time.new(2014,1,1,1)
95
+ metric_data.add_at(at_1am, '1' )
96
+ metric_data.add_at(at_1am, '2' )
97
+ at_2pm = Time.new(2014,1,1,14)
98
+ metric_data.add_at(at_2pm, '2' )
99
+ metric_data.add_at(at_2pm, '3' )
100
+ metric_data.add_at(at_2pm, '4' )
101
+ end
102
+
103
+ it "should sum the hours" do
104
+ metric_data.send(:generate_result_for_day, date).should == {"total" => 5.0}
105
+ end
106
+
107
+ it "should store the result if it's not today's result" do
108
+ day_key = metric_data.send(:key_for_date, date)
109
+ db.should_receive(:hash_multi_set).with(day_key, {"total" => 5})
110
+ metric_data.send(:generate_result_for_day, date)
111
+ end
112
+
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,91 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
+
3
+ module Blackbeard
4
+ module MetricData
5
+ describe Unique do
6
+
7
+ let(:metric) { Blackbeard::Metric.new(:unique, "join") }
8
+ let(:metric_data) { metric.metric_data }
9
+ let(:uid) { "unique identifier" }
10
+ let(:ouid) { "other unique identifier" }
11
+
12
+ describe "add" do
13
+ it "should increment the metric for the uid" do
14
+ expect{
15
+ metric_data.add(uid)
16
+ }.to change{ metric_data.result_for_hour(tz.now)["uniques"] }.from(nil).to(1)
17
+ end
18
+
19
+ it "should not increment the metric for duplicate uids" do
20
+ metric_data.add(uid)
21
+ expect{
22
+ metric_data.add(uid)
23
+ }.to_not change{ metric_data.result_for_hour(tz.now) }
24
+ end
25
+
26
+ context "with segment" do
27
+ it "should increment the segment" do
28
+ expect{
29
+ metric_data.add(uid, 1, "segment")
30
+ }.to change{ metric_data.result_for_hour(tz.now)["segment"] }
31
+ end
32
+
33
+ it "should not increment the global" do
34
+ expect{
35
+ metric_data.add(uid, 1, "segment")
36
+ }.to_not change{ metric_data.result_for_hour(tz.now)["uniques"] }
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ describe "result_for_hour" do
43
+ it "should empty if no metric has been recorded" do
44
+ metric_data.result_for_hour(tz.now).should be_empty
45
+ end
46
+
47
+ it "should return 1 if metric called once" do
48
+ metric_data.add(uid)
49
+ metric_data.result_for_hour(tz.now).should == {"uniques" => 1}
50
+ end
51
+
52
+ it "should return 1 if metric called more than once" do
53
+ 3.times{ metric_data.add(uid) }
54
+ metric_data.result_for_hour(tz.now).should == {"uniques" => 1}
55
+ end
56
+
57
+ it "should return 2 if metric was called with 2 uniques" do
58
+ metric_data.add(uid)
59
+ metric_data.add(ouid)
60
+ metric_data.result_for_hour(tz.now).should == {"uniques" => 2}
61
+ end
62
+
63
+ end
64
+
65
+ describe "generate_result_for_day" do
66
+ let(:date) { Date.new(2014,1,1) }
67
+ before :each do
68
+ at_1am = Time.new(2014,1,1,1)
69
+ metric_data.add_at(at_1am, '1' )
70
+ metric_data.add_at(at_1am, '2' )
71
+ at_2pm = Time.new(2014,1,1,14)
72
+ metric_data.add_at(at_2pm, '2' )
73
+ metric_data.add_at(at_2pm, '3' )
74
+ metric_data.add_at(at_2pm, '4' )
75
+ end
76
+
77
+ it "should sum the hours" do
78
+ metric_data.send(:generate_result_for_day, date).should == {"uniques" => 4}
79
+ end
80
+
81
+ it "should store the result if it's not today's result" do
82
+ day_key = metric_data.send(:key_for_date, date)
83
+ db.should_receive(:hash_multi_set).with(day_key, {"uniques" => 4})
84
+ metric_data.send(:generate_result_for_day, date)
85
+ end
86
+
87
+ end
88
+ end
89
+ end
90
+
91
+ end