totalizer 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 325cabe6e33e0ec8d6ff8f104b20cb50194f8ac0
4
- data.tar.gz: 19e48c1f6977e45f048cfa9e4a6b9456b5d30ede
3
+ metadata.gz: 64378db2593e41ea508fbd469dd20f36bb010652
4
+ data.tar.gz: aa0ea7c47ed3bed604157154b31bca1f21fcd1d3
5
5
  SHA512:
6
- metadata.gz: 543b8a101ff5fcacdb94703e9372b5a3f51ea0546934b677e5ff7a940ed83600e3027fa3c257a92cacefa9d4e1407c897ae3a519b1adb3e49263815738650cba
7
- data.tar.gz: 7a6495f66fc42afe02c187347a5b228959e0e5e38945ed6db666f18567a52233904937b69201be7441a0b79f10a0610c840f85c886852487643bf57d14eb44e5
6
+ metadata.gz: ee4d5424e72d83a47e1ea9fa91de4d5b144072d90ece8c5e69aebfa1c10b5aec40ce10b6277181c2e889de34721e2fc629cf392e1ccac0fc44bbb512b53ea880
7
+ data.tar.gz: 91462bfd5e919eedf7291b8bb4d9359df1816c7fa9fbba7b19ec87a90dd6f82d5e8c15621ddee4fc8b013c3a49531214308914cf5c874a2c495b603adda8f60c
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
- # Totalizer - Calculate important metrics in your Rails application.
1
+ # Totalizer - Calculate and share important metrics in your Rails application.
2
2
 
3
- Provides tools to Ruby on Rails developers to create calculations for Acquisition, Activation, Engagement, Retention and Churn.
3
+ Totalizer makes it easy for Ruby on Rails developers to report on Acquisition, Activation, Activity, Engagement, Retention and Churn.
4
+
5
+ By defining two key metrics, Growth and Key Activity, all five reports can be generated.
6
+
7
+ Metrics are only worthwhile if your team sees them so Totalizer includes notifiers for Slack and email.
4
8
 
5
9
  ### Installation
6
10
 
@@ -16,13 +20,172 @@ Or install it yourself as:
16
20
 
17
21
  $ gem install totalizer
18
22
 
