tricle 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +3 -0
- data/Appraisals +9 -0
- data/CONTRIBUTING.md +2 -3
- data/Gemfile +13 -1
- data/Guardfile +1 -1
- data/README.md +33 -12
- data/gemfiles/rails_3.gemfile +21 -0
- data/gemfiles/rails_4.gemfile +21 -0
- data/lib/tricle/aggregation.rb +46 -0
- data/lib/tricle/email_helper.rb +32 -7
- data/lib/tricle/metric.rb +10 -3
- data/lib/tricle/presenters/group.rb +3 -5
- data/lib/tricle/templates/email.css +2 -2
- data/lib/tricle/templates/email.html.erb +7 -5
- data/lib/tricle/version.rb +1 -1
- data/spec/app/no_total_test_mailer.rb +11 -0
- data/spec/app/test_metric.rb +2 -34
- data/spec/app/test_metric_with_no_total.rb +35 -0
- data/spec/config/timecop.rb +1 -1
- data/spec/presenters/group_spec.rb +3 -4
- data/spec/presenters/report_spec.rb +5 -5
- data/spec/spec_helper.rb +0 -1
- data/spec/unit/aggregation_spec.rb +41 -0
- data/spec/unit/email_helper_spec.rb +87 -7
- data/spec/unit/mail_preview_spec.rb +2 -2
- data/spec/unit/mailer_spec.rb +22 -15
- data/spec/unit/metric_spec.rb +2 -2
- data/spec/unit/range_data_spec.rb +2 -2
- data/spec/unit/test_metric_spec.rb +3 -4
- data/tricle.gemspec +3 -13
- metadata +36 -155
- data/lib/tricle/presenters/metric.rb +0 -51
- data/spec/presenters/metric_spec.rb +0 -42
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative '../../lib/tricle/mailer'
|
2
|
+
require_relative 'test_metric_with_no_total'
|
3
|
+
|
4
|
+
class NoTotalTestMailer < Tricle::Mailer
|
5
|
+
default(
|
6
|
+
to: ['recipient1@test.com', 'recipient2@test.com'],
|
7
|
+
from: 'sender@test.com'
|
8
|
+
)
|
9
|
+
|
10
|
+
metric TestMetricWithNoTotal
|
11
|
+
end
|
data/spec/app/test_metric.rb
CHANGED
@@ -1,38 +1,6 @@
|
|
1
|
-
|
2
|
-
require_relative '../../lib/tricle/metric'
|
3
|
-
require_relative '../../lib/tricle/range_data'
|
4
|
-
|
5
|
-
class TestMetric < Tricle::Metric
|
6
|
-
attr_accessor :data_by_start_on
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
super
|
10
|
-
self.load_data
|
11
|
-
end
|
12
|
-
|
13
|
-
def load_data
|
14
|
-
filename = File.join(File.dirname(__FILE__), '..', 'fixtures', 'weeks.csv')
|
15
|
-
data = CSV.read(filename)
|
16
|
-
|
17
|
-
self.data_by_start_on = Tricle::RangeData.new
|
18
|
-
|
19
|
-
data.each do |row|
|
20
|
-
start_on = Date.parse(row[0])
|
21
|
-
val = row[2].to_i
|
22
|
-
self.data_by_start_on.add(start_on, val)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def size_for_range(start_at, end_at)
|
27
|
-
self.items_for_range(start_at, end_at).reduce(&:+)
|
28
|
-
end
|
29
|
-
|
30
|
-
def items_for_range(start_at, end_at)
|
31
|
-
start_on = start_at.to_date
|
32
|
-
end_on = end_at.to_date
|
33
|
-
self.data_by_start_on.items_for_range(start_on, end_on)
|
34
|
-
end
|
1
|
+
require_relative 'test_metric_with_no_total'
|
35
2
|
|
3
|
+
class TestMetric < TestMetricWithNoTotal
|
36
4
|
def total
|
37
5
|
self.data_by_start_on.all_items.reduce(&:+)
|
38
6
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require_relative '../../lib/tricle/metric'
|
3
|
+
require_relative '../../lib/tricle/range_data'
|
4
|
+
|
5
|
+
class TestMetricWithNoTotal < Tricle::Metric
|
6
|
+
attr_accessor :data_by_start_on
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
super
|
10
|
+
self.load_data
|
11
|
+
end
|
12
|
+
|
13
|
+
def load_data
|
14
|
+
filename = File.join(File.dirname(__FILE__), '..', 'fixtures', 'weeks.csv')
|
15
|
+
data = CSV.read(filename)
|
16
|
+
|
17
|
+
self.data_by_start_on = Tricle::RangeData.new
|
18
|
+
|
19
|
+
data.each do |row|
|
20
|
+
start_on = Date.parse(row[0])
|
21
|
+
val = row[2].to_i
|
22
|
+
self.data_by_start_on.add(start_on, val)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def size_for_range(start_at, end_at)
|
27
|
+
self.items_for_range(start_at, end_at).reduce(&:+)
|
28
|
+
end
|
29
|
+
|
30
|
+
def items_for_range(start_at, end_at)
|
31
|
+
start_on = start_at.to_date
|
32
|
+
end_on = end_at.to_date
|
33
|
+
self.data_by_start_on.items_for_range(start_on, end_on)
|
34
|
+
end
|
35
|
+
end
|
data/spec/config/timecop.rb
CHANGED
@@ -8,10 +8,9 @@ describe Tricle::Presenters::Group do
|
|
8
8
|
describe '#add_metric' do
|
9
9
|
it "should add a new metric instance" do
|
10
10
|
group.add_metric(TestMetric)
|
11
|
-
group.
|
12
|
-
|
13
|
-
|
14
|
-
presenter.metric.should be_a(TestMetric)
|
11
|
+
expect(group.metrics.size).to eq(1)
|
12
|
+
metric = group.metrics.first
|
13
|
+
expect(metric).to be_a(TestMetric)
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end
|
@@ -7,13 +7,13 @@ describe Tricle::Presenters::Report do
|
|
7
7
|
|
8
8
|
describe '#add_metric' do
|
9
9
|
it "should add a new group if none are present" do
|
10
|
-
report.sections.
|
10
|
+
expect(report.sections).to eq([])
|
11
11
|
report.add_metric(TestMetric)
|
12
|
-
report.sections.size.
|
12
|
+
expect(report.sections.size).to eq(1)
|
13
13
|
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
metrics = report.sections.first.metrics
|
15
|
+
expect(metrics.size).to eq(1)
|
16
|
+
expect(metrics.first).to be_a(TestMetric)
|
17
17
|
end
|
18
18
|
end
|
19
19
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require_relative '../../lib/tricle/aggregation'
|
3
|
+
|
4
|
+
describe Tricle::Aggregation do
|
5
|
+
let(:metric) { TestMetric.new }
|
6
|
+
|
7
|
+
describe '#days_ago' do
|
8
|
+
it "should start and end at midnight" do
|
9
|
+
expect(metric).to receive(:size_for_range).with(Time.new(2013, 7, 29), Time.new(2013, 7, 30))
|
10
|
+
metric.days_ago(3)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe '#yesterday' do
|
15
|
+
it "should start and end at midnight" do
|
16
|
+
expect(metric).to receive(:size_for_range).with(Time.new(2013, 7, 31), Time.new(2013, 8, 1))
|
17
|
+
metric.yesterday
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#weeks_ago' do
|
22
|
+
it "should start and end on Monday" do
|
23
|
+
expect(metric).to receive(:size_for_range).with(Time.new(2013, 7, 8), Time.new(2013, 7, 15))
|
24
|
+
metric.weeks_ago(3)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#last_week' do
|
29
|
+
it "should start and end on Monday" do
|
30
|
+
expect(metric).to receive(:size_for_range).with(Time.new(2013, 7, 22), Time.new(2013, 7, 29))
|
31
|
+
metric.last_week
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#week_average_this_quarter' do
|
36
|
+
it "should average the values provided by #size_for_range" do
|
37
|
+
expect(metric).to receive(:size_for_range).exactly(13).times.and_return(1)
|
38
|
+
expect(metric.week_average_this_quarter).to eq(1)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -10,25 +10,105 @@ describe Tricle::EmailHelper do
|
|
10
10
|
|
11
11
|
describe "#number_with_delimiter" do
|
12
12
|
it "should put commas between every three digits" do
|
13
|
-
helper.number_with_delimiter(1234567.89).
|
13
|
+
expect(helper.number_with_delimiter(1234567.89)).to eq('1,234,567.89')
|
14
|
+
expect(helper.number_with_delimiter(1234567)).to eq('1,234,567')
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should not put commas between every three digits of the decimal portion of non-integers" do
|
18
|
+
expect(helper.number_with_delimiter(0.123456789)).to eq('0.123456789')
|
19
|
+
expect(helper.number_with_delimiter(1234.56789)).to eq('1,234.56789')
|
20
|
+
expect(helper.number_with_delimiter(-0.123456789)).to eq('-0.123456789')
|
21
|
+
expect(helper.number_with_delimiter(-1234.56789)).to eq('-1,234.56789')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#sig_figs" do
|
26
|
+
it "works for large numbers, positive and negative" do
|
27
|
+
expect(helper.sig_figs(1234.56789)).to eq(1230)
|
28
|
+
expect(helper.sig_figs(-1234.56789)).to eq(-1230)
|
29
|
+
expect(helper.sig_figs(1234.56789).to_s).to eq('1230')
|
30
|
+
expect(helper.sig_figs(-1234.56789).to_s).to eq('-1230')
|
31
|
+
end
|
32
|
+
|
33
|
+
it "works for small numbers, positive and negative" do
|
34
|
+
expect(helper.sig_figs(0.123456789)).to eq(0.123)
|
35
|
+
expect(helper.sig_figs(-0.123456789)).to eq(-0.123)
|
36
|
+
expect(helper.sig_figs(0.123456789).to_s).to eq('0.123')
|
37
|
+
expect(helper.sig_figs(-0.123456789).to_s).to eq('-0.123')
|
38
|
+
end
|
39
|
+
|
40
|
+
it "works for numbers with leading zeroes, positive and negative" do
|
41
|
+
expect(helper.sig_figs(0.00000123456789)).to eq(0.00000123)
|
42
|
+
expect(helper.sig_figs(-0.00000123456789)).to eq(-0.00000123)
|
43
|
+
expect(helper.sig_figs(0.00000123456789).to_s).to eq('1.23e-06')
|
44
|
+
expect(helper.sig_figs(-0.00000123456789).to_s).to eq('-1.23e-06')
|
45
|
+
end
|
46
|
+
|
47
|
+
it "works for integer-like numbers, positive and negative" do
|
48
|
+
expect(helper.sig_figs(10).is_a?(Float)).to be_truthy
|
49
|
+
expect(helper.sig_figs(10).to_s).to eq('10.0')
|
50
|
+
expect(helper.sig_figs(-10).is_a?(Float)).to be_truthy
|
51
|
+
expect(helper.sig_figs(-10).to_s).to eq('-10.0')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#format_number" do
|
56
|
+
it "does not decrease the precision of large integers" do
|
57
|
+
expect(helper.format_number(123456789)).to eq('123,456,789')
|
58
|
+
expect(helper.format_number(-123456789)).to eq('-123,456,789')
|
59
|
+
end
|
60
|
+
|
61
|
+
it "decreases the precision of large numbers to no fewer than 3 sig figs" do
|
62
|
+
expect(helper.format_number(123.45)).to eq('123')
|
63
|
+
expect(helper.format_number(-123.45)).to eq('-123')
|
64
|
+
end
|
65
|
+
|
66
|
+
it "decreases the precision of small numbers to no fewer than 3 sig figs" do
|
67
|
+
expect(helper.format_number(0.00123456789)).to eq('0.00123')
|
68
|
+
expect(helper.format_number(-0.00123456789)).to eq('-0.00123')
|
14
69
|
end
|
15
70
|
end
|
16
71
|
|
17
72
|
describe "#percent_change" do
|
18
73
|
it "should prefix positive values with a +" do
|
19
|
-
helper.percent_change(110, 100).
|
74
|
+
expect(helper.percent_change(110, 100)).to eq('+10.0%')
|
20
75
|
end
|
21
76
|
|
22
77
|
it "should prefix negative values with a -" do
|
23
|
-
helper.percent_change(90, 100).
|
78
|
+
expect(helper.percent_change(90, 100)).to eq('-10.0%')
|
24
79
|
end
|
25
80
|
|
26
81
|
it "should handle a zero as the old value with positive change" do
|
27
|
-
helper.percent_change(10, 0).
|
82
|
+
expect(helper.percent_change(10, 0)).to eq('+')
|
28
83
|
end
|
29
84
|
|
30
85
|
it "should handle a zero as the old value with negative change" do
|
31
|
-
helper.percent_change(-20, 0).
|
86
|
+
expect(helper.percent_change(-20, 0)).to eq('-')
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should not display small values as 0" do
|
90
|
+
expect(helper.percent_change(100_001_234, 100_000_000)).to eq('+0.00123%')
|
91
|
+
expect(helper.percent_change(100_000_000, 100_001_234)).to eq('-0.00123%')
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should display 'no change' when unchanged" do
|
95
|
+
expect(helper.percent_change(100, 100)).to eq('No change')
|
96
|
+
expect(helper.percent_change(0, 0)).to eq('No change')
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '#percent_change_cell' do
|
101
|
+
it "should be positive with positive change and better = :higher" do
|
102
|
+
expect(helper.percent_change_cell(4, 2, :higher)).to match('good')
|
103
|
+
end
|
104
|
+
|
105
|
+
it "should be negative with positive change and better = :lower" do
|
106
|
+
expect(helper.percent_change_cell(4, 2, :lower)).to match('bad')
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should not be positive or negative with positive change and better = :none" do
|
110
|
+
expect(helper.percent_change_cell(4, 2, :none)).to_not match('good')
|
111
|
+
expect(helper.percent_change_cell(4, 2, :none)).to_not match('bad')
|
32
112
|
end
|
33
113
|
end
|
34
114
|
|
@@ -36,8 +116,8 @@ describe Tricle::EmailHelper do
|
|
36
116
|
it "should not include the last day of the week" do
|
37
117
|
start_at = Time.new(2013, 7, 22) # a Monday
|
38
118
|
markup = helper.single_week_dates_cell(start_at)
|
39
|
-
markup.
|
40
|
-
markup.
|
119
|
+
expect(markup).to include('7/22/13')
|
120
|
+
expect(markup).to include('7/28/13')
|
41
121
|
end
|
42
122
|
end
|
43
123
|
end
|
@@ -5,11 +5,11 @@ require_relative '../../lib/tricle/mail_preview'
|
|
5
5
|
describe Tricle::MailPreview do
|
6
6
|
describe 'mailer methods' do
|
7
7
|
it "should have methods corresponding to all mailers" do
|
8
|
-
Tricle::MailPreview.instance_methods(false).
|
8
|
+
expect(Tricle::MailPreview.instance_methods(false)).to include(:test_mailer)
|
9
9
|
end
|
10
10
|
|
11
11
|
it "should return a Mail::Message" do
|
12
|
-
Tricle::MailPreview.new.test_mailer.
|
12
|
+
expect(Tricle::MailPreview.new.test_mailer).to be_a(Mail::Message)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
end
|
data/spec/unit/mailer_spec.rb
CHANGED
@@ -2,12 +2,13 @@ require 'spec_helper'
|
|
2
2
|
require_relative '../../lib/tricle/mailer'
|
3
3
|
require_relative '../app/group_test_mailer'
|
4
4
|
require_relative '../app/list_test_mailer'
|
5
|
+
require_relative '../app/no_total_test_mailer'
|
5
6
|
require_relative '../app/test_mailer'
|
6
7
|
|
7
8
|
describe Tricle::Mailer do
|
8
9
|
def deliver(klass)
|
9
10
|
klass.email.deliver
|
10
|
-
ActionMailer::Base.deliveries.length.
|
11
|
+
expect(ActionMailer::Base.deliveries.length).to eq(1)
|
11
12
|
end
|
12
13
|
|
13
14
|
let(:message) { ActionMailer::Base.deliveries.last }
|
@@ -19,48 +20,54 @@ describe Tricle::Mailer do
|
|
19
20
|
end
|
20
21
|
|
21
22
|
it "should respect all the options in the 'default hash'" do
|
22
|
-
message.to.
|
23
|
-
message.from.
|
23
|
+
expect(message.to).to eq(['recipient1@test.com', 'recipient2@test.com'])
|
24
|
+
expect(message.from).to eq(['sender@test.com'])
|
24
25
|
end
|
25
26
|
|
26
27
|
it "should set the subject based on the class name" do
|
27
|
-
message.subject.
|
28
|
+
expect(message.subject).to eq("Your Test Mailer")
|
28
29
|
end
|
29
30
|
|
30
31
|
it "should include the Metric values in the HTML part" do
|
31
|
-
markup.
|
32
|
-
markup.
|
33
|
-
markup.
|
32
|
+
expect(markup).to include('Test Metric')
|
33
|
+
expect(markup).to match(/\b62\b/) # last week
|
34
|
+
expect(markup).to match(/\b787\b/) # total
|
34
35
|
end
|
35
36
|
|
36
37
|
it "should link to the Issues page in the text part" do
|
37
38
|
source = message.text_part.body.to_s
|
38
|
-
source.
|
39
|
+
expect(source).to include('github.com/artsy/tricle')
|
39
40
|
end
|
40
41
|
end
|
41
42
|
|
43
|
+
it "should exclude the total if not defined" do
|
44
|
+
deliver(NoTotalTestMailer)
|
45
|
+
expect(markup).to_not include('total')
|
46
|
+
expect(markup).to_not match(/\b787\b/)
|
47
|
+
end
|
48
|
+
|
42
49
|
describe '.group' do
|
43
50
|
it "should include the group title" do
|
44
51
|
deliver(GroupTestMailer)
|
45
|
-
markup.
|
46
|
-
markup.
|
47
|
-
markup.
|
52
|
+
expect(markup).to include("Test Group 1")
|
53
|
+
expect(markup).to include("Test Group 2")
|
54
|
+
expect(markup).to include("Test Metric")
|
48
55
|
end
|
49
56
|
end
|
50
57
|
|
51
58
|
describe '.list' do
|
52
59
|
it "should include the list title" do
|
53
60
|
deliver(ListTestMailer)
|
54
|
-
markup.
|
55
|
-
markup.
|
56
|
-
markup.
|
61
|
+
expect(markup).to include("Test Metric")
|
62
|
+
expect(markup).to include('62.0')
|
63
|
+
expect(markup).not_to include('79.0')
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
60
67
|
describe '.send_all' do
|
61
68
|
it "should .deliver all defined mailers" do
|
62
69
|
Tricle::Mailer.send_all
|
63
|
-
ActionMailer::Base.deliveries.length.
|
70
|
+
expect(ActionMailer::Base.deliveries.length).to eq(4)
|
64
71
|
end
|
65
72
|
end
|
66
73
|
end
|
data/spec/unit/metric_spec.rb
CHANGED
@@ -11,8 +11,8 @@ describe Tricle::Metric do
|
|
11
11
|
end
|
12
12
|
|
13
13
|
it "should use the number of items by default" do
|
14
|
-
metric.
|
15
|
-
metric.size_for_range(Time.now.yesterday, Time.now).
|
14
|
+
allow(metric).to receive(:items_for_range) { [1,2,3] }
|
15
|
+
expect(metric.size_for_range(Time.now.yesterday, Time.now)).to eq(3)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
@@ -11,14 +11,14 @@ describe Tricle::RangeData do
|
|
11
11
|
|
12
12
|
describe '#all_items' do
|
13
13
|
it "should return all the items" do
|
14
|
-
rd.all_items.sort.
|
14
|
+
expect(rd.all_items.sort).to eq([5, 6, 7])
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
18
|
describe '#items_for_range' do
|
19
19
|
it "should return the items for the days provided" do
|
20
20
|
items = rd.items_for_range(11.days.ago, 3.days.ago)
|
21
|
-
items.sort.
|
21
|
+
expect(items.sort).to eq([5, 6])
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
@@ -1,20 +1,19 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require_relative '../app/test_metric'
|
3
|
-
require_relative '../../lib/tricle/presenters/metric'
|
4
3
|
|
5
4
|
# test the tests
|
6
5
|
describe TestMetric do
|
7
|
-
let(:
|
6
|
+
let(:metric) { TestMetric.new }
|
8
7
|
|
9
8
|
describe '#weeks_ago' do
|
10
9
|
it "should start and end on Monday" do
|
11
|
-
|
10
|
+
expect(metric.weeks_ago(3)).to eq(51)
|
12
11
|
end
|
13
12
|
end
|
14
13
|
|
15
14
|
describe '#last_week' do
|
16
15
|
it "should start and end on Monday" do
|
17
|
-
|
16
|
+
expect(metric.last_week).to eq(62)
|
18
17
|
end
|
19
18
|
end
|
20
19
|
end
|