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.
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