19
-
23
+ ### Configuration
24
+
25
+ You need to define your Growth Metric and Key Activity Metric in order to use Totalizer. You can do this by creating a Totalizer initializer file in your Rails application:
26
+
27
+ ```ruby
28
+ # config/initializers/totalizer.rb
29
+
30
+ Totalizer.growth_metric = Totalizer::Metric.new model: User
31
+ Totalizer.activity_metric = Totalizer::Metric.new model: Post, map: 'user_id'
32
+ ```
33
+
34
+ The Growth Metric and the Key Activity Metric need to map to the same id to allow comparison.
35
+
36
+ #### Metrics
37
+
38
+ A Metric is a calculation based on one of your models for a duration of time.
39
+
40
+ ```ruby
41
+ metric = Totalizer::Metric.new(model: User)
42
+
43
+ metric.value
44
+ #=> 10 (the number of records for the period)
45
+
46
+ metric.start
47
+ #=> 20 (the number of records before the period)
48
+
49
+ metric.finish
50
+ #=> 30 (the number of records at the end of the period)
51
+
52
+ metric.rate
53
+ #=> 0.5 (how much the number changed over the period)
54
+ ```
55
+
56
+ ##### Parameters
57
+
58
+ You can pass the following parameters into the Metric:
59
+ + `model` (required): The Rails model class that Totalizer will query.
60
+ + `date`: When to start measuring your records. Default is `now`.
61
+ + `duration`: Duration (in days) to measure your records from. Must be an integer. Default is `7`.
62
+ + `filter`: Write a custom query to determine which records to use in calculation. For example to find all users who created a public response you could pass in: `filter: "is_public = true"`.
63
+ + `map`: Which field to map records on. For example, to find unique users who did a response you could pass in: `map: 'user_id'`. Default is `id`.
64
+
65
+ #### Notifiers
66
+
67
+ By default, running the Totalizer rake tasks will output the metrics to your logs
68
+
69
+ ##### Action Mailer
70
+
71
+ You can configure Totalizer to email metrics using ActionMailer, just ensure you have ActionMailer configured.
72
+
73
+ To do this add the following lines to your initializer:
74
+
75
+ ```ruby
76
+ Totalizer.notifiers = {
77
+ action_mailer: {
78
+ from: %{"notifier" <notifier@example.com>},
79
+ to: %w{team@example.com}
80
+ }
81
+ }
82
+ ```
83
+
84
+ You can also optionally include: `subject`.
85
+
86
+ ##### Mandrill Mailer
87
+
88
+ If you are using [mandrill_mailer gem](https://github.com/renz45/mandrill_mailer) in your application then you can configure Totalizer to email metrics using MandrillMailer, just ensure you have MandrillMailer configured.
89
+
90
+ To do this add the following lines to your initializer:
91
+
92
+ ```ruby
93
+ Totalizer.notifiers = {
94
+ mandrill_mailer: {
95
+ from: %{"notifier" <notifier@example.com>},
96
+ to: %w{team@example.com}
97
+ }
98
+ }
99
+ ```
100
+
101
+ You can also optionally include: `subject`.
20
102
 
21
- ***
103
+ ##### Slack Notifier
22
104
 
23
- ## Factory
105
+ You can configure Totalizer to post to Slack by including the [slack-notifier gem](https://github.com/stevenosloan/slack-notifier) in your Gemfile.
24
106
 
25
- The Totalizer Factory makes it easy to report on Acquisition, Activation, Engagement, Retention and Churn.
107
+ ```ruby
108
+ gem 'slack-notifier'
109
+ ```
110
+
111
+ Then add the following lines to your initializer:
112
+
113
+ ```ruby
114
+ Totalizer.notifiers = {
115
+ slack: {
116
+ webhook_url: ENV['SLACK_WEBHOOK_URL'],
117
+ channel: ENV['SLACK_CHANNEL']
118
+ }
119
+ }
120
+ ```
121
+
122
+ You can also optionally include: `username`, `icon_emoji` and `color`.
123
+
124
+ ### Usage
125
+
126
+ Totalizer comes with two rake tasks you can run.
127
+
128
+ ### Daily
129
+
130
+ You don’t need to review every metric every day, but it’s good practice to monitor growth and activity on a daily basis so you can respond to any spikes or dips.
131
+
132
+ Totalizer generates your Acquisition and Activity metrics for the previous day and the previous week.
133
+
134
+ ```
135
+ $ rake totalizer:daily
136
+ ```
137
+
138
+ This will generate the following report:
139
+
140
+ ```
141
+ Acquisition
142
+ Yesterday: 20 (∆ 10%)
143
+ Last 7 days: 100 (∆ 7%)
144
+ Signed up this period (with rate of change)
145
+ Activity
146
+ Yesterday: 60 (∆ 9%)
147
+ Last 7 days: 120 (∆ 8%)
148
+ Key activities this period (with rate of change)
149
+ ```
150
+
151
+ ### Weekly
152
+
153
+ Every week it’s important to review your important metrics to ensure you are trending in the right direction.
154
+
155
+ Totalizer generates your Activation, Engagement, Retention and Churn for the previous week and previous month.
156
+
157
+ ```
158
+ $ rake totalizer:weekly
159
+ ```
160
+
161
+ This will generate the following report:
162
+
163
+ ```
164
+ Activation
165
+ Created this period and did key activity
166
+ Last 7 days: 77 → 58 (75.32%)
167
+ Last 30 days: 444 → 355 (79.95%)
168
+ Engagement
169
+ Created before this period and did key activity this period
170
+ Last 7 days: 56.18% (150/267)
171
+ Last 30 days: 34.14% (210/615)
172
+ Retention
173
+ Did key activity the previous period and again this period
174
+ Last 7 days: 61.19% (134/219)
175
+ Last 30 days: 69.42% (722/1040)
176
+ Churn
177
+ Did key activity last period but not this period over the total who did key activity last period plus new users
178
+ Last 7 days: 6.21% (9/145)
179
+ Last 30 days: 3.29% (34/1032)
180
+ ```
181
+
182
+ ***
183
+
184
+ In addition to Metric, you can also use the following underlying objects.
185
+
186
+ ### Factory
187
+
188
+ The Totalizer Factory is what generates the Acquisition, Activation, Engagement, Retention and Churn reports.
26
189
 
27
190
  By defining one growth metric, like new user creation, and one key activity metric, like creating a post, you can generate all five reports.
28
191
 
@@ -36,20 +199,16 @@ factory = Totalizer::Factory.new(growth_metric: growth_metric, activity_metric:
36
199
 
37
200
  This will return a message object for each calculation.
38
201
 
39
- #### Parameters
202
+ ##### Parameters
40
203
 
41
204
  You can pass the following parameters into the Factory:
42
205
 
43
206
  + `growth_metric`: (required) a Totalizer metric representing growth, usually a User model.
44
207
  + `activity_metric`: (required) a Totalizer metric representing the key activity a user should do within your application.
45
208
  + `date`: When to start measuring your records from. Must be a DateTime. Default is `now`.
46
- + `acquisition_duration`: Duration (in days) to measure your records. Must be an integer. Default is `7`.
47
- + `activation_duration`: Duration (in days) to measure your activation. Must be an integer. Default is `7`.
48
- + `engagement_duration`: Duration (in days) to measure your engagement. Must be an integer. Default is `7`.
49
- + `retention_duration`: Duration (in days) to measure your retention. Must be an integer. Default is `30`.
50
- + `churn_duration`: Duration (in days) to measure your churn. Must be an integer. Default is `30`.
209
+ + `duration`: Duration (in days) to measure your records. Must be an integer. Default is `7`.
51
210
 
52
- ### Acquisition
211
+ #### Acquisition
53
212
 
54
213
  ```ruby
55
214
  growth_metric = Totalizer::Metric.new(model: User)
@@ -67,7 +226,7 @@ acquisition.text
67
226
  #=> "74 (Growth rate: 10%)"
68
227
  ```
69
228
 
70
- ### Activation
229
+ #### Activation
71
230
 
72
231
  ```ruby
73
232
  growth_metric = Totalizer::Metric.new(model: User)
@@ -85,7 +244,7 @@ activation.text
85
244
  #=> "63/90 (Conversion rate: 70%)"
86
245
  ```
87
246
 
88
- ### Engagement
247
+ #### Engagement
89
248
 
90
249
  ```ruby
91
250
  growth_metric = Totalizer::Metric.new(model: User)
@@ -103,7 +262,7 @@ engagement.text
103
262
  #=> "42/350 (Engagement rate: 12%)"
104
263
  ```
105
264
 
106
- ### Retention
265
+ #### Retention
107
266
 
108
267
  ```ruby
109
268
  growth_metric = Totalizer::Metric.new(model: User)
@@ -121,7 +280,7 @@ retention.text
121
280
  #=> "42/75 56%"
122
281
  ```
123
282
 
124
- ### Churn
283
+ #### Churn
125
284
 
126
285
  ```ruby
127
286
  growth_metric = Totalizer::Metric.new(model: User)
@@ -139,57 +298,15 @@ churn.text
139
298
  #=> "33/75 44%"
140
299
  ```
141
300
 
142
- ## Make your metrics visible
143
-
144
- Metrics are only worthwhile if the team actually sees them.
145
-
146
- With the results of your Factory you can:
147
- + Post them to Slack
148
- + Send them via email
149
- + Create a dashboard view
150
-
151
- ***
152
-
153
- You can also access the underlying objects directly.
154
-
155
- ### Metric
156
-
157
- A Metric is a calculation based on one of your models for a duration of time. To create a Metric just use this in your Rails application:
158
-
159
- ```ruby
160
- metric = Totalizer::Metric.new(model: User)
161
-
162
- metric.value
163
- #=> 10 (the number of records for the period)
164
-
165
- metric.start
166
- #=> 20 (the number of records before the period)
167
-
168
- metric.finish
169
- #=> 30 (the number of records at the end of the period)
170
-
171
- metric.rate
172
- #=> 0.5 (how much the number changed over the period)
173
- ```
174
-
175
- ##### Parameters
176
-
177
- You can pass the following parameters into the Metric:
178
- + `model` (required): The Rails model class that Totalizer will query.
179
- + `date`: When to start measuring your records. Default is `now`.
180
- + `duration`: Duration (in days) to measure your records from. Must be an integer. Default is `7`.
181
- + `filter`: Write a custom query to determine which records to use in calculation. For example to find all users who created a public response you could pass in: `filter: "is_public = true"`.
182
- + `map`: Which field to map records on. For example, to find unique users who did a response you could pass in: `map: 'user_id'`. Default is `id`.
183
-
184
301
  ### Step
185
302
 
186
303
  A step allows you to easily compare two sets of ids to see who converted.
187
304
 
188
- To create a Funnel just use this in your Rails application:
305
+ To create a Step just use this in your Rails application:
189
306
 
190
307
  ```ruby
191
308
  first_metric = Totalizer::Metric.new(model: User)
192
- second_metric = Totalizer::Metric.new(model: User, filter: "actions > 0")
309
+ second_metric = Totalizer::Metric.new(model: Post, map: 'user_id')
193
310
  step = Totalizer::Step.new first_step_metric.ids, second_step_metric.ids
194
311
 
195
312
  step.start
@@ -208,15 +325,13 @@ step.ids
208
325
  You can use the result of one step to feed into another step. Continuing on the example above, you could do the following:
209
326
 
210
327
  ```ruby
211
- third_metric = Totalizer::Metric.new(model: User, filter: "actions > 5")
328
+ third_metric = Totalizer::Metric.new(model: Comment, map: 'user_id')
212
329
  next_step = Totalizer::Step.new step.ids, third_metric.ids
213
330
  ```
214
331
 
215
- ***
216
-
217
332
  ## Manual Calculations
218
333
 
219
- You can also report on Acquisition, Activation, Retention, Engagement and Churn yourself without using a Factory.
334
+ You can also report on Acquisition, Activity, Activation, Retention, Engagement and Churn yourself without using a Factory.
220
335
 
221
336
  ### Acquisition
222
337
 
@@ -230,11 +345,23 @@ acquisition.rate
230
345
  #=> 10
231
346
  ```
232
347
 
348
+ ### Activity
349
+
350
+ ```ruby
351
+ activity = Totalizer::Metric.new(model: Post, map: 'user_id')
352
+
353
+ acquisition.value
354
+ #=> 20
355
+
356
+ acquisition.rate
357
+ #=> 5
358
+ ```
359
+
233
360
  ### Activation
234
361
 
235
362
  ```ruby
236
363
  sign_up = Totalizer::Metric.new(model: User, duration: 7)
237
- do_action = Totalizer::Metric.new(model: User, filter: "actions > 0")
364
+ do_action = Totalizer::Metric.new(model: Post, map: 'user_id')
238
365
  activation = Totalizer::Step.new sign_up.ids, do_action.ids
239
366
 
240
367
  activation.start
@@ -251,7 +378,7 @@ activation.rate
251
378
 
252
379
  ```ruby
253
380
  sign_up = Totalizer::Metric.new(model: User, duration: 7)
254
- do_action = Totalizer::Metric.new(model: User, filter: "actions > 0")
381
+ do_action = Totalizer::Metric.new(model: Post, map: 'user_id')
255
382
 
256
383
  existing_active = (sign_up.start_ids & do_action.ids).size
257
384
  #=> 14
@@ -315,7 +442,7 @@ lost_existing_customers.to_f / (new_and_existing_customers - lost_existing_custo
315
442
 
316
443
  The MIT License (MIT)
317
444
 
318
- Copyright (c) 2014 Michael Dijkstra
445
+ Copyright (c) 2016 Michael Dijkstra
319
446
 
320
447
  Permission is hereby granted, free of charge, to any person obtaining a copy
321
448
  of this software and associated documentation files (the "Software"), to deal
@@ -0,0 +1,26 @@
1
+ namespace :totalizer do
2
+ task validate: :environment do
3
+ abort '! Growth metric not defined. Please define your growth metric before running this task. See https://github.com/micdijkstra/totalizer' unless Totalizer.growth_metric
4
+ abort '! Activity metric not defined. Please define your growth metric before running this task. See https://github.com/micdijkstra/totalizer' unless Totalizer.activity_metric
5
+ end
6
+
7
+ task daily: :validate do
8
+ Totalizer.logger.info "Totalizer: Daily"
9
+ messages = {
10
+ acquisition: [Totalizer.generate(:acquisition, 1), Totalizer.generate(:acquisition, 7)],
11
+ activity: [Totalizer.generate(:activity, 1), Totalizer.generate(:activity, 7)],
12
+ }
13
+ Totalizer.notify messages
14
+ end
15
+
16
+ task weekly: :validate do
17
+ Totalizer.logger.info "Totalizer: Daily"
18
+ messages = {
19
+ activation: [Totalizer.generate(:activation, 7), Totalizer.generate(:activation, 30)],
20
+ engagement: [Totalizer.generate(:engagement, 7), Totalizer.generate(:engagement, 30)],
21
+ retention: [Totalizer.generate(:retention, 7), Totalizer.generate(:retention, 30)],
22
+ churn: [Totalizer.generate(:churn, 7), Totalizer.generate(:churn, 30)],
23
+ }
24
+ Totalizer.notify messages
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ module Totalizer
2
+ extend self
3
+ NOTIFIERS = { log: Totalizer::LogNotifier, action_mailer: Totalizer::ActionMailerNotifier, mandrill_mailer: Totalizer::MandrillMailerNotifier, slack: Totalizer::SlackNotifier }
4
+ attr_reader :growth_metric, :activity_metric
5
+
6
+ def growth_metric= metric
7
+ @growth_metric = validate_metric(metric)
8
+ end
9
+
10
+ def activity_metric= metric
11
+ @activity_metric = validate_metric(metric)
12
+ end
13
+
14
+ def validate_metric metric
15
+ raise Errors::InvalidMetric unless metric.kind_of?(Totalizer::Metric)
16
+ metric
17
+ end
18
+
19
+ def factory
20
+ @factory ||= Totalizer::Factory.new growth_metric, activity_metric
21
+ end
22
+
23
+ def date= date
24
+ factory.date = date
25
+ end
26
+
27
+ def generate build, duration=nil
28
+ factory.duration = duration
29
+ factory.send(build)
30
+ end
31
+
32
+ def notifiers
33
+ @notifiers ||= {}
34
+ end
35
+
36
+ def notifiers= notifiers
37
+ @notifiers = notifiers
38
+ end
39
+
40
+ def notify message_groups
41
+ notifiers.merge({ log: {} }).each { |notifier| fire_notification(notifier, message_groups) }
42
+ end
43
+
44
+ def fire_notification notifier_options, message_groups
45
+ notifier = NOTIFIERS[notifier_options.first]
46
+ notifier.call(message_groups, notifier_options.last)
47
+ end
48
+ end
@@ -3,6 +3,9 @@ module Totalizer
3
3
  class InvalidData < StandardError
4
4
  end
5
5
 
6
+ class InvalidMetric < InvalidData
7
+ end
8
+
6
9
  class InvalidModel < InvalidData
7
10
  end
8
11
 
@@ -1,81 +1,59 @@
1
1
  module Totalizer
2
2
  class Factory
3
- attr_accessor :growth_metric, :activity_metric, :date, :acquisition_duration, :activation_duration, :engagement_duration, :retention_duration, :churn_duration
3
+ attr_accessor :growth_metric, :activity_metric, :date, :duration
4
4
 
5
- def initialize params
6
- self.growth_metric = params[:growth_metric]
7
- self.activity_metric = params[:activity_metric]
5
+ def initialize growth_metric, activity_metric, params={}
6
+ self.growth_metric = growth_metric
7
+ self.activity_metric = activity_metric
8
8
  self.date = params[:date] || DateTime.now
9
- self.acquisition_duration = params[:acquisition_duration] || 7
10
- self.activation_duration = params[:activation_duration] || 7
11
- self.engagement_duration = params[:engagement_duration] || 7
12
- self.retention_duration = params[:retention_duration] || 30
13
- self.churn_duration = params[:churn_duration] || 30
9
+ self.duration = params[:duration] || 7
14
10
  validate_attributes!
15
11
  end
16
12
 
17
13
  def acquisition
18
- @acquisition ||= calculate_acquisition
14
+ growth_metric = Totalizer::Metric.new self.growth_metric.attributes.merge(date: date, duration: duration)
15
+ AcqusitionMessage.new(growth_metric, duration)
19
16
  end
20
17
 
21
18
  def activation
22
- @activation ||= calculate_activation
19
+ growth_metric = Totalizer::Metric.new self.growth_metric.attributes.merge(date: date, duration: duration)
20
+ activity_metric = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date, duration: duration)
21
+ step = Totalizer::Step.new growth_metric.ids, activity_metric.ids
22
+ ActivationMessage.new(step, duration)
23
+ end
24
+
25
+ def activity
26
+ activity_metric = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date, duration: duration, map: 'id')
27
+ ActivityMessage.new(activity_metric, duration)
23
28
  end
24
29
 
25
30
  def engagement
26
- @engagement ||= calculate_engagement
31
+ growth_metric = Totalizer::Metric.new self.growth_metric.attributes.merge(date: date, duration: duration)
32
+ activity_metric = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date, duration: duration)
33
+ EngagementMessage.new(growth_metric, activity_metric, duration)
27
34
  end
28
35
 
29
36
  def retention
30
- @retention || calculate_retention
37
+ previous_period = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date - duration.days, duration: duration)
38
+ this_period = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date, duration: duration)
39
+ step = Totalizer::Step.new previous_period.ids, this_period.ids
40
+ RetentionMessage.new(step, duration)
31
41
  end
