rapport 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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