rapport 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.tmtags +96 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +34 -0
- data/LICENSE.txt +20 -0
- data/README.md +93 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/helpers.rb +41 -0
- data/lib/load_rapport.rb +4 -0
- data/lib/rapport/report.rb +177 -0
- data/lib/rapport/report_generator.rb +105 -0
- data/lib/rapport/report_generators/report_generator_csv.rb +58 -0
- data/lib/rapport/report_generators/report_generator_fake.rb +16 -0
- data/lib/rapport.rb +5 -0
- data/lib/test.rb +12 -0
- data/rapport.gemspec +88 -0
- data/test/logs/test.log +6 -0
- data/test/rapport/report_generator_test.rb +157 -0
- data/test/rapport/report_generators/report_generator_csv_test.rb +56 -0
- data/test/rapport/report_test.rb +253 -0
- data/test/test_helper.rb +112 -0
- metadata +244 -0
data/.document
ADDED
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
|
data/lib/load_rapport.rb
ADDED
@@ -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
|