32
42
 
33
43
  def churn
34
- @churn || calculate_churn
44
+ growth_metric = Totalizer::Metric.new self.growth_metric.attributes.merge(date: date, duration: duration)
45
+ previous_activity_metric = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date - duration.days, duration: duration)
46
+ this_activity_metc = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date, duration: duration)
47
+ ChurnMessage.new(growth_metric, previous_activity_metric, this_activity_metc, duration)
35
48
  end
36
49
 
37
50
  private
38
51
 
39
- def calculate_acquisition
40
- growth_metric = Totalizer::Metric.new self.growth_metric.attributes.merge(date: date, duration: acquisition_duration)
41
- AcqusitionMessage.new(growth_metric, acquisition_duration)
42
- end
43
-
44
- def calculate_activation
45
- growth_metric = Totalizer::Metric.new self.growth_metric.attributes.merge(date: date, duration: activation_duration)
46
- activity_metric = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date, duration: activation_duration)
47
- step = Totalizer::Step.new growth_metric.ids, activity_metric.ids
48
- ActivationMessage.new(step, activation_duration)
49
- end
50
-
51
- def calculate_engagement
52
- growth_metric = Totalizer::Metric.new self.growth_metric.attributes.merge(date: date, duration: engagement_duration)
53
- activity_metric = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date, duration: engagement_duration)
54
- EngagementMessage.new(growth_metric, activity_metric, engagement_duration)
55
- end
56
-
57
- def calculate_retention
58
- previous_period = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date - retention_duration.days, duration: retention_duration)
59
- this_period = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date, duration: retention_duration)
60
- step = Totalizer::Step.new previous_period.ids, this_period.ids
61
- RetentionMessage.new(step, retention_duration)
62
- end
63
-
64
- def calculate_churn
65
- growth_metric = Totalizer::Metric.new self.growth_metric.attributes.merge(date: date, duration: churn_duration)
66
- previous_activity_metric = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date - churn_duration.days, duration: churn_duration)
67
- this_activity_metc = Totalizer::Metric.new self.activity_metric.attributes.merge(date: date, duration: churn_duration)
68
- ChurnMessage.new(growth_metric, previous_activity_metric, this_activity_metc, churn_duration)
69
- end
70
-
71
52
  def validate_attributes!
