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.
Files changed (76) hide show
  1. data/AUTHORS +48 -0
  2. data/LICENSE +59 -0
  3. data/README +114 -0
  4. data/Rakefile +93 -0
  5. data/examples/RWEmerson.jpg +0 -0
  6. data/examples/anon.rb +43 -0
  7. data/examples/btree/commaleon/commaleon.rb +263 -0
  8. data/examples/btree/commaleon/sample_data/ticket_count.csv +124 -0
  9. data/examples/btree/commaleon/sample_data/ticket_count2.csv +119 -0
  10. data/examples/centered_pdf_text_box.rb +83 -0
  11. data/examples/data/tattle.dump +82 -0
  12. data/examples/example.csv +3 -0
  13. data/examples/line_plotter.rb +61 -0
  14. data/examples/pdf_report_with_common_base.rb +72 -0
  15. data/examples/png_embed.rb +54 -0
  16. data/examples/roadmap.png +0 -0
  17. data/examples/row_renderer.rb +39 -0
  18. data/examples/simple_pdf_lines.rb +25 -0
  19. data/examples/simple_templating_example.rb +34 -0
  20. data/examples/tattle_ruby_version.rb +39 -0
  21. data/examples/tattle_rubygems_version.rb +37 -0
  22. data/examples/trac_ticket_status.rb +59 -0
  23. data/lib/ruport.rb +127 -0
  24. data/lib/ruport/controller.rb +616 -0
  25. data/lib/ruport/controller/grouping.rb +71 -0
  26. data/lib/ruport/controller/table.rb +54 -0
  27. data/lib/ruport/data.rb +4 -0
  28. data/lib/ruport/data/feeder.rb +111 -0
  29. data/lib/ruport/data/grouping.rb +399 -0
  30. data/lib/ruport/data/record.rb +297 -0
  31. data/lib/ruport/data/table.rb +950 -0
  32. data/lib/ruport/extensions.rb +4 -0
  33. data/lib/ruport/formatter.rb +254 -0
  34. data/lib/ruport/formatter/csv.rb +149 -0
  35. data/lib/ruport/formatter/html.rb +161 -0
  36. data/lib/ruport/formatter/pdf.rb +591 -0
  37. data/lib/ruport/formatter/template.rb +187 -0
  38. data/lib/ruport/formatter/text.rb +231 -0
  39. data/lib/uport.rb +1 -0
  40. data/test/controller_test.rb +743 -0
  41. data/test/csv_formatter_test.rb +164 -0
  42. data/test/data_feeder_test.rb +88 -0
  43. data/test/grouping_test.rb +410 -0
  44. data/test/helpers.rb +11 -0
  45. data/test/html_formatter_test.rb +201 -0
  46. data/test/pdf_formatter_test.rb +354 -0
  47. data/test/record_test.rb +332 -0
  48. data/test/samples/addressbook.csv +6 -0
  49. data/test/samples/data.csv +3 -0
  50. data/test/samples/data.tsv +3 -0
  51. data/test/samples/dates.csv +1409 -0
  52. data/test/samples/erb_test.sql +1 -0
  53. data/test/samples/query_test.sql +1 -0
  54. data/test/samples/ruport_test.sql +8 -0
  55. data/test/samples/test.sql +2 -0
  56. data/test/samples/test.yaml +3 -0
  57. data/test/samples/ticket_count.csv +124 -0
  58. data/test/table_pivot_test.rb +134 -0
  59. data/test/table_test.rb +838 -0
  60. data/test/template_test.rb +48 -0
  61. data/test/text_formatter_test.rb +258 -0
  62. data/util/bench/data/record/bench_as_vs_to.rb +18 -0
  63. data/util/bench/data/record/bench_constructor.rb +46 -0
  64. data/util/bench/data/record/bench_indexing.rb +65 -0
  65. data/util/bench/data/record/bench_reorder.rb +35 -0
  66. data/util/bench/data/record/bench_to_a.rb +19 -0
  67. data/util/bench/data/table/bench_column_manip.rb +103 -0
  68. data/util/bench/data/table/bench_dup.rb +24 -0
  69. data/util/bench/data/table/bench_init.rb +67 -0
  70. data/util/bench/data/table/bench_manip.rb +125 -0
  71. data/util/bench/formatter/bench_csv.rb +14 -0
  72. data/util/bench/formatter/bench_html.rb +14 -0
  73. data/util/bench/formatter/bench_pdf.rb +14 -0
  74. data/util/bench/formatter/bench_text.rb +14 -0
  75. data/util/bench/samples/tattle.csv +1237 -0
  76. metadata +176 -0
