ruport 0.6.1 → 0.7.0
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 +6 -0
- data/Rakefile +4 -4
- data/TODO +3 -2
- data/bin/rope +2 -103
- data/examples/pdf_complex_report.rb +53 -0
- data/lib/ruport/config.rb +7 -7
- data/lib/ruport/data/collection.rb +8 -7
- data/lib/ruport/data/groupable.rb +3 -2
- data/lib/ruport/data/record.rb +13 -16
- data/lib/ruport/data/table.rb +89 -18
- data/lib/ruport/format/csv.rb +29 -0
- data/lib/ruport/format/html.rb +40 -0
- data/lib/ruport/format/latex.rb +50 -0
- data/lib/ruport/format/pdf.rb +78 -0
- data/lib/ruport/format/plugin.rb +20 -65
- data/lib/ruport/format/svg.rb +39 -0
- data/lib/ruport/format/text.rb +77 -0
- data/lib/ruport/format/xml.rb +32 -0
- data/lib/ruport/format.rb +1 -159
- data/lib/ruport/generator.rb +158 -0
- data/lib/ruport/layout/component.rb +7 -0
- data/lib/ruport/layout.rb +1 -0
- data/lib/ruport/query.rb +3 -3
- data/lib/ruport/renderer/graph.rb +48 -0
- data/lib/ruport/renderer/table.rb +132 -0
- data/lib/ruport/renderer.rb +193 -0
- data/lib/ruport/report/graph.rb +2 -2
- data/lib/ruport/report.rb +94 -96
- data/lib/ruport/system_extensions.rb +3 -6
- data/lib/ruport.rb +6 -4
- data/test/samples/dates.csv +1409 -0
- data/test/samples/foo.rtxt +3 -0
- data/test/test_collection.rb +0 -14
- data/test/test_config.rb +6 -6
- data/test/test_graph_renderer.rb +97 -0
- data/test/test_groupable.rb +1 -0
- data/test/test_query.rb +325 -324
- data/test/test_record.rb +3 -2
- data/test/test_renderer.rb +74 -0
- data/test/test_report.rb +29 -26
- data/test/test_table.rb +54 -29
- data/test/test_table_renderer.rb +93 -0
- data/test/test_text_table.rb +61 -0
- data/test/unit.log +24 -0
- metadata +41 -63
- data/CHANGELOG +0 -587
- data/examples/basic_grouping.rb +0 -19
- data/examples/fieldless_table.rb +0 -13
- data/examples/latex_table.rb +0 -17
- data/examples/line_graph.rb +0 -22
- data/examples/line_graph_report.rb +0 -23
- data/examples/line_plotter.rb +0 -46
- data/examples/long.txt +0 -24
- data/examples/new_plugin.rb +0 -24
- data/examples/report.rb +0 -35
- data/examples/sample_invoice_report.rb +0 -32
- data/examples/simple_mail.rb +0 -15
- data/examples/simple_table_interface.rb +0 -20
- data/examples/sql_erb.rb +0 -20
- data/examples/template.rb +0 -15
- data/examples/text_processors.rb +0 -13
- data/lib/ruport/format/engine/document.rb +0 -28
- data/lib/ruport/format/engine/graph.rb +0 -18
- data/lib/ruport/format/engine/invoice.rb +0 -23
- data/lib/ruport/format/engine/table.rb +0 -54
- data/lib/ruport/format/engine.rb +0 -108
- data/lib/ruport/format/plugin/csv_plugin.rb +0 -26
- data/lib/ruport/format/plugin/html_plugin.rb +0 -32
- data/lib/ruport/format/plugin/latex_plugin.rb +0 -50
- data/lib/ruport/format/plugin/pdf_plugin.rb +0 -126
- data/lib/ruport/format/plugin/svg_plugin.rb +0 -61
- data/lib/ruport/format/plugin/text_plugin.rb +0 -77
- data/lib/ruport/meta_tools.rb +0 -66
- data/lib/ruport/report/invoice.rb +0 -29
- data/test/_test_groupable.rb +0 -0
- data/test/test_format.rb +0 -39
- data/test/test_format_engine.rb +0 -264
- data/test/test_graph.rb +0 -93
- data/test/test_invoice.rb +0 -32
- data/test/test_latex.rb +0 -20
- data/test/test_meta_tools.rb +0 -14
- data/test/test_plugin.rb +0 -277
- data/test/ts_all.rb +0 -21
@@ -0,0 +1,78 @@
|
|
1
|
+
module Ruport::Format
|
2
|
+
|
3
|
+
# PDF generation plugin
|
4
|
+
#
|
5
|
+
# layout options:
|
6
|
+
# General:
|
7
|
+
# * paper_size #=> "LETTER"
|
8
|
+
# * orientation #=> :center
|
9
|
+
#
|
10
|
+
# Table:
|
11
|
+
# * table_width
|
12
|
+
# * max_table_width #=> 500
|
13
|
+
#
|
14
|
+
class PDF < Plugin
|
15
|
+
attr_writer :pdf_writer
|
16
|
+
attr_accessor :table_header_proc
|
17
|
+
attr_accessor :table_footer_proc
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
require "pdf/writer"
|
21
|
+
require "pdf/simpletable"
|
22
|
+
end
|
23
|
+
|
24
|
+
def pdf_writer
|
25
|
+
@pdf_writer ||=
|
26
|
+
::PDF::Writer.new( :paper => layout.paper_size || "LETTER" )
|
27
|
+
end
|
28
|
+
|
29
|
+
def build_table_header
|
30
|
+
table_header_proc[pdf_writer] if table_header_proc
|
31
|
+
end
|
32
|
+
|
33
|
+
def build_table_body
|
34
|
+
draw_table
|
35
|
+
end
|
36
|
+
|
37
|
+
def build_table_footer
|
38
|
+
table_footer_proc[pdf_writer] if table_footer_proc
|
39
|
+
end
|
40
|
+
|
41
|
+
def finalize_table
|
42
|
+
output << pdf_writer.render
|
43
|
+
end
|
44
|
+
|
45
|
+
def add_text(*args)
|
46
|
+
pdf_writer.text(*args)
|
47
|
+
end
|
48
|
+
|
49
|
+
def move_cursor(n)
|
50
|
+
pdf_writer.y += n
|
51
|
+
end
|
52
|
+
|
53
|
+
def move_cursor_to(n)
|
54
|
+
pdf_writer.y = n
|
55
|
+
end
|
56
|
+
|
57
|
+
def pad(y,&block)
|
58
|
+
move_cursor -y
|
59
|
+
block.call
|
60
|
+
move_cursor -y
|
61
|
+
end
|
62
|
+
|
63
|
+
def draw_table
|
64
|
+
m = "Sorry, cant build PDFs from array like things (yet)"
|
65
|
+
raise m if data.column_names.empty?
|
66
|
+
::PDF::SimpleTable.new do |table|
|
67
|
+
table.maximum_width = layout.max_table_width || 500
|
68
|
+
table.width = layout.table_width if layout.table_width
|
69
|
+
table.orientation = layout.orientation || :center
|
70
|
+
table.data = data
|
71
|
+
table.column_order = data.column_names
|
72
|
+
table.render_on(pdf_writer)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
data/lib/ruport/format/plugin.rb
CHANGED
@@ -1,76 +1,31 @@
|
|
1
|
-
class
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
class RenderingError < RuntimeError; end
|
1
|
+
# format/plugin.rb : Generalized formatting plugin base class for Ruby Reports
|
2
|
+
#
|
3
|
+
# Created by Gregory Brown. Copyright December 2006, All Rights Reserved.
|
4
|
+
#
|
5
|
+
# This is free software, please see LICENSE and COPYING for details.
|
8
6
|
|
9
7
|
module Ruport
|
10
|
-
|
11
|
-
|
12
|
-
class << self
|
8
|
+
module Format
|
9
|
+
class Plugin
|
13
10
|
|
11
|
+
attr_accessor :layout
|
14
12
|
attr_accessor :data
|
15
|
-
attr_accessor :options
|
16
|
-
|
17
|
-
include MetaTools
|
18
|
-
|
19
|
-
def helper(name,options={},&block)
|
20
|
-
if options[:engines]
|
21
|
-
options[:engines].each { |e|
|
22
|
-
helpers[e].send(:define_method, "#{name}_helper", &block)
|
23
|
-
}
|
24
|
-
elsif options[:engine]
|
25
|
-
helpers[options[:engine]].send( :define_method,
|
26
|
-
"#{name}_helper", &block)
|
27
|
-
else
|
28
|
-
singleton_class.send( :define_method, "#{name}_helper", &block )
|
29
|
-
end
|
30
|
-
end
|
31
13
|
|
32
|
-
|
33
|
-
|
14
|
+
# Stores a string used for outputting formatted data.
|
15
|
+
def output
|
16
|
+
@output ||= ""
|
34
17
|
end
|
35
18
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
def renderer(render_type,&block)
|
41
|
-
m = "render_#{render_type}".to_sym
|
42
|
-
block ||= lambda { data }
|
43
|
-
singleton_class.send(:define_method, m, &block)
|
44
|
-
end
|
45
|
-
|
46
|
-
def format_field_names(&block)
|
47
|
-
singleton_class.send( :define_method, :build_field_names, &block)
|
48
|
-
end
|
19
|
+
# Provides a generic OpenStruct for storing plugin options
|
20
|
+
def options
|
21
|
+
@options ||= OpenStruct.new
|
22
|
+
end
|
49
23
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
klass.accept_format_plugin(self)
|
57
|
-
}
|
58
|
-
rescue NoMethodError
|
59
|
-
p caller
|
60
|
-
end
|
61
|
-
|
62
|
-
def rendering_options(hash={})
|
63
|
-
@rendering_options ||= {}
|
64
|
-
@rendering_options.merge!(hash)
|
65
|
-
@rendering_options.dup
|
24
|
+
# clears output. Useful if you are building your own interface to
|
25
|
+
# plugins.
|
26
|
+
def clear_output
|
27
|
+
@output.replace("")
|
66
28
|
end
|
67
|
-
|
68
|
-
attr_accessor :rendered_field_names
|
69
|
-
attr_accessor :pre, :post
|
70
|
-
attr_accessor :header, :footer
|
71
|
-
|
72
29
|
end
|
73
|
-
end
|
30
|
+
end
|
74
31
|
end
|
75
|
-
plugins = %w[text csv pdf svg html latex]
|
76
|
-
plugins.each { |p| require "ruport/format/plugin/#{p}_plugin" }
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Ruport::Format
|
2
|
+
class SVG < Plugin
|
3
|
+
|
4
|
+
|
5
|
+
def themes
|
6
|
+
{ :mephisto => Scruffy::Themes::Mephisto.new,
|
7
|
+
:keynote => Scruffy::Themes::Keynote.new,
|
8
|
+
:ruby_blog => Scruffy::Themes::RubyBlog.new }
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
require 'scruffy'
|
13
|
+
|
14
|
+
@graph = Scruffy::Graph.new
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :graph
|
18
|
+
|
19
|
+
def prepare_graph
|
20
|
+
@graph.title ||= options.title
|
21
|
+
@graph.theme = layout.theme if layout.theme
|
22
|
+
@graph.point_markers ||= data.column_names
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def build_graph
|
27
|
+
data.each_with_index do |r,i|
|
28
|
+
add_line(r.data,r.tags[0] || "series #{i+1}")
|
29
|
+
end
|
30
|
+
|
31
|
+
output << @graph.render(:size => [layout.width, layout.height])
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_line(row,label)
|
35
|
+
@graph.add( layout.style, label, row )
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Ruport
|
2
|
+
module Format
|
3
|
+
class Text < Plugin
|
4
|
+
|
5
|
+
def prepare_table
|
6
|
+
raise "Can't output empty table" if data.empty?
|
7
|
+
calculate_max_col_widths
|
8
|
+
end
|
9
|
+
|
10
|
+
def build_table_header
|
11
|
+
return if data.column_names.empty? || !layout.show_table_headers
|
12
|
+
c = data.column_names.dup
|
13
|
+
c.each_with_index { |f,i|
|
14
|
+
c[i] = f.to_s.center(layout.max_col_width[i])
|
15
|
+
}
|
16
|
+
output << fit_to_width("#{hr}| #{c.to_a.join(' | ')} |\n")
|
17
|
+
end
|
18
|
+
|
19
|
+
def build_table_body
|
20
|
+
s = hr
|
21
|
+
|
22
|
+
data.each { |r|
|
23
|
+
line = Array.new
|
24
|
+
r.each_with_index { |f,i|
|
25
|
+
if layout.alignment.eql? :center
|
26
|
+
line << f.to_s.center(layout.max_col_width[i])
|
27
|
+
else
|
28
|
+
align = f.is_a?(Numeric) ? :rjust : :ljust
|
29
|
+
line << f.to_s.send(align, layout.max_col_width[i])
|
30
|
+
end
|
31
|
+
}
|
32
|
+
s += "| #{line.join(' | ')} |\n"
|
33
|
+
}
|
34
|
+
s += hr
|
35
|
+
|
36
|
+
output << fit_to_width(s)
|
37
|
+
end
|
38
|
+
|
39
|
+
def hr
|
40
|
+
len = layout.max_col_width.inject(data[0].to_a.length * 3) {|s,e|s+e}+1
|
41
|
+
"+" + "-"*(len-2) + "+\n"
|
42
|
+
end
|
43
|
+
|
44
|
+
def width
|
45
|
+
require "ruport/system_extensions"
|
46
|
+
layout.table_width || SystemExtensions.terminal_width
|
47
|
+
end
|
48
|
+
|
49
|
+
def fit_to_width(s)
|
50
|
+
s.split("\n").each { |r|
|
51
|
+
r.gsub!(/\A.{#{width+1},}/) { |m| m[0,width-2] + ">>" }
|
52
|
+
}.join("\n") + "\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
def calculate_max_col_widths
|
56
|
+
# allow override
|
57
|
+
return if layout.max_col_width
|
58
|
+
|
59
|
+
layout.max_col_width=Array.new
|
60
|
+
unless data.column_names.empty?
|
61
|
+
data.column_names.each_index do |i|
|
62
|
+
layout.max_col_width[i] = data.column_names[i].to_s.length
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
data.each { |r|
|
67
|
+
r.each_with_index { |f,i|
|
68
|
+
if !layout.max_col_width[i] || f.to_s.length > layout.max_col_width[i]
|
69
|
+
layout.max_col_width[i] = f.to_s.length
|
70
|
+
end
|
71
|
+
}
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Ruport::Format
|
2
|
+
class XML < Plugin
|
3
|
+
|
4
|
+
def prepare_graph
|
5
|
+
require_gem "builder"
|
6
|
+
@builder = Builder::XmlMarkup.new(:indent => 2)
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_graph
|
10
|
+
output << @builder.chart do |b|
|
11
|
+
b.chart_type(layout.style.to_s)
|
12
|
+
|
13
|
+
b.chart_data do |cd|
|
14
|
+
|
15
|
+
cd.row { |first|
|
16
|
+
first.null
|
17
|
+
data.column_names.each { |c| first.string(c) }
|
18
|
+
}
|
19
|
+
|
20
|
+
data.each_with_index { |r,i|
|
21
|
+
label = r.tags[0] || "Region #{i}"
|
22
|
+
cd.row { |data_row|
|
23
|
+
data_row.string(label)
|
24
|
+
r.each { |e| data_row.number(e) }
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
data/lib/ruport/format.rb
CHANGED
@@ -1,159 +1 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
# Author: Gregory T. Brown (gregory.t.brown at gmail dot com)
|
4
|
-
#
|
5
|
-
# Copyright (c) 2006, 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
|
-
module Ruport
|
12
|
-
|
13
|
-
# Ruport makes heavy use of ruby's advanced meta programming features in
|
14
|
-
# this Class.
|
15
|
-
#
|
16
|
-
# All subclasses of Ruport::Format::Engine and Ruport::Format::Plugin
|
17
|
-
# (both Ruports' internal ones and any custom ones outside the Ruport
|
18
|
-
# library) should dynamically register themselves with this class.
|
19
|
-
#
|
20
|
-
# All report generation is then done via Format, not with the engines
|
21
|
-
# and plugins directly.
|
22
|
-
#
|
23
|
-
# For each engine that is registered with Format, 2 methods are created:
|
24
|
-
# - <enginename>; and
|
25
|
-
# - <enginename>_object
|
26
|
-
#
|
27
|
-
# Either one of these methods can be used to create your report, depending
|
28
|
-
# on your requirments.
|
29
|
-
#
|
30
|
-
# = Format.enginename
|
31
|
-
#
|
32
|
-
# A brief example of creating a simple report with the table engine
|
33
|
-
#
|
34
|
-
# data = [[1,2],[5,3],[3,10]].to_table(%w[a b])
|
35
|
-
# File.open("myreport.pdf","w") { |f| f.puts Ruport::Format.table(:plugin => :pdf, :data => data)}
|
36
|
-
#
|
37
|
-
# = Format.enginename_object
|
38
|
-
#
|
39
|
-
# A slightly different way to create a simple report with the table engine.
|
40
|
-
# This technique gives you a chance to modify some of the engines settings
|
41
|
-
# before calling render manually.
|
42
|
-
#
|
43
|
-
# data = [[1,2],[5,3],[3,10]].to_table(%w[a b])
|
44
|
-
# myreport = Ruport::Format.table_object :plugin => :pdf, :data => data
|
45
|
-
# File.open("myreport.pdf","w") { |f| f.puts myreport.render }
|
46
|
-
#
|
47
|
-
class Format
|
48
|
-
|
49
|
-
# Builds a simple interface to a formatting engine.
|
50
|
-
# Two of these interfaces are built into Ruport:
|
51
|
-
# Format.document and Format.table
|
52
|
-
#
|
53
|
-
# These interfaces pass a hash of keywords to the associative engine.
|
54
|
-
# Here is a simple example:
|
55
|
-
#
|
56
|
-
# Format.build_interface_for Format::Engine::Table, "table"
|
57
|
-
#
|
58
|
-
# This will allow the following code to work:
|
59
|
-
#
|
60
|
-
# Format.table :data => [[1,2],[3,4]], :plugin => :csv
|
61
|
-
#
|
62
|
-
# So, if you want to create a standard interface to a
|
63
|
-
# custom built engine, you could simply do something like:
|
64
|
-
#
|
65
|
-
# Format.build_interface_for MyCustomEngine, "my_name"
|
66
|
-
#
|
67
|
-
# which would be accessible via
|
68
|
-
#
|
69
|
-
# Format.my_name ...
|
70
|
-
def self.build_interface_for(engine,name)
|
71
|
-
singleton_class.send(:define_method, name,
|
72
|
-
lambda { |options| simple_interface(engine, options) })
|
73
|
-
singleton_class.send(:define_method, "#{name}_object",
|
74
|
-
lambda { |options|
|
75
|
-
options[:auto_render] = false; simple_interface(engine,options) })
|
76
|
-
end
|
77
|
-
|
78
|
-
%w[engine plugin].each { |lib|
|
79
|
-
require "ruport/format/#{lib}"
|
80
|
-
}
|
81
|
-
|
82
|
-
@@filters ||= Hash.new
|
83
|
-
|
84
|
-
# To hook up a Format object to your current class, you need to pass it a
|
85
|
-
# binding. This way, when filters are being processed, they will be
|
86
|
-
# evaluated in the context of the object they are being called from, rather
|
87
|
-
# than within an instance of Format.
|
88
|
-
#
|
89
|
-
def initialize(class_binding=binding)
|
90
|
-
@binding = class_binding
|
91
|
-
end
|
92
|
-
|
93
|
-
# This is the text to be processed by the filters
|
94
|
-
attr_accessor :content
|
95
|
-
|
96
|
-
# This is the binding to the object Format is tied to
|
97
|
-
attr_accessor :binding
|
98
|
-
|
99
|
-
# Processes the ERB text in <tt>@content</tt> in the context
|
100
|
-
# of the object that Format is bound to.
|
101
|
-
def filter_erb
|
102
|
-
self.class.document :data => @content,
|
103
|
-
:class_binding => @binding,
|
104
|
-
:plugin => :text
|
105
|
-
end
|
106
|
-
|
107
|
-
# Processes the RedCloth text in <tt>@content</tt> in the context
|
108
|
-
# of the object that Format is bound to.
|
109
|
-
def filter_red_cloth
|
110
|
-
self.class.document :data => @content, :plugin => :html
|
111
|
-
end
|
112
|
-
|
113
|
-
# Takes a name and a block and creates a filter method
|
114
|
-
# This will define methods in the form of
|
115
|
-
# <tt>Format#filter_my_filter_name</tt>.
|
116
|
-
#
|
117
|
-
# This code will run as an instance method on Format.
|
118
|
-
# You can access format and binding through their accessors,
|
119
|
-
# as well as any other filters.
|
120
|
-
#
|
121
|
-
# Example:
|
122
|
-
#
|
123
|
-
# Format.register_filter :no_ohz do
|
124
|
-
# content.gsub(/O/i,"")
|
125
|
-
# end
|
126
|
-
def self.register_filter(name,&filter_proc)
|
127
|
-
@@filters["filter_#{name}".to_sym] = filter_proc
|
128
|
-
end
|
129
|
-
|
130
|
-
def method_missing(m,*args)
|
131
|
-
@@filters[m] ? @@filters[m][@content] : super
|
132
|
-
end
|
133
|
-
|
134
|
-
|
135
|
-
private
|
136
|
-
|
137
|
-
def self.simple_interface(engine, options={})
|
138
|
-
my_engine = engine.dup
|
139
|
-
|
140
|
-
my_engine.send(:plugin=,options[:plugin])
|
141
|
-
options = my_engine.active_plugin.rendering_options.merge(options)
|
142
|
-
|
143
|
-
options[:auto_render] = true unless options.has_key? :auto_render
|
144
|
-
|
145
|
-
|
146
|
-
options[:data] = options[:data].dup if options[:data]
|
147
|
-
|
148
|
-
options.each do |k,v|
|
149
|
-
my_engine.send("#{k}=",v) if my_engine.respond_to? k
|
150
|
-
end
|
151
|
-
|
152
|
-
options[:auto_render] ? my_engine.render : my_engine.dup
|
153
|
-
end
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
1
|
+
require "ruport/format/plugin"
|
@@ -0,0 +1,158 @@
|
|
1
|
+
module Ruport
|
2
|
+
class Generator
|
3
|
+
extend FileUtils
|
4
|
+
|
5
|
+
begin
|
6
|
+
require "rubygems"
|
7
|
+
rescue LoadError
|
8
|
+
nil
|
9
|
+
end
|
10
|
+
require "ruport"
|
11
|
+
|
12
|
+
def self.build(proj)
|
13
|
+
@project = proj
|
14
|
+
build_directory_structure
|
15
|
+
build_init
|
16
|
+
build_config
|
17
|
+
build_rakefile
|
18
|
+
build_utils
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.build_init
|
22
|
+
File.open("#{project}/app/init.rb","w") { |f| f << INIT }
|
23
|
+
end
|
24
|
+
|
25
|
+
# Generates a trivial rakefile for use with Ruport.
|
26
|
+
def self.build_rakefile
|
27
|
+
File.open("#{project}/Rakefile","w") { |f| f << RAKEFILE }
|
28
|
+
end
|
29
|
+
|
30
|
+
# Generates the build.rb, sql_exec.rb, and cabinet.rb utilities
|
31
|
+
def self.build_utils
|
32
|
+
File.open("#{project}/util/build.rb","w") { |f| f << BUILD }
|
33
|
+
File.open("#{project}/util/sql_exec.rb","w") { |f| f << SQL_EXEC }
|
34
|
+
end
|
35
|
+
|
36
|
+
# sets up the basic directory layout for a Ruport application
|
37
|
+
def self.build_directory_structure
|
38
|
+
mkdir project
|
39
|
+
%w[ test config output data app app/reports
|
40
|
+
templates sql log util].each do |d|
|
41
|
+
mkdir "#{project}/#{d}"
|
42
|
+
end
|
43
|
+
|
44
|
+
touch("#{project}/app/reports.rb")
|
45
|
+
touch("#{project}/app/helpers.rb")
|
46
|
+
end
|
47
|
+
|
48
|
+
# Builds a file called config/ruport_config.rb which stores a Ruport::Config
|
49
|
+
# skeleton
|
50
|
+
def self.build_config
|
51
|
+
File.open("#{project}/config/ruport_config.rb","w") { |f| f << CONFIG }
|
52
|
+
end
|
53
|
+
|
54
|
+
# returns the project's name
|
55
|
+
def self.project; @project; end
|
56
|
+
|
57
|
+
RAKEFILE = <<END_RAKEFILE
|
58
|
+
begin; require "rubygems"; rescue LoadError; end
|
59
|
+
require "rake/testtask"
|
60
|
+
|
61
|
+
task :default => [:test]
|
62
|
+
|
63
|
+
Rake::TestTask.new do |test|
|
64
|
+
test.libs << "test"
|
65
|
+
test.pattern = 'test/**/test_*.rb'
|
66
|
+
test.verbose = true
|
67
|
+
end
|
68
|
+
END_RAKEFILE
|
69
|
+
|
70
|
+
CONFIG = <<END_CONFIG
|
71
|
+
require "ruport"
|
72
|
+
|
73
|
+
# For details, see Ruport::Config documentation
|
74
|
+
Ruport.configure { |c|
|
75
|
+
c.source :default, :user => "root",
|
76
|
+
:dsn => "dbi:mysql:mydb"
|
77
|
+
c.log_file "log/ruport.log"
|
78
|
+
}
|
79
|
+
END_CONFIG
|
80
|
+
|
81
|
+
BUILD = <<'END_BUILD'
|
82
|
+
require 'fileutils'
|
83
|
+
include FileUtils
|
84
|
+
|
85
|
+
def format_class_name(string)
|
86
|
+
string.downcase.split("_").map { |s| s.capitalize }.join
|
87
|
+
end
|
88
|
+
|
89
|
+
unless ARGV.length > 1
|
90
|
+
puts "usage build.rb [command] [options]"
|
91
|
+
exit
|
92
|
+
end
|
93
|
+
|
94
|
+
class_name = format_class_name(ARGV[1])
|
95
|
+
|
96
|
+
exit if File.exist? "app/reports/#{ARGV[1]}.rb"
|
97
|
+
if ARGV[0].eql? "report"
|
98
|
+
File.open("app/reports.rb", "a") { |f|
|
99
|
+
f.puts("require \"app/reports/#{ARGV[1]}\"")
|
100
|
+
}
|
101
|
+
REP = <<EOR
|
102
|
+
require "app/init"
|
103
|
+
class #{class_name} < Ruport::Report
|
104
|
+
|
105
|
+
def prepare
|
106
|
+
|
107
|
+
end
|
108
|
+
|
109
|
+
def generate
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
def cleanup
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
|
119
|
+
if __FILE__ == $0
|
120
|
+
#{class_name}.run { |res| puts res.results }
|
121
|
+
end
|
122
|
+
EOR
|
123
|
+
|
124
|
+
TEST = <<EOR
|
125
|
+
require "test/unit"
|
126
|
+
require "app/reports/#{ARGV[1]}"
|
127
|
+
|
128
|
+
class Test#{class_name} < Test::Unit::TestCase
|
129
|
+
def test_flunk
|
130
|
+
flunk "Write your real tests here or in any test/test_* file"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
EOR
|
134
|
+
File.open("app/reports/#{ARGV[1]}.rb", "w") { |f| f << REP }
|
135
|
+
File.open("test/test_#{ARGV[1]}.rb","w") { |f| f << TEST }
|
136
|
+
end
|
137
|
+
END_BUILD
|
138
|
+
|
139
|
+
SQL_EXEC = <<'END_SQL'
|
140
|
+
require "app/init"
|
141
|
+
|
142
|
+
puts Ruport::Query.new(ARGF.read).result
|
143
|
+
END_SQL
|
144
|
+
|
145
|
+
INIT = <<END_INIT
|
146
|
+
begin
|
147
|
+
require "rubygems"
|
148
|
+
require_gem "ruport","=#{Ruport::VERSION}"
|
149
|
+
rescue LoadError
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
require "ruport"
|
153
|
+
require "app/helpers"
|
154
|
+
require "config/ruport_config"
|
155
|
+
END_INIT
|
156
|
+
|
157
|
+
end
|
158
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require "ruport/layout/component" #:nodoc:
|
data/lib/ruport/query.rb
CHANGED
@@ -78,8 +78,8 @@ module Ruport
|
|
78
78
|
options = { :source => :default, :origin => :string }.merge(options)
|
79
79
|
options[:binding] ||= binding
|
80
80
|
options[:origin] = :file if sql =~ /.sql$/
|
81
|
-
|
82
|
-
|
81
|
+
|
82
|
+
q = ERB.new(get_query(options[:origin],sql)).result(options[:binding])
|
83
83
|
@statements = SqlSplit.new(q)
|
84
84
|
@sql = @statements.join
|
85
85
|
|
@@ -181,7 +181,7 @@ module Ruport
|
|
181
181
|
|
182
182
|
# Returns a csv dump of the query.
|
183
183
|
def to_csv
|
184
|
-
|
184
|
+
fetch.to_csv
|
185
185
|
end
|
186
186
|
|
187
187
|
# Returns a Generator object of the result set.
|