dossier 2.12.2 → 2.13.1
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/README.md +31 -6
- data/Rakefile +12 -5
- data/VERSION +1 -0
- data/app/controllers/dossier/reports_controller.rb +1 -1
- data/app/views/dossier/reports/show.html.haml +1 -1
- data/lib/dossier.rb +1 -1
- data/lib/dossier/client.rb +4 -1
- data/lib/dossier/configuration.rb +10 -5
- data/lib/dossier/{naming.rb → model.rb} +10 -2
- data/lib/dossier/multi_report.rb +1 -1
- data/lib/dossier/query.rb +1 -1
- data/lib/dossier/report.rb +13 -10
- data/lib/dossier/responder.rb +2 -0
- data/lib/dossier/result.rb +29 -12
- data/lib/dossier/version.rb +1 -1
- data/lib/generators/dossier/views/templates/show.html.haml +1 -1
- data/spec/dossier/adapter/active_record_spec.rb +4 -4
- data/spec/dossier/client_spec.rb +19 -19
- data/spec/dossier/configuration_spec.rb +29 -9
- data/spec/dossier/{naming_spec.rb → model_spec.rb} +6 -1
- data/spec/dossier/query_spec.rb +12 -12
- data/spec/dossier/report_spec.rb +7 -7
- data/spec/dossier/responder_spec.rb +5 -5
- data/spec/dossier/result_spec.rb +5 -5
- data/spec/dossier/stream_csv_spec.rb +5 -3
- data/spec/dossier/version_spec.rb +8 -0
- data/spec/dossier_spec.rb +4 -4
- data/spec/dummy/Rakefile +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/reports/cats/are/super_fun_report.rb +13 -1
- data/spec/dummy/app/reports/employee_report.rb +14 -1
- data/spec/dummy/app/reports/employee_with_custom_view_report.rb +5 -1
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/config/application.rb +30 -0
- data/spec/dummy/config/database.yml +6 -5
- data/spec/dummy/db/{test.sqlite3 → dossier_test.sqlite3} +0 -0
- data/spec/dummy/log/development.log +0 -0
- data/spec/dummy/log/test.log +21983 -0
- data/spec/features/employee_spec.rb +13 -4
- data/spec/fixtures/db/postgresql.yml +4 -0
- data/spec/fixtures/db/postgresql.yml.example +5 -0
- data/spec/fixtures/db/postgresql.yml.travis +4 -0
- data/spec/fixtures/db/sqlite3.yml +1 -1
- data/spec/fixtures/reports/employee.csv +3 -3
- data/spec/fixtures/reports/employee.xls +3 -3
- data/spec/generators/dossier/views/views_spec.rb +17 -10
- data/spec/routing/dossier_routes_spec.rb +1 -1
- data/spec/spec_helper.rb +7 -10
- data/spec/support/factory.rb +38 -0
- metadata +57 -44
- data/spec/dummy/config/database.yml.example +0 -13
- data/spec/dummy/config/database.yml.travis +0 -5
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Dossier::Configuration do
|
4
4
|
|
5
|
-
let(:connection_options){ YAML.
|
5
|
+
let(:connection_options){ YAML.load(ERB.new(File.read Rails.root.join('config', 'dossier.yml')).result)[Rails.env].symbolize_keys }
|
6
6
|
let(:old_database_url) { ENV.delete "DATABASE_URL"}
|
7
7
|
|
8
8
|
before :each do
|
@@ -10,13 +10,17 @@ describe Dossier::Configuration do
|
|
10
10
|
@config = Dossier.configuration
|
11
11
|
end
|
12
12
|
|
13
|
+
after :each do
|
14
|
+
ENV.delete "DATABASE_URL" if ENV.has_key? "DATABASE_URL"
|
15
|
+
end
|
16
|
+
|
13
17
|
after :each do
|
14
18
|
ENV["DATABASE_URL"] = old_database_url
|
15
19
|
end
|
16
20
|
|
17
21
|
describe "defaults" do
|
18
22
|
it "uses the rails configuration directory for the config path" do
|
19
|
-
@config.config_path.
|
23
|
+
expect(@config.config_path).to eq(Rails.root.join("config", "dossier.yml"))
|
20
24
|
end
|
21
25
|
end
|
22
26
|
|
@@ -30,20 +34,36 @@ describe Dossier::Configuration do
|
|
30
34
|
end
|
31
35
|
|
32
36
|
it "uses config/dossier.yml to setup the client" do
|
33
|
-
ENV.delete "DATABASE_URL" if ENV.has_key? "DATABASE_URL"
|
34
37
|
expect(Dossier::Client).to receive(:new).with(connection_options)
|
35
38
|
Dossier.configure
|
36
39
|
end
|
37
40
|
|
38
|
-
|
39
|
-
config_path
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
describe "missing a dossier.yml" do
|
42
|
+
let(:config_path) { Rails.root.join('config') }
|
43
|
+
|
44
|
+
before :each do
|
45
|
+
FileUtils.mv config_path.join('dossier.yml'),
|
46
|
+
config_path.join('dossier.yml.test')
|
47
|
+
end
|
48
|
+
|
49
|
+
after :each do
|
50
|
+
FileUtils.mv config_path.join('dossier.yml.test'),
|
51
|
+
config_path.join('dossier.yml')
|
52
|
+
end
|
53
|
+
|
54
|
+
it "will not raise an exception if config/dossier.yml cannot be read and DATABSE_URL is set" do
|
55
|
+
ENV['DATABASE_URL'] = "mysql2://localhost/dossier_test"
|
56
|
+
expect { Dossier.configure }.not_to raise_error
|
57
|
+
end
|
58
|
+
|
59
|
+
it "will raise an error if connection options is blank" do
|
60
|
+
expect { Dossier.configure }.to(
|
61
|
+
raise_error(Dossier::ConfigurationMissingError))
|
62
|
+
end
|
43
63
|
end
|
44
64
|
|
45
65
|
it "will setup the connection options" do
|
46
|
-
@config.connection_options.
|
66
|
+
expect(@config.connection_options).to be_a(Hash)
|
47
67
|
end
|
48
68
|
end
|
49
69
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe Dossier::
|
3
|
+
describe Dossier::Model do
|
4
4
|
describe "report naming" do
|
5
5
|
let(:klass) { HelloMyFriendsReport }
|
6
6
|
let(:name) { 'hello_my_friends' }
|
@@ -13,6 +13,11 @@ describe Dossier::Naming do
|
|
13
13
|
expect(described_class.name_to_class(name)).to eq(klass)
|
14
14
|
end
|
15
15
|
|
16
|
+
it "has a to_model" do
|
17
|
+
instance = klass.new
|
18
|
+
expect(instance.to_model).to eq(instance)
|
19
|
+
end
|
20
|
+
|
16
21
|
describe "with namespaces" do
|
17
22
|
let(:klass) { Cats::Are::SuperFunReport }
|
18
23
|
let(:name) { 'cats/are/super_fun' }
|
data/spec/dossier/query_spec.rb
CHANGED
@@ -6,8 +6,8 @@ describe Dossier::Query do
|
|
6
6
|
let(:query) { Dossier::Query.new(report) }
|
7
7
|
|
8
8
|
before :each do
|
9
|
-
report.
|
10
|
-
report.
|
9
|
+
allow(report).to receive(:salary).and_return(2)
|
10
|
+
allow(report).to receive(:ids).and_return([1,2,3])
|
11
11
|
end
|
12
12
|
|
13
13
|
describe "replacing symbols by calling methods of the same name" do
|
@@ -17,10 +17,10 @@ describe Dossier::Query do
|
|
17
17
|
context "when the methods return single values" do
|
18
18
|
|
19
19
|
before :each do
|
20
|
-
report.
|
21
|
-
report.
|
22
|
-
report.
|
23
|
-
report.
|
20
|
+
allow(report).to receive(:sql).and_return("SELECT * FROM employees WHERE id = :id OR girth < :girth OR hired_on = :hired_on")
|
21
|
+
allow(report).to receive(:id).and_return(92)
|
22
|
+
allow(report).to receive(:girth).and_return(3.14)
|
23
|
+
allow(report).to receive(:hired_on).and_return('2013-03-29')
|
24
24
|
end
|
25
25
|
|
26
26
|
it "escapes the values" do
|
@@ -39,8 +39,8 @@ describe Dossier::Query do
|
|
39
39
|
context "when the methods return arrays" do
|
40
40
|
|
41
41
|
before :each do
|
42
|
-
report.
|
43
|
-
report.
|
42
|
+
allow(report).to receive(:sql).and_return("SELECT * FROM employees WHERE stuff IN :stuff")
|
43
|
+
allow(report).to receive(:stuff).and_return([38, 'blue', 'mandible', 2])
|
44
44
|
end
|
45
45
|
|
46
46
|
it "escapes each value in the array" do
|
@@ -60,14 +60,14 @@ describe Dossier::Query do
|
|
60
60
|
context "when it's another string that includes :" do
|
61
61
|
|
62
62
|
it "does not escape a namespaced constant" do
|
63
|
-
report.
|
64
|
-
query.
|
63
|
+
allow(report).to receive(:sql).and_return("SELECT * FROM employees WHERE type = 'Foo::Bar'")
|
64
|
+
expect(query).not_to receive(:Bar)
|
65
65
|
query.to_s
|
66
66
|
end
|
67
67
|
|
68
68
|
it "does not escape a top-level constant" do
|
69
|
-
report.
|
70
|
-
query.
|
69
|
+
allow(report).to receive(:sql).and_return("SELECT * FROM employees WHERE type = '::Foo'")
|
70
|
+
expect(query).not_to receive(:Foo)
|
71
71
|
query.to_s
|
72
72
|
end
|
73
73
|
|
data/spec/dossier/report_spec.rb
CHANGED
@@ -5,7 +5,7 @@ describe Dossier::Report do
|
|
5
5
|
let(:report) { TestReport.new(:foo => 'bar') }
|
6
6
|
|
7
7
|
it "has a report name" do
|
8
|
-
TestReport.report_name.
|
8
|
+
expect(TestReport.report_name).to eq('test')
|
9
9
|
end
|
10
10
|
|
11
11
|
it "has a template name that is the report name" do
|
@@ -29,15 +29,15 @@ describe Dossier::Report do
|
|
29
29
|
end
|
30
30
|
|
31
31
|
it "takes options when initializing" do
|
32
|
-
report.options.
|
32
|
+
expect(report.options).to eq('foo' => 'bar')
|
33
33
|
end
|
34
34
|
|
35
35
|
it 'generates column headers' do
|
36
|
-
report.format_header('Foo').
|
36
|
+
expect(report.format_header('Foo')).to eq 'Foo'
|
37
37
|
end
|
38
38
|
|
39
39
|
it 'allows for column header customization' do
|
40
|
-
report_with_custom_header.format_header(:generic).
|
40
|
+
expect(report_with_custom_header.format_header(:generic)).to eq 'customized'
|
41
41
|
end
|
42
42
|
|
43
43
|
it "has a formatted title" do
|
@@ -62,8 +62,8 @@ describe Dossier::Report do
|
|
62
62
|
end
|
63
63
|
|
64
64
|
it "has callbacks for execute" do
|
65
|
-
Dossier.client.
|
66
|
-
report.
|
65
|
+
allow(Dossier.client).to receive(:execute).and_return([])
|
66
|
+
allow(report).to receive(:before_test_for_build_query)
|
67
67
|
expect(report).to receive(:after_test_for_execute)
|
68
68
|
report.run
|
69
69
|
end
|
@@ -86,7 +86,7 @@ describe Dossier::Report do
|
|
86
86
|
it "will cache the results of the run in `results`" do
|
87
87
|
report = EmployeeReport.new
|
88
88
|
report.run
|
89
|
-
report.results.
|
89
|
+
expect(report.results).not_to be_nil
|
90
90
|
end
|
91
91
|
end
|
92
92
|
|
@@ -2,18 +2,18 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Dossier::Responder do
|
4
4
|
|
5
|
-
def
|
5
|
+
def mock_out_report_results(report)
|
6
6
|
report.tap { |r|
|
7
|
-
r.
|
8
|
-
r.
|
7
|
+
allow(r).to receive(:results).and_return(results)
|
8
|
+
allow(r).to receive(:raw_results).and_return(results)
|
9
9
|
}
|
10
10
|
end
|
11
11
|
|
12
12
|
let(:results) { double(arrays: [%w[hi], %w[there]], hashes: [{hi: 'there'}]) }
|
13
13
|
let(:report) { EmployeeReport.new }
|
14
|
-
let(:reports) { [
|
14
|
+
let(:reports) { [mock_out_report_results(report)] }
|
15
15
|
let(:controller) {
|
16
|
-
ActionController::Base.new.tap { |controller| controller.
|
16
|
+
ActionController::Base.new.tap { |controller| allow(controller).to receive(:headers).and_return({}) }
|
17
17
|
}
|
18
18
|
let(:responder) { described_class.new(controller, reports, {}) }
|
19
19
|
|
data/spec/dossier/result_spec.rb
CHANGED
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Dossier::Result do
|
4
4
|
|
5
|
-
module
|
5
|
+
module AbstractMock
|
6
6
|
def each
|
7
7
|
adapter_results.rows.each do |row|
|
8
8
|
yield row
|
@@ -17,7 +17,7 @@ describe Dossier::Result do
|
|
17
17
|
let(:report) { TestReport.new }
|
18
18
|
let(:result_row) { {'mascot' => 'platapus', 'cheese' => 'bleu'} }
|
19
19
|
let(:adapter_result) { double(:adapter_result, rows: [result_row.values], headers: result_row.keys) }
|
20
|
-
let(:result_class) { Class.new(described_class) { include
|
20
|
+
let(:result_class) { Class.new(described_class) { include AbstractMock } }
|
21
21
|
let(:result) { result_class.new(adapter_result, report) }
|
22
22
|
|
23
23
|
it "requires each to be overridden" do
|
@@ -51,7 +51,7 @@ describe Dossier::Result do
|
|
51
51
|
end
|
52
52
|
|
53
53
|
it "can return an array of arrays" do
|
54
|
-
result.
|
54
|
+
allow(result).to receive(:headers).and_return(%w[mascot cheese])
|
55
55
|
expect(result.arrays).to eq([%w[mascot cheese], %w[platapus bleu]])
|
56
56
|
end
|
57
57
|
|
@@ -122,7 +122,7 @@ describe Dossier::Result do
|
|
122
122
|
let(:adapter_result_rows) { 7.times.map { result_row.values } }
|
123
123
|
|
124
124
|
before :each do
|
125
|
-
adapter_result.
|
125
|
+
allow(adapter_result).to receive(:rows).and_return(adapter_result_rows)
|
126
126
|
end
|
127
127
|
|
128
128
|
it "has 4 result rows" do
|
@@ -157,7 +157,7 @@ describe Dossier::Result do
|
|
157
157
|
end
|
158
158
|
|
159
159
|
it "does not format the results" do
|
160
|
-
result.
|
160
|
+
expect(result).not_to receive(:format)
|
161
161
|
result.each { |result| }
|
162
162
|
end
|
163
163
|
|
@@ -57,16 +57,18 @@ describe Dossier::StreamCSV do
|
|
57
57
|
describe "exceptions" do
|
58
58
|
let(:output) { String.new }
|
59
59
|
let(:error) { "Woooooooo cats are fluffy!" }
|
60
|
-
before(:each) {
|
60
|
+
before(:each) {
|
61
|
+
allow(collection[0]).to receive(:to_csv).and_raise(error)
|
62
|
+
}
|
61
63
|
|
62
64
|
it "provides a backtrace if local request" do
|
63
|
-
Rails.application.config.
|
65
|
+
allow(Rails.application.config).to receive(:consider_all_requests_local).and_return(true)
|
64
66
|
streamer.each { |line| output << line }
|
65
67
|
expect(output).to include(error)
|
66
68
|
end
|
67
69
|
|
68
70
|
it "provides a simple error if not a local request" do
|
69
|
-
Rails.application.config.
|
71
|
+
allow(Rails.application.config).to receive(:consider_all_requests_local).and_return(false)
|
70
72
|
streamer.each { |line| output << line }
|
71
73
|
expect(output).to match(/something went wrong/)
|
72
74
|
end
|
data/spec/dossier_spec.rb
CHANGED
@@ -2,17 +2,17 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
describe Dossier do
|
4
4
|
it "is a module" do
|
5
|
-
Dossier.
|
5
|
+
expect(Dossier).to be_a(Module)
|
6
6
|
end
|
7
7
|
|
8
8
|
it "is configurable" do
|
9
9
|
Dossier.configure
|
10
|
-
Dossier.configuration.
|
10
|
+
expect(Dossier.configuration).to_not be_nil
|
11
11
|
end
|
12
12
|
|
13
13
|
it "has a configuration" do
|
14
14
|
Dossier.configure
|
15
|
-
Dossier.configuration.
|
15
|
+
expect(Dossier.configuration).to be_a(Dossier::Configuration)
|
16
16
|
end
|
17
17
|
|
18
18
|
it "allows configuration via a block" do
|
@@ -20,7 +20,7 @@ describe Dossier do
|
|
20
20
|
Dossier.configure do |config|
|
21
21
|
config.client = some_client
|
22
22
|
end
|
23
|
-
Dossier.configuration.client.
|
23
|
+
expect(Dossier.configuration.client).to eq(some_client)
|
24
24
|
end
|
25
25
|
|
26
26
|
it "exposes the configurations client via Dossier.client" do
|
data/spec/dummy/Rakefile
ADDED
@@ -2,7 +2,19 @@ module Cats
|
|
2
2
|
module Are
|
3
3
|
class SuperFunReport < Dossier::Report
|
4
4
|
def sql
|
5
|
-
"select '
|
5
|
+
"select #{selections.join(', ')}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def selections
|
9
|
+
columns = %w(cats are super fun)
|
10
|
+
selections = columns.map { |x| "'#{x}' as #{x}" }
|
11
|
+
if ENV['DOSSIER_DB'].to_s === 'postgresql'
|
12
|
+
selections.map! { |x|
|
13
|
+
parts = x.split(' as ')
|
14
|
+
"'#{parts[0][1..-2]}'::character(7) as #{parts[1]}"
|
15
|
+
}
|
16
|
+
end
|
17
|
+
selections
|
6
18
|
end
|
7
19
|
end
|
8
20
|
end
|
@@ -55,12 +55,17 @@ class EmployeeReport < Dossier::Report
|
|
55
55
|
@names ||= options.fetch(:names) { [] }.dup
|
56
56
|
end
|
57
57
|
|
58
|
+
def display_column?(name)
|
59
|
+
name != 'id'
|
60
|
+
end
|
61
|
+
|
58
62
|
def format_salary(amount, row)
|
59
63
|
return "Who's Asking?" if row[:division] == "Corporate Malfeasance"
|
60
64
|
formatter.number_to_currency(amount)
|
61
65
|
end
|
62
66
|
|
63
67
|
def format_hired_on(date)
|
68
|
+
date = Date.parse(date) if String === date
|
64
69
|
date.to_s(:db)
|
65
70
|
end
|
66
71
|
|
@@ -69,11 +74,19 @@ class EmployeeReport < Dossier::Report
|
|
69
74
|
end
|
70
75
|
|
71
76
|
def format_suspended(value)
|
72
|
-
value.
|
77
|
+
value.to_s.in?(%w(1 t)) ? 'Yes' : 'No'
|
73
78
|
end
|
74
79
|
|
75
80
|
def example_before_hook
|
76
81
|
# do some stuff
|
77
82
|
end
|
78
83
|
|
84
|
+
def raw_results
|
85
|
+
super
|
86
|
+
results = query_results.rows.map { |qr|
|
87
|
+
qr.tap { |q| q[4] = format_suspended(q[4]) }
|
88
|
+
}
|
89
|
+
@raw_results ||= Result::Unformatted.new(results, self)
|
90
|
+
end
|
91
|
+
|
79
92
|
end
|
@@ -6,7 +6,7 @@ class EmployeeWithCustomViewReport < Dossier::Report
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def sql
|
9
|
-
"SELECT * FROM employees WHERE suspended =
|
9
|
+
"SELECT * FROM employees WHERE suspended = :suspended"
|
10
10
|
end
|
11
11
|
|
12
12
|
def dragon_color
|
@@ -17,6 +17,10 @@ class EmployeeWithCustomViewReport < Dossier::Report
|
|
17
17
|
@formatter ||= CustomFormatter
|
18
18
|
end
|
19
19
|
|
20
|
+
def suspended
|
21
|
+
true
|
22
|
+
end
|
23
|
+
|
20
24
|
module CustomFormatter
|
21
25
|
extend Dossier::Formatter
|
22
26
|
def margery_butts(word)
|
data/spec/dummy/bin/rake
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
ENV['BUNDLE_GEMFILE'] = File.expand_path('../../../../Gemfile', __FILE__)
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
|
6
|
+
Bundler.setup
|
7
|
+
|
8
|
+
require "rails/all"
|
9
|
+
|
10
|
+
Bundler.require
|
11
|
+
|
12
|
+
module Dummy
|
13
|
+
class Application < ::Rails::Application
|
14
|
+
config.cache_classes = true
|
15
|
+
config.active_support.deprecation = :stderr
|
16
|
+
config.eager_load = false
|
17
|
+
config.action_controller.allow_forgery_protection = false
|
18
|
+
|
19
|
+
# Raise exceptions instead of rendering exception templates
|
20
|
+
config.action_dispatch.show_exceptions = false
|
21
|
+
# because this belongs here for some reason...??? also in spec_helper
|
22
|
+
# thanks rails 5 :/
|
23
|
+
config.active_support.test_order = :random
|
24
|
+
|
25
|
+
config.secret_token = config.secret_key_base =
|
26
|
+
'http://s3-ec.buzzfed.com/static/enhanced/webdr03/2013/5/25/8/anigif_enhanced-buzz-11857-1369483324-0.gif'
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Dummy::Application.initialize!
|