@@ -0,0 +1,72 @@
1
+ #################################################################
2
+ # This example shows how to build custom PDF output with Ruport #
3
+ # that shares some common elements between reports. Basically, #
4
+ # CompanyPDFBase implements some default rendering options, #
5
+ # and derived classes such as ClientPDF would implement the #
6
+ # stuff specific to a given report. #
7
+ #################################################################
8
+
9
+ require "ruport"
10
+
11
+ # only used for the titleize call in ClientController#setup
12
+ # tweak as needed if you don't want to install AS.
13
+ require "active_support"
14
+
15
+ # This looks a little more messy than usual, but it addresses the
16
+ # concern of wanting to have a standard template for reports.
17
+ #
18
+ class ClientController < Ruport::Controller
19
+ prepare :standard_report
20
+ stage :company_header, :client_header, :client_body, :client_footer
21
+ finalize :standard_report
22
+
23
+ def setup
24
+ data.rename_columns { |c| c.to_s.titleize }
25
+ end
26
+ end
27
+
28
+ # This defines the base PDF output, you'd do similar for other
29
+ # formats if needed. It implements the common hooks that will be used
30
+ # across the company's reports.
31
+ #
32
+ class CompanyPDFBase < Ruport::Formatter::PDF
33
+ def prepare_standard_report
34
+ # defaults to US Letter, but this overrides
35
+ options.paper_size = "A4"
36
+ end
37
+
38
+ def build_company_header
39
+ add_text "This would be my company header",
40
+ :justification => :center, :font_size => 14
41
+ end
42
+
43
+ def finalize_standard_report
44
+ render_pdf
45
+ end
46
+ end
47
+
48
+ # This is actual report's formatter
49
+ #
50
+ # It implements the remaining hooks the standard formatter didn't
51
+ # Notice the footer is not implemented and it doesn't complain.
52
+ #
53
+ class ClientPDF < CompanyPDFBase
54
+ renders :pdf, :for => ClientController
55
+
56
+ def build_client_header
57
+ pad(10) do
58
+ add_text "Specific Report Header with example=#{options.example}",
59
+ :justification => :center, :font_size => 12
60
+ end
61
+ end
62
+
63
+ def build_client_body
64
+ draw_table(data, :width => 300)
65
+ end
66
+ end
67
+
68
+ table = Table([:a,:b,:c]) << [1,2,3] << [4,5,6]
69
+
70
+ File.open("example.pdf","w") do |f|
71
+ f << ClientController.render_pdf(:data => table,:example => "apple")
72
+ end
@@ -0,0 +1,54 @@
1
+ require "rubygems"
2
+ require "ruport"
3
+
4
+ class RoadmapController < Ruport::Controller
5
+ stage :roadmap_image, :roadmap_text_body
6
+ finalize :roadmap
7
+ end
8
+
9
+ class HTMLRoadmap < Ruport::Formatter
10
+
11
+ renders :html, :for => RoadmapController
12
+
13
+ def layout
14
+ output << "<html><body>\n"
15
+ yield
16
+ output << "</body></html>\n"
17
+ end
18
+
19
+ def build_roadmap_image
20
+ output << "<img src='#{options.image_file}'/>"
21
+ end
22
+
23
+ def build_roadmap_text_body
24
+ output << "<h2>This is a sample HTML report w. PDF</h2>"
25
+ end
26
+
27
+ end
28
+
29
+ class PDFRoadmap < Ruport::Formatter::PDF
30
+
31
+ renders :pdf, :for => RoadmapController
32
+
33
+ def build_roadmap_image
34
+ center_image_in_box options.image_file, :x => 0, :y => 200,
35
+ :width => 624, :height => 432
36
+ move_cursor_to 80
37
+ end
38
+
39
+ def build_roadmap_text_body
40
+ draw_text "This is a sample PDF with embedded PNG", :font_size => 16,
41
+ :x1 => 150
42
+ end
43
+
44
+ def finalize_roadmap
45
+ render_pdf
46
+ end
47
+
48
+ end
49
+
50
+ formats = [:html, :pdf]
51
+ formats.each do |format|
52
+ RoadmapController.render(format, :image_file => "roadmap.png",
53
+ :file => "roadmap.#{format}")
54
+ end
Binary file
@@ -0,0 +1,39 @@
1
+ require "ruport"
2
+
3
+ class CSV2Something < Ruport::Controller
4
+ required_option :csv_file
5
+ stage :table_body
6
+
7
+ module Helpers
8
+ def table_feeder
9
+ Table(options.csv_file,:has_names => false) { |t,r| yield(r) }
10
+ end
11
+ end
12
+
13
+ end
14
+
15
+ class HTML < Ruport::Formatter::HTML
16
+
17
+ renders :html, :for => CSV2Something
18
+
19
+ def layout
20
+ output << "<html><div id='ruport'><table>\n"
21
+ yield
22
+ output << "</table></div></html>\n"
23
+ end
24
+
25
+ def build_table_body
26
+ table_feeder { |r| render_row(r) }
27
+ end
28
+ end
29
+
30
+ class Text < Ruport::Formatter::Text
31
+ renders :text, :for => CSV2Something
32
+
33
+ def build_table_body
34
+ table_feeder { |r| output << r.to_a.join("$") }
35
+ end
36
+
37
+ end
38
+
39
+ CSV2Something.render_html(:csv_file => "example.csv",:io => STDOUT)
@@ -0,0 +1,25 @@
1
+ require "ruport"
2
+
3
+ # draws pretty little lines all over the place on a PDF
4
+
5
+ class SimpleLines < Ruport::Controller
6
+ stage :horizontal_lines
7
+ end
8
+
9
+ class PDFLines < Ruport::Formatter::PDF
10
+ renders :pdf, :for => SimpleLines
11
+
12
+ def build_horizontal_lines
13
+ data.each do |points|
14
+ pad(10) { horizontal_line(*points) }
15
+ end
16
+ render_pdf
17
+ end
18
+ end
19
+
20
+ # generate 35 random lines
21
+ data = (0..34).inject([]) { |s,r|
22
+ s << [rand(100),100+rand(400)]
23
+ }
24
+
25
+ puts SimpleLines.render_pdf(:data => data)
@@ -0,0 +1,34 @@
1
+ require "ruport"
2
+
3
+ Ruport::Formatter::Template.create(:simple) do |format|
4
+ format.page = {
5
+ :size => "LETTER",
6
+ :layout => :landscape
7
+ }
8
+ format.text = {
9
+ :font_size => 16
10
+ }
11
+ format.table = {
12
+ :font_size => 16,
13
+ :show_headings => false
14
+ }
15
+ format.column = {
16
+ :alignment => :center,
17
+ }
18
+ format.heading = {
19
+ :alignment => :right
20
+ }
21
+ format.grouping = {
22
+ :style => :separated
23
+ }
24
+ end
25
+
26
+ Ruport::Formatter::Template.create(:derived, :base => :simple) do |t|
27
+ t.table_format[:show_headings] = true
28
+ end
29
+
30
+ t = Table(%w[a b c]) << [1,2,3] << [1,"hello",6] << [2,3,4]
31
+ g = Grouping(t, :by => "a")
32
+
33
+ puts g.to_pdf(:template => :simple)
34
+ #puts g.to_pdf(:template => :derived)
@@ -0,0 +1,39 @@
1
+ # A dump of the database for this example can be found in ./data/tattle.dump
2
+
3
+ require "active_record"
4
+ require "ruport"
5
+
6
+ # Update with your connection parameters
7
+ ActiveRecord::Base.establish_connection(
8
+ :adapter => 'mysql',
9
+ :host => 'localhost',
10
+ :username => 'root',
11
+ :database => 'tattle')
12
+
13
+ class Report < ActiveRecord::Base
14
+ acts_as_reportable
15
+ end
16
+
17
+ table = Report.report_table(:all,
18
+ :only => %w[host_os ruby_version user_key],
19
+ :conditions => "user_key is not null and user_key <> ''",
20
+ :group => "host_os, ruby_version, user_key")
21
+
22
+ grouping = Grouping(table, :by => "host_os")
23
+
24
+ ruby_versions = Table(%w[platform ruby_version count])
25
+
26
+ grouping.each do |name,group|
27
+ Grouping(group, :by => "ruby_version").each do |vname,group|
28
+ ruby_versions << { "platform" => name,
29
+ "ruby_version" => vname,
30
+ "count" => group.length }
31
+ end
32
+ end
33
+
34
+ sorted_table = ruby_versions.sort_rows_by { |r| -r.count }
35
+ g = Grouping(sorted_table, :by => "platform")
36
+
37
+ File.open("platforms_ruby.html", "w") do |f|
38
+ f.write g.to_html(:style => :justified)
39
+ end
@@ -0,0 +1,37 @@
1
+ # A dump of the database for this example can be found in ./data/tattle.dump
2
+
3
+ require "active_record"
4
+ require "ruport"
5
+
6
+ # Update with your connection parameters
7
+ ActiveRecord::Base.establish_connection(
8
+ :adapter => 'mysql',
9
+ :host => 'localhost',
10
+ :username => 'root',
11
+ :database => 'tattle')
12
+
13
+ class Report < ActiveRecord::Base
14
+ acts_as_reportable
15
+ end
16
+
17
+ table = Report.report_table(:all,
18
+ :only => %w[host_os rubygems_version user_key],
19
+ :conditions => "user_key is not null and user_key <> ''",
20
+ :group => "host_os, rubygems_version, user_key")
21
+
22
+ grouping = Grouping(table, :by => "host_os")
23
+
24
+ rubygems_versions = Table(%w[platform rubygems_version count])
25
+
26
+ grouping.each do |name,group|
27
+ Grouping(group, :by => "rubygems_version").each do |vname,group|
28
+ rubygems_versions << { "platform" => name,
29
+ "rubygems_version" => vname,
30
+ "count" => group.length }
31
+ end
32
+ end
33
+
34
+ sorted_table = rubygems_versions.sort_rows_by("count", :order => :descending)
35
+ sorted_table.reduce { |r| r["platform"] !~ /darwin/i }
36
+ g = Grouping(sorted_table, :by => "platform", :order => "name")
37
+ puts g.to_pdf
@@ -0,0 +1,59 @@
1
+ %w[ruport hpricot open-uri].each { |lib| require lib }
2
+
3
+ class TracSummaryReport
4
+
5
+ include Ruport::Controller::Hooks
6
+
7
+ renders_as_table
8
+
9
+ def initialize(options={})
10
+ @days = options[:days] || 7
11
+ @timeline_uri = options[:timeline_uri]
12
+ end
13
+
14
+ class TicketStatus < Ruport::Data::Record
15
+
16
+ def closed
17
+ title =~ /Ticket.+(\w+ closed)/ ? 1 : 0
18
+ end
19
+
20
+ def opened
21
+ title =~ /Ticket.+(\w+ created)|(\w+ reopened)/ ? 1 : 0
22
+ end
23
+
24
+ end
25
+
26
+ def feed_data
27
+ uri = @timeline_uri + "?wiki=on&milestone=on&ticket=on&changeset=on"+
28
+ "&max=10000&daysback=#{@days-1}&format=rss"
29
+
30
+ feed = Hpricot(open(uri))
31
+
32
+ table = Table([:title, :date], :record_class => TicketStatus) do |table|
33
+ (feed/"item").each do |r|
34
+ title = (r/"title").innerHTML
35
+ next unless title =~ /Ticket.*(created|closed|reopened)/
36
+ table << { :title => title,
37
+ :date => Date.parse((r/"pubdate").innerHTML) }
38
+ end
39
+ end
40
+
41
+ Grouping(table,:by => :date)
42
+ end
43
+
44
+ def renderable_data(format)
45
+ summary = feed_data.summary :date,
46
+ :opened => lambda { |g| g.sigma { |r| r.opened } },
47
+ :closed => lambda { |g| g.sigma { |r| r.closed } },
48
+ :order => [:date,:opened,:closed]
49
+
50
+ summary.sort_rows_by! { |r| r.date }
51
+ return summary
52
+ end
53
+ end
54
+
55
+ timeline = "http://stonecode.svnrepository.com/ruport/trac.cgi/timeline"
56
+
57
+ report = TracSummaryReport.new(:timeline_uri => timeline, :days => 30)
58
+ puts report.as(:text)
59
+
data/lib/ruport.rb ADDED
@@ -0,0 +1,127 @@
1
+ # ruport.rb : Ruby Reports top level module
2
+ #
3
+ # Author: Gregory T. Brown (gregory.t.brown at gmail dot com)
4
+ #
5
+ # Copyright (c) 2005-2007, All Rights Reserved.
6
+ #
7
+ # This is free software. You may modify and redistribute this freely under
8
+ # your choice of the GNU General Public License or the Ruby License.
9
+ #
10
+ # See LICENSE and COPYING for details
11
+ #
12
+
13
+
14
+ if RUBY_VERSION > "1.9"
15
+ require "csv"
16
+ unless defined? FCSV
17
+ class Object
18
+ FCSV = CSV
19
+ alias_method :FCSV, :CSV
20
+ end
21
+ end
22
+ end
23
+
24
+
25
+ module Ruport #:nodoc:#
26
+
27
+ VERSION = "1.7.0"
28
+
29
+ class FormatterError < RuntimeError #:nodoc:
30
+ end
31
+
32
+ # SystemExtensions lovingly ganked from HighLine 1.2.1
33
+ #
34
+ # The following modifications have been made by Gregory Brown on 2006.06.25
35
+ #
36
+ # - Outer Module is changed from HighLine to Ruport
37
+ # - terminal_width / terminal_height added
38
+ #
39
+ # The following modifications have been made by Gregory Brown on 2006.08.13
40
+ # - removed most methods, preserving only terminal geometry features
41
+ #
42
+ # All modifications are under the distributions terms of Ruport.
43
+ # Copyright 2006, Gregory Brown. All Rights Reserved
44
+ #
45
+ # Original copyright notice preserved below.
46
+ # --------------------------------------------------------------------------
47
+ #
48
+ # Created by James Edward Gray II on 2006-06-14.
49
+ # Copyright 2006 Gray Productions. All rights reserved.
50
+ #
51
+ # This is Free Software. See LICENSE and COPYING for details.
52
+
53
+ module SystemExtensions #:nodoc:
54
+ module_function
55
+
56
+ # This section builds character reading and terminal size functions
57
+ # to suit the proper platform we're running on. Be warned: Here be
58
+ # dragons!
59
+ #
60
+ begin
61
+ require "Win32API" # See if we're on Windows.
62
+
63
+ # A Windows savvy method to fetch the console columns, and rows.
64
+ def terminal_size
65
+ m_GetStdHandle = Win32API.new( 'kernel32',
66
+ 'GetStdHandle',
67
+ ['L'],
68
+ 'L' )
69
+ m_GetConsoleScreenBufferInfo = Win32API.new(
70
+ 'kernel32', 'GetConsoleScreenBufferInfo', ['L', 'P'], 'L'
71
+ )
72
+
73
+ format = 'SSSSSssssSS'
74
+ buf = ([0] * format.size).pack(format)
75
+ stdout_handle = m_GetStdHandle.call(0xFFFFFFF5)
76
+
77
+ m_GetConsoleScreenBufferInfo.call(stdout_handle, buf)
78
+ bufx, bufy, curx, cury, wattr,
79
+ left, top, right, bottom, maxx, maxy = buf.unpack(format)
80
+ return right - left + 1, bottom - top + 1
81
+ end
82
+ rescue LoadError # If we're not on Windows try...
83
+ # A Unix savvy method to fetch the console columns, and rows.
84
+ def terminal_size
85
+ size = if /solaris/ =~ RUBY_PLATFORM
86
+ output = `stty`
87
+ [output.match('columns = (\d+)')[1].to_i,
88
+ output.match('rows = (\d+)')[1].to_i]
89
+ else
90
+ `stty size`.split.map { |x| x.to_i }.reverse
91
+ end
92
+ return $? == 0 ? size : [80,24]
93
+ end
94
+
95
+ end
96
+
97
+ def terminal_width
98
+ terminal_size.first
99
+ end
100
+
101
+ end
102
+
103
+ # quiets warnings for block
104
+ def quiet #:nodoc:
105
+ warns = $VERBOSE
106
+ $VERBOSE = nil
107
+ result = yield
108
+ $VERBOSE = warns
109
+ return result
110
+ end
111
+
112
+ module_function :quiet
113
+
114
+ end
115
+
116
+ require "enumerator"
117
+ require "ruport/controller"
118
+ require "ruport/data"
119
+ require "ruport/formatter"
120
+
121
+ begin
122
+ if Object.const_defined? :ActiveRecord
123
+ require "ruport/acts_as_reportable"
124
+ end
125
+ rescue LoadError
126
+ nil
127
+ end