72
- raise Errors::InvalidData unless growth_metric.kind_of?(Totalizer::Metric)
73
- raise Errors::InvalidData unless activity_metric.kind_of?(Totalizer::Metric)
53
+ raise Errors::InvalidMetric unless growth_metric.kind_of?(Totalizer::Metric)
54
+ raise Errors::InvalidMetric unless activity_metric.kind_of?(Totalizer::Metric)
74
55
  raise Errors::InvalidDate unless date.kind_of?(DateTime)
75
- raise Errors::InvalidDuration unless acquisition_duration.kind_of?(Integer)
76
- raise Errors::InvalidDuration unless activation_duration.kind_of?(Integer)
77
- raise Errors::InvalidDuration unless retention_duration.kind_of?(Integer)
78
- raise Errors::InvalidDuration unless churn_duration.kind_of?(Integer)
56
+ raise Errors::InvalidDuration unless duration.kind_of?(Integer)
79
57
  end
80
58
  end
81
59
  end
@@ -0,0 +1,9 @@
1
+ module Totalizer
2
+ def self.logger
3
+ @@logger ||= defined?(Rails) ? Rails.logger : Logger.new(STDOUT)
4
+ end
5
+
6
+ def self.logger=(logger)
7
+ @@logger = logger
8
+ end
9
+ end
@@ -1,67 +1,76 @@
1
1
  module Totalizer
