jsanders-ruport 1.7.1
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/AUTHORS +48 -0
- data/LICENSE +59 -0
- data/README +114 -0
- data/Rakefile +93 -0
- data/examples/RWEmerson.jpg +0 -0
- data/examples/anon.rb +43 -0
- data/examples/btree/commaleon/commaleon.rb +263 -0
- data/examples/btree/commaleon/sample_data/ticket_count.csv +124 -0
- data/examples/btree/commaleon/sample_data/ticket_count2.csv +119 -0
- data/examples/centered_pdf_text_box.rb +83 -0
- data/examples/data/tattle.dump +82 -0
- data/examples/example.csv +3 -0
- data/examples/line_plotter.rb +61 -0
- data/examples/pdf_report_with_common_base.rb +72 -0
- data/examples/png_embed.rb +54 -0
- data/examples/roadmap.png +0 -0
- data/examples/row_renderer.rb +39 -0
- data/examples/simple_pdf_lines.rb +25 -0
- data/examples/simple_templating_example.rb +34 -0
- data/examples/tattle_ruby_version.rb +39 -0
- data/examples/tattle_rubygems_version.rb +37 -0
- data/examples/trac_ticket_status.rb +59 -0
- data/lib/ruport.rb +127 -0
- data/lib/ruport/controller.rb +616 -0
- data/lib/ruport/controller/grouping.rb +71 -0
- data/lib/ruport/controller/table.rb +54 -0
- data/lib/ruport/data.rb +4 -0
- data/lib/ruport/data/feeder.rb +111 -0
- data/lib/ruport/data/grouping.rb +399 -0
- data/lib/ruport/data/record.rb +297 -0
- data/lib/ruport/data/table.rb +950 -0
- data/lib/ruport/extensions.rb +4 -0
- data/lib/ruport/formatter.rb +254 -0
- data/lib/ruport/formatter/csv.rb +149 -0
- data/lib/ruport/formatter/html.rb +161 -0
- data/lib/ruport/formatter/pdf.rb +591 -0
- data/lib/ruport/formatter/template.rb +187 -0
- data/lib/ruport/formatter/text.rb +231 -0
- data/lib/uport.rb +1 -0
- data/test/controller_test.rb +743 -0
- data/test/csv_formatter_test.rb +164 -0
- data/test/data_feeder_test.rb +88 -0
- data/test/grouping_test.rb +410 -0
- data/test/helpers.rb +11 -0
- data/test/html_formatter_test.rb +201 -0
- data/test/pdf_formatter_test.rb +354 -0
- data/test/record_test.rb +332 -0
- data/test/samples/addressbook.csv +6 -0
- data/test/samples/data.csv +3 -0
- data/test/samples/data.tsv +3 -0
- data/test/samples/dates.csv +1409 -0
- data/test/samples/erb_test.sql +1 -0
- data/test/samples/query_test.sql +1 -0
- data/test/samples/ruport_test.sql +8 -0
- data/test/samples/test.sql +2 -0
- data/test/samples/test.yaml +3 -0
- data/test/samples/ticket_count.csv +124 -0
- data/test/table_pivot_test.rb +134 -0
- data/test/table_test.rb +838 -0
- data/test/template_test.rb +48 -0
- data/test/text_formatter_test.rb +258 -0
- data/util/bench/data/record/bench_as_vs_to.rb +18 -0
- data/util/bench/data/record/bench_constructor.rb +46 -0
- data/util/bench/data/record/bench_indexing.rb +65 -0
- data/util/bench/data/record/bench_reorder.rb +35 -0
- data/util/bench/data/record/bench_to_a.rb +19 -0
- data/util/bench/data/table/bench_column_manip.rb +103 -0
- data/util/bench/data/table/bench_dup.rb +24 -0
- data/util/bench/data/table/bench_init.rb +67 -0
- data/util/bench/data/table/bench_manip.rb +125 -0
- data/util/bench/formatter/bench_csv.rb +14 -0
- data/util/bench/formatter/bench_html.rb +14 -0
- data/util/bench/formatter/bench_pdf.rb +14 -0
- data/util/bench/formatter/bench_text.rb +14 -0
- data/util/bench/samples/tattle.csv +1237 -0
- metadata +176 -0
data/AUTHORS
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
= Developers
|
2
|
+
|
3
|
+
- {Gregory Brown}[mailto:gregory.t.brown@gmail.com]
|
4
|
+
- {Dudley Flanders}[mailto:dudley@misnomer.us]
|
5
|
+
- {James Healy}[mailto:jimmy@deefa.com]
|
6
|
+
- Dinko Mehinovic
|
7
|
+
- {Michael Milner}[mailto:mikem836@gmail.com]
|
8
|
+
|
9
|
+
= Contributors / People we've (legally) stolen from:
|
10
|
+
|
11
|
+
Iain Broadfoot:
|
12
|
+
- RuportDay 2006 Participant
|
13
|
+
|
14
|
+
Eric Pugh:
|
15
|
+
- RuportDay 2006 Participant
|
16
|
+
|
17
|
+
James Edward Gray II:
|
18
|
+
- Original inspiration via query.rb
|
19
|
+
- system_extensions.rb
|
20
|
+
|
21
|
+
Mathijs Mohlmann:
|
22
|
+
- Performance Enhancement
|
23
|
+
TextPlugin r264-r265
|
24
|
+
HTMLPlugin r266
|
25
|
+
|
26
|
+
Francis Hwang:
|
27
|
+
- SQLSplit
|
28
|
+
|
29
|
+
Simon Claret:
|
30
|
+
- Initial PDF table support (now deprecated)
|
31
|
+
|
32
|
+
Daniel Berger:
|
33
|
+
- we vendored and modified attempt.rb to support it directly in Report.
|
34
|
+
Original website: http://raa.ruby-lang.org/project/attempt/
|
35
|
+
|
36
|
+
Marshall T. Vandegrift:
|
37
|
+
- Fixed a bug in Record's struct-like accessors (method_missing)
|
38
|
+
- Provided performance enhancements and tests for query.rb
|
39
|
+
- Improved alignment for Text Tables.
|
40
|
+
|
41
|
+
Stefan Mahlitz:
|
42
|
+
- Table#sort_rows_by
|
43
|
+
|
44
|
+
Chris Carter
|
45
|
+
- Table#remove_columns (r440)
|
46
|
+
|
47
|
+
Dave Nelson
|
48
|
+
- Grouping#sigma (r1131)
|
data/LICENSE
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
Ruport is copyrighted free software originally produced by Gregory Brown
|
2
|
+
<gregory.t.brown@gmail.com> which now contains a number of community
|
3
|
+
contributions and is actively developed by Michael Milner <mikem836@gmail.com>.
|
4
|
+
|
5
|
+
See the AUTHORS file for a complete list of contributors.
|
6
|
+
|
7
|
+
Licensing terms follow (License of Ruby 1.8):
|
8
|
+
|
9
|
+
You can redistribute Ruport and/or modify it under either the terms of the GPL
|
10
|
+
(see COPYING file), or the conditions below:
|
11
|
+
|
12
|
+
1. You may make and give away verbatim copies of the source form of the
|
13
|
+
software without restriction, provided that you duplicate all of the
|
14
|
+
original copyright notices and associated disclaimers.
|
15
|
+
|
16
|
+
2. You may modify your copy of the software in any way, provided that
|
17
|
+
you do at least ONE of the following:
|
18
|
+
|
19
|
+
a) place your modifications in the Public Domain or otherwise
|
20
|
+
make them Freely Available, such as by posting said
|
21
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
22
|
+
the author to include your modifications in the software.
|
23
|
+
|
24
|
+
b) use the modified software only within your corporation or
|
25
|
+
organization.
|
26
|
+
|
27
|
+
c) rename any non-standard executables so the names do not conflict
|
28
|
+
with standard executables, which must also be provided.
|
29
|
+
|
30
|
+
d) make other distribution arrangements with the author.
|
31
|
+
|
32
|
+
3. You may distribute the software in object code or executable
|
33
|
+
form, provided that you do at least ONE of the following:
|
34
|
+
|
35
|
+
a) distribute the executables and library files of the software,
|
36
|
+
together with instructions (in the manual page or equivalent)
|
37
|
+
on where to get the original distribution.
|
38
|
+
|
39
|
+
b) accompany the distribution with the machine-readable source of
|
40
|
+
the software.
|
41
|
+
|
42
|
+
c) give non-standard executables non-standard names, with
|
43
|
+
instructions on where to get the original software distribution.
|
44
|
+
|
45
|
+
d) make other distribution arrangements with the author.
|
46
|
+
|
47
|
+
4. You may modify and include the part of the software into any other
|
48
|
+
software (possibly commercial).
|
49
|
+
|
50
|
+
5. The scripts and library files supplied as input to or produced as
|
51
|
+
output from the software do not automatically fall under the
|
52
|
+
copyright of the software, but belong to whomever generated them,
|
53
|
+
and may be sold commercially, and may be aggregated with this
|
54
|
+
software.
|
55
|
+
|
56
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
57
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
58
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
59
|
+
PURPOSE.
|
data/README
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# -----------------------------------------------------------------
|
2
|
+
# Contents:
|
3
|
+
#
|
4
|
+
# + What Ruport Is
|
5
|
+
# + Installation
|
6
|
+
# + Resources
|
7
|
+
# + Hacking
|
8
|
+
#
|
9
|
+
# = What Ruport Is
|
10
|
+
#
|
11
|
+
# Ruby Reports (Ruport) is an extensible reporting system.
|
12
|
+
#
|
13
|
+
# It aims to be as lightweight as possible while still providing core support
|
14
|
+
# for data aggregation and manipulation as well as multi-format rendering
|
15
|
+
# of reports.
|
16
|
+
#
|
17
|
+
# Ruport provides tools for using a number of data sources, including CSV files,
|
18
|
+
# ActiveRecord models, and raw SQL connections via RubyDBI (through ruport-util).
|
19
|
+
#
|
20
|
+
# Data manipulation is easy as there are standard structures that support
|
21
|
+
# record, table, and grouping operations. These all can be extended to
|
22
|
+
# implement custom behavior as needed.
|
23
|
+
#
|
24
|
+
# For common tasks, Ruport provides formatters for CSV, HTML, PDF, and text-
|
25
|
+
# based reports. However, the real power lies in building custom report
|
26
|
+
# controllers and formatters. The base formatting libraries provide a number
|
27
|
+
# of helper functions that will let you build complex reports while maintaining
|
28
|
+
# a DRY and consistent interface.
|
29
|
+
#
|
30
|
+
# To get a quick feel for what you can accomplish with Ruport, take a look at
|
31
|
+
# a few simple examples provided on our web site.
|
32
|
+
#
|
33
|
+
# http://rubyreports.org/examples.html
|
34
|
+
#
|
35
|
+
# Since Ruport's core support is intentionally minimalistic, you may be looking
|
36
|
+
# for some higher level support for specific needs such as graphing, invoices,
|
37
|
+
# report mailing support, etc. For this, you may wish to take a look at the
|
38
|
+
# ruport-util package, which contains some generally useful tools and libraries
|
39
|
+
# to extend Ruport's capabilities.
|
40
|
+
#
|
41
|
+
# = Installation
|
42
|
+
#
|
43
|
+
# To install ruport via rubygems:
|
44
|
+
#
|
45
|
+
# sudo gem install ruport
|
46
|
+
#
|
47
|
+
# Check to see if it installed properly:
|
48
|
+
#
|
49
|
+
# ruby -rubygems -e "require 'ruport'; puts Ruport::VERSION"
|
50
|
+
#
|
51
|
+
# If you get an error, please let us know on our mailing list.
|
52
|
+
#
|
53
|
+
# Dependencies Details:
|
54
|
+
#
|
55
|
+
# -- formatting
|
56
|
+
#
|
57
|
+
# Ruport relies on PDF::Writer and FasterCSV for its formatting support.
|
58
|
+
# If you want to make use of textile helpers, you'll also need RedCloth.
|
59
|
+
#
|
60
|
+
# -- database interaction
|
61
|
+
#
|
62
|
+
# If you wish to use Ruport to report against a rails project,
|
63
|
+
# a camping project, or do standalone acts_as_reportable reports, you'll need
|
64
|
+
# ActiveRecord and the acts_as_reportable gem.
|
65
|
+
#
|
66
|
+
# If you want to use Ruport::Query for raw SQL support, you'll need to
|
67
|
+
# install ruport-util, RubyDBI and whatever database drivers you might need.
|
68
|
+
#
|
69
|
+
# = Resources
|
70
|
+
#
|
71
|
+
# Our developers have published a free-content book about all things
|
72
|
+
# Ruport, including complete coverage of acts_as_reportable and some of
|
73
|
+
# ruport-util's features. This book serves as the definitive guide to
|
74
|
+
# Ruport, so all users should become acquainted with it:
|
75
|
+
#
|
76
|
+
# http://ruportbook.com
|
77
|
+
#
|
78
|
+
# The next best way to get help and make suggestions is the Ruport mailing list.
|
79
|
+
# This software is on the move, so the list is the most reliable way of getting
|
80
|
+
# up to date information.
|
81
|
+
#
|
82
|
+
# - You can sign up and/or view the archives here:
|
83
|
+
# http://groups.google.com/group/ruby-reports
|
84
|
+
#
|
85
|
+
# If you are looking to dig a little deeper, there are a couple more resources
|
86
|
+
# that may be helpful to you.
|
87
|
+
#
|
88
|
+
# - The latest stable API documentation is available at:
|
89
|
+
# http://api.rubyreports.org
|
90
|
+
#
|
91
|
+
# - Our Trac is at: http://code.rubyreports.org/ruport
|
92
|
+
# You may use the username ruport and password blinky to file tickets.
|
93
|
+
#
|
94
|
+
# = Hacking
|
95
|
+
#
|
96
|
+
# If you'd like to contribute code to Ruport, please join our development
|
97
|
+
# mailing list, and let us know what you'd like to do!
|
98
|
+
#
|
99
|
+
# http://groups.google.com/group/ruport-dev
|
100
|
+
#
|
101
|
+
# It also may be worthwhile to join this list if you plan on running edge
|
102
|
+
# versions of Ruport, as this is where we make announcements about major
|
103
|
+
# breakage in trunk.
|
104
|
+
#
|
105
|
+
# We are very responsive to contributors, and review every patch we receive
|
106
|
+
# fairly quickly. Most contributors who successfully get a patch or two applied
|
107
|
+
# are given write access to the repositories and invited to join Ruport's
|
108
|
+
# development team. Since we view every user as potential contributor, this
|
109
|
+
# approach works well for us.
|
110
|
+
#
|
111
|
+
# So if you want to help out with Ruport, we'll happy accept your efforts!
|
112
|
+
|
113
|
+
|
114
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
require "rake/rdoctask"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
RUPORT_VERSION = "1.7.0"
|
5
|
+
|
6
|
+
begin
|
7
|
+
require "rubygems"
|
8
|
+
rescue LoadError
|
9
|
+
nil
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => [:test]
|
13
|
+
|
14
|
+
Rake::TestTask.new do |test|
|
15
|
+
test.libs << "test"
|
16
|
+
test.test_files = Dir[ "test/*_test.rb" ]
|
17
|
+
test.verbose = true
|
18
|
+
end
|
19
|
+
|
20
|
+
Rake::RDocTask.new do |rdoc|
|
21
|
+
rdoc.rdoc_files.include( "README",
|
22
|
+
#"CHANGELOG",
|
23
|
+
"AUTHORS", "COPYING",
|
24
|
+
"LICENSE", "lib/" )
|
25
|
+
rdoc.main = "README"
|
26
|
+
rdoc.rdoc_dir = "doc/html"
|
27
|
+
rdoc.title = "Ruport Documentation"
|
28
|
+
end
|
29
|
+
|
30
|
+
task :build_archives => [:package,:rcov,:rdoc] do
|
31
|
+
mv "pkg/ruport-#{RUPORT_VERSION}.tgz", "pkg/ruport-#{RUPORT_VERSION}.tar.gz"
|
32
|
+
sh "tar cjvf pkg/ruport_coverage-#{RUPORT_VERSION}.tar.bz2 coverage"
|
33
|
+
sh "tar cjvf pkg/ruport_doc-#{RUPORT_VERSION}.tar.bz2 doc/html"
|
34
|
+
cd "pkg"
|
35
|
+
sh "tar cjvf ruport-#{RUPORT_VERSION}.tar.bz2 ruport-#{RUPORT_VERSION}"
|
36
|
+
end
|
37
|
+
|
38
|
+
task :run_benchmarks do
|
39
|
+
files = FileList["util/bench/**/**/*.rb"]
|
40
|
+
files.sort!
|
41
|
+
files.uniq!
|
42
|
+
names = files.map { |r| r.sub("util/bench","").split("/").map { |e| e.capitalize } }
|
43
|
+
names.map! { |e| e[1..-2].join("::") + " <BENCH: #{e[-1].sub('Bench_','').sub('.rb','')}>" }
|
44
|
+
start_time = Time.now
|
45
|
+
files.zip(names).each { |f,n|
|
46
|
+
puts "\n#{n}\n\n"
|
47
|
+
sh "ruby -Ilib #{f}"
|
48
|
+
puts "\n"
|
49
|
+
}
|
50
|
+
end_time = Time.now
|
51
|
+
puts "\n** Total Run Time: #{end_time-start_time}s **"
|
52
|
+
end
|
53
|
+
|
54
|
+
begin
|
55
|
+
require 'rcov/rcovtask'
|
56
|
+
Rcov::RcovTask.new do |t|
|
57
|
+
t.test_files = Dir[ "test/*_test.rb" ]
|
58
|
+
end
|
59
|
+
rescue LoadError
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
|
63
|
+
begin
|
64
|
+
require 'jeweler'
|
65
|
+
Jeweler::Tasks.new do |s|
|
66
|
+
s.name = "ruport"
|
67
|
+
s.version = RUPORT_VERSION
|
68
|
+
s.platform = Gem::Platform::RUBY
|
69
|
+
s.summary = "A generalized Ruby report generation and templating engine."
|
70
|
+
s.files = Dir.glob("{examples,lib,test,bin,util/bench}/**/**/*") +
|
71
|
+
["Rakefile"]
|
72
|
+
s.require_path = "lib"
|
73
|
+
|
74
|
+
s.test_files = Dir[ "test/*_test.rb" ]
|
75
|
+
s.has_rdoc = true
|
76
|
+
s.extra_rdoc_files = %w{README LICENSE AUTHORS}
|
77
|
+
s.rdoc_options << '--title' << 'Ruport Documentation' <<
|
78
|
+
'--main' << 'README' << '-q'
|
79
|
+
s.add_dependency('fastercsv')
|
80
|
+
s.add_dependency('pdf-writer','= 1.1.8')
|
81
|
+
s.author = "Gregory Brown"
|
82
|
+
s.email = " gregory.t.brown@gmail.com"
|
83
|
+
s.rubyforge_project = "ruport"
|
84
|
+
s.homepage = "http://rubyreports.org"
|
85
|
+
s.description = <<-END_DESC
|
86
|
+
Ruby Reports is a software library that aims to make the task of reporting
|
87
|
+
less tedious and painful. It provides tools for data acquisition,
|
88
|
+
database interaction, formatting, and parsing/munging.
|
89
|
+
END_DESC
|
90
|
+
end
|
91
|
+
rescue LoadError
|
92
|
+
puts 'Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com'
|
93
|
+
end
|
Binary file
|
data/examples/anon.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# Demonstrates building a parent controller which provides additional 'built in'
|
2
|
+
# formats, allowing anonymous formatter support to use the simple interface
|
3
|
+
# rather than the :format => FormatterClass approach.
|
4
|
+
|
5
|
+
require "ruport"
|
6
|
+
module FooCorp
|
7
|
+
class Controller < Ruport::Controller
|
8
|
+
def self.built_in_formats
|
9
|
+
super.merge(:xml => FooCorp::Formatter::XML)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class Formatter
|
14
|
+
class XML < Ruport::Formatter
|
15
|
+
|
16
|
+
def xmlify(stuff)
|
17
|
+
output << "Wouldn't you like to see #{stuff} in XML?"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class MyController < FooCorp::Controller
|
23
|
+
stage :foo
|
24
|
+
|
25
|
+
formatter :xml do
|
26
|
+
build :foo do
|
27
|
+
xmlify "Red Snapper"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
formatter :text do
|
32
|
+
build :foo do
|
33
|
+
output << "Red Snapper"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
puts "XML:"
|
40
|
+
puts FooCorp::MyController.render_xml
|
41
|
+
|
42
|
+
puts "Text:"
|
43
|
+
puts FooCorp::MyController.render_text
|
@@ -0,0 +1,263 @@
|
|
1
|
+
# This example is a simplified version of a tool actually used at BTree.
|
2
|
+
# It does a very basic task:
|
3
|
+
#
|
4
|
+
# Given a master CSV file and a key column, it will compare the file
|
5
|
+
# to another CSV, and report back what is missing in the second CSV
|
6
|
+
# that was in the first, as well as what is changed in the second CSV
|
7
|
+
# based on the first.
|
8
|
+
#
|
9
|
+
# It is not bidirectional, and is mostly meant to compare snapshots
|
10
|
+
# of CSV dumps to see what has been removed or altered (we don't care
|
11
|
+
# about new records )
|
12
|
+
#
|
13
|
+
# It's a camping app, but the core of it is a controller/formatter combo.
|
14
|
+
# (Marked by %%%%%%%%%%% below)
|
15
|
+
#
|
16
|
+
# You'll need the camping omnibus and the F() ruport plugin to run this app.
|
17
|
+
#
|
18
|
+
# gem install camping-omnibus --source http://code.whytheluckystiff.net -y
|
19
|
+
# gem install f --source http://gems.rubyreports.org
|
20
|
+
#
|
21
|
+
# Once you have them, just run camping commaleon.rb and browse to
|
22
|
+
# http://localhost:3301
|
23
|
+
#
|
24
|
+
# Use ticket_count.csv as your master file and ticket_count2.csv as your
|
25
|
+
# comparison file. Use title as your key.
|
26
|
+
#
|
27
|
+
# Try out the different outputs, and tweak the app if you'd like to play
|
28
|
+
# with it.
|
29
|
+
#
|
30
|
+
# If your company has a need for tiny hackish camping/ruport amalgams,
|
31
|
+
# you can always ask Gregory if he's looking for work:
|
32
|
+
# <gregory.t.brown at gmail.com>
|
33
|
+
#
|
34
|
+
require "rubygems"
|
35
|
+
require "camping"
|
36
|
+
require "camping/session"
|
37
|
+
require "ruport"
|
38
|
+
require "ruport/extensions"
|
39
|
+
|
40
|
+
Camping.goes :Commaleon
|
41
|
+
|
42
|
+
module Commaleon
|
43
|
+
include Camping::Session
|
44
|
+
end
|
45
|
+
|
46
|
+
def Commaleon.create
|
47
|
+
Camping::Models::Session.create_schema
|
48
|
+
end
|
49
|
+
|
50
|
+
module Commaleon::Helpers
|
51
|
+
|
52
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
53
|
+
# This is the bulk of the Ruport code in this app
|
54
|
+
# (CSVDiffController and CSVDiffFormatter)
|
55
|
+
# The rest is just camping. The interesting thing here is that
|
56
|
+
# you could easily define these in another file and just require
|
57
|
+
# them here, and use them standalone outside of your web app.
|
58
|
+
# %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
59
|
+
|
60
|
+
class CSVDiffController < Ruport::Controller
|
61
|
+
stage :diff_report
|
62
|
+
option :key, :mcsv, :ccsv
|
63
|
+
|
64
|
+
# This setup() idiom has become the default way of doing some
|
65
|
+
# manipulations on the data and options before handing off the
|
66
|
+
# rendering task to the formatters.
|
67
|
+
#
|
68
|
+
# We're using grouping mainly for the controller support,
|
69
|
+
# and rather than reducing a table, we're building up the
|
70
|
+
# group objects via the helper methods missing_from_compare
|
71
|
+
# and different_from_compare
|
72
|
+
def setup
|
73
|
+
@master_table = Table(:string => mcsv)
|
74
|
+
@compare_table = Table(:string => ccsv)
|
75
|
+
options.diff_report = Grouping(:by => "issue")
|
76
|
+
options.diff_report << missing_from_compare
|
77
|
+
options.diff_report << different_from_compare
|
78
|
+
end
|
79
|
+
|
80
|
+
# pulls the rows that are present in the master csv but not
|
81
|
+
# in the comparison csv and returns a group.
|
82
|
+
def missing_from_compare
|
83
|
+
missing_data = @master_table.rows_with(key) do |k|
|
84
|
+
missing_keys.include?(k)
|
85
|
+
end
|
86
|
+
|
87
|
+
Group("missing from main csv",
|
88
|
+
:data => missing_data,
|
89
|
+
:column_names => @master_table.column_names )
|
90
|
+
end
|
91
|
+
|
92
|
+
# pulls the rows that are present in the master csv but match
|
93
|
+
# a row with the same key in the compare csv that do not have
|
94
|
+
# identical attributes. Returns a group
|
95
|
+
def different_from_compare
|
96
|
+
shared = master_keys & compare_keys
|
97
|
+
m = @master_table.rows_with(key) do |k|
|
98
|
+
shared.include?(k)
|
99
|
+
end
|
100
|
+
|
101
|
+
diff = m.reject do |r|
|
102
|
+
@compare_table.any? { |s| s == r }
|
103
|
+
end
|
104
|
+
|
105
|
+
diff.each do |r|
|
106
|
+
comp = @compare_table.rows_with(key => r[key])[0]
|
107
|
+
r.to_hash.each do |k,v|
|
108
|
+
v << " ## " << comp[k] unless v == comp[k]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
Group("different from main csv",
|
113
|
+
:data => diff, :column_names => @master_table.column_names)
|
114
|
+
end
|
115
|
+
|
116
|
+
def missing_keys
|
117
|
+
master_keys - compare_keys
|
118
|
+
end
|
119
|
+
|
120
|
+
def master_keys
|
121
|
+
@master_table.column(key)
|
122
|
+
end
|
123
|
+
|
124
|
+
def compare_keys
|
125
|
+
@compare_table.column(key)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# This is using the F plugin which saves us a line of code and just
|
130
|
+
# looks neat, IMO
|
131
|
+
#
|
132
|
+
# For more details:
|
133
|
+
#
|
134
|
+
# http://stonecode.svnrepository.com/ruport/trac.cgi/wiki/F
|
135
|
+
#
|
136
|
+
class CSVDiffFormatter < F([:html,:text,:csv,:pdf], :for => CSVDiffController)
|
137
|
+
def build_diff_report
|
138
|
+
# this is using the selective blocks for formatters that implement
|
139
|
+
# more than one format. The block below will only be called when this
|
140
|
+
# formatter is rendering HTML
|
141
|
+
html { gussy_up_html }
|
142
|
+
|
143
|
+
render_grouping( options.diff_report,
|
144
|
+
:style => options.style || :inline )
|
145
|
+
end
|
146
|
+
|
147
|
+
# adds headers to group name to make the output a little prettier
|
148
|
+
def gussy_up_html
|
149
|
+
options.diff_report.each do |n,g|
|
150
|
+
g.send(:name=, "<h4>#{n}</h4>")
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
module Commaleon::Controllers
|
158
|
+
|
159
|
+
class Index < R "/"
|
160
|
+
def get
|
161
|
+
redirect R(CSVDifference)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
class CSVDifference < R '/csv_diff'
|
166
|
+
def get
|
167
|
+
render :get_diff_files
|
168
|
+
end
|
169
|
+
|
170
|
+
def post
|
171
|
+
@state.mfile = @input.mfile.tempfile.read
|
172
|
+
@state.cfile = @input.cfile.tempfile.read
|
173
|
+
redirect R(GenerateDiffReport)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
class GenerateDiffReport < R '/csv_diff/report'
|
178
|
+
def get
|
179
|
+
@id_fields = Table(:string=>@state.mfile).column_names &
|
180
|
+
Table(:string=>@state.cfile).column_names
|
181
|
+
render :csv_get_id
|
182
|
+
end
|
183
|
+
|
184
|
+
def post
|
185
|
+
@state.key = @input.csv_id
|
186
|
+
@table = CSVDiffController.render_html(:key => @state.key,
|
187
|
+
:mcsv => @state.mfile,
|
188
|
+
:ccsv => @state.cfile )
|
189
|
+
render :html_diff
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
class CSVDiffReportFormatted < R '/csv_diff/report.(.*)'
|
194
|
+
def set_headers(format)
|
195
|
+
types = { "csv" => "application/vnd.ms-excel",
|
196
|
+
"pdf" => "application/pdf",
|
197
|
+
"txt" => "text/plain" }
|
198
|
+
@headers["Content-Type"] = types[format]
|
199
|
+
@headers["Content-Disposition"] = "attachment; filename=diff.#{format}"
|
200
|
+
end
|
201
|
+
|
202
|
+
def get(format)
|
203
|
+
options = { :key => @state.key,
|
204
|
+
:mcsv => @state.mfile,
|
205
|
+
:ccsv => @state.cfile }
|
206
|
+
|
207
|
+
set_headers(format)
|
208
|
+
case(format)
|
209
|
+
when "csv"
|
210
|
+
text CSVDiffController.render_csv(options)
|
211
|
+
when "pdf"
|
212
|
+
text CSVDiffController.render_pdf(options.merge(:style => :justified))
|
213
|
+
when "txt"
|
214
|
+
text CSVDiffController.render_text(options)
|
215
|
+
else
|
216
|
+
text "no format!"
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
module Commaleon::Views
|
224
|
+
def get_diff_files
|
225
|
+
form :action => "?upload_id=#{Time.now.to_f}", :method => 'post',
|
226
|
+
:enctype => 'multipart/form-data' do
|
227
|
+
p do
|
228
|
+
label "Master File: ", :for => "mfile"
|
229
|
+
input({:name => "mfile", :type => 'file'})
|
230
|
+
end
|
231
|
+
p do
|
232
|
+
label "Comparison File: ", :for => "cfile"
|
233
|
+
input({:name => "cfile", :type => 'file'})
|
234
|
+
end
|
235
|
+
p do
|
236
|
+
input.newfile! :type => "submit", :value => "Upload"
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def csv_get_id
|
242
|
+
form :method => "post" do
|
243
|
+
label "ID column: ", :for => "csv_id"
|
244
|
+
select(:name => "csv_id") do
|
245
|
+
@id_fields.each { |f| option(f) }
|
246
|
+
end
|
247
|
+
input :type => "submit", :value => "Set ID"
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def html_diff
|
252
|
+
text @table
|
253
|
+
hr
|
254
|
+
ul do
|
255
|
+
li { a "New Diff", :href => R(CSVDifference) }
|
256
|
+
li { a "New Key for Diff", :href => R(GenerateDiffReport) }
|
257
|
+
li { a "CSV Download", :href => R(CSVDiffReportFormatted,"csv") }
|
258
|
+
li { a "Text Download", :href => R(CSVDiffReportFormatted,"txt") }
|
259
|
+
li { a "PDF Download", :href => R(CSVDiffReportFormatted,"pdf") }
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|