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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +31 -6
  3. data/Rakefile +12 -5
  4. data/VERSION +1 -0
  5. data/app/controllers/dossier/reports_controller.rb +1 -1
  6. data/app/views/dossier/reports/show.html.haml +1 -1
  7. data/lib/dossier.rb +1 -1
  8. data/lib/dossier/client.rb +4 -1
  9. data/lib/dossier/configuration.rb +10 -5
  10. data/lib/dossier/{naming.rb → model.rb} +10 -2
  11. data/lib/dossier/multi_report.rb +1 -1
  12. data/lib/dossier/query.rb +1 -1
  13. data/lib/dossier/report.rb +13 -10
  14. data/lib/dossier/responder.rb +2 -0
  15. data/lib/dossier/result.rb +29 -12
  16. data/lib/dossier/version.rb +1 -1
  17. data/lib/generators/dossier/views/templates/show.html.haml +1 -1
  18. data/spec/dossier/adapter/active_record_spec.rb +4 -4
  19. data/spec/dossier/client_spec.rb +19 -19
  20. data/spec/dossier/configuration_spec.rb +29 -9
  21. data/spec/dossier/{naming_spec.rb → model_spec.rb} +6 -1
  22. data/spec/dossier/query_spec.rb +12 -12
  23. data/spec/dossier/report_spec.rb +7 -7
  24. data/spec/dossier/responder_spec.rb +5 -5
  25. data/spec/dossier/result_spec.rb +5 -5
  26. data/spec/dossier/stream_csv_spec.rb +5 -3
  27. data/spec/dossier/version_spec.rb +8 -0
  28. data/spec/dossier_spec.rb +4 -4
  29. data/spec/dummy/Rakefile +4 -0
  30. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  31. data/spec/dummy/app/reports/cats/are/super_fun_report.rb +13 -1
  32. data/spec/dummy/app/reports/employee_report.rb +14 -1
  33. data/spec/dummy/app/reports/employee_with_custom_view_report.rb +5 -1
  34. data/spec/dummy/bin/rails +4 -0
  35. data/spec/dummy/bin/rake +4 -0
  36. data/spec/dummy/config/application.rb +30 -0
  37. data/spec/dummy/config/database.yml +6 -5
  38. data/spec/dummy/db/{test.sqlite3 → dossier_test.sqlite3} +0 -0
  39. data/spec/dummy/log/development.log +0 -0
  40. data/spec/dummy/log/test.log +21983 -0
  41. data/spec/features/employee_spec.rb +13 -4
  42. data/spec/fixtures/db/postgresql.yml +4 -0
  43. data/spec/fixtures/db/postgresql.yml.example +5 -0
  44. data/spec/fixtures/db/postgresql.yml.travis +4 -0
  45. data/spec/fixtures/db/sqlite3.yml +1 -1
  46. data/spec/fixtures/reports/employee.csv +3 -3
  47. data/spec/fixtures/reports/employee.xls +3 -3
  48. data/spec/generators/dossier/views/views_spec.rb +17 -10
  49. data/spec/routing/dossier_routes_spec.rb +1 -1
  50. data/spec/spec_helper.rb +7 -10
  51. data/spec/support/factory.rb +38 -0
  52. metadata +57 -44
  53. data/spec/dummy/config/database.yml.example +0 -13
  54. 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.load_file(Rails.root.join('config', 'dossier.yml'))[Rails.env].symbolize_keys }
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.should eq(Rails.root.join("config", "dossier.yml"))
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
- it "will raise an exception if config/dossier.yml cannot be read" do
39
- config_path = Rails.root.join('config')
40
- FileUtils.mv config_path.join('dossier.yml'), config_path.join('dossier.yml.test')
41
- expect { Dossier.configure }.to raise_error(Dossier::ConfigurationMissingError)
42
- FileUtils.mv config_path.join('dossier.yml.test'), config_path.join('dossier.yml')
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.should be_a(Hash)
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::Naming do
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' }
@@ -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.stub(:salary).and_return(2)
10
- report.stub(:ids).and_return([1,2,3])
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.stub(:sql).and_return("SELECT * FROM employees WHERE id = :id OR girth < :girth OR hired_on = :hired_on")
21
- report.stub(:id).and_return(92)
22
- report.stub(:girth).and_return(3.14)
23
- report.stub(:hired_on).and_return('2013-03-29')
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.stub(:sql).and_return("SELECT * FROM employees WHERE stuff IN :stuff")
43
- report.stub(:stuff).and_return([38, 'blue', 'mandible', 2])
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.stub(:sql).and_return("SELECT * FROM employees WHERE type = 'Foo::Bar'")
64
- query.should_not_receive(:Bar)
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.stub(:sql).and_return("SELECT * FROM employees WHERE type = '::Foo'")
70
- query.should_not_receive(:Foo)
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
 
@@ -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.should eq('test')
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.should eq('foo' => 'bar')
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').should eq '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).should eq 'customized'
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.stub(:execute).and_return([])
66
- report.stub(:before_test_for_build_query)
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.should_not be_nil
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 stub_out_report_results(report)
5
+ def mock_out_report_results(report)
6
6
  report.tap { |r|
7
- r.stub(:results).and_return(results)
8
- r.stub(:raw_results).and_return(results)
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) { [stub_out_report_results(report)] }
14
+ let(:reports) { [mock_out_report_results(report)] }
15
15
  let(:controller) {
16
- ActionController::Base.new.tap { |controller| controller.stub(:headers).and_return({}) }
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
 
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe Dossier::Result do
4
4
 
5
- module AbstractStub
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 AbstractStub } }
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.stub(:headers).and_return(%w[mascot cheese])
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.stub(:rows).and_return(adapter_result_rows)
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.should_not_receive(:format)
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) { collection[0].stub(:to_csv).and_return { raise error } }
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.stub(:consider_all_requests_local).and_return(true)
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.stub(:consider_all_requests_local).and_return(false)
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
@@ -0,0 +1,8 @@
1
+ RSpec.describe Dossier::VERSION do
2
+
3
+ it "is a string" do
4
+ expect(subject).to be_a String
5
+ end
6
+
7
+ end
8
+
@@ -2,17 +2,17 @@ require 'spec_helper'
2
2
 
3
3
  describe Dossier do
4
4
  it "is a module" do
5
- Dossier.should be_a(Module)
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.should_not be_nil
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.should be_a(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.should eq(some_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
@@ -0,0 +1,4 @@
1
+ require File.expand_path('../config/application', __FILE__)
2
+
3
+ Dummy::Application.load_tasks
4
+
@@ -0,0 +1,3 @@
1
+ ApplicationController = Class.new(ActionController::Base) do
2
+ protect_from_forgery with: :exception
3
+ end
@@ -2,7 +2,19 @@ module Cats
2
2
  module Are
3
3
  class SuperFunReport < Dossier::Report
4
4
  def sql
5
- "select 'cats', 'are', 'super', 'fun'"
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.to_i == 1 ? 'Yes' : 'No'
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 = true"
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)
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
3
+ require APP_PATH
4
+ require 'rails/commands'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../config/application'
3
+ require 'rake'
4
+ Rake.application.run
@@ -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!