ruport 0.4.23 → 0.4.99
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 +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
|
|