modesty 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +13 -0
- data/Gemfile.lock +18 -0
- data/LICENSE +21 -0
- data/README.md +121 -0
- data/Rakefile +29 -0
- data/VERSION +1 -0
- data/init.rb +1 -0
- data/lib/modesty.rb +26 -0
- data/lib/modesty/api.rb +14 -0
- data/lib/modesty/core_ext.rb +5 -0
- data/lib/modesty/core_ext/array.rb +21 -0
- data/lib/modesty/core_ext/fixnum.rb +5 -0
- data/lib/modesty/core_ext/hash.rb +39 -0
- data/lib/modesty/core_ext/string.rb +9 -0
- data/lib/modesty/core_ext/symbol.rb +33 -0
- data/lib/modesty/datastore.rb +51 -0
- data/lib/modesty/datastore/redis.rb +180 -0
- data/lib/modesty/experiment.rb +87 -0
- data/lib/modesty/experiment/base.rb +47 -0
- data/lib/modesty/experiment/builder.rb +48 -0
- data/lib/modesty/experiment/console.rb +4 -0
- data/lib/modesty/experiment/data.rb +75 -0
- data/lib/modesty/experiment/interface.rb +29 -0
- data/lib/modesty/experiment/significance.rb +376 -0
- data/lib/modesty/experiment/stats.rb +163 -0
- data/lib/modesty/frameworks/rails.rb +27 -0
- data/lib/modesty/identity.rb +32 -0
- data/lib/modesty/load.rb +80 -0
- data/lib/modesty/load/load_experiments.rb +14 -0
- data/lib/modesty/load/load_metrics.rb +17 -0
- data/lib/modesty/metric.rb +56 -0
- data/lib/modesty/metric/base.rb +38 -0
- data/lib/modesty/metric/builder.rb +23 -0
- data/lib/modesty/metric/data.rb +133 -0
- data/modesty.gemspec +192 -0
- data/spec/core_ext_spec.rb +17 -0
- data/spec/experiment_spec.rb +239 -0
- data/spec/identity_spec.rb +161 -0
- data/spec/load_spec.rb +87 -0
- data/spec/metric_spec.rb +176 -0
- data/spec/rails_spec.rb +48 -0
- data/spec/redis_spec.rb +29 -0
- data/spec/significance_spec.rb +147 -0
- data/spec/spec.opts +1 -0
- data/test/myapp/config/modesty.yml +9 -0
- data/test/myapp/modesty/experiments/cookbook.rb +4 -0
- data/test/myapp/modesty/metrics/kitchen_metrics.rb +9 -0
- data/test/myapp/modesty/metrics/stove/burner_metrics.rb +2 -0
- data/vendor/.piston.yml +8 -0
- data/vendor/mock_redis/.gitignore +2 -0
- data/vendor/mock_redis/README +8 -0
- data/vendor/mock_redis/lib/mock_redis.rb +10 -0
- data/vendor/mock_redis/lib/mock_redis/hash.rb +61 -0
- data/vendor/mock_redis/lib/mock_redis/list.rb +6 -0
- data/vendor/mock_redis/lib/mock_redis/misc.rb +69 -0
- data/vendor/mock_redis/lib/mock_redis/set.rb +108 -0
- data/vendor/mock_redis/lib/mock_redis/string.rb +32 -0
- data/vendor/redis-rb/.gitignore +8 -0
- data/vendor/redis-rb/LICENSE +20 -0
- data/vendor/redis-rb/README.markdown +129 -0
- data/vendor/redis-rb/Rakefile +155 -0
- data/vendor/redis-rb/benchmarking/logging.rb +62 -0
- data/vendor/redis-rb/benchmarking/pipeline.rb +51 -0
- data/vendor/redis-rb/benchmarking/speed.rb +21 -0
- data/vendor/redis-rb/benchmarking/suite.rb +24 -0
- data/vendor/redis-rb/benchmarking/thread_safety.rb +38 -0
- data/vendor/redis-rb/benchmarking/worker.rb +71 -0
- data/vendor/redis-rb/examples/basic.rb +15 -0
- data/vendor/redis-rb/examples/dist_redis.rb +43 -0
- data/vendor/redis-rb/examples/incr-decr.rb +17 -0
- data/vendor/redis-rb/examples/list.rb +26 -0
- data/vendor/redis-rb/examples/pubsub.rb +31 -0
- data/vendor/redis-rb/examples/sets.rb +36 -0
- data/vendor/redis-rb/examples/unicorn/config.ru +3 -0
- data/vendor/redis-rb/examples/unicorn/unicorn.rb +20 -0
- data/vendor/redis-rb/lib/redis.rb +676 -0
- data/vendor/redis-rb/lib/redis/client.rb +201 -0
- data/vendor/redis-rb/lib/redis/compat.rb +21 -0
- data/vendor/redis-rb/lib/redis/connection.rb +134 -0
- data/vendor/redis-rb/lib/redis/distributed.rb +526 -0
- data/vendor/redis-rb/lib/redis/hash_ring.rb +131 -0
- data/vendor/redis-rb/lib/redis/pipeline.rb +13 -0
- data/vendor/redis-rb/lib/redis/subscribe.rb +79 -0
- data/vendor/redis-rb/redis.gemspec +29 -0
- data/vendor/redis-rb/test/commands_on_hashes_test.rb +46 -0
- data/vendor/redis-rb/test/commands_on_lists_test.rb +50 -0
- data/vendor/redis-rb/test/commands_on_sets_test.rb +78 -0
- data/vendor/redis-rb/test/commands_on_sorted_sets_test.rb +109 -0
- data/vendor/redis-rb/test/commands_on_strings_test.rb +70 -0
- data/vendor/redis-rb/test/commands_on_value_types_test.rb +88 -0
- data/vendor/redis-rb/test/connection_handling_test.rb +87 -0
- data/vendor/redis-rb/test/db/.gitignore +1 -0
- data/vendor/redis-rb/test/distributd_key_tags_test.rb +53 -0
- data/vendor/redis-rb/test/distributed_blocking_commands_test.rb +54 -0
- data/vendor/redis-rb/test/distributed_commands_on_hashes_test.rb +12 -0
- data/vendor/redis-rb/test/distributed_commands_on_lists_test.rb +18 -0
- data/vendor/redis-rb/test/distributed_commands_on_sets_test.rb +85 -0
- data/vendor/redis-rb/test/distributed_commands_on_strings_test.rb +50 -0
- data/vendor/redis-rb/test/distributed_commands_on_value_types_test.rb +73 -0
- data/vendor/redis-rb/test/distributed_commands_requiring_clustering_test.rb +141 -0
- data/vendor/redis-rb/test/distributed_connection_handling_test.rb +25 -0
- data/vendor/redis-rb/test/distributed_internals_test.rb +18 -0
- data/vendor/redis-rb/test/distributed_persistence_control_commands_test.rb +24 -0
- data/vendor/redis-rb/test/distributed_publish_subscribe_test.rb +90 -0
- data/vendor/redis-rb/test/distributed_remote_server_control_commands_test.rb +31 -0
- data/vendor/redis-rb/test/distributed_sorting_test.rb +21 -0
- data/vendor/redis-rb/test/distributed_test.rb +60 -0
- data/vendor/redis-rb/test/distributed_transactions_test.rb +34 -0
- data/vendor/redis-rb/test/encoding_test.rb +16 -0
- data/vendor/redis-rb/test/helper.rb +86 -0
- data/vendor/redis-rb/test/internals_test.rb +27 -0
- data/vendor/redis-rb/test/lint/hashes.rb +90 -0
- data/vendor/redis-rb/test/lint/internals.rb +53 -0
- data/vendor/redis-rb/test/lint/lists.rb +93 -0
- data/vendor/redis-rb/test/lint/sets.rb +66 -0
- data/vendor/redis-rb/test/lint/sorted_sets.rb +132 -0
- data/vendor/redis-rb/test/lint/strings.rb +98 -0
- data/vendor/redis-rb/test/lint/value_types.rb +84 -0
- data/vendor/redis-rb/test/persistence_control_commands_test.rb +22 -0
- data/vendor/redis-rb/test/pipelining_commands_test.rb +78 -0
- data/vendor/redis-rb/test/publish_subscribe_test.rb +151 -0
- data/vendor/redis-rb/test/redis_mock.rb +64 -0
- data/vendor/redis-rb/test/remote_server_control_commands_test.rb +56 -0
- data/vendor/redis-rb/test/sorting_test.rb +44 -0
- data/vendor/redis-rb/test/test.conf +8 -0
- data/vendor/redis-rb/test/thread_safety_test.rb +34 -0
- data/vendor/redis-rb/test/transactions_test.rb +91 -0
- data/vendor/redis-rb/test/unknown_commands_test.rb +14 -0
- data/vendor/redis-rb/test/url_param_test.rb +52 -0
- metadata +277 -0
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'modesty'
|
2
|
+
|
3
|
+
describe Modesty, "Working with identities" do
|
4
|
+
before :each do
|
5
|
+
@id = rand 10**10
|
6
|
+
end
|
7
|
+
|
8
|
+
it "can make an identity" do
|
9
|
+
Modesty.identify! @id
|
10
|
+
Modesty.identity.should == @id
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can identify as nil" do
|
14
|
+
Modesty.identify! nil
|
15
|
+
Modesty.identity.should be_nil
|
16
|
+
end
|
17
|
+
|
18
|
+
it "can't identify as anything other than a Fixnum or nil." do
|
19
|
+
lambda do
|
20
|
+
Modesty.identify! "foo"
|
21
|
+
end.should raise_error(Modesty::IdentityError)
|
22
|
+
end
|
23
|
+
|
24
|
+
after :all do
|
25
|
+
Modesty.instance_variable_set("@identity", nil)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe Modesty, "contextual identity" do
|
30
|
+
before :each do
|
31
|
+
Modesty.experiments.clear
|
32
|
+
Modesty.metrics.clear
|
33
|
+
Modesty.data.flushdb
|
34
|
+
|
35
|
+
Modesty.new_metric :donation_amount do |m|
|
36
|
+
m.submetric :birthday_wish
|
37
|
+
end
|
38
|
+
Modesty.new_metric :donation
|
39
|
+
Modesty.new_metric :creation
|
40
|
+
|
41
|
+
Modesty.new_experiment :creation_page do |e|
|
42
|
+
e.metric :creation
|
43
|
+
e.metric :donation, :as => :creator
|
44
|
+
e.metric :donation_amount, :as => :creator
|
45
|
+
end
|
46
|
+
@e = Modesty.experiments[:creation_page]
|
47
|
+
Modesty.identify! nil
|
48
|
+
|
49
|
+
@e.chooses :experiment, :for => 500
|
50
|
+
@e.chooses :control, :for => 600
|
51
|
+
@e.chooses :experiment, :for => 700
|
52
|
+
@group_exp = Modesty.metrics[:donation_amount/:creation_page/:experiment]
|
53
|
+
@group_ctrl = Modesty.metrics[:donation_amount/:creation_page/:control]
|
54
|
+
end
|
55
|
+
|
56
|
+
it "leaves the other metrics alone" do
|
57
|
+
Modesty.with_identity 700 do
|
58
|
+
@e.group.should == :experiment
|
59
|
+
Modesty.track! :creation
|
60
|
+
end
|
61
|
+
|
62
|
+
m = Modesty.metrics[:creation/:creation_page/:experiment]
|
63
|
+
m.distribution.should == {1 => 1}
|
64
|
+
end
|
65
|
+
|
66
|
+
it "expects a passed-in identity for the specified metrics" do
|
67
|
+
Modesty.identify! 700
|
68
|
+
lambda do
|
69
|
+
Modesty.track! :donation
|
70
|
+
end.should raise_error(Modesty::IdentityError)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "aggregates counts by the contextual identity's experiment group" do
|
74
|
+
Modesty.with_identity 600 do
|
75
|
+
lambda do
|
76
|
+
#user 600 donated 10 bucks to user 700's birthday wish!
|
77
|
+
Modesty.track! :donation_amount, 10, :with => {:creator => 700}
|
78
|
+
end.should_not raise_error
|
79
|
+
end
|
80
|
+
@group_exp.distribution.should == {10 => 1}
|
81
|
+
@group_ctrl.distribution.should == {}
|
82
|
+
|
83
|
+
@e.chooses :experiment, :for => 500
|
84
|
+
#user 500 donated 2000 to 600's wish
|
85
|
+
Modesty.with_identity 500 do
|
86
|
+
Modesty.track! :donation_amount, 2000, :with => {:creator => 600}
|
87
|
+
end
|
88
|
+
|
89
|
+
@group_exp.distribution.should == {10 => 1}
|
90
|
+
@group_ctrl.distribution.should == {2000 => 1}
|
91
|
+
end
|
92
|
+
|
93
|
+
describe "with some data" do
|
94
|
+
before :each do
|
95
|
+
Modesty.data.flushdb
|
96
|
+
|
97
|
+
# users 500 and 700 hit the experiment
|
98
|
+
@e.chooses :experiment, :for => 700
|
99
|
+
@e.chooses :control, :for => 500
|
100
|
+
|
101
|
+
# user 600, who is in the control group, donates $45 to user 700's wish
|
102
|
+
Modesty.with_identity 600 do
|
103
|
+
Modesty.track! :donation_amount, 45, :with => {:creator => 700}
|
104
|
+
end
|
105
|
+
|
106
|
+
# user 500, who is in the experiment group, donates $30 to user 700's wish
|
107
|
+
Modesty.with_identity 500 do
|
108
|
+
Modesty.track! :donation_amount/:birthday_wish, 30, :with => {:creator => 700}
|
109
|
+
end
|
110
|
+
|
111
|
+
# a guest donates $15 to user 700's wish
|
112
|
+
Modesty.with_identity nil do
|
113
|
+
Modesty.track! :donation_amount, 15, :with => {:creator => 700}
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it "knows its experiment" do
|
118
|
+
@group_exp.experiment.should == @e
|
119
|
+
end
|
120
|
+
|
121
|
+
it "aggregates work" do
|
122
|
+
@group_exp.aggregate_by(:creators).should == {700 => 90}
|
123
|
+
@group_ctrl.aggregate_by(:creators).should == {}
|
124
|
+
end
|
125
|
+
|
126
|
+
it "distributions work" do
|
127
|
+
@group_exp.distribution_by(:creator).should == {90 => 1}
|
128
|
+
@group_ctrl.distribution_by(:creator).should == {}
|
129
|
+
end
|
130
|
+
|
131
|
+
it "defaults to the given param as identity" do
|
132
|
+
@group_exp.aggregate.should == @group_exp.aggregate_by(:creator)
|
133
|
+
@group_exp.distribution.should == @group_exp.distribution_by(:creator)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "all, unique, and unidentified_users work" do
|
137
|
+
@group_exp.all(:creators).should == [700]
|
138
|
+
@group_exp.all(:users).sort.should == [500, 600]
|
139
|
+
@group_exp.data.unidentified_users.should == 1
|
140
|
+
end
|
141
|
+
|
142
|
+
it "aggregates and distributes on the experiment" do
|
143
|
+
@e.aggregates(:donation_amount).should == {
|
144
|
+
:experiment => {
|
145
|
+
700 => 90
|
146
|
+
},
|
147
|
+
:control => {
|
148
|
+
500 => 0,
|
149
|
+
}
|
150
|
+
}
|
151
|
+
@e.distributions(:donation_amount).should == {
|
152
|
+
:experiment => {
|
153
|
+
90 => 1,
|
154
|
+
},
|
155
|
+
:control => {
|
156
|
+
0 => 1
|
157
|
+
}
|
158
|
+
}
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/spec/load_spec.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'modesty'
|
2
|
+
|
3
|
+
describe "the config file" do
|
4
|
+
before :all do
|
5
|
+
Modesty.root = File.join(
|
6
|
+
Modesty::TEST,
|
7
|
+
'myapp',
|
8
|
+
'modesty'
|
9
|
+
)
|
10
|
+
Modesty.environment = 'test'
|
11
|
+
Modesty.load_config!
|
12
|
+
end
|
13
|
+
|
14
|
+
it "can set the metrics directory" do
|
15
|
+
Modesty.metrics_dir.should ==
|
16
|
+
File.expand_path(
|
17
|
+
File.join(
|
18
|
+
File.dirname(__FILE__),
|
19
|
+
"../test/myapp/modesty/metrics"
|
20
|
+
)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
|
24
|
+
after :all do
|
25
|
+
Modesty.root = nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "loading metrics" do
|
30
|
+
before :all do
|
31
|
+
Modesty.root = File.join(
|
32
|
+
Modesty::TEST,
|
33
|
+
'myapp'
|
34
|
+
)
|
35
|
+
|
36
|
+
Modesty.environment = 'test'
|
37
|
+
Modesty.metrics.clear
|
38
|
+
end
|
39
|
+
|
40
|
+
it "can load metrics" do
|
41
|
+
lambda do
|
42
|
+
Modesty.load_all_metrics!
|
43
|
+
end.should_not raise_error
|
44
|
+
end
|
45
|
+
|
46
|
+
it "doesn't try to load directories" do
|
47
|
+
lambda do
|
48
|
+
Modesty.load_all_metrics!
|
49
|
+
end.should_not raise_error(Errno::EISDIR)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "actually loads the metrics" do
|
53
|
+
[
|
54
|
+
:baked_goods,
|
55
|
+
:baked_goods/:cookies,
|
56
|
+
:baked_goods/:brownies,
|
57
|
+
:baked_goods/:cake,
|
58
|
+
:baked_goods/:cake/:chocolate,
|
59
|
+
:baked_goods/:cake/:ice_cream,
|
60
|
+
].each do |m|
|
61
|
+
Modesty.metrics.should include m
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
after :all do
|
66
|
+
Modesty.root = nil
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
describe "Loading experiments" do
|
71
|
+
before :all do
|
72
|
+
Modesty.environment = 'test'
|
73
|
+
Modesty.metrics.clear
|
74
|
+
Modesty.experiments.clear
|
75
|
+
Modesty.load_all_metrics!
|
76
|
+
end
|
77
|
+
|
78
|
+
it "can load experiments" do
|
79
|
+
lambda do
|
80
|
+
Modesty.load_all_experiments!
|
81
|
+
end.should_not raise_error
|
82
|
+
end
|
83
|
+
|
84
|
+
it "actually loads experiments" do
|
85
|
+
Modesty.experiments.should include :cookbook
|
86
|
+
end
|
87
|
+
end
|
data/spec/metric_spec.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'modesty'
|
2
|
+
|
3
|
+
describe Modesty::Metric, "Creating Metrics" do
|
4
|
+
before :each do
|
5
|
+
Modesty.set_store :redis, :mock => true
|
6
|
+
Modesty.metrics.clear
|
7
|
+
end
|
8
|
+
|
9
|
+
it "can create a metric without a block" do
|
10
|
+
m = Modesty.new_metric(:foo)
|
11
|
+
m.slug.should == :foo
|
12
|
+
Modesty.metrics[:foo].should == m
|
13
|
+
end
|
14
|
+
|
15
|
+
it "can create a metric with a block" do
|
16
|
+
m = Modesty.new_metric :foo do |m|
|
17
|
+
m.description "Foo"
|
18
|
+
end
|
19
|
+
m.slug.should == :foo
|
20
|
+
m.description.should == "Foo"
|
21
|
+
Modesty.metrics[:foo].should == m
|
22
|
+
end
|
23
|
+
|
24
|
+
it "can create submetrics" do
|
25
|
+
Modesty.new_metric :foo do |foo|
|
26
|
+
foo.description "Foo"
|
27
|
+
|
28
|
+
foo.submetric :bar do |bar|
|
29
|
+
bar.description "Bar"
|
30
|
+
|
31
|
+
bar.submetric :baz do |baz|
|
32
|
+
baz.description "Baz"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
Modesty.metrics.should include :foo
|
38
|
+
Modesty.metrics.should include :foo/:bar
|
39
|
+
Modesty.metrics.should include :foo/:bar/:baz
|
40
|
+
|
41
|
+
Modesty.metrics[:foo].parent.should == nil
|
42
|
+
Modesty.metrics[:foo/:bar].parent.should_not == nil
|
43
|
+
Modesty.metrics[:foo/:bar].parent.slug.should == :foo
|
44
|
+
Modesty.metrics[:foo/:bar/:baz].parent.should_not == nil
|
45
|
+
Modesty.metrics[:foo/:bar/:baz].parent.slug.should == :foo/:bar
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe Modesty::Metric, "Tracking Metrics" do
|
50
|
+
before :each do
|
51
|
+
Modesty.set_store :redis, :mock => true
|
52
|
+
Modesty.data.flushdb
|
53
|
+
Modesty.metrics.clear
|
54
|
+
Modesty.new_metric :foo do |foo|
|
55
|
+
foo.description "Foo"
|
56
|
+
|
57
|
+
foo.submetric :bar do |bar|
|
58
|
+
bar.description "Bar"
|
59
|
+
|
60
|
+
bar.submetric :baz do |baz|
|
61
|
+
baz.description "Baz"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it "can track a metric" do
|
68
|
+
(1..100).each do |i|
|
69
|
+
lambda { Modesty.track! :foo }.should_not raise_error
|
70
|
+
Modesty.metrics[:foo].count.to_i.should == i
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it "can track a metric with a count" do
|
75
|
+
(1..100).each do |i|
|
76
|
+
lambda { Modesty.track! :foo, 3 }.should_not raise_error
|
77
|
+
Modesty.metrics[:foo].count.to_i.should == i*3
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it "can fetch a metric for a date" do
|
82
|
+
now = Time.now
|
83
|
+
Time.stub!(:now).and_return(now-1.day)
|
84
|
+
25.times {|i| Modesty.track! :foo}
|
85
|
+
Modesty.metrics[:foo].count((now-1.day).to_date).should == 25
|
86
|
+
end
|
87
|
+
|
88
|
+
it "can fetch a metric over a date range" do
|
89
|
+
now = Time.now
|
90
|
+
Time.stub!(:now).and_return(now-1.day)
|
91
|
+
25.times {|i| Modesty.track! :foo}
|
92
|
+
Time.stub!(:now).and_return(now)
|
93
|
+
50.times {|i| Modesty.track! :foo}
|
94
|
+
Modesty.metrics[:foo].count((now-1.day).to_date..(now.to_date)).should == [25, 50]
|
95
|
+
end
|
96
|
+
|
97
|
+
it "can fetch a metric over a date range as array" do
|
98
|
+
now = Time.now
|
99
|
+
Time.stub!(:now).and_return(now-1.day)
|
100
|
+
25.times {|i| Modesty.track! :foo}
|
101
|
+
Time.stub!(:now).and_return(now)
|
102
|
+
50.times {|i| Modesty.track! :foo}
|
103
|
+
Modesty.metrics[:foo].count((now-1.day).to_date, now.to_date).should == [25, 50]
|
104
|
+
end
|
105
|
+
|
106
|
+
it "can track one submetric" do
|
107
|
+
(1..100).each do |i|
|
108
|
+
lambda { Modesty.track! :foo/:bar }.should_not raise_error
|
109
|
+
Modesty.metrics[:foo].count.to_i.should == i
|
110
|
+
Modesty.metrics[:foo/:bar].count.to_i.should == i
|
111
|
+
Modesty.metrics[:foo/:bar/:baz].count.to_i.should == 0
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
it "can track more than one submetric" do
|
116
|
+
(1..100).each do |i|
|
117
|
+
lambda { Modesty.track! :foo/:bar/:baz }.should_not raise_error
|
118
|
+
Modesty.metrics[:foo].count.to_i.should == i
|
119
|
+
Modesty.metrics[:foo/:bar].count.to_i.should == i
|
120
|
+
Modesty.metrics[:foo/:bar/:baz].count.to_i.should == i
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "can track with custom data" do
|
125
|
+
before :each do
|
126
|
+
@m = Modesty.metrics[:foo/:bar]
|
127
|
+
lambda do
|
128
|
+
Modesty.track! :foo/:bar, :with => {:zing => 56, :user => 1}
|
129
|
+
end.should_not raise_error
|
130
|
+
@m.unique(:zings).should == 1
|
131
|
+
@m.all(:zings).should include 56
|
132
|
+
|
133
|
+
lambda do
|
134
|
+
Modesty.track! :foo/:bar, :with => {:zing => 97, :user => 2}, :count => 4
|
135
|
+
end.should_not raise_error
|
136
|
+
@m.unique(:zings).should == 2
|
137
|
+
@m.all(:zings).should include 97
|
138
|
+
|
139
|
+
lambda do
|
140
|
+
Modesty.track! :foo/:bar, 7, :with => {:zing => 97}
|
141
|
+
end.should_not raise_error
|
142
|
+
end
|
143
|
+
|
144
|
+
it "and count unique" do
|
145
|
+
@m.unique(:zings).should == 2
|
146
|
+
@m.unique(:zings, :all).should == 2
|
147
|
+
end
|
148
|
+
|
149
|
+
it "and bucket by dates" do
|
150
|
+
@m.unique(:zings, Date.parse('1/1/2002')).should == 0
|
151
|
+
end
|
152
|
+
|
153
|
+
it "and count all" do
|
154
|
+
@m.all(:zings).count.should == 2
|
155
|
+
end
|
156
|
+
|
157
|
+
|
158
|
+
it "and keep an aggregate by users" do
|
159
|
+
@m.aggregate.should == {1 => 1, 2 => 4}
|
160
|
+
end
|
161
|
+
|
162
|
+
it "and keep a distribution by users" do
|
163
|
+
@m.distribution.should == {1 => 1, 4 => 1}
|
164
|
+
@m.distribution(:all).should == {1 => 1, 4 => 1}
|
165
|
+
end
|
166
|
+
|
167
|
+
it "and keep an aggregate by custom data" do
|
168
|
+
@m.aggregate_by(:zings).should == {56 => 1, 97 => 11}
|
169
|
+
end
|
170
|
+
|
171
|
+
it "and keep a distribution by custom data" do
|
172
|
+
@m.distribution_by(:zings).should == {1 => 1, 11 => 1}
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
end
|
data/spec/rails_spec.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'modesty'
|
2
|
+
|
3
|
+
describe "bootstrap" do
|
4
|
+
before :all do
|
5
|
+
Modesty.data.flushdb
|
6
|
+
Modesty.metrics.clear
|
7
|
+
Modesty.experiments.clear
|
8
|
+
|
9
|
+
unless defined? Rails
|
10
|
+
class Rails
|
11
|
+
def self.root
|
12
|
+
File.join(
|
13
|
+
File.expand_path(File.dirname(__FILE__)),
|
14
|
+
'../test/myapp'
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.configuration
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.env
|
23
|
+
'test'
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.after_initialize
|
27
|
+
yield
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
require 'modesty/frameworks/rails'
|
33
|
+
end
|
34
|
+
|
35
|
+
it "bootstraps Redis" do
|
36
|
+
Modesty.data.store.should be_an_instance_of Redis
|
37
|
+
Modesty.data.store.client.port.should == 6379
|
38
|
+
Modesty.data.store.client.host.should == 'localhost'
|
39
|
+
end
|
40
|
+
|
41
|
+
it "loads metrics" do
|
42
|
+
Modesty.metrics.keys.should include :baked_goods
|
43
|
+
end
|
44
|
+
|
45
|
+
it "loads experiments" do
|
46
|
+
Modesty.experiments.keys.should include :cookbook
|
47
|
+
end
|
48
|
+
end
|