2
2
  class Message
3
- attr_accessor :title, :pretext, :text, :duration
3
+ attr_accessor :description, :text, :duration
4
4
 
5
- def days_string
6
- "#{duration if duration != 1} #{'day'.pluralize(duration)}"
5
+ def period_string
6
+ if duration > 1
7
+ "Last #{duration} #{'day'.pluralize(duration)}"
8
+ else
9
+ duration == 0 ? 'Today' : 'Yesterday'
10
+ end
11
+ end
12
+
13
+ def percentage_string value
14
+ "#{(value.to_f * 100).round(2).to_s.gsub(/.0$/, '')}%"
7
15
  end
8
16
  end
9
17
 
10
- class AcqusitionMessage < Message
11
- def initialize growth_metric, duration
18
+ class MetricMessage < Message
19
+ def initialize metric, duration
12
20
  self.duration = duration
13
- self.title = 'Acqusition'
14
- self.pretext = "Signed up in the last #{days_string}"
15
- self.text = "#{growth_metric.value} (Growth rate: #{(growth_metric.rate * 100).round(0)}%)"
21
+ self.text = "#{period_string}: #{metric.value} (∆ #{percentage_string(metric.rate)})"
22
+ end
23
+ end
24
+
25
+ class AcqusitionMessage < MetricMessage
26
+ def initialize metric, duration
27
+ super
28
+ self.description = "Signed up this period (with rate of change)"
29
+ end
30
+ end
31
+
32
+ class ActivityMessage < MetricMessage
33
+ def initialize metric, duration
34
+ super
35
+ self.description = "Key activities this period (with rate of change)"
16
36
  end
17
37
  end
18
38
 
19
- class StepMessage < Message
39
+ class ActivationMessage < Message
20
40
  def initialize step, duration
21
41
  self.duration = duration
22
- self.text = "#{step.finish}/#{step.start} (Conversion rate: #{(step.rate * 100).round(0)}%)"
42
+ self.text = "#{period_string}: #{step.start} → #{step.finish} (#{percentage_string(step.rate)})"
43
+ self.description = "Created this period and did key activity"
23
44
  end
24
45
  end
25
46
 
26
47
  class EngagementMessage < Message
27
48
  def initialize growth_metric, activity_metric, duration
28
49
  self.duration = duration
29
- self.title = 'Engagement'
30
50
 
31
51
  existing_active = (growth_metric.start_ids & activity_metric.ids).size
32
-
33
- self.pretext = "Signed up more than #{days_string} ago and did key activity in the last #{days_string}"
34
- self.text = "#{existing_active}/#{growth_metric.start} (Engagement rate: #{(existing_active.to_f / growth_metric.start.to_f * 100).round(0)}%)"
52
+ self.text = "#{period_string}: #{percentage_string existing_active.to_f / growth_metric.start.to_f} (#{existing_active}/#{growth_metric.start})"
53
+ self.description = "Created before this period and did key activity this period"
35
54
  end
36
55
  end
37
56
 
38
- class ActivationMessage < StepMessage
57
+ class RetentionMessage < Message
39
58
  def initialize step, duration
40
- super
41
- self.title = 'Activation'
42
- self.pretext = "Signed up in the last #{days_string} and did key activity"
43
- end
44
- end
45
-
46
- class RetentionMessage < StepMessage
47
- def initialize step, duration
48
- super
49
- self.title = 'Retention'
50
- self.pretext = "Did key activity more than #{days_string} ago and again in the last #{days_string}"
59
+ self.duration = duration
60
+ self.text = "#{period_string}: #{percentage_string(step.rate)} (#{step.finish}/#{step.start})"
61
+ self.description = "Did key activity the previous period and again this period"
51
62
  end
52
63
  end
53
64
 
54
65
  class ChurnMessage < Message
55
66
  def initialize growth_metric, previous_activity_metric, this_activity_metc, duration
56
67
  self.duration = duration
57
- self.title = 'Churn'
58
- self.pretext = "Acquired more than #{days_string} ago and did not do key activity in last #{days_string} over total acquired"
59
68
 
60
- new_and_existing = growth_metric.finish
69
+ new_and_existing = previous_activity_metric.value + growth_metric.value
61
70
  lost_existing = (previous_activity_metric.ids - this_activity_metc.ids).size
62
71
  final = new_and_existing - lost_existing
63
-
64
- self.text = "#{lost_existing}/#{new_and_existing} (Churn rate: #{(lost_existing.to_f / new_and_existing.to_f * 100).round(0)}%)"
72
+ self.text = "#{period_string}: #{percentage_string lost_existing.to_f / new_and_existing.to_f} (#{lost_existing}/#{new_and_existing})"
73
+ self.description = "Did key activity last period but not this period over the total who did key activity last period plus new users"
65
74
  end
66
75
  end
67
76
  end
@@ -48,7 +48,7 @@ module Totalizer
48
48
  end
49
49
 
50
50
  def rate
51
- (finish - start).to_f / start.to_f
51
+ start == 0 ? 0 : (finish - start).to_f / start.to_f
52
52
  end
53
53
 
54
54
  private
