ruport 0.4.11 → 0.4.13
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/CHANGELOG +29 -1
- data/README +16 -26
- data/Rakefile +2 -1
- data/TODO +2 -16
- data/examples/report.rb +35 -0
- data/examples/template.rb +15 -0
- data/examples/text_processors.rb +20 -0
- data/lib/ruport.rb +9 -12
- data/lib/ruport/config.rb +2 -2
- data/lib/ruport/data_set.rb +10 -7
- data/lib/ruport/format.rb +58 -36
- data/lib/ruport/format/engine.rb +1 -1
- data/lib/ruport/format/plugin.rb +4 -3
- data/lib/ruport/mailer.rb +10 -9
- data/lib/ruport/query.rb +7 -14
- data/lib/ruport/report.rb +233 -32
- data/test/tc_config.rb +2 -2
- data/test/tc_data_set.rb +0 -1
- data/test/tc_query.rb +5 -0
- data/test/tc_report.rb +36 -1
- data/test/tc_ruport.rb +1 -1
- data/test/unit.log +111 -2506
- metadata +17 -9
- data/examples/create.sql +0 -2
- data/examples/pdf.rb +0 -36
- data/examples/sql_query.rb +0 -19
- data/test/tc_reading.rb +0 -60
data/lib/ruport/format/engine.rb
CHANGED
data/lib/ruport/format/plugin.rb
CHANGED
@@ -42,6 +42,7 @@ module Ruport
|
|
42
42
|
|
43
43
|
attr_accessor :rendered_field_names
|
44
44
|
attr_accessor :pre, :post
|
45
|
+
attr_accessor :header, :footer
|
45
46
|
end
|
46
47
|
|
47
48
|
|
@@ -78,7 +79,7 @@ module Ruport
|
|
78
79
|
}
|
79
80
|
|
80
81
|
a = data.inject(th){ |s,r|
|
81
|
-
s
|
82
|
+
s + "| #{r.to_a.join(' | ')} |\n"
|
82
83
|
} << hr
|
83
84
|
|
84
85
|
width = self.right_margin || SystemExtensions.terminal_width
|
@@ -111,7 +112,7 @@ module Ruport
|
|
111
112
|
f = data.fields if data.respond_to? :fields
|
112
113
|
d = DataSet.new f, :data => data
|
113
114
|
|
114
|
-
d[0].fields.inject(0) { |s,e| s
|
115
|
+
d[0].fields.inject(0) { |s,e| s + max_col_width(e) }
|
115
116
|
end
|
116
117
|
|
117
118
|
def self.hr
|
@@ -158,7 +159,7 @@ module Ruport
|
|
158
159
|
renderer :table do
|
159
160
|
rc = data.inject(rendered_field_names) { |s,r|
|
160
161
|
row = r.map { |e| e.to_s.empty? ? " " : e }
|
161
|
-
s
|
162
|
+
s + "|#{row.to_a.join('|')}|\n"
|
162
163
|
}
|
163
164
|
Format.document :data => rc, :plugin => :html
|
164
165
|
end
|
data/lib/ruport/mailer.rb
CHANGED
@@ -11,7 +11,8 @@ module Ruport
|
|
11
11
|
extend Forwardable
|
12
12
|
|
13
13
|
def initialize( mailer_label=:default )
|
14
|
-
select_mailer(mailer_label);
|
14
|
+
select_mailer(mailer_label);
|
15
|
+
mail_object.from = @mailer.address if mail_object.from.to_s.empty?
|
15
16
|
rescue
|
16
17
|
raise "you need to specify a mailer to use"
|
17
18
|
end
|
@@ -29,14 +30,14 @@ module Ruport
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def select_mailer(label)
|
32
|
-
mailer = Ruport::Config.mailers[label]
|
33
|
-
@host = mailer.host
|
34
|
-
@user = mailer.user
|
35
|
-
@password = mailer.password
|
36
|
-
@address = mailer.address
|
37
|
-
@port = mailer.port || 25
|
38
|
-
@auth = mailer.auth_type || :plain
|
39
|
-
@mail_klass = mailer.mail_klass
|
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
|
40
41
|
end
|
41
42
|
|
42
43
|
def mail_object
|
data/lib/ruport/query.rb
CHANGED
@@ -61,8 +61,7 @@ module Ruport
|
|
61
61
|
# # uses a SQL file stored on disk
|
62
62
|
# Ruport::Query.new("my_query.sql",:origin => :file)
|
63
63
|
def initialize(sql, options={})
|
64
|
-
options
|
65
|
-
options[:origin] ||= :string
|
64
|
+
options = { :source => :default, :origin => :string }.merge(options)
|
66
65
|
@sql = sql
|
67
66
|
@statements = SqlSplit.new(get_query(options[:origin],sql))
|
68
67
|
|
@@ -116,15 +115,14 @@ module Ruport
|
|
116
115
|
|
117
116
|
# clears the contents of the cache
|
118
117
|
def clear_cache
|
119
|
-
@
|
118
|
+
@CAched_data = nil
|
120
119
|
end
|
121
120
|
|
122
121
|
# clears the contents of the cache and then runs the query, filling the
|
123
122
|
# cache with the new result
|
124
123
|
def update_cache
|
125
|
-
|
126
|
-
|
127
|
-
fetch; @cache_enabled = caching_flag
|
124
|
+
return unless @cache_enabled
|
125
|
+
clear_cache; fetch
|
128
126
|
end
|
129
127
|
|
130
128
|
# Turns on caching. New data will not be loaded until cache is clear or
|
@@ -135,7 +133,7 @@ module Ruport
|
|
135
133
|
|
136
134
|
# Turns off caching and flushes the cached data
|
137
135
|
def disable_caching
|
138
|
-
|
136
|
+
clear_cache
|
139
137
|
@cache_enabled = false
|
140
138
|
end
|
141
139
|
|
@@ -148,7 +146,7 @@ module Ruport
|
|
148
146
|
|
149
147
|
# Returns a csv dump of the query
|
150
148
|
def to_csv
|
151
|
-
|
149
|
+
Format.table :plugin => :csv, :data => fetch
|
152
150
|
end
|
153
151
|
|
154
152
|
# Returns a Generator object of the result set
|
@@ -176,12 +174,7 @@ module Ruport
|
|
176
174
|
end
|
177
175
|
|
178
176
|
def get_query(type,query)
|
179
|
-
|
180
|
-
when :string
|
181
|
-
query
|
182
|
-
when :file
|
183
|
-
load_file( query )
|
184
|
-
end
|
177
|
+
type.eql?(:file) ? load_file( query ) : query
|
185
178
|
end
|
186
179
|
|
187
180
|
def load_file( query_file )
|
data/lib/ruport/report.rb
CHANGED
@@ -1,27 +1,140 @@
|
|
1
|
-
#
|
1
|
+
# report.rb : High Level Interface to Ruport
|
2
|
+
#
|
3
|
+
# Author: Gregory Brown
|
4
|
+
# Copyright 2006, All Rights Reserved
|
5
|
+
#
|
6
|
+
# This is Free Software. See LICENSE and COPYING files for details.
|
2
7
|
|
3
8
|
#load the needed standard libraries.
|
4
9
|
%w[erb yaml date logger fileutils].each { |lib| require lib }
|
5
10
|
|
6
11
|
require "ruport/mailer"
|
12
|
+
require "forwardable"
|
7
13
|
|
8
14
|
module Ruport
|
15
|
+
# === Overview
|
16
|
+
#
|
17
|
+
# The Ruport::Report class povides a high level interface to most of Ruport's
|
18
|
+
# functionality. It is designed to allow you to build and run reports easily.
|
19
|
+
# If your needs are complicated, you will probably need to take a look at the
|
20
|
+
# individual classes of the library, but if they are fairly simple, you may be
|
21
|
+
# able to get away using this class alone.
|
22
|
+
#
|
23
|
+
# === Example
|
24
|
+
#
|
25
|
+
# Here is a simple example of using the Report class to run a simple query and
|
26
|
+
# then email the results as a CSV file, deleting the file from the local
|
27
|
+
# machine after it has been emailed:
|
28
|
+
#
|
29
|
+
# require "ruport"
|
30
|
+
# require "fileutils"
|
31
|
+
# class MyReport < Ruport::Report
|
32
|
+
# prepare do
|
33
|
+
# log_file "f.log"
|
34
|
+
# log "preparing report", :status => :info
|
35
|
+
# source :default,
|
36
|
+
# :dsn => "dbi:mysql:foo",
|
37
|
+
# :user => "root"
|
38
|
+
# mailer :default,
|
39
|
+
# :host => "mail.adelphia.net",
|
40
|
+
# :address => "gregory.t.brown@gmail.com"
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# generate do
|
44
|
+
# log "generated csv from query", :status => :info
|
45
|
+
# query "select * from bar", :as => :csv
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# cleanup do
|
49
|
+
# log "removing foo.csv", :status => :info
|
50
|
+
# FileUtils.rm("foo.csv")
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# MyReport.run { |res|
|
55
|
+
# res.write "foo.csv";
|
56
|
+
# res.send_to("greg7224@gmail.com") do |mail|
|
57
|
+
# mail.subject = "Sample report"
|
58
|
+
# mail.attach "foo.csv"
|
59
|
+
# mail.text = <<-EOS
|
60
|
+
# this is a sample of sending an emailed report from within Ruport.
|
61
|
+
# EOS
|
62
|
+
# end
|
63
|
+
# }
|
64
|
+
#
|
65
|
+
#
|
66
|
+
# This class can also be used to run templates and process text filters
|
67
|
+
#
|
68
|
+
# See the examples in the documentation below to see how to use these
|
69
|
+
# features. (Namely Report#process_text , Report#text_processor, and
|
70
|
+
# Report#eval_template )
|
71
|
+
#
|
9
72
|
class Report
|
73
|
+
extend Forwardable
|
74
|
+
|
75
|
+
# When initializing a report, you can provide a default mailer and source by
|
76
|
+
# giving a name of a valid source or mailer you've defined via
|
77
|
+
# Ruport::Config
|
78
|
+
#
|
79
|
+
# If your report does not need any sort of specialized information, you can
|
80
|
+
# simply use Report.run (Or MyReportName.run if you've inherited)
|
81
|
+
#
|
82
|
+
# This will auto-initialize a report.
|
10
83
|
def initialize( source_name=:default, mailer_name=:default )
|
11
|
-
|
12
|
-
|
84
|
+
use_source source_name
|
85
|
+
use_mailer mailer_name
|
86
|
+
@report_name = @results = ""
|
13
87
|
@file = nil
|
14
88
|
end
|
15
89
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
22
|
-
|
90
|
+
#by default, this file will be used by Report#write
|
91
|
+
attr_accessor :file
|
92
|
+
|
93
|
+
#this attribute will get the results of Report#generate when the report is
|
94
|
+
#run.
|
95
|
+
attr_accessor :results
|
96
|
+
|
97
|
+
# Preserved for backwards compatability, please do not use this.
|
98
|
+
alias_method :report, :results
|
99
|
+
|
100
|
+
# Preserved for backwards compabilitity, please do not use this.
|
101
|
+
alias_method :report=, :results=
|
102
|
+
|
103
|
+
|
104
|
+
# Simplified interface to Ruport::Query
|
105
|
+
#
|
106
|
+
# === Can read SQL statements from file or string
|
107
|
+
#
|
108
|
+
# #from string
|
109
|
+
# result = query "select * from foo"
|
110
|
+
#
|
111
|
+
# #from file
|
112
|
+
# result = query "my_query.sql", :origin => :file
|
113
|
+
#
|
114
|
+
# === Can use multistatement SQL
|
115
|
+
#
|
116
|
+
# # will return the value of the last statement, "select * from foo"
|
117
|
+
# result = query "insert into foo values(1,2); select * from foo"
|
118
|
+
#
|
119
|
+
# === Can iterate by row or return entire set
|
120
|
+
#
|
121
|
+
# query("select * from foo", :yield_type => :by_row) { |r|
|
122
|
+
# #do something with the rows here
|
123
|
+
# }
|
124
|
+
#
|
125
|
+
# === Can return raw DBI:Row objects or Ruport's data structures.
|
126
|
+
#
|
127
|
+
# # will return a DataSet
|
128
|
+
# result = query "select * from foo"
|
23
129
|
#
|
24
|
-
#
|
130
|
+
# # will return an Array of DBI::Row objects
|
131
|
+
# result = query "select * from foo", :raw_data => true
|
132
|
+
#
|
133
|
+
# === Can quickly output in a number of formats
|
134
|
+
#
|
135
|
+
# result = query "select * from foo", :as => :csv
|
136
|
+
# result = query "select * from foo", :as => :html
|
137
|
+
# result = query "select * from foo", :as => :pdf
|
25
138
|
#
|
26
139
|
# See source of this function and methods of Ruport::Query for details.
|
27
140
|
def query(sql, options={}, &action)
|
@@ -31,6 +144,8 @@ module Ruport
|
|
31
144
|
q = Query.new(sql, options)
|
32
145
|
if options[:yield_type].eql?(:by_row)
|
33
146
|
q.each { |r| action.call(r) }
|
147
|
+
elsif options[:as]
|
148
|
+
Format.table :data => q.result, :plugin => options[:as]
|
34
149
|
else
|
35
150
|
block_given? ? action.call(q.result) : q.result
|
36
151
|
end
|
@@ -38,41 +153,32 @@ module Ruport
|
|
38
153
|
|
39
154
|
# Evaluates _code_ from _filename_ as pure ruby code for files ending in
|
40
155
|
# .rb, and as ERb templates for anything else.
|
41
|
-
def eval_template( filename, code )
|
42
|
-
filename =~ /\.rb/ ? eval(code) : ERB.new(code, 0, "%").run(binding)
|
43
|
-
end
|
44
|
-
|
45
|
-
|
46
|
-
# Generates the report. If @pre or @post are defined with lambdas,
|
47
|
-
# they will be called before and after the main code.
|
48
|
-
#
|
49
|
-
# If @file != nil, ruport will print to the
|
50
|
-
# file with the specified name. Otherwise, it will print to STDOUT by
|
51
|
-
# default.
|
52
156
|
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
def
|
56
|
-
|
57
|
-
@file ? File.open(@file,"w") { |f| f.puts @report } : puts(@report)
|
58
|
-
@post.call if @post
|
157
|
+
# This code will be evaluated in the context of the instance on which it is
|
158
|
+
# called.
|
159
|
+
def eval_template( code, filename=nil )
|
160
|
+
filename =~ /\.rb/ ? eval(code) : ERB.new(code, 0, "%").run(binding)
|
59
161
|
end
|
60
|
-
|
162
|
+
|
61
163
|
# sets the active source to the Ruport::Config source requested by label.
|
62
164
|
def use_source(label)
|
63
165
|
@source = label
|
64
166
|
end
|
167
|
+
|
168
|
+
# sets the active mailer to the Ruport::Config source requested by label.
|
169
|
+
def use_mailer(label)
|
170
|
+
@mailer = label
|
171
|
+
end
|
65
172
|
|
66
173
|
# Provides a nice way to execute templates and filters.
|
67
174
|
#
|
68
175
|
# Example:
|
69
176
|
#
|
70
|
-
#
|
71
|
-
# :filters => [:erb,:red_cloth] )
|
177
|
+
# process_text "_<%= @some_internal_var %>_", :filters => [:erb,:red_cloth]
|
72
178
|
#
|
73
179
|
# This method automatically passes a binding into the filters, so you are
|
74
180
|
# free to access data from your Report instance in your templates.
|
75
|
-
def
|
181
|
+
def process_text(string, options)
|
76
182
|
options[:filters].each do |f|
|
77
183
|
format = Format.new(binding)
|
78
184
|
format.content = string
|
@@ -80,9 +186,104 @@ module Ruport
|
|
80
186
|
end
|
81
187
|
string
|
82
188
|
end
|
189
|
+
|
190
|
+
# preserved for backwards compatibility. please do not use.
|
191
|
+
alias_method :render, :process_text
|
83
192
|
|
193
|
+
# This allows you to create filters to be used by process_text
|
194
|
+
#
|
195
|
+
# The block is evaluated in the context of the instance.
|
196
|
+
#
|
197
|
+
# E.g
|
198
|
+
#
|
199
|
+
# text_processor(:unix_newlines) { results.gsub!(/\r\n/,"\n") }
|
200
|
+
def text_processor(label,&block)
|
201
|
+
Format.register_filter(label, &block)
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
# Writes the contents of <tt>results</tt> to file. If a filename is
|
206
|
+
# specified, it will use it. Otherwise, it will try to write to the file
|
207
|
+
# specified by the <tt>file</tt> attribute.
|
208
|
+
def write(my_file=file)
|
209
|
+
File.open(my_file,"w") { |f| f << results }
|
210
|
+
end
|
211
|
+
|
212
|
+
# Like Report#write, but will append to a file rather than overwrite it if
|
213
|
+
# the file already exists
|
214
|
+
def append(my_file=file)
|
215
|
+
File.open(my_file,"a") { |f| f << results }
|
216
|
+
end
|
217
|
+
|
218
|
+
class << self
|
219
|
+
|
220
|
+
# Defines an instance method which will be run before the
|
221
|
+
# <tt>generate</tt> method when Ruport.run is executed
|
222
|
+
#
|
223
|
+
# Good for setting config info and perhaps files and/or loggers
|
224
|
+
#
|
225
|
+
def prepare(&block); define_method(:prepare,&block) end
|
226
|
+
|
227
|
+
# Defines an instance method which will be executed by Report.run
|
228
|
+
#
|
229
|
+
# The return value of this method is assigned to the <tt>results</tt>
|
230
|
+
# attribute
|
231
|
+
#
|
232
|
+
def generate(&block); define_method(:generate,&block) end
|
233
|
+
|
234
|
+
# Defines an instance method which will be executed after the object is
|
235
|
+
# yielded in Report.run
|
236
|
+
#
|
237
|
+
def cleanup(&block); define_method(:cleanup,&block) end
|
238
|
+
|
239
|
+
# Runs the reports specified. If no reports are specified, then it
|
240
|
+
# creates a new instance via <tt>self.new</tt>
|
241
|
+
#
|
242
|
+
# Tries to execute the prepare instance method, then runs generate.
|
243
|
+
# It then yields the object so that you may do something with it
|
244
|
+
# (print something out, write to file, email, etc)
|
245
|
+
#
|
246
|
+
# Finally, it tries to call cleanup.
|
247
|
+
def run(*reports)
|
248
|
+
reports[0] ||= self.new
|
249
|
+
reports.each { |rep|
|
250
|
+
rep.prepare if rep.respond_to? :prepare;
|
251
|
+
rep.results = rep.generate;
|
252
|
+
yield(rep) if block_given?
|
253
|
+
rep.cleanup if rep.respond_to? :cleanup;
|
254
|
+
}
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# this method passes <tt>self</tt> to Report.run
|
259
|
+
#
|
260
|
+
# Please see the class method for details.
|
261
|
+
def run(&block)
|
262
|
+
self.class.run(self,&block)
|
263
|
+
end
|
264
|
+
|
265
|
+
# Preserved for backwards compatibility, please do not use this.
|
266
|
+
alias_method :generate_report, :run
|
84
267
|
|
268
|
+
# Allows logging and other fun stuff. See Ruport.log
|
269
|
+
def log(*args); Ruport.log(*args) end
|
270
|
+
|
271
|
+
# Creates a new Mailer and sets the <tt>to</tt> attribute to the addresses
|
272
|
+
# specified. Yields a Mailer object, which can be modified before delivery.
|
273
|
+
#
|
274
|
+
# By default, this will
|
275
|
+
def send_to(adds)
|
276
|
+
m = Mailer.new
|
277
|
+
m.to = adds
|
278
|
+
yield(m)
|
279
|
+
m.select_mailer @mailer
|
280
|
+
m.deliver :from => m.from, :to => m.to
|
281
|
+
end
|
282
|
+
|
283
|
+
def_delegators Ruport::Config, :source, :mailer, :log_file, :log_file=
|
284
|
+
|
85
285
|
end
|
286
|
+
|
86
287
|
end
|
87
288
|
|
88
289
|
|