rapport 0.0.4

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.
@@ -0,0 +1,58 @@
1
+ require 'zip/zip'
2
+ require 'fastercsv'
3
+
4
+ module Rapport
5
+ class ReportGeneratorCsv
6
+ include ReportGenerator
7
+
8
+ generate_with do |report|
9
+ if report.options[:to_string].nil?
10
+ FasterCSV.open(output_filename, 'w') {|csv| generate_internal(report, csv) }
11
+
12
+ zip_output_file! if report.options[:zip]
13
+
14
+ output_filename
15
+ else
16
+ FasterCSV.generate {|csv| generate_internal(report, csv) }
17
+ end
18
+ end
19
+
20
+ cell_format(:cents) do |value|
21
+ !value.nil? &&
22
+ ("%.2f" % (value.to_s.to_d / '100'.to_d))
23
+ end
24
+
25
+ cell_format(Time) do |value|
26
+ value.strftime('%B %e, %Y')
27
+ end
28
+
29
+ cell_format(Date) do |value|
30
+ format_as(Time,Time.utc(value.year, value.month, value.day))
31
+ end
32
+
33
+ def zip_output_file!
34
+ zip_file_name = output_filename.sub(/(\.[^\.]*)?$/,'.zip')
35
+ Zip::ZipFile.open(zip_file_name, Zip::ZipFile::CREATE) do |zipfile|
36
+ zipfile.add(File.basename(output_filename),output_filename)
37
+ end
38
+ @output_filename = zip_file_name
39
+ end
40
+
41
+ def report_name
42
+ report.table_name.sub('reports_','')
43
+ end
44
+
45
+ private
46
+
47
+ def self.generate_internal(report, csv)
48
+ i = 0
49
+ csv << report.column_headers
50
+ report.each_row do |row|
51
+ csv << row
52
+ Rapport.logger.debug(row.inspect) if i%1000==0
53
+ i+=1
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,16 @@
1
+ module Rapport
2
+ class ReportGeneratorFake
3
+ include ReportGenerator
4
+
5
+ attr_accessor :output
6
+
7
+ generate_with do |report|
8
+ @output << report.column_headers
9
+ report.each_row do |row|
10
+ @output << row
11
+ end
12
+ @output
13
+ end
14
+
15
+ end
16
+ end
data/lib/rapport.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'helpers'
2
+ require 'rapport/report'
3
+ require 'rapport/report_generator'
4
+ require 'rapport/report_generators/report_generator_fake'
5
+
data/lib/test.rb ADDED
@@ -0,0 +1,12 @@
1
+ class Foo
2
+ instance_variable_set(:@hello, [1])
3
+
4
+ def self.inherited(subclass)
5
+ subclass.instance_variable_set( :@hello, self.instance_variable_get(:@hello).dup )
6
+ end
7
+
8
+ def self.hello
9
+ @hello
10
+ end
11
+
12
+ end
data/rapport.gemspec ADDED
@@ -0,0 +1,88 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rapport}
8
+ s.version = "0.0.4"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = [%q{Andrew Wheeler}]
12
+ s.date = %q{2012-01-10}
13
+ s.description = %q{Allows you to create tablular reports and output them to various formats interchangeably.}
14
+ s.email = %q{andrew.wheeler@bookrenter.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".tmtags",
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE.txt",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "lib/helpers.rb",
29
+ "lib/load_rapport.rb",
30
+ "lib/rapport.rb",
31
+ "lib/rapport/report.rb",
32
+ "lib/rapport/report_generator.rb",
33
+ "lib/rapport/report_generators/report_generator_csv.rb",
34
+ "lib/rapport/report_generators/report_generator_fake.rb",
35
+ "lib/test.rb",
36
+ "rapport.gemspec",
37
+ "test/logs/test.log",
38
+ "test/rapport/report_generator_test.rb",
39
+ "test/rapport/report_generators/report_generator_csv_test.rb",
40
+ "test/rapport/report_test.rb",
41
+ "test/test_helper.rb"
42
+ ]
43
+ s.homepage = %q{http://github.com/jawheeler/rapport}
44
+ s.licenses = [%q{MIT}]
45
+ s.require_paths = [%q{lib}]
46
+ s.rubygems_version = %q{1.8.6}
47
+ s.summary = %q{Rapport Reporting}
48
+
49
+ if s.respond_to? :specification_version then
50
+ s.specification_version = 3
51
+
52
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
53
+ s.add_development_dependency(%q<rake>, ["= 0.8.7"])
54
+ s.add_development_dependency(%q<shoulda>, ["~> 2.11.3"])
55
+ s.add_development_dependency(%q<mocha>, ["= 0.9.8"])
56
+ s.add_development_dependency(%q<shared_should>, ["= 0.8.1"])
57
+ s.add_development_dependency(%q<always_execute>, ["= 0.0.2"])
58
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
59
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
60
+ s.add_development_dependency(%q<rcov>, [">= 0"])
61
+ s.add_development_dependency(%q<rubyzip>, ["= 0.9.5"])
62
+ s.add_development_dependency(%q<fastercsv>, ["= 1.5.1"])
63
+ else
64
+ s.add_dependency(%q<rake>, ["= 0.8.7"])
65
+ s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
66
+ s.add_dependency(%q<mocha>, ["= 0.9.8"])
67
+ s.add_dependency(%q<shared_should>, ["= 0.8.1"])
68
+ s.add_dependency(%q<always_execute>, ["= 0.0.2"])
69
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
70
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
71
+ s.add_dependency(%q<rcov>, [">= 0"])
72
+ s.add_dependency(%q<rubyzip>, ["= 0.9.5"])
73
+ s.add_dependency(%q<fastercsv>, ["= 1.5.1"])
74
+ end
75
+ else
76
+ s.add_dependency(%q<rake>, ["= 0.8.7"])
77
+ s.add_dependency(%q<shoulda>, ["~> 2.11.3"])
78
+ s.add_dependency(%q<mocha>, ["= 0.9.8"])
79
+ s.add_dependency(%q<shared_should>, ["= 0.8.1"])
80
+ s.add_dependency(%q<always_execute>, ["= 0.0.2"])
81
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
82
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
83
+ s.add_dependency(%q<rcov>, [">= 0"])
84
+ s.add_dependency(%q<rubyzip>, ["= 0.9.5"])
85
+ s.add_dependency(%q<fastercsv>, ["= 1.5.1"])
86
+ end
87
+ end
88
+
@@ -0,0 +1,6 @@
1
+ I, [2012-01-10T11:15:14.417199 #688] INFO -- : Generating Rapport::TestReport csv...
2
+ D, [2012-01-10T11:15:14.418053 #688] DEBUG -- : [0, "T0 0", "Formatted~0", "~0~0"]
3
+ I, [2012-01-10T11:15:14.419484 #688] INFO -- : Generated Rapport::TestReport csv.
4
+ I, [2012-01-10T11:15:14.419983 #688] INFO -- : Generating Rapport::TestReport csv...
5
+ D, [2012-01-10T11:15:14.420228 #688] DEBUG -- : [0, "T0 0", "Formatted~0", "~0~0"]
6
+ I, [2012-01-10T11:15:14.421822 #688] INFO -- : Generated Rapport::TestReport csv.
@@ -0,0 +1,157 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class ReportTest < Test::Unit::TestCase
4
+ setup do
5
+ @report = Rapport::TestReport.new(:format => 'test')
6
+ @report_generator = @report.report_generator
7
+ end
8
+
9
+ context "ReportGenerator.generate_with" do
10
+ setup do
11
+ @class = Class.new
12
+ @class.send(:include, Rapport::ReportGenerator)
13
+ @proc = lambda { 'hello' }
14
+ end
15
+
16
+ execute do
17
+ proc = @proc
18
+ @class.class_eval do
19
+ generate_with do
20
+ proc.call
21
+ end
22
+ end
23
+ end
24
+
25
+ should "define #generate" do
26
+ assert @class.public_method_defined?(:generate)
27
+ end
28
+
29
+ should "produce #generate with logging that returns whatever the generate_with block returns" do
30
+ rg = @class.new
31
+ rg.report = Rapport::TestReport.new
32
+ Logger.any_instance.expects(:info).twice
33
+ assert_equal 'hello', rg.generate
34
+ end
35
+
36
+ context "error handling" do
37
+ setup do
38
+ @proc = lambda { @report.current_model = 'current_model'; raise "Boom!" }
39
+ end
40
+
41
+ should "log the current model" do
42
+ rg = @class.new
43
+ rg.report = @report = Rapport::TestReport.new
44
+ @error = ''
45
+ Logger.any_instance.expects(:info)
46
+ Logger.any_instance.expects(:error).with{|message| @error = message }
47
+ rg.generate
48
+ assert_match /current_model/, @error
49
+ assert_match /#{@report}/, @error
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ context "cell formatter" do
56
+ setup do
57
+ @cf = @report_generator.cell_formatter
58
+ end
59
+
60
+ context ".dup" do
61
+ setup do
62
+ @cf.add_cell_format(:test) {|value| "Test#{value}" }
63
+ @dup = @cf.dup
64
+ end
65
+
66
+ should "inherit procs from parent" do
67
+ assert_equal "TestMe", @dup.format(:test, "Me")
68
+ end
69
+
70
+ should "added cell formats should not affect parent" do
71
+ @dup.add_cell_format(:dup_test) {|value| "Dup#{value}"}
72
+ assert_equal "DupMe", @dup.format(:dup_test, "Me")
73
+ assert_equal "Me", @cf.format(:dup_test, "Me")
74
+ end
75
+
76
+ should "overridden cell formats should not affect parent" do
77
+ @dup.add_cell_format(:test) {|value| "DupTest#{value}" }
78
+ assert_equal "DupTestMe", @dup.format(:test, "Me")
79
+ assert_equal "TestMe", @cf.format(:test, "Me")
80
+ end
81
+ end
82
+
83
+ context ".add_cell_format" do
84
+ setup do
85
+ @proc = Proc.new {|x| "Test#{x}" }
86
+ end
87
+
88
+ execute do
89
+ @cf.add_cell_format(@type, &@proc)
90
+ end
91
+
92
+ share_should("format for symbol") do
93
+ assert_equal "TestMe", @cf.format(:string, "Me")
94
+ end
95
+
96
+ share_should("format for class") do
97
+ assert_equal "TestMe", @cf.format(nil, "Me")
98
+ end
99
+
100
+ use_should("format for symbol").when("symbol type") do
101
+ @type = :string
102
+ end
103
+
104
+ context "symbol type" do
105
+ setup do
106
+ @type = String
107
+ end
108
+
109
+ use_should("format for symbol")
110
+ use_should("format for class")
111
+ end
112
+ end
113
+
114
+ context ".format" do
115
+ setup do
116
+ @value = "Me"
117
+ end
118
+
119
+ execute do
120
+ @cf.format(@type, @value)
121
+ end
122
+
123
+ share_should "return value" do
124
+ assert_equal @value, @execute_result
125
+ end
126
+
127
+ context "type is a proc" do
128
+ setup do
129
+ @type = lambda{|v| "Lambda#{v}" }
130
+ end
131
+
132
+ should "call the lambda" do
133
+ assert_equal "LambdaMe", @execute_result
134
+ end
135
+ end
136
+
137
+ context "type has been added" do
138
+ setup do
139
+ @type = :happy
140
+ end
141
+
142
+ should "format happily" do
143
+ assert_equal "HappyMe", @execute_result
144
+ end
145
+ end
146
+
147
+ use_should("return value").when("type has not been added") do
148
+ @type = :sad
149
+ end
150
+
151
+ use_should("return value").when("type is nil") do
152
+ @type = nil
153
+ end
154
+ end
155
+ end
156
+ end
157
+
@@ -0,0 +1,56 @@
1
+ require File.dirname(__FILE__) + '/../../test_helper'
2
+ require 'rapport/report_generators/report_generator_csv'
3
+
4
+ class ReportTest < Test::Unit::TestCase
5
+ setup do
6
+ @rg_csv = Rapport::ReportGeneratorCsv.new
7
+ end
8
+
9
+ context "cell_format Time" do
10
+ setup do
11
+ @time = Time.new
12
+ end
13
+
14
+ execute do
15
+ @rg_csv.format(Time, @time)
16
+ end
17
+
18
+ should "format as a string" do
19
+ assert_equal @time.strftime('%B %e, %Y'), @execute_result
20
+ end
21
+ end
22
+
23
+
24
+ context "cell_format Date" do
25
+ setup do
26
+ @date = Date.new
27
+ end
28
+
29
+ execute do
30
+ @rg_csv.format(Date, @date)
31
+ end
32
+
33
+ should "format the date as a time" do
34
+ assert_equal Time.utc(@date.year, @date.month, @date.day ).strftime('%B %e, %Y'), @execute_result
35
+ end
36
+ end
37
+
38
+ context ".generate" do
39
+ setup do
40
+ @report = Rapport::TestReport.new(:format => 'csv', :to_string => true)
41
+ end
42
+
43
+ execute do
44
+ @report.generate
45
+ end
46
+
47
+ should "write the header" do
48
+ assert_match "Column 0,Column 1,Column 2,Column 3\n", @execute_result
49
+ end
50
+
51
+ should "write rows" do
52
+ assert_match "1,T0 2,Formatted~2,~2~1\n", @execute_result
53
+ assert_match "1,T1 3,Formatted~3,~3~1\n", @execute_result
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,253 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class ReportTest < Test::Unit::TestCase
4
+ setup do
5
+ @report = Rapport::TestReport.new
6
+ end
7
+
8
+ context ".each_row" do
9
+ execute do
10
+ execute_result = []
11
+ @report.each_row {|row| execute_result << row}
12
+ execute_result
13
+ end
14
+
15
+ should "return a row for each raw row" do
16
+ raw_rows = []
17
+ @report.each_model {|row, type| raw_rows << row}
18
+ assert_equal raw_rows.length, @execute_result.length
19
+ end
20
+
21
+ should "return formatted rows" do
22
+ assert_match /Formatted/, @execute_result[0][2]
23
+ end
24
+ end
25
+
26
+ context ".column_headers" do
27
+ execute do
28
+ @report.column_headers
29
+ end
30
+
31
+ should "return column headers only" do
32
+ @execute_result.each do |column_header|
33
+ assert_match /Column/, column_header
34
+ end
35
+ end
36
+ end
37
+
38
+ context ".column_symbols" do
39
+ execute do
40
+ @report.column_symbols
41
+ end
42
+
43
+ should "return symbols only" do
44
+ @execute_result.each do |column_symbol|
45
+ assert_match /calc/, column_symbol.to_s
46
+ end
47
+ end
48
+ end
49
+
50
+ context ".table_name" do
51
+ should "return a configured table name" do
52
+ @report.options[:report_table_name] = 'llama'
53
+ assert_equal 'llama', @report.table_name
54
+ end
55
+
56
+ should "return a table name based on the reports actual class if :report_class isn't configured" do
57
+ assert_equal 'reports_rapport_test', @report.table_name
58
+ end
59
+
60
+ end
61
+
62
+ context ".cell_calculator_for" do
63
+
64
+ setup do
65
+ @lambda = lambda{ 'hi' }
66
+ end
67
+
68
+ execute do
69
+ @report.bypass._calculators[:key] = @cell_calculator
70
+ @report.bypass.cell_calculator_for(:key, :row_type)
71
+ end
72
+
73
+ share_setup("with :through") do
74
+ @cell_calculator[:through] = :child
75
+ end
76
+
77
+ share_should("return a proc") do
78
+ assert @execute_result.respond_to?(:call)
79
+ end
80
+
81
+ share_should "return the lambda" do
82
+ assert_equal @lambda, @execute_result
83
+ end
84
+
85
+ share_should "wrap the lambda" do
86
+ assert_equal 'hi', @execute_result.call(Struct.new(:child).new)
87
+ end
88
+
89
+ share_should("return a proc sending") do |symbols|
90
+ model = mock('model')
91
+ symbols = [symbols] if symbols.is_a?(Symbol)
92
+ symbols.each do |symbol|
93
+ model.expects(symbol).returns(model)
94
+ end
95
+ @execute_result.call(model)
96
+ end
97
+
98
+ share_should("return a proc never sending") do |symbols|
99
+ model = mock('model')
100
+ symbols = [symbols] if symbols.is_a?(Symbol)
101
+ symbols.each do |symbol|
102
+ model.expects(symbol).never
103
+ end
104
+ @execute_result.call(model)
105
+ end
106
+
107
+ context "explicit row type with lambda" do
108
+ setup do
109
+ @cell_calculator = {
110
+ :row_type => @lambda
111
+ }
112
+ end
113
+
114
+ use_should("return the lambda")
115
+ use_setup("with :through").use_should("return the lambda")
116
+ end
117
+
118
+ context "explicit row type with symbol" do
119
+ setup do
120
+ @cell_calculator = {
121
+ :row_type => :foo
122
+ }
123
+ end
124
+
125
+ use_should("return a proc sending").given("method"){ :foo }
126
+
127
+ use_setup("with :through").context do
128
+ use_should("return a proc sending").given("method"){ :foo }
129
+ use_should("return a proc never sending").given("through"){ :child }
130
+ end
131
+ end
132
+
133
+ context "map_to with lambda" do
134
+ setup do
135
+ @cell_calculator = {
136
+ :map_to => @lambda
137
+ }
138
+ end
139
+
140
+ use_should("wrap the lambda")
141
+
142
+ use_setup("with :through").context do
143
+ use_should("return a proc sending").given("through"){ :child }
144
+ use_should("wrap the lambda")
145
+ end
146
+ end
147
+
148
+ context "map_to with symbol" do
149
+ setup do
150
+ @cell_calculator = {
151
+ :map_to => :foo
152
+ }
153
+ end
154
+
155
+ use_should("return a proc")
156
+ use_should("return a proc sending").given("method"){ :foo }
157
+
158
+ use_setup("with :through").use_should("return a proc sending").given("through and method"){ [:child, :foo] }
159
+
160
+ end
161
+
162
+ context "through list" do
163
+ setup do
164
+ @cell_calculator = {
165
+ :through => [:first, :second]
166
+ }
167
+ end
168
+
169
+ should "work for a model with first and second" do
170
+ model = mock
171
+ first_model = mock
172
+ second_model = mock
173
+ model.expects(:first).returns(first_model)
174
+ first_model.expects(:second).returns(second_model)
175
+ second_model.expects(:key)
176
+ @execute_result.call(model)
177
+ end
178
+
179
+ should "work for a model with only first" do
180
+ model = mock
181
+ first_model = mock
182
+ model.expects(:first).returns(first_model)
183
+ first_model.expects(:key)
184
+ @execute_result.call(model)
185
+ end
186
+
187
+ should "use second if model returns nil for first" do
188
+ model = mock
189
+ second_model = mock
190
+ model.expects(:first).returns(nil)
191
+ model.expects(:second).returns(second_model)
192
+ second_model.expects(:key)
193
+ @execute_result.call(model)
194
+ end
195
+
196
+ should "work for a model with only second" do
197
+ model = mock
198
+ second_model = mock
199
+ model.expects(:second).returns(second_model)
200
+ second_model.expects(:key)
201
+ @execute_result.call(model)
202
+ end
203
+
204
+ should "work for a model with neither" do
205
+ model = mock
206
+ model.expects(:key)
207
+ @execute_result.call(model)
208
+ end
209
+ end
210
+ end # end .cell_calculator_for
211
+
212
+ context ".safe_proc" do
213
+ setup do
214
+ @orig_proc = lambda{|m| m.foo }
215
+ end
216
+
217
+ execute do
218
+ @proc = Rapport.safe_proc(@orig_proc)
219
+ @proc.call(@model)
220
+ end
221
+
222
+ use_should("return nil").when("model doesn't respond correctly") { @model = Struct.new(:no_foo).new('happy') }
223
+
224
+ context "nil model" do
225
+ setup do
226
+ @model = nil
227
+ end
228
+
229
+
230
+ use_should("return nil").when("unhappy proc")
231
+
232
+ context "happy proc" do
233
+ setup do
234
+ @orig_proc = lambda { 'happy' }
235
+ end
236
+
237
+ should "be happy" do
238
+ assert_equal 'happy', @execute_result
239
+ end
240
+ end
241
+ end
242
+
243
+ context "happy model" do
244
+ setup do
245
+ @model = Struct.new(:foo).new('happy')
246
+ end
247
+
248
+ should "be happy" do
249
+ assert_equal 'happy', @execute_result
250
+ end
251
+ end
252
+ end
253
+ end