@@ -0,0 +1,9 @@
1
+ require 'action_mailer'
2
+
3
+ module Totalizer
4
+ class ActionMailerNotifier < EmailNotifier
5
+ def self.send body, options
6
+ ActionMailer::Base.mail(from: options[:from], to: options[:to], subject: options[:subject], body: body).deliver_now
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,4 @@
1
+ module Totalizer
2
+ class BaseNotifier
3
+ end
4
+ end
@@ -0,0 +1,23 @@
1
+ require 'action_mailer'
2
+
3
+ module Totalizer
4
+ class EmailNotifier < BaseNotifier
5
+ DEFAULT_OPTIONS = { subject: '[Totalizer] Your important metrics', from: 'totalizer@totalizer.io' }
6
+
7
+ def self.call(message_groups, options)
8
+ body = ""
9
+ message_groups.each do |message_type, messages|
10
+ body += "\n#{message_type.to_s.capitalize}\n"
11
+ description = messages.map{ |message| message.description }.uniq.join("\n")
12
+ body += "#{description}\n"
13
+
14
+ text = messages.map{ |message| message.text }.join("\n")
15
+ body += "#{text}\n"
16
+ end
17
+
18
+ body += "\n— Totalizer"
19
+
20
+ self.send body, DEFAULT_OPTIONS.merge(options)
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ module Totalizer
2
+ class LogNotifier < BaseNotifier
3
+ def self.call(message_groups, options)
4
+ message_groups.each do |message_type, messages|
5
+ Totalizer.logger.info message_type.to_s.capitalize
6
+
7
+ description = messages.map{ |message| message.description }.uniq.join(", ")
8
+ Totalizer.logger.info " #{description}"
9
+
10
+ messages.each { |message| Totalizer.logger.info " #{message.text}" }
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ require 'mandrill_mailer'
2
+
3
+ module Totalizer
4
+ class MandrillMailerNotifier< EmailNotifier
5
+ def self.send body, options
6
+ MandrillMailer::MessageMailer.mandrill_mail(from: options[:from], to: options[:to], subject: options[:subject], text: body).deliver_now
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ require 'slack-notifier'
2
+
3
+ module Totalizer
4
+ class SlackNotifier < BaseNotifier
5
+ DEFAULT_OPTIONS = { username: 'Totalizer', icon_emoji: ":nerd_face:", color: '#5767ff' }
6
+
7
+ def self.call(message_groups, options)
8
+ notifier = Slack::Notifier.new options[:webhook_url], DEFAULT_OPTIONS.merge(options)
9
+ message_groups.each do |message_type, messages|
10
+ text = messages.map{ |message| message.text }.join("\n")
11
+ description = messages.map{ |message| message.description }.uniq.join("\n")
12
+ notifier.ping message_type.to_s.capitalize, attachments: [{ fallback: text, color: options[:color], text: text, footer: description }]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,6 @@
1
+ require "totalizer/notifier/base_notifier"
2
+ require "totalizer/notifier/log_notifier"
3
+ require 'totalizer/notifier/email_notifier'
4
+ require 'totalizer/notifier/action_mailer_notifier'
5
+ require 'totalizer/notifier/mandrill_mailer_notifier'
6
+ require 'totalizer/notifier/slack_notifier'
@@ -0,0 +1,12 @@
1
+ require 'totalizer'
2
+ require 'rails'
3
+
4
+ module Totalizer
5
+ class Railtie < Rails::Railtie
6
+ railtie_name :totalizer
7
+
8
+ rake_tasks do
9
+ load "tasks/totalizer.rake"
10
+ end
11
+ end
12
+ end
@@ -1,3 +1,3 @@
1
1
  module Totalizer
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/totalizer.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  require "totalizer/version"
2
+ require 'totalizer/logger'
2
3
  require 'totalizer/errors'
3
4
  require 'totalizer/metric'
4
5
  require 'totalizer/step'
5
6
  require 'totalizer/factory'
6
7
  require 'totalizer/message'
8
+ require 'totalizer/notifier'
9
+ require 'totalizer/railtie' if defined?(Rails)
10
+ require "totalizer/base"
@@ -5,39 +5,27 @@ describe Totalizer::Factory do
5
5
  let(:activity_metric) { Totalizer::Metric.new model: Post, map: 'user_id' }
6
6
  let(:duration) { nil }
7
7
  let(:date) { nil }
8
- let(:factory) { Totalizer::Factory.new growth_metric: growth_metric, activity_metric: activity_metric, date: date, retention_duration: 7, churn_duration: 7 }
8
+ let(:factory) { Totalizer::Factory.new growth_metric, activity_metric, date: date }
9
9
 
10
10
  describe "Validate" do
11
11
  it "requires metrics" do
12
- expect{ Totalizer::Factory.new(growth_metric: 'fake', activity_metric: 'metric') }.to raise_exception(Totalizer::Errors::InvalidData)
12
+ expect{ Totalizer::Factory.new('fake', 'metric') }.to raise_exception(Totalizer::Errors::InvalidMetric)
13
13
  end
14
14
 
15
15
  it "requires activity metric" do
16
- expect{ Totalizer::Factory.new(growth_metric: growth_metric, activity_metric: 'metric') }.to raise_exception(Totalizer::Errors::InvalidData)
16
+ expect{ Totalizer::Factory.new(growth_metric, 'metric') }.to raise_exception(Totalizer::Errors::InvalidMetric)
17
17
  end
18
18
 
19
19
  it "requires growth metric" do
20
- expect{ Totalizer::Factory.new(growth_metric: 'fake', activity_metric: activity_metric) }.to raise_exception(Totalizer::Errors::InvalidData)
20
+ expect{ Totalizer::Factory.new('fake', activity_metric) }.to raise_exception(Totalizer::Errors::InvalidMetric)
21
21
  end
22
22
 
23
23
  it "requires a valid datetime" do
24
- expect{ Totalizer::Factory.new(growth_metric: growth_metric, activity_metric: activity_metric, date: 'Whenever') }.to raise_exception(Totalizer::Errors::InvalidDate)
24
+ expect{ Totalizer::Factory.new(growth_metric, activity_metric, date: 'Whenever') }.to raise_exception(Totalizer::Errors::InvalidDate)
25
25
  end
26
26
 
