active_reporter 0.5.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +14 -0
  3. data/README.md +436 -0
  4. data/Rakefile +23 -0
  5. data/lib/active_reporter.rb +26 -0
  6. data/lib/active_reporter/aggregator.rb +9 -0
  7. data/lib/active_reporter/aggregator/array.rb +14 -0
  8. data/lib/active_reporter/aggregator/average.rb +9 -0
  9. data/lib/active_reporter/aggregator/base.rb +73 -0
  10. data/lib/active_reporter/aggregator/count.rb +23 -0
  11. data/lib/active_reporter/aggregator/count_if.rb +23 -0
  12. data/lib/active_reporter/aggregator/max.rb +9 -0
  13. data/lib/active_reporter/aggregator/min.rb +9 -0
  14. data/lib/active_reporter/aggregator/ratio.rb +23 -0
  15. data/lib/active_reporter/aggregator/sum.rb +13 -0
  16. data/lib/active_reporter/calculator.rb +2 -0
  17. data/lib/active_reporter/calculator/base.rb +19 -0
  18. data/lib/active_reporter/calculator/ratio.rb +9 -0
  19. data/lib/active_reporter/dimension.rb +8 -0
  20. data/lib/active_reporter/dimension/base.rb +150 -0
  21. data/lib/active_reporter/dimension/bin.rb +123 -0
  22. data/lib/active_reporter/dimension/bin/set.rb +162 -0
  23. data/lib/active_reporter/dimension/bin/table.rb +43 -0
  24. data/lib/active_reporter/dimension/category.rb +29 -0
  25. data/lib/active_reporter/dimension/enum.rb +32 -0
  26. data/lib/active_reporter/dimension/number.rb +51 -0
  27. data/lib/active_reporter/dimension/time.rb +93 -0
  28. data/lib/active_reporter/evaluator.rb +2 -0
  29. data/lib/active_reporter/evaluator/base.rb +17 -0
  30. data/lib/active_reporter/evaluator/block.rb +15 -0
  31. data/lib/active_reporter/inflector.rb +8 -0
  32. data/lib/active_reporter/invalid_params_error.rb +4 -0
  33. data/lib/active_reporter/report.rb +102 -0
  34. data/lib/active_reporter/report/aggregation.rb +297 -0
  35. data/lib/active_reporter/report/definition.rb +195 -0
  36. data/lib/active_reporter/report/metrics.rb +75 -0
  37. data/lib/active_reporter/report/validation.rb +106 -0
  38. data/lib/active_reporter/serializer.rb +7 -0
  39. data/lib/active_reporter/serializer/base.rb +103 -0
  40. data/lib/active_reporter/serializer/csv.rb +22 -0
  41. data/lib/active_reporter/serializer/form_field.rb +134 -0
  42. data/lib/active_reporter/serializer/hash_table.rb +12 -0
  43. data/lib/active_reporter/serializer/highcharts.rb +200 -0
  44. data/lib/active_reporter/serializer/nested_hash.rb +11 -0
  45. data/lib/active_reporter/serializer/table.rb +21 -0
  46. data/lib/active_reporter/tracker.rb +2 -0
  47. data/lib/active_reporter/tracker/base.rb +15 -0
  48. data/lib/active_reporter/tracker/delta.rb +9 -0
  49. data/lib/active_reporter/version.rb +3 -0
  50. data/lib/tasks/active_reporter_tasks.rake +4 -0
  51. data/spec/acceptance/data_spec.rb +381 -0
  52. data/spec/active_reporter/aggregator_spec.rb +102 -0
  53. data/spec/active_reporter/dimension/base_spec.rb +102 -0
  54. data/spec/active_reporter/dimension/bin/set_spec.rb +83 -0
  55. data/spec/active_reporter/dimension/bin/table_spec.rb +47 -0
  56. data/spec/active_reporter/dimension/bin_spec.rb +77 -0
  57. data/spec/active_reporter/dimension/category_spec.rb +60 -0
  58. data/spec/active_reporter/dimension/enum_spec.rb +94 -0
  59. data/spec/active_reporter/dimension/number_spec.rb +71 -0
  60. data/spec/active_reporter/dimension/time_spec.rb +61 -0
  61. data/spec/active_reporter/report_spec.rb +597 -0
  62. data/spec/active_reporter/serializer/hash_table_spec.rb +45 -0
  63. data/spec/active_reporter/serializer/highcharts_spec.rb +113 -0
  64. data/spec/active_reporter/serializer/table_spec.rb +62 -0
  65. data/spec/dummy/README.rdoc +28 -0
  66. data/spec/dummy/Rakefile +6 -0
  67. data/spec/dummy/app/assets/config/manifest.js +0 -0
  68. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  69. data/spec/dummy/app/assets/stylesheets/application.css +26 -0
  70. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  71. data/spec/dummy/app/controllers/site_controller.rb +11 -0
  72. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  73. data/spec/dummy/app/models/author.rb +4 -0
  74. data/spec/dummy/app/models/comment.rb +4 -0
  75. data/spec/dummy/app/models/data_builder.rb +112 -0
  76. data/spec/dummy/app/models/post.rb +6 -0
  77. data/spec/dummy/app/models/post_report.rb +14 -0
  78. data/spec/dummy/app/views/layouts/application.html.erb +17 -0
  79. data/spec/dummy/app/views/site/report.html.erb +73 -0
  80. data/spec/dummy/bin/bundle +3 -0
  81. data/spec/dummy/bin/rails +4 -0
  82. data/spec/dummy/bin/rake +4 -0
  83. data/spec/dummy/bin/setup +29 -0
  84. data/spec/dummy/config.ru +4 -0
  85. data/spec/dummy/config/application.rb +26 -0
  86. data/spec/dummy/config/boot.rb +5 -0
  87. data/spec/dummy/config/database.yml +22 -0
  88. data/spec/dummy/config/environment.rb +5 -0
  89. data/spec/dummy/config/environments/development.rb +41 -0
  90. data/spec/dummy/config/environments/production.rb +79 -0
  91. data/spec/dummy/config/environments/test.rb +42 -0
  92. data/spec/dummy/config/initializers/assets.rb +11 -0
  93. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  94. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  95. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  96. data/spec/dummy/config/initializers/inflections.rb +16 -0
  97. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  98. data/spec/dummy/config/initializers/session_store.rb +3 -0
  99. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  100. data/spec/dummy/config/locales/en.yml +23 -0
  101. data/spec/dummy/config/routes.rb +57 -0
  102. data/spec/dummy/config/secrets.yml +22 -0
  103. data/spec/dummy/db/migrate/20150714202319_add_dummy_models.rb +25 -0
  104. data/spec/dummy/db/schema.rb +43 -0
  105. data/spec/dummy/db/seeds.rb +1 -0
  106. data/spec/dummy/log/test.log +37033 -0
  107. data/spec/dummy/public/404.html +67 -0
  108. data/spec/dummy/public/422.html +67 -0
  109. data/spec/dummy/public/500.html +66 -0
  110. data/spec/dummy/public/favicon.ico +0 -0
  111. data/spec/factories/factories.rb +29 -0
  112. data/spec/spec_helper.rb +40 -0
  113. data/spec/support/float.rb +8 -0
  114. 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