ruport 0.4.23 → 0.4.99
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +16 -8
- data/CHANGELOG +30 -1
- data/README +144 -114
- data/Rakefile +12 -4
- data/TODO +4 -7
- data/bin/rope +21 -28
- data/examples/line_graph.rb +36 -0
- data/examples/sample_invoice_report.rb +1 -1
- data/examples/simple_graph.rb +8 -0
- data/lib/SVG/Graph/Bar.rb +137 -0
- data/lib/SVG/Graph/BarBase.rb +140 -0
- data/lib/SVG/Graph/BarHorizontal.rb +136 -0
- data/lib/SVG/Graph/Graph.rb +977 -0
- data/lib/SVG/Graph/Line.rb +444 -0
- data/lib/SVG/Graph/Pie.rb +394 -0
- data/lib/SVG/Graph/Plot.rb +494 -0
- data/lib/SVG/Graph/Schedule.rb +373 -0
- data/lib/SVG/Graph/TimeSeries.rb +241 -0
- data/lib/ruport.rb +2 -2
- data/lib/ruport/config.rb +47 -3
- data/lib/ruport/data/collection.rb +17 -1
- data/lib/ruport/data/record.rb +101 -8
- data/lib/ruport/data/set.rb +81 -2
- data/lib/ruport/data/set.rb.rej +147 -0
- data/lib/ruport/data/set.rb~ +73 -0
- data/lib/ruport/data/table.rb +127 -2
- data/lib/ruport/data/taggable.rb +21 -2
- data/lib/ruport/format.rb +36 -44
- data/lib/ruport/format/engine.rb +21 -1
- data/lib/ruport/format/plugin.rb +64 -1
- data/lib/ruport/mailer.rb +70 -36
- data/lib/ruport/meta_tools.rb +15 -6
- data/lib/ruport/query.rb +1 -1
- data/lib/ruport/rails/reportable.rb +23 -1
- data/lib/ruport/report.rb +11 -11
- data/lib/ruport/report/invoice.rb +16 -0
- data/lib/ruport/system_extensions.rb +3 -55
- data/test/{tc_database.rb → _test_database.rb} +0 -0
- data/test/{tc_config.rb → test_config.rb} +0 -0
- data/test/{tc_format.rb → test_format.rb} +1 -0
- data/test/{tc_format_engine.rb → test_format_engine.rb} +14 -2
- data/test/test_graph.rb +101 -0
- data/test/{tc_invoice.rb → test_invoice.rb} +7 -1
- data/test/test_mailer.rb +108 -0
- data/test/test_meta_tools.rb +14 -0
- data/test/{tc_plugin.rb → test_plugin.rb} +12 -1
- data/test/{tc_query.rb → test_query.rb} +0 -0
- data/test/{tc_record.rb → test_record.rb} +9 -0
- data/test/{tc_report.rb → test_report.rb} +2 -1
- data/test/{tc_ruport.rb → test_ruport.rb} +0 -0
- data/test/test_set.rb +118 -0
- data/test/test_set.rb.rej +16 -0
- data/test/{tc_set.rb → test_set.rb~} +17 -0
- data/test/{tc_sql_split.rb → test_sql_split.rb} +0 -0
- data/test/{tc_table.rb → test_table.rb} +15 -0
- data/test/{tc_taggable.rb → test_taggable.rb} +0 -0
- data/test/unit.log +361 -0
- metadata +52 -30
- data/examples/bar.pdf +0 -193
- data/examples/f.log +0 -5
- data/examples/foo.pdf +0 -193
- data/lib/ruport/format/document.rb +0 -78
- data/lib/ruport/format/open_node.rb +0 -38
- data/test/tc_data_row.rb +0 -132
- data/test/tc_data_set.rb +0 -386
- data/test/tc_document.rb +0 -42
- data/test/tc_element.rb +0 -18
- data/test/tc_page.rb +0 -42
- data/test/tc_section.rb +0 -45
- data/test/ts_all.rb +0 -12
- data/test/ts_format.rb +0 -7
data/lib/ruport/format.rb
CHANGED
@@ -9,49 +9,41 @@
|
|
9
9
|
#
|
10
10
|
# See LICENSE and COPYING for details
|
11
11
|
module Ruport
|
12
|
-
|
13
|
-
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
#
|
48
|
-
# Also, see Report#render for how to bind Format objects to your own classes.
|
49
|
-
#
|
50
|
-
# When combined, filters, data set output templates, and structured printable
|
51
|
-
# document facilities create a complete Formatting system.
|
52
|
-
#
|
53
|
-
# This part of Ruport is under active development. Please do feel free to
|
54
|
-
# submit feature requests or suggestions.
|
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
|
+
#
|
55
47
|
class Format
|
56
48
|
|
57
49
|
# Builds a simple interface to a formatting engine.
|
@@ -83,7 +75,7 @@ module Ruport
|
|
83
75
|
options[:auto_render] = false; simple_interface(engine,options) })
|
84
76
|
end
|
85
77
|
|
86
|
-
%w[
|
78
|
+
%w[engine plugin].each { |lib|
|
87
79
|
require "ruport/format/#{lib}"
|
88
80
|
}
|
89
81
|
|
data/lib/ruport/format/engine.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
class InvalidPluginError < RuntimeError; end
|
2
|
+
|
1
3
|
module Ruport
|
2
4
|
class Format::Engine
|
3
5
|
require "forwardable"
|
@@ -44,6 +46,11 @@ module Ruport
|
|
44
46
|
end
|
45
47
|
|
46
48
|
def plugin=(p)
|
49
|
+
if @format_plugins[p].nil?
|
50
|
+
raise(InvalidPluginError,
|
51
|
+
'The requested plugin and engine combination is invalid')
|
52
|
+
end
|
53
|
+
|
47
54
|
@plugin = p
|
48
55
|
@format_plugins[:current] = @format_plugins[p].dup
|
49
56
|
@format_plugins[:current].data = self.data.dup if self.data
|
@@ -94,6 +101,18 @@ module Ruport
|
|
94
101
|
private_class_method :new
|
95
102
|
end
|
96
103
|
|
104
|
+
class Format::Engine::Graph < Ruport::Format::Engine
|
105
|
+
|
106
|
+
renderer do
|
107
|
+
super
|
108
|
+
active_plugin.render_graph
|
109
|
+
end
|
110
|
+
|
111
|
+
alias_engine Graph, :graph_engine
|
112
|
+
Ruport::Format.build_interface_for Graph, :graph
|
113
|
+
|
114
|
+
end
|
115
|
+
|
97
116
|
class Format::Engine::Invoice < Ruport::Format::Engine
|
98
117
|
|
99
118
|
# order meta data
|
@@ -150,7 +169,8 @@ module Ruport
|
|
150
169
|
|
151
170
|
def build_field_names
|
152
171
|
if active_plugin.respond_to?(:build_field_names)
|
153
|
-
active_plugin.rendered_field_names =
|
172
|
+
active_plugin.rendered_field_names =
|
173
|
+
active_plugin.build_field_names
|
154
174
|
end
|
155
175
|
end
|
156
176
|
|
data/lib/ruport/format/plugin.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'bigdecimal'
|
2
|
+
|
1
3
|
module Ruport
|
2
4
|
class Format::Plugin
|
3
5
|
|
@@ -64,6 +66,66 @@ module Ruport
|
|
64
66
|
register_on :table_engine
|
65
67
|
end
|
66
68
|
|
69
|
+
class SVGPlugin < Format::Plugin
|
70
|
+
|
71
|
+
helper(:init_plugin) { |eng|
|
72
|
+
# check the supplied data can be used for graphing
|
73
|
+
data.each { |r|
|
74
|
+
if data.column_names.size != r.data.size
|
75
|
+
raise ArgumentError, "Column names and data do not match"
|
76
|
+
end
|
77
|
+
r.data.each { |c|
|
78
|
+
begin
|
79
|
+
c = BigDecimal.new(c) unless c.kind_of?(Float) ||
|
80
|
+
c.kind_of?(Fixnum) || c.kind_of?(BigDecimal)
|
81
|
+
rescue
|
82
|
+
raise ArgumentError, "Unable to convert #{c.to_s} into a number"
|
83
|
+
end
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
raise RuntimeError, 'You must provide an options hash before rendering a graph' if self.options.nil?
|
88
|
+
|
89
|
+
# load the appropriate SVG::Graph class based on the graph_style option
|
90
|
+
case options[:graph_style]
|
91
|
+
when :bar
|
92
|
+
require "SVG/Graph/Bar"
|
93
|
+
graphclass = SVG::Graph::Bar
|
94
|
+
when :bar_horizontal
|
95
|
+
require "SVG/Graph/BarHorizontal"
|
96
|
+
graphclass = SVG::Graph::BarHorizontal
|
97
|
+
when :line
|
98
|
+
require "SVG/Graph/Line"
|
99
|
+
graphclass = SVG::Graph::Line
|
100
|
+
when :pie
|
101
|
+
require "SVG/Graph/Pie"
|
102
|
+
graphclass = SVG::Graph::Pie
|
103
|
+
else
|
104
|
+
raise "Unsupported graph type requested"
|
105
|
+
end
|
106
|
+
|
107
|
+
# create an instance of the graphing class
|
108
|
+
options[:fields] = data.column_names
|
109
|
+
@graph = graphclass.new(options)
|
110
|
+
}
|
111
|
+
|
112
|
+
renderer :graph do
|
113
|
+
|
114
|
+
data.each_with_index { |r,i|
|
115
|
+
@graph.add_data({
|
116
|
+
:data => r.data,
|
117
|
+
:title => r.tags[0] || 'series ' + (i+1).to_s
|
118
|
+
})
|
119
|
+
}
|
120
|
+
|
121
|
+
# return the rendered graph
|
122
|
+
@graph.burn()
|
123
|
+
end
|
124
|
+
|
125
|
+
plugin_name :svg
|
126
|
+
register_on :graph_engine
|
127
|
+
end
|
128
|
+
|
67
129
|
class TextPlugin < Format::Plugin
|
68
130
|
rendering_options :erb_enabled => true, :red_cloth_enabled => false
|
69
131
|
|
@@ -195,7 +257,8 @@ module Ruport
|
|
195
257
|
pdf.stroke_style! PDF::Writer::StrokeStyle::DEFAULT
|
196
258
|
if eng.comments
|
197
259
|
pdf.y -= 20
|
198
|
-
text_box eng.comments, :position => 110, :width => 400
|
260
|
+
text_box eng.comments, :position => 110, :width => 400,
|
261
|
+
:font_size => 14
|
199
262
|
end
|
200
263
|
pdf.add_text_wrap( 50, 20, 200, "Printed at " +
|
201
264
|
Time.now.strftime("%H:%M %d/%m/%Y"), 8)
|
data/lib/ruport/mailer.rb
CHANGED
@@ -7,45 +7,79 @@
|
|
7
7
|
require "net/smtp"
|
8
8
|
require "forwardable"
|
9
9
|
module Ruport
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
10
|
+
|
11
|
+
# This class uses SMTP to provide a simple mail sending mechanism.
|
12
|
+
# It also uses MailFactory to provide attachment and HTML email support.
|
13
|
+
#
|
14
|
+
# Here is a simple example of a message which attaches a readme file:
|
15
|
+
#
|
16
|
+
# require "ruport"
|
17
|
+
#
|
18
|
+
# Ruport.configure do |conf|
|
19
|
+
# conf.mailer :default,
|
20
|
+
# :host => "mail.adelphia.net", :address => "gregory.t.brown@gmail.com"
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# mailer = Ruport::Mailer.new
|
24
|
+
#
|
25
|
+
# mailer.attach "README"
|
26
|
+
#
|
27
|
+
# mailer.deliver :to => "gregory.t.brown@gmail.com",
|
28
|
+
# :from => "gregory.t.brown@gmail.com",
|
29
|
+
# :subject => "Hey there",
|
30
|
+
# :text => "This is what you asked for"
|
31
|
+
class Mailer
|
32
|
+
extend Forwardable
|
33
|
+
|
34
|
+
|
35
|
+
# Creates a new Mailer object. Optionally, can select a mailer specified
|
36
|
+
# by Ruport::Config.
|
37
|
+
#
|
38
|
+
# a = Mailer.new # uses the :default mailer
|
39
|
+
# a = Mailer.new :foo # uses :foo mail config from Ruport::Config
|
40
|
+
#
|
41
|
+
def initialize( mailer_label=:default )
|
42
|
+
select_mailer(mailer_label);
|
43
|
+
mail_object.from = @mailer.address if mail_object.from.to_s.empty?
|
44
|
+
rescue
|
45
|
+
raise "you need to specify a mailer to use"
|
46
|
+
end
|
47
|
+
|
48
|
+
def_delegators( :@mail, :to, :to=, :from, :from=,
|
49
|
+
:subject, :subject=, :attach,
|
50
|
+
:text, :text=, :html, :html= )
|
51
|
+
|
52
|
+
# sends the message
|
53
|
+
#
|
54
|
+
# mailer.deliver :from => "gregory.t.brown@gmail.com",
|
55
|
+
# :to => "greg7224@gmail.com"
|
56
|
+
def deliver(options={})
|
57
|
+
options.each { |k,v| send("#{k}=",v) if respond_to? "#{k}=" }
|
23
58
|
|
24
|
-
|
25
|
-
options
|
26
|
-
|
27
|
-
Net::SMTP.start(@host,@port,@host,@user,@password,@auth) do |smtp|
|
28
|
-
smtp.send_message((options[:mail_object] || mail_object).to_s, options[:from], options[:to] )
|
29
|
-
end
|
59
|
+
Net::SMTP.start(@host,@port,@host,@user,@password,@auth) do |smtp|
|
60
|
+
smtp.send_message((options[:mail_object] || mail_object).to_s, options[:from], options[:to] )
|
30
61
|
end
|
62
|
+
end
|
31
63
|
|
32
|
-
|
33
|
-
@mailer = Ruport::Config.mailers[label]
|
34
|
-
@host = @mailer.host
|
35
|
-
@user = @mailer.user
|
36
|
-
@password = @mailer.password
|
37
|
-
@address = @mailer.address
|
38
|
-
@port = @mailer.port || 25
|
39
|
-
@auth = @mailer.auth_type || :plain
|
40
|
-
@mail_klass = @mailer.mail_klass
|
41
|
-
end
|
64
|
+
private
|
42
65
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
66
|
+
def select_mailer(label)
|
67
|
+
@mailer = Ruport::Config.mailers[label]
|
68
|
+
@host = @mailer.host
|
69
|
+
@user = @mailer.user
|
70
|
+
@password = @mailer.password
|
71
|
+
@address = @mailer.address
|
72
|
+
@port = @mailer.port || 25
|
73
|
+
@auth = @mailer.auth_type || :plain
|
74
|
+
@mail_klass = @mailer.mail_klass
|
75
|
+
end
|
76
|
+
|
77
|
+
def mail_object
|
78
|
+
return @mail if @mail
|
79
|
+
return @mail ||= @mail_klass.new if @mail_klass
|
80
|
+
require "mailfactory"
|
81
|
+
@mail ||= MailFactory.new
|
50
82
|
end
|
83
|
+
|
84
|
+
end
|
51
85
|
end
|
data/lib/ruport/meta_tools.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
class ActionAlreadyDefinedError < RuntimeError; end
|
1
2
|
module Ruport
|
2
3
|
# This module provides a few tools for doing some manipulations of the
|
3
4
|
# eigenclass on an object. These are used in the implementation of Ruport's
|
@@ -9,8 +10,7 @@ module Ruport
|
|
9
10
|
# Example:
|
10
11
|
#
|
11
12
|
# class A
|
12
|
-
#
|
13
|
-
#
|
13
|
+
# extend Ruport::MetaTools
|
14
14
|
# attribute :foo
|
15
15
|
# end
|
16
16
|
#
|
@@ -21,22 +21,31 @@ module Ruport
|
|
21
21
|
self.send("#{sym}=",value)
|
22
22
|
end
|
23
23
|
|
24
|
-
# same as
|
24
|
+
# same as attribute, but takes an array of attributes
|
25
25
|
#
|
26
26
|
# e.g. attributes [:foo,:bar,:baz]
|
27
27
|
def attributes(syms)
|
28
28
|
syms.each { |s| attribute s }
|
29
29
|
end
|
30
|
-
|
30
|
+
|
31
|
+
# allows you to define a method on the singleton_class
|
32
|
+
#
|
33
|
+
# Example:
|
34
|
+
#
|
35
|
+
# class A
|
36
|
+
# extend Ruport::MetaTools
|
37
|
+
# action(:bar) { |x| x + 1 }
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# A.bar(3) #=> 4
|
31
41
|
def action(name,&block)
|
42
|
+
raise ActionAlreadyDefinedError if respond_to? name
|
32
43
|
singleton_class.send(:define_method, name, &block)
|
33
44
|
end
|
34
45
|
end
|
35
46
|
end
|
36
47
|
|
37
48
|
class Module
|
38
|
-
|
39
49
|
# provides the singleton_class object
|
40
50
|
def singleton_class; (class << self; self; end); end
|
41
|
-
|
42
51
|
end
|
data/lib/ruport/query.rb
CHANGED
@@ -1,13 +1,33 @@
|
|
1
1
|
module Ruport
|
2
|
-
|
2
|
+
|
3
|
+
# This module is designed to be mixed in with an ActiveRecord model
|
4
|
+
# to add easy conversion to ruport data structures.
|
5
|
+
#
|
6
|
+
# In the ActiveRecord Model you wish to integrate with report, add the
|
7
|
+
# following line just below the class definition:
|
8
|
+
#
|
9
|
+
# acts_as_reportable
|
10
|
+
#
|
11
|
+
# This will automatically make all the methods in this module available
|
12
|
+
# in the model.
|
3
13
|
module Reportable
|
4
14
|
|
15
|
+
# Converts the models' data into a Ruport::Data::Table, then renders
|
16
|
+
# it using the requested plugin. If :find is specified as an option
|
17
|
+
# it is passed directly on to ActiveRecords find method
|
18
|
+
#
|
19
|
+
# User.formatted_table(:pdf, :find => "age > 18")
|
5
20
|
def formatted_table(type,options={})
|
6
21
|
to_table(:find => options[:find],:columns => options[:columns]).as(type){ |e|
|
7
22
|
yield(e) if block_given?
|
8
23
|
}
|
9
24
|
end
|
10
25
|
|
26
|
+
# Converts the models' data into a Ruport::Data::Table object
|
27
|
+
# If :find is supplied as an option it is passed directly on to
|
28
|
+
# the models find method.
|
29
|
+
#
|
30
|
+
# data = User.to_table(:find => "age > 18")
|
11
31
|
def to_table(options={})
|
12
32
|
options[:columns] ||= column_names
|
13
33
|
Ruport::Data::Table.new(
|
@@ -27,6 +47,8 @@ module Ruport
|
|
27
47
|
end
|
28
48
|
end
|
29
49
|
|
50
|
+
# Extend rails ActiveRecord::Base class to add the option of mixing in
|
51
|
+
# the Ruport::Reportable Module in the standard rails way
|
30
52
|
class ActiveRecord::Base
|
31
53
|
def self.acts_as_reportable
|
32
54
|
extend Ruport::Reportable
|
data/lib/ruport/report.rb
CHANGED
@@ -95,12 +95,6 @@ module Ruport
|
|
95
95
|
#run.
|
96
96
|
attr_accessor :results
|
97
97
|
|
98
|
-
# Preserved for backwards compatability, please do not use this.
|
99
|
-
alias_method :report, :results
|
100
|
-
|
101
|
-
# Preserved for backwards compabilitity, please do not use this.
|
102
|
-
alias_method :report=, :results=
|
103
|
-
|
104
98
|
# Simplified interface to Ruport::Query
|
105
99
|
#
|
106
100
|
# === Can read SQL statements from file or string
|
@@ -259,19 +253,25 @@ module Ruport
|
|
259
253
|
self.class.run(self,&block)
|
260
254
|
end
|
261
255
|
|
256
|
+
|
257
|
+
# loads a CSV in from file.
|
258
|
+
#
|
259
|
+
# Example
|
260
|
+
#
|
261
|
+
# my_table = load_csv "foo.csv" #=> Data::Table
|
262
|
+
# my_array = load_csv "foo.csv", :as => :array #=> Array
|
263
|
+
#
|
264
|
+
# See also, Ruport::Data::Table.load
|
262
265
|
def load_csv(file,options={})
|
263
266
|
case options[:as]
|
264
267
|
when :array
|
265
268
|
a = []
|
266
|
-
Data::Table.load(file) { |s,r| a << r } ; a
|
269
|
+
Data::Table.load(file,options) { |s,r| a << r } ; a
|
267
270
|
else
|
268
|
-
Data::Table.load(file)
|
271
|
+
Data::Table.load(file,options)
|
269
272
|
end
|
270
273
|
end
|
271
274
|
|
272
|
-
# Preserved for backwards compatibility, please do not use this.
|
273
|
-
alias_method :generate_report, :run
|
274
|
-
|
275
275
|
# Allows logging and other fun stuff. See Ruport.log
|
276
276
|
def log(*args); Ruport.log(*args) end
|
277
277
|
|