rapport 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.tmtags ADDED
@@ -0,0 +1,96 @@
1
+ !_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
2
+ !_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
3
+ !_TAG_PROGRAM_AUTHOR Darren Hiebert /dhiebert@users.sourceforge.net/
4
+ !_TAG_PROGRAM_NAME Exuberant Ctags //
5
+ !_TAG_PROGRAM_URL http://ctags.sourceforge.net /official site/
6
+ !_TAG_PROGRAM_VERSION 5.7 //
7
+ Bypass test/test_helper.rb /^ class Bypass$/;" class line:22
8
+ CellFormatter lib/rapport/report_generator.rb /^ class CellFormatter$/;" class line:89
9
+ ClassMethods lib/rapport/report.rb /^ module ClassMethods$/;" module line:15
10
+ ClassMethods lib/rapport/report_generator.rb /^ module ClassMethods$/;" module line:20
11
+ Foo lib/test.rb /^class Foo$/;" class line:1
12
+ Object test/test_helper.rb /^class Object$/;" class line:21
13
+ Rapport lib/helpers.rb /^module Rapport$/;" module line:1
14
+ Rapport lib/rapport/report.rb /^module Rapport$/;" module line:1
15
+ Rapport lib/rapport/report_generator.rb /^module Rapport$/;" module line:3
16
+ Rapport lib/rapport/report_generators/report_generator_csv.rb /^module Rapport$/;" module line:3
17
+ Rapport lib/rapport/report_generators/report_generator_fake.rb /^module Rapport$/;" module line:1
18
+ Rapport test/test_helper.rb /^module Rapport$/;" module line:47
19
+ Report lib/rapport/report.rb /^ module Report$/;" module line:2
20
+ ReportGenerator lib/rapport/report_generator.rb /^ module ReportGenerator$/;" module line:4
21
+ ReportGeneratorCsv lib/rapport/report_generators/report_generator_csv.rb /^ class ReportGeneratorCsv$/;" class line:4
22
+ ReportGeneratorFake lib/rapport/report_generators/report_generator_fake.rb /^ class ReportGeneratorFake$/;" class line:2
23
+ ReportGeneratorTest test/test_helper.rb /^ class ReportGeneratorTest$/;" class line:79
24
+ ReportTest test/rapport/report_generator_test.rb /^class ReportTest < Test::Unit::TestCase$/;" class line:3
25
+ ReportTest test/rapport/report_test.rb /^class ReportTest < Test::Unit::TestCase$/;" class line:3
26
+ Test build/test/helper.rb /^class Test::Unit::TestCase$/;" class line:17
27
+ Test test/test_helper.rb /^class Test::Unit::TestCase$/;" class line:41
28
+ TestBuild build/test/test_build.rb /^class TestBuild < Test::Unit::TestCase$/;" class line:3
29
+ TestReport test/test_helper.rb /^ class TestReport$/;" class line:48
30
+ _calculators lib/rapport/report.rb /^ def _calculators$/;" method line:120
31
+ add_calculator lib/rapport/report.rb /^ def add_calculator(symbol, options)$/;" method line:34
32
+ add_cell_format lib/rapport/report_generator.rb /^ def add_cell_format(type, &block)$/;" method line:99
33
+ base_calculator lib/rapport/report.rb /^ def base_calculator(key, calculator)$/;" method line:157
34
+ bypass test/test_helper.rb /^ def bypass$/;" method line:36
35
+ cell_calculator_for lib/rapport/report.rb /^ def cell_calculator_for(key,row_type)$/;" method line:124
36
+ cell_format lib/rapport/report_generator.rb /^ def cell_format(type, &block)$/;" method line:48
37
+ cell_format_for lib/rapport/report.rb /^ def cell_format_for(key)$/;" method line:166
38
+ cell_formatter lib/rapport/report_generator.rb /^ def cell_formatter$/;" method line:59
39
+ column lib/rapport/report.rb /^ def column(*args, &block)$/;" method line:16
40
+ column_headers lib/rapport/report.rb /^ def column_headers$/;" method line:68
41
+ column_symbols lib/rapport/report.rb /^ def column_symbols$/;" method line:72
42
+ columns lib/rapport/report.rb /^ def columns$/;" method line:60
43
+ dup lib/rapport/report_generator.rb /^ def dup$/;" method line:95
44
+ each_model lib/rapport/report.rb /^ def each_model(&block)$/;" method line:105
45
+ each_model test/test_helper.rb /^ def each_model$/;" method line:55
46
+ each_row lib/rapport/report.rb /^ def each_row$/;" method line:53
47
+ each_row lib/rapport/report_generator.rb /^ def each_row$/;" method line:79
48
+ format lib/rapport/report_generator.rb /^ def format(type, value)$/;" method line:63
49
+ format lib/rapport/report_generator.rb /^ def format(type,value)$/;" method line:111
50
+ format for class test/rapport/report_generator_test.rb /^ share_should("format for class") do$/;" shared_should line:96
51
+ format for symbol test/rapport/report_generator_test.rb /^ share_should("format for symbol") do$/;" shared_should line:92
52
+ format_camel_case lib/helpers.rb /^ def format_camel_case(value)$/;" method line:22
53
+ format_underscore lib/helpers.rb /^ def format_underscore(value)$/;" method line:18
54
+ formatted_cell_value lib/rapport/report.rb /^ def formatted_cell_value(key, model, row_type)$/;" method line:88
55
+ formatted_row lib/rapport/report.rb /^ def formatted_row(model, row_type)$/;" method line:111
56
+ from lib/rapport/report_generator.rb /^ def from(report)$/;" method line:23
57
+ from lib/rapport/report_generator.rb /^ def self.from(report)$/;" singleton method line:6
58
+ generate lib/rapport/report.rb /^ def generate$/;" method line:45
59
+ generate_internal lib/rapport/report_generators/report_generator_fake.rb /^ def generate_internal$/;" method line:11
60
+ generate_with lib/rapport/report_generator.rb /^ def generate_with(&block)$/;" method line:29
61
+ hello lib/test.rb /^ def self.hello$/;" singleton method line:8
62
+ included lib/rapport/report.rb /^ def self.included(base)$/;" singleton method line:4
63
+ included lib/rapport/report_generator.rb /^ def self.included(base)$/;" singleton method line:11
64
+ inherited lib/rapport/report.rb /^ def base.inherited(subclass)$/;" singleton method line:8
65
+ inherited lib/rapport/report_generator.rb /^ def base.inherited(subclass)$/;" singleton method line:14
66
+ inherited lib/test.rb /^ def self.inherited(subclass)$/;" singleton method line:4
67
+ initialize lib/rapport/report_generator.rb /^ def initialize(procs = {})$/;" method line:91
68
+ initialize lib/rapport/report_generators/report_generator_fake.rb /^ def initialize(report)$/;" method line:7
69
+ initialize test/test_helper.rb /^ def initialize$/;" method line:84
70
+ initialize test/test_helper.rb /^ def initialize(options = {})$/;" method line:51
71
+ initialize test/test_helper.rb /^ def initialize(ref)$/;" method line:27
72
+ logger test/test_helper.rb /^ def logger$/;" method line:88
73
+ method_missing lib/rapport/report_generator.rb /^ def method_missing(method, *args)$/;" method line:67
74
+ method_missing test/test_helper.rb /^ def method_missing(sym, *args)$/;" method line:31
75
+ options lib/rapport/report.rb /^ def options$/;" method line:49
76
+ options lib/rapport/report_generator.rb /^ def options$/;" method line:55
77
+ output_filename lib/rapport/report_generator.rb /^ def output_filename$/;" method line:83
78
+ raw_cell_value lib/rapport/report.rb /^ def raw_cell_value(key, model, row_type)$/;" method line:84
79
+ report_generator lib/rapport/report.rb /^ def report_generator$/;" method line:41
80
+ report_name lib/rapport/report_generators/report_generator_csv.rb /^ def report_name$/;" method line:31
81
+ return a proc test/rapport/report_test.rb /^ share_should("return a proc") do$/;" shared_should line:77
82
+ return a proc never sending test/rapport/report_test.rb /^ share_should("return a proc never sending") do |symbols|$/;" shared_should line:98
83
+ return a proc sending test/rapport/report_test.rb /^ share_should("return a proc sending") do |symbols|$/;" shared_should line:89
84
+ return nil test/test_helper.rb /^ share_should("return nil") do$/;" shared_should line:42
85
+ return the lambda test/rapport/report_test.rb /^ share_should "return the lambda" do$/;" shared_should line:81
86
+ return value test/rapport/report_generator_test.rb /^ share_should "return value" do$/;" shared_should line:123
87
+ safe_proc lib/helpers.rb /^ def safe_proc(proc)$/;" method line:4
88
+ safe_send lib/helpers.rb /^ def safe_send(key)$/;" method line:14
89
+ section lib/rapport/report.rb /^ def section(section_data = {}, &block)$/;" method line:99
90
+ table_name lib/rapport/report.rb /^ def table_name$/;" method line:80
91
+ through_calculator lib/rapport/report.rb /^ def through_calculator(throughs, base_calculator)$/;" method line:144
92
+ to_model_class lib/rapport/report.rb /^ def to_model_class$/;" method line:76
93
+ to_s lib/rapport/report.rb /^ def to_s$/;" method line:93
94
+ with :through test/rapport/report_test.rb /^ share_setup("with :through") do$/;" shared_should line:73
95
+ wrap the lambda test/rapport/report_test.rb /^ share_should "wrap the lambda" do$/;" shared_should line:85
96
+ zip_output_file! lib/rapport/report_generators/report_generator_csv.rb /^ def zip_output_file!$/;" method line:23
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rake", "0.8.7"
10
+ gem "shoulda", "~> 2.11.3", :require => nil
11
+ gem "mocha", "0.9.8", :require => nil
12
+ gem "shared_should", "0.8.1", :require => nil
13
+ gem "always_execute", "0.0.2", :require => nil
14
+ gem "bundler", "~> 1.0.0"
15
+ gem "jeweler", "~> 1.6.4"
16
+ gem "rcov", ">= 0"
17
+ gem "rubyzip", "0.9.5"
18
+ gem "fastercsv", "1.5.1"
19
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,34 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ always_execute (0.0.2)
5
+ shoulda
6
+ fastercsv (1.5.1)
7
+ git (1.2.5)
8
+ jeweler (1.6.4)
9
+ bundler (~> 1.0)
10
+ git (>= 1.2.5)
11
+ rake
12
+ mocha (0.9.8)
13
+ rake
14
+ rake (0.8.7)
15
+ rcov (0.9.11)
16
+ rubyzip (0.9.5)
17
+ shared_should (0.8.1)
18
+ shoulda
19
+ shoulda (2.11.3)
20
+
21
+ PLATFORMS
22
+ ruby
23
+
24
+ DEPENDENCIES
25
+ always_execute (= 0.0.2)
26
+ bundler (~> 1.0.0)
27
+ fastercsv (= 1.5.1)
28
+ jeweler (~> 1.6.4)
29
+ mocha (= 0.9.8)
30
+ rake (= 0.8.7)
31
+ rcov
32
+ rubyzip (= 0.9.5)
33
+ shared_should (= 0.8.1)
34
+ shoulda (~> 2.11.3)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Andrew Wheeler
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,93 @@
1
+ # Rapport
2
+
3
+ Rapport allows you to create tabular reports with a DSL, which can be outputted via customizable formatters.
4
+
5
+ ## Quick-Start
6
+
7
+ Include the Rapport::Report module in any object to allow it to generate reports in the pre-built formatters (csv, fake) or
8
+ in a custom formatter. Use an easy DSL to write all your reports once, and all your formats once, and then mix and match as you please.
9
+
10
+ ### Creating a Report
11
+
12
+ require 'rubygems'
13
+ require 'rapport'
14
+
15
+ class OrderReport
16
+ include Rapport::Report
17
+
18
+ def initialize(options)
19
+ self.options = options
20
+ end
21
+
22
+ # Add columns to the report with column(column_header, *args)
23
+ # Column Header is what appears in the report, and *args specify how to calculate the values using the model
24
+ #
25
+ # examples:
26
+ # column('column header')
27
+ # column('column header', :column_symbol)
28
+ # column('column header', :model_type => :column_symbol, ...)
29
+ #
30
+ # All equivalent:
31
+ # column 'Product ID', :map_to => lambda {|model| !model.nil? and model.respond_to?(:product_id) ? model.product_id : nil }
32
+ # column 'Product ID', :map_to => :product_id
33
+ # column 'Product ID', :product_id
34
+ column 'Product ID' # by default converts 'Product ID' to :product_id
35
+
36
+ # special calculation depending on type of model
37
+ column 'Price', :additional_charge => :amount,
38
+ :line_item => :price, # not required; defaults to :price
39
+ :special_line_item => lambda {|line_item| line_item.price - line_item.tax },
40
+ # Display price (in cents) as dollars
41
+ :format => :cents
42
+
43
+ # Navigate an object heirarchy using :through
44
+ column 'Order ID', :through -> :order, :map_to => :id
45
+ column 'Customer Name', :through => [:order, :customer], :map_to => lambda {|customer| "#{customer.first_name} #{customer.last_name}" }
46
+
47
+ # Implement each_model to specify which models belong to the report.
48
+ # The method should yield each model, and optionally specify the model type in order to customize how the value is calculated.
49
+ def each_model
50
+ AdditionalCharge.in_order_report.each do |additional_charge|
51
+ yield additional_charge # equivalent to yield additional_charge, :additional_charge
52
+ end
53
+
54
+ LineItem.in_order_report.each do |line_item|
55
+ if line_item.special?
56
+ yield line_item, :special_line_item # uses special price calculation above
57
+ else
58
+ yield line_item # equivalent to yield line_item, :line_item
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ ### Running a Report
65
+
66
+ order_report = OrderReport.new(:format => 'csv', :output_dir => 'my_dir')
67
+ order_report.generate # generates a csv file named 'my_dir/OrderReport_{timestamp}.csv
68
+
69
+ order_report = OrderReport.new(:format => 'my_custom')
70
+ order_report.generate # generates the same report, but in your custom format
71
+
72
+ ### Creating a Custom Format
73
+ module Rapport
74
+ class ReportGeneratorMyCustom
75
+ include ReportGenerator
76
+
77
+ generate_with do |report|
78
+ File.open(output_filename,'w') do |file|
79
+ file.puts(report.column_headers.join('|'))
80
+ report.each_row do |row|
81
+ file.puts(row.join('|'))
82
+ end
83
+ end
84
+ end
85
+
86
+ end
87
+ end
88
+
89
+ ## Copyright
90
+
91
+ Copyright (c) 2011 Andrew Wheeler. See LICENSE.txt for
92
+ further details.
93
+
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "rapport"
18
+ gem.homepage = "http://github.com/jawheeler/rapport"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Rapport Reporting}
21
+ gem.description = %Q{Allows you to create tablular reports and output them to various formats interchangeably.}
22
+ gem.email = "andrew.wheeler@bookrenter.com"
23
+ gem.authors = ["Andrew Wheeler"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/*_test.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ test.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "rapport #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.4
data/lib/helpers.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'logger'
2
+
3
+ module Rapport
4
+
5
+ class << self
6
+ def safe_proc(proc)
7
+ lambda do |m|
8
+ begin
9
+ proc.call(m)
10
+ rescue NameError => ne
11
+ nil
12
+ end
13
+ end
14
+ end
15
+
16
+ def safe_send(key)
17
+ lambda{|m| !m.nil? and m.respond_to?(key) ? m.send(key) : nil }
18
+ end
19
+
20
+ def format_underscore(value)
21
+ value.to_s.gsub(/\W/,'_').gsub(/(.)([A-Z])/,'\1_\2').gsub(/_+/,'_').downcase
22
+ end
23
+
24
+ def format_camel_case(value)
25
+ value.to_s.capitalize.gsub(/_(.)/){ $1.upcase }
26
+ end
27
+
28
+ def logger
29
+ if Module.const_defined?("Rails")
30
+ @_logger ||= Rails.logger
31
+ else
32
+ @_logger ||= Logger.new(STDERR)
33
+ end
34
+ end
35
+
36
+ def logger=(logger)
37
+ @_logger = logger
38
+ end
39
+ end
40
+
41
+ end
@@ -0,0 +1,4 @@
1
+ require 'lib/helpers'
2
+ require 'lib/rapport/report'
3
+ require 'lib/rapport/report_generator'
4
+ require 'lib/rapport/report_generators/report_generator_fake'
@@ -0,0 +1,177 @@
1
+ module Rapport
2
+ module Report
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ base.instance_variable_set(:@columns, [])
7
+ base.instance_variable_set(:@cell_calculators, {})
8
+ def base.inherited(subclass)
9
+ super(subclass)
10
+ subclass.instance_variable_set(:@columns, base.instance_variable_get(:@columns).dup )
11
+ subclass.instance_variable_set(:@cell_calculators, base.instance_variable_get(:@cell_calculators).dup )
12
+ end
13
+ end
14
+
15
+ module ClassMethods
16
+ attr_accessor :row_data
17
+
18
+ def column(*args, &block)
19
+ raise ArgumentError.new("wrong number of arguments (#{args.length} for 1,2, or 3)") unless args.length >= 1 and args.length <= 3
20
+ header = args[0]
21
+ if args.length == 3
22
+ symbol = args[1].to_sym; options = args[2].to_hash
23
+ elsif args.length == 2
24
+ if args[1].is_a?(Symbol)
25
+ symbol = args[1]; options = {}
26
+ else
27
+ symbol = Rapport.format_underscore(header.downcase).to_sym; options = args[1].to_hash
28
+ end
29
+ end
30
+ add_calculator(symbol, options) unless options.nil?
31
+ instance_variable_get(:@columns) << [header, symbol]
32
+ end
33
+
34
+ private
35
+
36
+ def add_calculator(symbol, options)
37
+ instance_variable_get(:@cell_calculators)[symbol] = options
38
+ end
39
+ end
40
+
41
+ attr_accessor :section_data, :current_model, :options
42
+
43
+ def row_data
44
+ self.class.row_data
45
+ end
46
+
47
+ def report_generator
48
+ @_report_generator ||= ReportGenerator.from(self)
49
+ end
50
+
51
+ def generate
52
+ report_generator.generate
53
+ end
54
+
55
+ def options
56
+ @options ||= {:format => 'fake'}
57
+ end
58
+
59
+ def each_row
60
+ each_model do |model, row_type|
61
+ self.current_model = model # For error reporting
62
+ yield formatted_row(model, row_type || Rapport.format_underscore(model.class.to_s))
63
+ end
64
+ end
65
+
66
+ def columns
67
+ if @_columns.nil?
68
+ @_columns = []
69
+ self.class.send(:instance_variable_get, :@columns).each {|col| @_columns << col.dup }
70
+ end
71
+ @_columns
72
+ end
73
+
74
+ def column_headers
75
+ @_column_headers ||= columns.map{|c| c[0]}
76
+ end
77
+
78
+ def column_symbols
79
+ @_column_symbols ||= columns.map{|c| c[1]}
80
+ end
81
+
82
+ def to_model_class
83
+ @_to_model_class ||= Struct.new("#{self.class}Model", *column_symbols)
84
+ end
85
+
86
+ def table_name
87
+ @table_name ||= options[:report_table_name] || "reports_#{Rapport.format_underscore(self.class).sub(/_?report_?/,'')}"
88
+ end
89
+
90
+ def raw_cell_value(key, model, row_type)
91
+ self.row_data[key] = cell_calculator_for(key,row_type).call(model)
92
+ end
93
+
94
+ def formatted_cell_value(key, model, row_type)
95
+ row_data[key] = raw_value = raw_cell_value(key, model, row_type)
96
+ report_generator.format(cell_format_for(key), raw_value)
97
+ end
98
+
99
+ def to_s
100
+ [self.class, options[:format]].compact.map{|p| p.to_s }.join(' ')
101
+ end
102
+
103
+ protected
104
+
105
+ def section(section_data = {}, &block)
106
+ self.section_data = section_data
107
+ yield
108
+ self.section_data = nil
109
+ end
110
+
111
+ def each_model(&block)
112
+ raise "#each_model has not been implemented"
113
+ end
114
+
115
+ private
116
+
117
+ def formatted_row(model, row_type)
118
+ self.class.row_data = {}
119
+ out = column_symbols.map do |key|
120
+ formatted_cell_value(key, model, row_type)
121
+ end
122
+ self.class.row_data = {}
123
+ out
124
+ end
125
+
126
+ def _calculators
127
+ @_calculators ||= self.class.send(:instance_variable_get, :@cell_calculators).dup
128
+ end
129
+
130
+ def cell_calculator_for(key,row_type)
131
+ calculator = _calculators[key] ||= {}
132
+
133
+ if calculator[row_type].nil?
134
+ base_calculator = base_calculator(key,calculator)
135
+ calculator_for_row_type = if calculator[:through]
136
+ calculator[:through] = [calculator[:through]] unless calculator[:through].is_a?(Enumerable)
137
+ through_calculator(calculator[:through], base_calculator)
138
+ else
139
+ base_calculator
140
+ end
141
+ calculator[row_type] = calculator_for_row_type
142
+ elsif !calculator[row_type].respond_to?(:call)
143
+ row_type_key = calculator[row_type]
144
+ calculator[row_type] = Rapport.safe_send(row_type_key)
145
+ end
146
+
147
+ calculator[row_type]
148
+ end
149
+
150
+ def through_calculator(throughs, base_calculator)
151
+ lambda do |m|
152
+ path = m
153
+ throughs.each do |hint|
154
+ if path.respond_to?(hint)
155
+ new_path = path.send(hint)
156
+ path = new_path unless new_path.nil?
157
+ end
158
+ end
159
+ base_calculator.call(path)
160
+ end
161
+ end
162
+
163
+ def base_calculator(key, calculator)
164
+ base_key = key
165
+ if calculator[:map_to]
166
+ return Rapport.safe_proc(calculator[:map_to]) if calculator[:map_to].respond_to?(:call)
167
+ base_key = calculator[:map_to].to_sym
168
+ end
169
+ Rapport.safe_send(base_key)
170
+ end
171
+
172
+ def cell_format_for(key)
173
+ _calculators[key][:"format_#{options[:format]}"] || _calculators[key][:format]
174
+ end
175
+
176
+ end
177
+ end
@@ -0,0 +1,105 @@
1
+ module Rapport
2
+ module ReportGenerator
3
+
4
+ def self.from(report)
5
+ options = {:format => 'fake'}.merge(report.options)
6
+ Rapport.const_get("ReportGenerator#{Rapport.format_camel_case(options[:format].to_s)}").from(report)
7
+ end
8
+
9
+ def self.included(base)
10
+ base.extend(ClassMethods)
11
+ base.instance_variable_set(:@cell_formatter, CellFormatter.new)
12
+ def base.inherited(subclass)
13
+ super(subclass)
14
+ subclass.instance_variable_set(:@cell_formatter, base.instance_variable_get(:@cell_formatter).dup)
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ # Override to customize the report generator based on the charactersistics of the report or its options
20
+ # Default implementation uses default constructor and assigns to the report attribute.
21
+ def from(report)
22
+ out = self.new
23
+ out.report = report
24
+ out
25
+ end
26
+
27
+ # generate_with {|report| ... }
28
+ # Provides a logging/error handling wrapper for the passed block. Automatically cretes report_generator.generate.
29
+ def generate_with(&block)
30
+ raise "Only one call to generate_with is permitted (or none if #generate is implemented)" if public_method_defined?(:generate)
31
+ define_method :generate do
32
+ raise "No report to generate!" if report.nil?
33
+ out = nil
34
+ begin
35
+ Rapport.logger.info("Generating #{report}...")
36
+ out = block.call(report)
37
+ Rapport.logger.info("Generated #{report}.")
38
+ rescue Exception => e
39
+ error = report.current_model.nil? ? '' : "While processing:\n\n#{report.current_model.inspect}\n\n"
40
+ error += "#{report} failed:\n\n#{e.message}\n\n#{e.backtrace.join("\n")}"
41
+ Rapport.logger.error(error)
42
+ Rapport.logger.mail(:error,error) if Rapport.logger.respond_to?(:mail)
43
+ end
44
+ out
45
+ end
46
+ end
47
+
48
+ def cell_format(type, &block)
49
+ instance_variable_get(:@cell_formatter).add_cell_format(type, &block)
50
+ end
51
+
52
+ def format_as(type, value)
53
+ instance_variable_get(:@cell_formatter).format(type, value)
54
+ end
55
+ end
56
+
57
+ attr_accessor :report
58
+
59
+ def cell_formatter
60
+ @cell_formatter ||= self.class.instance_variable_get(:@cell_formatter).dup
61
+ end
62
+
63
+ def format(type, value)
64
+ cell_formatter.format(type, value)
65
+ end
66
+
67
+ def output_filename
68
+ @output_filename ||= File.join(@options[:output_dir] || '.', "#{report_name}_#{Time.now.strftime('%Y-%m-%d-%H%M%S')}.csv")
69
+ end
70
+
71
+ end
72
+
73
+ class CellFormatter
74
+
75
+ def initialize(procs = {})
76
+ @procs = procs
77
+ end
78
+
79
+ def dup
80
+ CellFormatter.new(@procs.dup)
81
+ end
82
+
83
+ def add_cell_format(type, &block)
84
+ proc = Proc.new(&block)
85
+ @procs[type] = proc
86
+ @procs[Rapport.format_underscore(type.to_s)] = proc if type.is_a?(Class)
87
+
88
+ # Support ActiveSupport::TimeWithZone automatically
89
+ if type == Time
90
+ add_cell_format(ActiveSupport::TimeWithZone, &block) rescue nil
91
+ end
92
+
93
+ end
94
+
95
+ def format(type,value)
96
+ if type.is_a?(Proc)
97
+ type.call(value)
98
+ else
99
+ proc = @procs[type] || @procs[value.class]
100
+ proc.nil? ? value : proc.call(value)
101
+ end
102
+ end
103
+ end
104
+
105
+ end