27
- it "requires a acquisition duration" do
28
- expect{ Totalizer::Factory.new(growth_metric: growth_metric, activity_metric: activity_metric, acquisition_duration: 'Whatever') }.to raise_exception(Totalizer::Errors::InvalidDuration)
29
- end
30
-
31
- it "requires a activation duration" do
32
- expect{ Totalizer::Factory.new(growth_metric: growth_metric, activity_metric: activity_metric, activation_duration: 'Whatever') }.to raise_exception(Totalizer::Errors::InvalidDuration)
33
- end
34
-
35
- it "requires a retention duration" do
36
- expect{ Totalizer::Factory.new(growth_metric: growth_metric, activity_metric: activity_metric, retention_duration: 'Whatever') }.to raise_exception(Totalizer::Errors::InvalidDuration)
37
- end
38
-
39
- it "requires a churn duration" do
40
- expect{ Totalizer::Factory.new(growth_metric: growth_metric, activity_metric: activity_metric, churn_duration: 'Whatever') }.to raise_exception(Totalizer::Errors::InvalidDuration)
27
+ it "requires a duration" do
28
+ expect{ Totalizer::Factory.new(growth_metric, activity_metric, duration: 'Whatever') }.to raise_exception(Totalizer::Errors::InvalidDuration)
41
29
  end
42
30
  end
43
31
 
@@ -47,7 +35,7 @@ describe Totalizer::Factory do
47
35
  end
48
36
 
49
37
  it "defaults to 7 day acquisition duration" do
50
- expect(factory.acquisition_duration).to eq 7
38
+ expect(factory.duration).to eq 7
51
39
  end
52
40
  end
53
41
 
@@ -69,72 +57,43 @@ describe Totalizer::Factory do
69
57
  end
70
58
 
71
59
  describe "Acquisition" do
72
- it "returns title" do
73
- expect(factory.acquisition.title).to eq 'Acqusition'
74
- end
75
-
76
- it "returns pretext" do
77
- expect(factory.acquisition.pretext).to eq 'Signed up in the last 7 days'
60
+ it "returns text" do
61
+ expect(factory.acquisition.text).to eq "Last 7 days: 2 (∆ 50%)"
78
62
  end
63
+ end
79
64
 
65
+ describe "Activity" do
80
66
  it "returns text" do
81
- expect(factory.acquisition.text).to eq "2 (Growth rate: 50%)"
67
+ expect(factory.activity.text).to eq "Last 7 days: 2 ( 50%)"
82
68
  end
83
69
  end
84
70
 
85
71
  describe "Activation" do
86
- it "returns title" do
87
- expect(factory.activation.title).to eq 'Activation'
88
- end
89
-
90
- it "returns pretext" do
91
- expect(factory.activation.pretext).to eq 'Signed up in the last 7 days and did key activity'
92
- end
93
-
94
72
  it "returns text" do
95
- expect(factory.activation.text).to eq "1/2 (Conversion rate: 50%)"
73
+ expect(factory.activation.text).to eq "Last 7 days: 2 → 1 (50%)"
96
74
  end
97
75
  end
98
76
 
99
77
  describe "Engagement" do
100
- it "returns title" do
101
- expect(factory.engagement.title).to eq 'Engagement'
102
- end
103
-
104
- it "returns pretext" do
105
- expect(factory.engagement.pretext).to eq 'Signed up more than 7 days ago and did key activity in the last 7 days'
106
- end
107
-
108
78
  it "returns text" do
109
- expect(factory.engagement.text).to eq "1/4 (Engagement rate: 25%)"
79
+ expect(factory.engagement.text).to eq "Last 7 days: 25% (1/4)"
110
80
  end
111
81
  end
112
82
 
113
83
  describe "Retention" do
114
- it "returns title" do
115
- expect(factory.retention.title).to eq 'Retention'
116
- end
117
-
118
- it "returns pretext" do
119
- expect(factory.retention.pretext).to eq 'Did key activity more than 7 days ago and again in the last 7 days'
120
- end
121
-
122
84
  it "returns text" do
123
- expect(factory.retention.text).to eq "1/4 (Conversion rate: 25%)"
85
+ expect(factory.retention.text).to eq "Last 7 days: 25% (1/4)"
124
86
  end
125
87
  end
126
88
 
127
89
  describe "Churn" do
128
- it "returns title" do
129
- expect(factory.churn.title).to eq 'Churn'
130
- end
131
-
132
- it "returns pretext" do
133
- expect(factory.churn.pretext).to eq 'Acquired more than 7 days ago and did not do key activity in last 7 days over total acquired'
90
+ let(:user_7) { FactoryGirl.create :user, created_at: 13.days.ago }
91
+ before do
92
+ FactoryGirl.create :post, user_id: user_7.id, created_at: 13.days.ago
134
93
  end
135
94
 
136
95
  it "returns text" do
137
- expect(factory.churn.text).to eq "3/6 (Churn rate: 50%)"
96
+ expect(factory.churn.text).to eq "Last 7 days: 57.14% (4/7)"
138
97
  end
139
98
  end
140
99
  end
@@ -66,7 +66,7 @@ describe Totalizer::Metric do
66
66
  expect(metric.finish).to eq 3
67
67
  end
68
68
 
69
- it "calculates the rate of rate" do
69
+ it "calculates the rate of change" do
70
70
  expect(metric.rate).to eq 2
71
71
  end
72
72
 
@@ -77,6 +77,14 @@ describe Totalizer::Metric do
77
77
  expect{ metric.value }.to raise_exception(ActiveRecord::StatementInvalid)
78
78
  end
79
79
  end
80
+
81
+ describe 'with 0 records' do
82
+ let(:metric) { Totalizer::Metric.new(model: User, filter: ['created_at > ?', DateTime.now] ) }
83
+
84
+ it "calculates the rate of change" do
85
+ expect(metric.rate).to eq 0
86
+ end
87
+ end
80
88
  end
81
89
 
82
90
  describe "Filter" do
@@ -109,7 +117,7 @@ describe Totalizer::Metric do
109
117
  expect(metric.finish).to eq 2
110
118
  end
111
119
 
112
- it "calculates the rate of rate" do
120
+ it "calculates the rate of change" do
113
121
  expect(metric.rate).to eq 1
114
122
  end
115
123
  end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe Totalizer do
