active_reporter 0.5.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +14 -0
- data/README.md +436 -0
- data/Rakefile +23 -0
- data/lib/active_reporter.rb +26 -0
- data/lib/active_reporter/aggregator.rb +9 -0
- data/lib/active_reporter/aggregator/array.rb +14 -0
- data/lib/active_reporter/aggregator/average.rb +9 -0
- data/lib/active_reporter/aggregator/base.rb +73 -0
- data/lib/active_reporter/aggregator/count.rb +23 -0
- data/lib/active_reporter/aggregator/count_if.rb +23 -0
- data/lib/active_reporter/aggregator/max.rb +9 -0
- data/lib/active_reporter/aggregator/min.rb +9 -0
- data/lib/active_reporter/aggregator/ratio.rb +23 -0
- data/lib/active_reporter/aggregator/sum.rb +13 -0
- data/lib/active_reporter/calculator.rb +2 -0
- data/lib/active_reporter/calculator/base.rb +19 -0
- data/lib/active_reporter/calculator/ratio.rb +9 -0
- data/lib/active_reporter/dimension.rb +8 -0
- data/lib/active_reporter/dimension/base.rb +150 -0
- data/lib/active_reporter/dimension/bin.rb +123 -0
- data/lib/active_reporter/dimension/bin/set.rb +162 -0
- data/lib/active_reporter/dimension/bin/table.rb +43 -0
- data/lib/active_reporter/dimension/category.rb +29 -0
- data/lib/active_reporter/dimension/enum.rb +32 -0
- data/lib/active_reporter/dimension/number.rb +51 -0
- data/lib/active_reporter/dimension/time.rb +93 -0
- data/lib/active_reporter/evaluator.rb +2 -0
- data/lib/active_reporter/evaluator/base.rb +17 -0
- data/lib/active_reporter/evaluator/block.rb +15 -0
- data/lib/active_reporter/inflector.rb +8 -0
- data/lib/active_reporter/invalid_params_error.rb +4 -0
- data/lib/active_reporter/report.rb +102 -0
- data/lib/active_reporter/report/aggregation.rb +297 -0
- data/lib/active_reporter/report/definition.rb +195 -0
- data/lib/active_reporter/report/metrics.rb +75 -0
- data/lib/active_reporter/report/validation.rb +106 -0
- data/lib/active_reporter/serializer.rb +7 -0
- data/lib/active_reporter/serializer/base.rb +103 -0
- data/lib/active_reporter/serializer/csv.rb +22 -0
- data/lib/active_reporter/serializer/form_field.rb +134 -0
- data/lib/active_reporter/serializer/hash_table.rb +12 -0
- data/lib/active_reporter/serializer/highcharts.rb +200 -0
- data/lib/active_reporter/serializer/nested_hash.rb +11 -0
- data/lib/active_reporter/serializer/table.rb +21 -0
- data/lib/active_reporter/tracker.rb +2 -0
- data/lib/active_reporter/tracker/base.rb +15 -0
- data/lib/active_reporter/tracker/delta.rb +9 -0
- data/lib/active_reporter/version.rb +3 -0
- data/lib/tasks/active_reporter_tasks.rake +4 -0
- data/spec/acceptance/data_spec.rb +381 -0
- data/spec/active_reporter/aggregator_spec.rb +102 -0
- data/spec/active_reporter/dimension/base_spec.rb +102 -0
- data/spec/active_reporter/dimension/bin/set_spec.rb +83 -0
- data/spec/active_reporter/dimension/bin/table_spec.rb +47 -0
- data/spec/active_reporter/dimension/bin_spec.rb +77 -0
- data/spec/active_reporter/dimension/category_spec.rb +60 -0
- data/spec/active_reporter/dimension/enum_spec.rb +94 -0
- data/spec/active_reporter/dimension/number_spec.rb +71 -0
- data/spec/active_reporter/dimension/time_spec.rb +61 -0
- data/spec/active_reporter/report_spec.rb +597 -0
- data/spec/active_reporter/serializer/hash_table_spec.rb +45 -0
- data/spec/active_reporter/serializer/highcharts_spec.rb +113 -0
- data/spec/active_reporter/serializer/table_spec.rb +62 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +26 -0
- data/spec/dummy/app/controllers/application_controller.rb +5 -0
- data/spec/dummy/app/controllers/site_controller.rb +11 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/models/author.rb +4 -0
- data/spec/dummy/app/models/comment.rb +4 -0
- data/spec/dummy/app/models/data_builder.rb +112 -0
- data/spec/dummy/app/models/post.rb +6 -0
- data/spec/dummy/app/models/post_report.rb +14 -0
- data/spec/dummy/app/views/layouts/application.html.erb +17 -0
- data/spec/dummy/app/views/site/report.html.erb +73 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +26 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +22 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +42 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +57 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/db/migrate/20150714202319_add_dummy_models.rb +25 -0
- data/spec/dummy/db/schema.rb +43 -0
- data/spec/dummy/db/seeds.rb +1 -0
- data/spec/dummy/log/test.log +37033 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/factories/factories.rb +29 -0
- data/spec/spec_helper.rb +40 -0
- data/spec/support/float.rb +8 -0
- metadata +385 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveReporter::Dimension::Base do
|
4
|
+
def new_dimension(dimension_params = {}, report_params = {}, opts = {})
|
5
|
+
report_params[:dimensions] = { foo: dimension_params }
|
6
|
+
ActiveReporter::Dimension::Base.new(
|
7
|
+
:foo,
|
8
|
+
OpenStruct.new(params: report_params),
|
9
|
+
opts
|
10
|
+
)
|
11
|
+
end
|
12
|
+
|
13
|
+
describe '#filter_values' do
|
14
|
+
it 'accepts one' do
|
15
|
+
dimension = new_dimension(only: 'bar')
|
16
|
+
expect(dimension.filter_values).to eq %w(bar)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'accepts many' do
|
20
|
+
dimension = new_dimension(only: %w(bar baz))
|
21
|
+
expect(dimension.filter_values).to eq %w(bar baz)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'determines #filtering?' do
|
25
|
+
dimension = new_dimension(only: %w(bar baz))
|
26
|
+
expect(dimension).to be_filtering
|
27
|
+
|
28
|
+
dimension = new_dimension
|
29
|
+
expect(dimension).not_to be_filtering
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe '#sort_order' do
|
34
|
+
it 'can be desc/asc, falls back to root, defaults to asc' do
|
35
|
+
dimension = new_dimension
|
36
|
+
expect(dimension.sort_order).to eq 'ASC'
|
37
|
+
|
38
|
+
dimension = new_dimension(sort_desc: true)
|
39
|
+
expect(dimension.sort_order).to eq 'DESC'
|
40
|
+
|
41
|
+
dimension = new_dimension(sort_desc: false)
|
42
|
+
expect(dimension.sort_order).to eq 'ASC'
|
43
|
+
|
44
|
+
dimension = new_dimension({}, sort_desc: true)
|
45
|
+
expect(dimension.sort_order).to eq 'DESC'
|
46
|
+
|
47
|
+
dimension = new_dimension({}, sort_desc: false)
|
48
|
+
expect(dimension.sort_order).to eq 'ASC'
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#null_order' do
|
53
|
+
it 'can be first/last, falls back to root, defaults to first (only if postgres)' do
|
54
|
+
if ActiveReporter.database_type == :postgres
|
55
|
+
dimension = new_dimension
|
56
|
+
expect(dimension.null_order).to eq 'NULLS FIRST'
|
57
|
+
|
58
|
+
dimension = new_dimension(nulls_last: true)
|
59
|
+
expect(dimension.null_order).to eq 'NULLS LAST'
|
60
|
+
|
61
|
+
dimension = new_dimension(nulls_last: false)
|
62
|
+
expect(dimension.null_order).to eq 'NULLS FIRST'
|
63
|
+
|
64
|
+
dimension = new_dimension({}, nulls_last: true)
|
65
|
+
expect(dimension.null_order).to eq 'NULLS LAST'
|
66
|
+
|
67
|
+
dimension = new_dimension({}, nulls_last: false)
|
68
|
+
expect(dimension.null_order).to eq 'NULLS FIRST'
|
69
|
+
else
|
70
|
+
dimension = new_dimension
|
71
|
+
expect(dimension.null_order).to be_blank
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#relate' do
|
77
|
+
it 'defaults to the identity function' do
|
78
|
+
dimension = new_dimension
|
79
|
+
expect(dimension.relate(5)).to eq 5
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'can be overridden, e.g. for joins' do
|
83
|
+
dimension = new_dimension({}, {}, relation: ->(r) { r + 5 })
|
84
|
+
expect(dimension.relate(5)).to eq 10
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#expression' do
|
89
|
+
it 'defaults to treating name as a column of the report klass table' do
|
90
|
+
dimension = ActiveReporter::Dimension::Base.new(
|
91
|
+
:bar,
|
92
|
+
OpenStruct.new(table_name: 'foo')
|
93
|
+
)
|
94
|
+
expect(dimension.expression).to eq('foo.bar')
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'can be overridden' do
|
98
|
+
dimension = new_dimension({}, {}, table_name: :baz, attribute: :bat)
|
99
|
+
expect(dimension.expression).to eq 'baz.bat'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveReporter::Dimension::Bin::Set do
|
4
|
+
describe '.from_hash' do
|
5
|
+
it 'builds a bin from a hash or nil' do
|
6
|
+
bin = described_class.from_hash(min: 1, max: 2)
|
7
|
+
expect(bin.min).to eq 1
|
8
|
+
expect(bin.max).to eq 2
|
9
|
+
|
10
|
+
bin = described_class.from_hash(nil)
|
11
|
+
expect(bin.min).to eq nil
|
12
|
+
expect(bin.max).to eq nil
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.from_sql' do
|
17
|
+
it 'builds a bin from a bin text string' do
|
18
|
+
bin = described_class.from_sql("1,2")
|
19
|
+
expect(bin.min).to eq '1'
|
20
|
+
expect(bin.max).to eq '2'
|
21
|
+
|
22
|
+
bin = described_class.from_sql("1,")
|
23
|
+
expect(bin.min).to eq '1'
|
24
|
+
expect(bin.max).to eq nil
|
25
|
+
|
26
|
+
bin = described_class.from_sql(",2")
|
27
|
+
expect(bin.min).to eq nil
|
28
|
+
expect(bin.max).to eq '2'
|
29
|
+
|
30
|
+
bin = described_class.from_sql(",")
|
31
|
+
expect(bin.min).to eq nil
|
32
|
+
expect(bin.max).to eq nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '#contains_sql' do
|
37
|
+
it 'returns SQL checking if expr is in the bin' do
|
38
|
+
bin = described_class.new(1, 2)
|
39
|
+
expect(bin.contains_sql('foo')).to eq "(foo >= 1 AND foo < 2)"
|
40
|
+
|
41
|
+
bin = described_class.new(1, nil)
|
42
|
+
expect(bin.contains_sql('foo')).to eq "foo >= 1"
|
43
|
+
|
44
|
+
bin = described_class.new(nil, 2)
|
45
|
+
expect(bin.contains_sql('foo')).to eq "foo < 2"
|
46
|
+
|
47
|
+
bin = described_class.new(nil, nil)
|
48
|
+
expect(bin.contains_sql('foo')).to eq "foo IS NULL"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#to_json' do
|
53
|
+
it 'reexpresses the bin as a hash' do
|
54
|
+
bin = described_class.new(1, 2)
|
55
|
+
json = { a: bin }.to_json
|
56
|
+
expect(JSON.parse(json)).to eq('a' => { 'min' => 1, 'max' => 2 })
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'hashing' do
|
61
|
+
it 'works with hashes' do
|
62
|
+
bin1 = described_class.new(1, 2)
|
63
|
+
bin2 = described_class.new(1, 2)
|
64
|
+
bin3 = { min: 1, max: 2 }
|
65
|
+
|
66
|
+
h = { bin3 => 'foo' }
|
67
|
+
expect(h[bin1]).to eq 'foo'
|
68
|
+
expect(h[bin2]).to eq 'foo'
|
69
|
+
expect(h[bin3]).to eq 'foo'
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'works with nil' do
|
73
|
+
bin1 = described_class.new(nil, nil)
|
74
|
+
bin2 = described_class.new(nil, nil)
|
75
|
+
bin3 = nil
|
76
|
+
|
77
|
+
h = { bin3 => 'foo' }
|
78
|
+
expect(h[bin1]).to eq 'foo'
|
79
|
+
expect(h[bin2]).to eq 'foo'
|
80
|
+
expect(h[bin3]).to eq 'foo'
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveReporter::Dimension::Bin::Table do
|
4
|
+
let(:bin_set) { ActiveReporter::Dimension::Bin::Set }
|
5
|
+
|
6
|
+
describe '#filter' do
|
7
|
+
it 'ORs together predicates across bins' do
|
8
|
+
table = described_class.new([
|
9
|
+
bin_set.new(nil, nil),
|
10
|
+
bin_set.new(0, nil),
|
11
|
+
bin_set.new(nil, 10),
|
12
|
+
bin_set.new(3, 5)
|
13
|
+
])
|
14
|
+
|
15
|
+
sql = table.filter(Post, 'x').to_sql
|
16
|
+
|
17
|
+
expect(sql).to include "WHERE (x IS NULL OR x >= 0 OR x < 10 OR (x >= 3 AND x < 5))"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe '#group' do
|
22
|
+
it 'joins to a union of bin rows, then groups by the range' do
|
23
|
+
table = described_class.new([
|
24
|
+
bin_set.new(nil, nil),
|
25
|
+
bin_set.new(0, nil),
|
26
|
+
bin_set.new(nil, 10),
|
27
|
+
bin_set.new(3, 5)
|
28
|
+
])
|
29
|
+
|
30
|
+
sql = table.group(Post, 'likes', 'likes').to_sql
|
31
|
+
|
32
|
+
expect(sql).to start_with "SELECT likes_bin_table.bin_text AS likes"
|
33
|
+
|
34
|
+
if ActiveReporter.database_type == :mysql
|
35
|
+
expect(sql).to include "SELECT NULL AS min, NULL AS max, ',' AS bin_text"
|
36
|
+
expect(sql).to include "SELECT 0 AS min, NULL AS max, '0,' AS bin_text"
|
37
|
+
expect(sql).to include "SELECT NULL AS min, 10 AS max, ',10' AS bin_text"
|
38
|
+
expect(sql).to include "SELECT 3 AS min, 5 AS max, '3,5' AS bin_text"
|
39
|
+
else
|
40
|
+
expect(sql).to include "SELECT NULL AS min, NULL AS max, CAST(',' AS text) AS bin_text"
|
41
|
+
expect(sql).to include "SELECT 0 AS min, NULL AS max, CAST('0,' AS text) AS bin_text"
|
42
|
+
expect(sql).to include "SELECT NULL AS min, 10 AS max, CAST(',10' AS text) AS bin_text"
|
43
|
+
expect(sql).to include "SELECT 3 AS min, 5 AS max, CAST('3,5' AS text) AS bin_text"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveReporter::Dimension::Bin do
|
4
|
+
def new_dimension(dimension_params = {}, report_params = {}, opts = {})
|
5
|
+
report_params[:dimensions] = { foo: dimension_params }
|
6
|
+
ActiveReporter::Dimension::Bin.new(:foo,
|
7
|
+
OpenStruct.new(params: report_params),
|
8
|
+
opts
|
9
|
+
)
|
10
|
+
end
|
11
|
+
|
12
|
+
def expect_error(&block)
|
13
|
+
expect { yield }.to raise_error(ActiveReporter::InvalidParamsError)
|
14
|
+
end
|
15
|
+
|
16
|
+
describe 'param validation' do
|
17
|
+
it 'yells unless :bin_count is numeric' do
|
18
|
+
expect_error { new_dimension(bin_count: 'hey') }
|
19
|
+
expect_error { new_dimension(bin_count: nil) }
|
20
|
+
new_dimension(bin_count: 5)
|
21
|
+
new_dimension(bin_count: 1.24)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#min/max' do
|
26
|
+
it 'finds the extremes in filter_values' do
|
27
|
+
dimension = new_dimension(only: [{ min: 1, max: 3 }, { min: -3 }, { min: 17, max: 40 }])
|
28
|
+
expect(dimension.min).to eq -3
|
29
|
+
expect(dimension.max).to eq 40
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'falls back to the smallest value in the data' do
|
33
|
+
dimension = ActiveReporter::Dimension::Bin.new(:likes,
|
34
|
+
OpenStruct.new(records: Post, params: {}),
|
35
|
+
model: :post, attribute: :likes
|
36
|
+
)
|
37
|
+
expect(dimension.min).to be_nil
|
38
|
+
expect(dimension.max).to be_nil
|
39
|
+
create(:post, likes: 3)
|
40
|
+
create(:post, likes: 10)
|
41
|
+
create(:post, likes: 1)
|
42
|
+
expect(dimension.min).to eq 1
|
43
|
+
expect(dimension.max).to eq 10
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe '#group_values' do
|
48
|
+
it 'defaults to dividing the domain into bins of bin_width' do
|
49
|
+
dimension = new_dimension(only: { min: 0, max: 3 })
|
50
|
+
allow(dimension).to receive(:bin_width).and_return(1)
|
51
|
+
allow(dimension).to receive(:data_contains_nil?).and_return(false)
|
52
|
+
expect(dimension.group_values).to eq [
|
53
|
+
{ min: 0, max: 1 },
|
54
|
+
{ min: 1, max: 2 },
|
55
|
+
{ min: 2, max: 3 }
|
56
|
+
]
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'is inclusive of max if data-driven' do
|
60
|
+
dimension = new_dimension(only: { min: 0 })
|
61
|
+
allow(dimension.report).to receive(:records).and_return(Post)
|
62
|
+
allow(dimension).to receive(:expression).and_return('posts.likes')
|
63
|
+
allow(dimension).to receive(:bin_width).and_return(1)
|
64
|
+
create(:post, likes: 2)
|
65
|
+
expect(dimension.group_values).to eq [
|
66
|
+
{ min: 0, max: 1 },
|
67
|
+
{ min: 1, max: 2 },
|
68
|
+
{ min: 2, max: 3 }
|
69
|
+
]
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'can be customized' do
|
73
|
+
dimension = new_dimension(bins: { min: 0, max: 1 })
|
74
|
+
expect(dimension.group_values).to eq [{ min: 0, max: 1 }]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveReporter::Dimension::Category do
|
4
|
+
def author_dimension(report)
|
5
|
+
described_class.new(:author, report, model: :authors, attribute: :name, relation: ->(r) { r.joins(
|
6
|
+
"LEFT OUTER JOIN authors ON authors.id = posts.author_id") })
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#filter' do
|
10
|
+
it 'filters to rows matching at least one value' do
|
11
|
+
p1 = create(:post, author: 'Alice')
|
12
|
+
p2 = create(:post, author: 'Bob')
|
13
|
+
p3 = create(:post, author: nil)
|
14
|
+
|
15
|
+
def filter_by(author_values)
|
16
|
+
report = OpenStruct.new(
|
17
|
+
table_name: 'posts',
|
18
|
+
params: { dimensions: { author: { only: author_values } } }
|
19
|
+
)
|
20
|
+
dimension = author_dimension(report)
|
21
|
+
dimension.filter(dimension.relate(Post))
|
22
|
+
end
|
23
|
+
|
24
|
+
expect(filter_by(['Alice'])).to eq [p1]
|
25
|
+
expect(filter_by([nil])).to eq [p3]
|
26
|
+
expect(filter_by(['Alice', nil])).to eq [p1, p3]
|
27
|
+
expect(filter_by(['Alice', 'Bob'])).to eq [p1, p2]
|
28
|
+
expect(filter_by([])).to eq []
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#group' do
|
33
|
+
it 'groups the relation by the exact value of the SQL expression' do
|
34
|
+
p1 = create(:post, author: 'Alice')
|
35
|
+
p2 = create(:post, author: 'Alice')
|
36
|
+
p3 = create(:post, author: nil)
|
37
|
+
p4 = create(:post, author: 'Bob')
|
38
|
+
p5 = create(:post, author: 'Bob')
|
39
|
+
p6 = create(:post, author: 'Bob')
|
40
|
+
|
41
|
+
report = OpenStruct.new(table_name: 'posts', params: {})
|
42
|
+
dimension = author_dimension(report)
|
43
|
+
|
44
|
+
results = dimension.group(dimension.relate(Post)).select("COUNT(*) AS count").map do |r|
|
45
|
+
r.attributes.values_at(dimension.send(:sql_value_name), 'count')
|
46
|
+
end
|
47
|
+
|
48
|
+
expect(results).to eq [[nil, 1], ['Alice', 2], ['Bob', 3]]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#group_values' do
|
53
|
+
it 'echoes filter_values if filtering' do
|
54
|
+
dimension = author_dimension(OpenStruct.new(params: {
|
55
|
+
dimensions: { author: { only: ['foo', 'bar'] } }
|
56
|
+
}))
|
57
|
+
expect(dimension.group_values).to eq %w(foo bar)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe ActiveReporter::Dimension::Enum do
|
4
|
+
def new_dimension(dimension_params = {}, report_params = {}, opts = { model: :post })
|
5
|
+
report_params[:dimensions] = dimension_params
|
6
|
+
ActiveReporter::Dimension::Enum.new(
|
7
|
+
:status,
|
8
|
+
OpenStruct.new(params: report_params, groupers: { status: {}, category: {} }, raw_data: {
|
9
|
+
['published', 'post_count'] => 5, ['published', 'post_total'] => 500.00, ['published', 'post_average'] => 100.00,
|
10
|
+
['archived', 'post_count'] => 7, ['archived', 'post_total'] => 530.25, ['archived', 'post_average'] => 75.75,
|
11
|
+
}),
|
12
|
+
opts
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
def status_dimension(report)
|
17
|
+
described_class.new(:status, report, model: :posts, attribute: :status)
|
18
|
+
end
|
19
|
+
|
20
|
+
def author_dimension(report)
|
21
|
+
described_class.new(:author, report, model: :authors, attribute: :name, relation: ->(r) { r.joins(
|
22
|
+
"LEFT OUTER JOIN authors ON authors.id = posts.author_id") })
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '#group_values' do
|
26
|
+
# it 'echoes filter_values if filtering' do
|
27
|
+
# dimension = status_dimension(OpenStruct.new(params: {
|
28
|
+
# groupers: [:status]
|
29
|
+
# }))
|
30
|
+
# expect(dimension.group_values).to eq [nil, 'draft', 'unpublished', 'published', 'archived']
|
31
|
+
# end
|
32
|
+
|
33
|
+
|
34
|
+
it 'determines #filtering?' do
|
35
|
+
dimension = new_dimension({ status: {}, category: {} }, { groupers: [:status] })
|
36
|
+
expect(dimension).not_to be_filtering
|
37
|
+
expect(dimension.group_values).to match %w(published archived)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# describe '#all_values' do
|
42
|
+
# it 'returns model enum values' do
|
43
|
+
# dimension = author_dimension(OpenStruct.new(params: {
|
44
|
+
# dimensions: { author: { only: [nil, 'draft', 'unpublished', 'published', 'archived'] } }
|
45
|
+
# }))
|
46
|
+
# expect(dimension.all_values).to eq described_class.
|
47
|
+
# end
|
48
|
+
# end
|
49
|
+
|
50
|
+
# describe '#filter' do
|
51
|
+
# it 'filters to rows matching at least one value' do
|
52
|
+
# p1 = create(:post, author: 'Alice')
|
53
|
+
# p2 = create(:post, author: 'Bob')
|
54
|
+
# p3 = create(:post, author: nil)
|
55
|
+
|
56
|
+
# def filter_by(author_values)
|
57
|
+
# report = OpenStruct.new(
|
58
|
+
# table_name: 'posts',
|
59
|
+
# params: { dimensions: { author: { only: author_values } } }
|
60
|
+
# )
|
61
|
+
# dimension = author_dimension(report)
|
62
|
+
# dimension.filter(dimension.relate(Post))
|
63
|
+
# end
|
64
|
+
|
65
|
+
# expect(filter_by(['Alice'])).to eq [p1]
|
66
|
+
# expect(filter_by([nil])).to eq [p3]
|
67
|
+
# expect(filter_by(['Alice', nil])).to eq [p1, p3]
|
68
|
+
# expect(filter_by(['Alice', 'Bob'])).to eq [p1, p2]
|
69
|
+
# expect(filter_by([])).to eq []
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
|
73
|
+
# describe '#group' do
|
74
|
+
# it 'groups the relation by the exact value of the SQL expression' do
|
75
|
+
# p1 = create(:post, author: 'Alice')
|
76
|
+
# p2 = create(:post, author: 'Alice')
|
77
|
+
# p3 = create(:post, author: nil)
|
78
|
+
# p4 = create(:post, author: 'Bob')
|
79
|
+
# p5 = create(:post, author: 'Bob')
|
80
|
+
# p6 = create(:post, author: 'Bob')
|
81
|
+
|
82
|
+
# report = OpenStruct.new(table_name: 'posts', params: {})
|
83
|
+
# dimension = author_dimension(report)
|
84
|
+
|
85
|
+
# results = dimension.group(dimension.relate(Post)).select("COUNT(*) AS count").map do |r|
|
86
|
+
# r.attributes.values_at(dimension.send(:sql_value_name), 'count')
|
87
|
+
# end
|
88
|
+
|
89
|
+
# expect(results).to eq [[nil, 1], ['Alice', 2], ['Bob', 3]]
|
90
|
+
# end
|
91
|
+
# end
|
92
|
+
|
93
|
+
|
94
|
+
end
|