4
+ describe "Validate" do
5
+ it "require a growth metrics" do
6
+ expect{ Totalizer.growth_metric = 'fake' }.to raise_exception(Totalizer::Errors::InvalidMetric)
7
+ end
8
+
9
+ it "require an activity metric" do
10
+ expect{ Totalizer.activity_metric = 'fake' }.to raise_exception(Totalizer::Errors::InvalidMetric)
11
+ end
12
+ end
13
+
14
+ describe "Initialize" do
15
+ let(:growth_metric) { Totalizer::Metric.new model: User }
16
+ let(:activity_metric) { Totalizer::Metric.new model: Post, map: 'user_id' }
17
+
18
+ before do
19
+ Totalizer.growth_metric = growth_metric
20
+ Totalizer.activity_metric = activity_metric
21
+ end
22
+
23
+ it "creates a factory" do
24
+ expect(Totalizer.factory).not_to eq nil
25
+ expect(Totalizer.factory.kind_of?(Totalizer::Factory)).to eq true
26
+ end
27
+ end
28
+
29
+ describe ".notify" do
30
+ let(:growth_metric) { Totalizer::Metric.new model: User }
31
+ let(:message) { Totalizer::AcqusitionMessage.new(growth_metric, 1) }
32
+ let(:messages) { { activation: [message] } }
33
+ before do
34
+ allow(Totalizer::ActionMailerNotifier).to receive(:send)
35
+ allow(Totalizer::MandrillMailerNotifier).to receive(:send)
36
+ allow( Slack::Notifier::DefaultHTTPClient).to receive(:post)
37
+ end
38
+
39
+ it "does not send to Slack by default" do
40
+ Totalizer.notify messages
41
+ expect(Totalizer::SlackNotifier).not_to receive(:call)
42
+ end
43
+
44
+ describe "with action mailer notifier configured" do
45
+ before do
46
+ Totalizer.notifiers = {
47
+ action_mailer: {
48
+ to: 'admin@example.com'
49
+ }
50
+ }
51
+ end
52
+
53
+ it "sends Email" do
54
+ Totalizer.notify messages
55
+ expect(Totalizer::ActionMailerNotifier).to respond_to(:send).with(2).arguments
56
+ end
57
+ end
58
+
59
+ describe "with mandrill mailer notifier configured" do
60
+ before do
61
+ Totalizer.notifiers = {
62
+ mandrill_mailer: {
63
+ to: 'admin@example.com'
64
+ }
65
+ }
66
+ end
67
+
68
+ it "sends Email" do
69
+ Totalizer.notify messages
70
+ expect(Totalizer::MandrillMailerNotifier).to respond_to(:send).with(2).arguments
71
+ end
72
+ end
73
+
74
+
75
+ describe "with Slack notifier configured" do
76
+ before do
77
+ Totalizer.notifiers = {
78
+ slack: {
79
+ webhook_url: 'http://slack.webhook.url',
80
+ channel: '#general'
81
+ }
82
+ }
83
+ end
84
+
85
+ it "sends to Slack" do
86
+ Totalizer.notify messages
87
+ expect(Totalizer::SlackNotifier).to respond_to(:call).with(2).arguments
88
+ end
89
+ end
90
+ end
91
+ end
data/spec/spec_helper.rb CHANGED
@@ -11,6 +11,7 @@ require 'active_record'
11
11
  require 'factory_girl'
12
12
  require 'database_cleaner'
13
13
  require 'timecop'
14
+ require 'mandrill_mailer/offline'
14
15
 
15
16
  ActiveRecord::Base.establish_connection adapter: "sqlite3", database: ":memory:"
16
17
  load File.dirname(__FILE__) + '/support/schema.rb'
data/totalizer.gemspec CHANGED
@@ -26,4 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_development_dependency "factory_girl_rails"
27
27
  spec.add_development_dependency "database_cleaner"
28
28
  spec.add_development_dependency "timecop"
29
+ spec.add_development_dependency "actionmailer"
30
+ spec.add_development_dependency "mandrill_mailer"
31
+ spec.add_development_dependency "slack-notifier"
29
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: totalizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Dijkstra
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-01 00:00:00.000000000 Z
11
+ date: 2016-10-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -122,6 +122,48 @@ dependencies:
122
122
  - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: actionmailer
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: mandrill_mailer
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: slack-notifier
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
125
167
  description: Provides tools to Ruby on Rails developers to create calculations for
126
168
  acquisiton, activation, engagement, retention and churn.
127
169
  email:
@@ -135,16 +177,28 @@ files:
135
177
  - LICENSE.txt
136
178
  - README.md
137
179
  - Rakefile
180
+ - lib/tasks/totalizer.rake
138
181
  - lib/totalizer.rb
182
+ - lib/totalizer/base.rb
139
183
  - lib/totalizer/errors.rb
140
184
  - lib/totalizer/factory.rb
185
+ - lib/totalizer/logger.rb
141
186
  - lib/totalizer/message.rb
142
187
  - lib/totalizer/metric.rb
188
+ - lib/totalizer/notifier.rb
189
+ - lib/totalizer/notifier/action_mailer_notifier.rb
190
+ - lib/totalizer/notifier/base_notifier.rb
191
+ - lib/totalizer/notifier/email_notifier.rb
192
+ - lib/totalizer/notifier/log_notifier.rb
193
+ - lib/totalizer/notifier/mandrill_mailer_notifier.rb
194
+ - lib/totalizer/notifier/slack_notifier.rb
195
+ - lib/totalizer/railtie.rb
143
196
  - lib/totalizer/step.rb
144
197
  - lib/totalizer/version.rb
145
198
  - spec/lib/totalizer/factory_spec.rb
146
199
  - spec/lib/totalizer/metric_spec.rb
147
200
  - spec/lib/totalizer/step_spec.rb
201
+ - spec/lib/totalizer_spec.rb
148
202
  - spec/spec_helper.rb
149
203
  - spec/support/factories.rb
150
204
  - spec/support/models.rb
@@ -178,6 +232,7 @@ test_files:
178
232
  - spec/lib/totalizer/factory_spec.rb
179
233
  - spec/lib/totalizer/metric_spec.rb
180
234
  - spec/lib/totalizer/step_spec.rb
235
+ - spec/lib/totalizer_spec.rb
181
236
  - spec/spec_helper.rb
182
237
  - spec/support/factories.rb
183
238
  - spec/support/models.rb