ruport 0.5.0 → 0.5.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.
- data/CHANGELOG +7 -1
- data/Rakefile +1 -1
- data/lib/ruport.rb +1 -1
- data/lib/ruport/format/engine.rb +2 -109
- data/lib/ruport/format/engine/document.rb +28 -0
- data/lib/ruport/format/engine/graph.rb +18 -0
- data/lib/ruport/format/engine/invoice.rb +23 -0
- data/lib/ruport/format/engine/table.rb +53 -0
- data/lib/ruport/format/plugin.rb +7 -287
- data/lib/ruport/format/plugin/csv_plugin.rb +22 -0
- data/lib/ruport/format/plugin/html_plugin.rb +27 -0
- data/lib/ruport/format/plugin/pdf_plugin.rb +126 -0
- data/lib/ruport/format/plugin/svg_plugin.rb +61 -0
- data/lib/ruport/format/plugin/text_plugin.rb +71 -0
- data/lib/ruport/system_extensions.rb +8 -8
- data/lib/ruport/system_extensions.rb.rej +32 -0
- data/test/test_collection.rb +5 -0
- data/test/unit.log +12 -0
- metadata +14 -2
data/CHANGELOG
CHANGED
@@ -1,4 +1,10 @@
|
|
1
|
-
Tnghe current version of Ruby Reports is 0.5.
|
1
|
+
Tnghe current version of Ruby Reports is 0.5.1
|
2
|
+
|
3
|
+
changes since 0.5.0
|
4
|
+
|
5
|
+
- Broke plugins and engines out into their own files
|
6
|
+
|
7
|
+
- Fixed a bug which prevented TextPlugin from working right in Windows
|
2
8
|
|
3
9
|
changes since 0.4.99
|
4
10
|
|
data/Rakefile
CHANGED
@@ -23,7 +23,7 @@ end
|
|
23
23
|
|
24
24
|
spec = Gem::Specification.new do |spec|
|
25
25
|
spec.name = LEAN ? "lean-ruport" : "ruport"
|
26
|
-
spec.version = "0.5.
|
26
|
+
spec.version = "0.5.1"
|
27
27
|
spec.platform = Gem::Platform::RUBY
|
28
28
|
spec.summary = "A generalized Ruby report generation and templating engine."
|
29
29
|
spec.files = Dir.glob("{examples,lib,test,bin}/**/**/*") +
|
data/lib/ruport.rb
CHANGED
data/lib/ruport/format/engine.rb
CHANGED
@@ -101,114 +101,7 @@ module Ruport
|
|
101
101
|
end
|
102
102
|
|
103
103
|
private_class_method :new
|
104
|
+
engines = %w[graph invoice table document]
|
105
|
+
engines.each { |e| require "lib/ruport/format/engine/#{e}" }
|
104
106
|
end
|
105
|
-
|
106
|
-
class Format::Engine::Graph < Ruport::Format::Engine
|
107
|
-
|
108
|
-
attributes [:width, :height, :style, :title]
|
109
|
-
|
110
|
-
renderer do
|
111
|
-
super
|
112
|
-
active_plugin.render_graph
|
113
|
-
end
|
114
|
-
|
115
|
-
alias_engine Graph, :graph_engine
|
116
|
-
Ruport::Format.build_interface_for Graph, :graph
|
117
|
-
|
118
|
-
end
|
119
|
-
|
120
|
-
class Format::Engine::Invoice < Ruport::Format::Engine
|
121
|
-
|
122
|
-
# order meta data
|
123
|
-
attributes [ :customer_info, :company_info,
|
124
|
-
:comments, :order_info, :title]
|
125
|
-
|
126
|
-
renderer do
|
127
|
-
super
|
128
|
-
build_headers
|
129
|
-
build_body
|
130
|
-
build_footer
|
131
|
-
active_plugin.render_invoice
|
132
|
-
end
|
133
|
-
|
134
|
-
alias_engine Invoice, :invoice_engine
|
135
|
-
Ruport::Format.build_interface_for Invoice, :invoice
|
136
|
-
|
137
|
-
end
|
138
|
-
|
139
|
-
class Format::Engine::Table < Format::Engine
|
140
|
-
|
141
|
-
renderer do
|
142
|
-
super
|
143
|
-
active_plugin.rendered_field_names = ""
|
144
|
-
build_field_names if (data.respond_to?(:column_names) &&
|
145
|
-
data.column_names && show_field_names)
|
146
|
-
a = active_plugin.render_table
|
147
|
-
end
|
148
|
-
|
149
|
-
class << self
|
150
|
-
|
151
|
-
def rewrite_column(key,&block)
|
152
|
-
data.each { |r| r[key] = block[r] }
|
153
|
-
end
|
154
|
-
|
155
|
-
def num_cols
|
156
|
-
data[0].to_a.length
|
157
|
-
end
|
158
|
-
|
159
|
-
def prune(limit=data[0].length)
|
160
|
-
limit.times do |field|
|
161
|
-
last = ""
|
162
|
-
data.each_cons(2) { |l,e|
|
163
|
-
next if field.nonzero? && e[field-1]
|
164
|
-
last = l[field] if l[field]
|
165
|
-
e[field] = nil if e[field] == last
|
166
|
-
}
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
attr_accessor :show_field_names
|
171
|
-
|
172
|
-
private
|
173
|
-
|
174
|
-
def build_field_names
|
175
|
-
if active_plugin.respond_to?(:build_field_names)
|
176
|
-
active_plugin.rendered_field_names =
|
177
|
-
active_plugin.build_field_names
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
end
|
182
|
-
|
183
|
-
alias_engine Table, :table_engine
|
184
|
-
Format.build_interface_for Table, :table
|
185
|
-
self.show_field_names = true
|
186
|
-
end
|
187
|
-
|
188
|
-
class Format::Engine::Document < Format::Engine
|
189
|
-
|
190
|
-
renderer do
|
191
|
-
super
|
192
|
-
apply_erb if erb_enabled
|
193
|
-
apply_red_cloth if red_cloth_enabled
|
194
|
-
active_plugin.render_document
|
195
|
-
end
|
196
|
-
|
197
|
-
class << self
|
198
|
-
|
199
|
-
attr_accessor :red_cloth_enabled
|
200
|
-
attr_accessor :erb_enabled
|
201
|
-
|
202
|
-
def apply_red_cloth
|
203
|
-
require "redcloth"
|
204
|
-
active_plugin.data = RedCloth.new(active_plugin.data).to_html
|
205
|
-
end
|
206
|
-
|
207
|
-
end
|
208
|
-
|
209
|
-
alias_engine Document, :document_engine
|
210
|
-
Format.build_interface_for Document, :document
|
211
|
-
end
|
212
|
-
|
213
|
-
|
214
107
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Ruport
|
2
|
+
class Format::Engine
|
3
|
+
class Document < Format::Engine
|
4
|
+
renderer do
|
5
|
+
super
|
6
|
+
apply_erb if erb_enabled
|
7
|
+
apply_red_cloth if red_cloth_enabled
|
8
|
+
active_plugin.render_document
|
9
|
+
end
|
10
|
+
|
11
|
+
class << self
|
12
|
+
|
13
|
+
attr_accessor :red_cloth_enabled
|
14
|
+
attr_accessor :erb_enabled
|
15
|
+
|
16
|
+
def apply_red_cloth
|
17
|
+
require "redcloth"
|
18
|
+
active_plugin.data = RedCloth.new(active_plugin.data).to_html
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_engine Document, :document_engine
|
24
|
+
Format.build_interface_for Document, :document
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Ruport
|
2
|
+
class Format::Engine
|
3
|
+
class Graph < Format::Engine
|
4
|
+
|
5
|
+
attributes [:width, :height, :style, :title]
|
6
|
+
|
7
|
+
renderer do
|
8
|
+
super
|
9
|
+
active_plugin.render_graph
|
10
|
+
end
|
11
|
+
|
12
|
+
alias_engine Graph, :graph_engine
|
13
|
+
Ruport::Format.build_interface_for Graph, :graph
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Ruport
|
2
|
+
class Format::Engine
|
3
|
+
class Format::Engine::Invoice < Ruport::Format::Engine
|
4
|
+
|
5
|
+
# order meta data
|
6
|
+
attributes [ :customer_info, :company_info,
|
7
|
+
:comments, :order_info, :title]
|
8
|
+
|
9
|
+
renderer do
|
10
|
+
super
|
11
|
+
build_headers
|
12
|
+
build_body
|
13
|
+
build_footer
|
14
|
+
active_plugin.render_invoice
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_engine Invoice, :invoice_engine
|
18
|
+
Ruport::Format.build_interface_for Invoice, :invoice
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Ruport
|
2
|
+
class Format::Engine
|
3
|
+
class Table < Format::Engine
|
4
|
+
|
5
|
+
renderer do
|
6
|
+
super
|
7
|
+
active_plugin.rendered_field_names = ""
|
8
|
+
build_field_names if (data.respond_to?(:column_names) &&
|
9
|
+
data.column_names && show_field_names)
|
10
|
+
a = active_plugin.render_table
|
11
|
+
end
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
def rewrite_column(key,&block)
|
16
|
+
data.each { |r| r[key] = block[r] }
|
17
|
+
end
|
18
|
+
|
19
|
+
def num_cols
|
20
|
+
data[0].to_a.length
|
21
|
+
end
|
22
|
+
|
23
|
+
def prune(limit=data[0].length)
|
24
|
+
limit.times do |field|
|
25
|
+
last = ""
|
26
|
+
data.each_cons(2) { |l,e|
|
27
|
+
next if field.nonzero? && e[field-1]
|
28
|
+
last = l[field] if l[field]
|
29
|
+
e[field] = nil if e[field] == last
|
30
|
+
}
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
attr_accessor :show_field_names
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def build_field_names
|
39
|
+
if active_plugin.respond_to?(:build_field_names)
|
40
|
+
active_plugin.rendered_field_names =
|
41
|
+
active_plugin.build_field_names
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
alias_engine Table, :table_engine
|
48
|
+
Format.build_interface_for Table, :table
|
49
|
+
self.show_field_names = true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
data/lib/ruport/format/plugin.rb
CHANGED
@@ -2,10 +2,13 @@ class InvalidGraphDataError < RuntimeError; end
|
|
2
2
|
class InvalidGraphOptionError < RuntimeError; end
|
3
3
|
|
4
4
|
require 'bigdecimal'
|
5
|
+
require 'tempfile'
|
6
|
+
|
7
|
+
class RenderingError < RuntimeError; end
|
5
8
|
|
6
9
|
module Ruport
|
7
10
|
class Format::Plugin
|
8
|
-
|
11
|
+
|
9
12
|
class << self
|
10
13
|
|
11
14
|
attr_accessor :data
|
@@ -62,292 +65,9 @@ module Ruport
|
|
62
65
|
attr_accessor :rendered_field_names
|
63
66
|
attr_accessor :pre, :post
|
64
67
|
attr_accessor :header, :footer
|
65
|
-
end
|
66
|
-
|
67
|
-
|
68
|
-
class CSVPlugin < Format::Plugin
|
69
|
-
|
70
|
-
helper(:init_plugin) { |eng| require "fastercsv" }
|
71
|
-
|
72
|
-
format_field_names do
|
73
|
-
FasterCSV.generate { |csv| csv << data.column_names }
|
74
|
-
end
|
75
|
-
|
76
|
-
renderer :table do
|
77
|
-
rendered_field_names +
|
78
|
-
FasterCSV.generate { |csv| data.each { |r| csv << r } }
|
79
|
-
end
|
80
|
-
|
81
|
-
plugin_name :csv
|
82
|
-
register_on :table_engine
|
83
|
-
end
|
84
|
-
|
85
|
-
class SVGPlugin < Format::Plugin
|
86
|
-
|
87
|
-
helper(:init_plugin) { |eng|
|
88
|
-
# check the supplied data can be used for graphing
|
89
|
-
data.each { |r|
|
90
|
-
if data.column_names.size != r.data.size
|
91
|
-
raise InvalidGraphDataError, "Column names and data do not match"
|
92
|
-
end
|
93
|
-
r.data.each { |c|
|
94
|
-
begin
|
95
|
-
c = BigDecimal.new(c) unless c.kind_of?(Float) ||
|
96
|
-
c.kind_of?(Fixnum) || c.kind_of?(BigDecimal)
|
97
|
-
rescue
|
98
|
-
raise InvalidGraphDataError, "Unable to convert #{c.to_s} into a number"
|
99
|
-
end
|
100
|
-
}
|
101
|
-
}
|
102
|
-
|
103
|
-
raise InvalidGraphOptionError, 'You must provide a width before rendering a graph' if eng.width.nil?
|
104
|
-
raise InvalidGraphOptionError, 'You must provide a height before rendering a graph' if eng.height.nil?
|
105
|
-
raise InvalidGraphOptionError, 'You must provide a style before rendering a graph' if eng.style.nil?
|
106
|
-
if eng.style != :area && eng.style != :bar &&
|
107
|
-
eng.style != :line &&
|
108
|
-
eng.style != :smiles &&
|
109
|
-
eng.style != :stacked
|
110
|
-
raise InvalidGraphOptionError, 'Invalid graph style'
|
111
|
-
end
|
112
|
-
|
113
|
-
require 'scruffy'
|
114
|
-
@graph = Scruffy::Graph.new
|
115
|
-
@graph.title = eng.title unless eng.title.nil?
|
116
|
-
@graph.theme = Scruffy::Themes::Mephisto.new
|
117
|
-
@graph.point_markers = @data.column_names
|
118
|
-
@graph_style = eng.style
|
119
|
-
@graph_width = eng.width
|
120
|
-
@graph_height = eng.height
|
121
|
-
}
|
122
|
-
|
123
|
-
renderer :graph do
|
124
|
-
|
125
|
-
data.each_with_index { |r,i|
|
126
|
-
@graph.add(@graph_style,
|
127
|
-
r.tags[0] || 'series ' + (i+1).to_s,
|
128
|
-
r.data)
|
129
|
-
}
|
130
|
-
|
131
|
-
# return the rendered graph
|
132
|
-
@graph.render(:size => [@graph_width, @graph_height])
|
133
|
-
end
|
134
|
-
|
135
|
-
plugin_name :svg
|
136
|
-
register_on :graph_engine
|
137
|
-
end
|
138
|
-
|
139
|
-
class TextPlugin < Format::Plugin
|
140
|
-
rendering_options :erb_enabled => true, :red_cloth_enabled => false
|
141
|
-
|
142
|
-
renderer :document
|
143
|
-
|
144
|
-
renderer :table do
|
145
|
-
require "ruport/system_extensions"
|
146
|
-
|
147
|
-
th = "#{rendered_field_names}#{hr}"
|
148
|
-
|
149
|
-
data.each { |r|
|
150
|
-
r.each_with_index { |f,i|
|
151
|
-
r[i] = f.to_s.center(max_col_width(i))
|
152
|
-
}
|
153
|
-
}
|
154
|
-
|
155
|
-
a = data.inject(th){ |s,r|
|
156
|
-
s + "| #{r.to_a.join(' | ')} |\n"
|
157
|
-
} << hr
|
158
|
-
|
159
|
-
width = self.right_margin || SystemExtensions.terminal_width
|
160
|
-
|
161
|
-
a.to_a.each { |r|
|
162
|
-
r.gsub!(/\A.{#{width+1},}/) { |m| m[0,width-2] + ">>" }
|
163
|
-
}.join
|
164
|
-
end
|
165
|
-
|
166
|
-
format_field_names do
|
167
|
-
data.column_names.each_with_index { |f,i|
|
168
|
-
data.column_names[i] = f.to_s.center(max_col_width(i))
|
169
|
-
}
|
170
|
-
"#{hr}| #{data.column_names.to_a.join(' | ')} |\n"
|
171
|
-
end
|
172
|
-
|
173
|
-
action :max_col_width do |index|
|
174
|
-
f = data.column_names if data.respond_to? :column_names
|
175
|
-
d = Data::Table.new :column_names => f, :data => data
|
176
|
-
|
177
|
-
cw = d.map { |r| r[index].to_s.length }.max
|
178
|
-
|
179
|
-
return cw unless d.column_names
|
180
|
-
|
181
|
-
nw = (index.kind_of?(Integer) ? d.column_names[index] : index ).to_s.length
|
182
|
-
|
183
|
-
[cw,nw].max
|
184
|
-
end
|
185
|
-
|
186
|
-
action :table_width do
|
187
|
-
f = data.column_names if data.respond_to? :column_names
|
188
|
-
d = Data::Table.new:column_names => f, :data => data
|
189
|
-
|
190
|
-
f = d[0].attributes || (0...d[0].length)
|
191
|
-
|
192
|
-
f.inject(0) { |s,e| s + max_col_width(e) }
|
193
|
-
end
|
194
|
-
|
195
|
-
action :hr do
|
196
|
-
len = data[0].to_a.length * 3 + table_width + 1
|
197
|
-
"+" + "-"*(len-2) + "+\n"
|
198
|
-
end
|
199
|
-
|
200
|
-
attribute :right_margin
|
201
|
-
plugin_name :text
|
202
|
-
register_on :table_engine
|
203
|
-
register_on :document_engine
|
204
|
-
end
|
205
|
-
|
206
|
-
class PDFPlugin < Format::Plugin
|
207
|
-
attribute :pdf
|
208
|
-
attribute :paper
|
209
|
-
|
210
|
-
helper(:init_plugin) {
|
211
|
-
require "pdf/writer"
|
212
|
-
require "pdf/simpletable"
|
213
|
-
self.pdf = PDF::Writer.new( :paper => paper || "LETTER" )
|
214
|
-
}
|
215
|
-
|
216
|
-
renderer :table do
|
217
|
-
pre[pdf] if pre
|
218
|
-
PDF::SimpleTable.new do |table|
|
219
|
-
table.maximum_width = 500
|
220
|
-
table.orientation = :center
|
221
|
-
table.data = data
|
222
|
-
m = "Sorry, cant build PDFs from array like things (yet)"
|
223
|
-
raise m if self.rendered_field_names.empty?
|
224
|
-
table.column_order = self.rendered_field_names
|
225
|
-
table.render_on(pdf)
|
226
|
-
end
|
227
|
-
post[pdf] if post
|
228
|
-
pdf.render
|
229
|
-
end
|
230
|
-
|
231
|
-
format_field_names { data.column_names }
|
232
|
-
|
233
|
-
renderer(:invoice) { pdf.render }
|
234
|
-
|
235
|
-
# Company Information in top lefthand corner
|
236
|
-
helper(:build_company_header, :engine => :invoice_engine) { |eng|
|
237
|
-
@tod = pdf.y
|
238
|
-
text_box(eng.company_info)
|
239
|
-
}
|
240
|
-
|
241
|
-
helper(:build_headers, :engine => :invoice_engine) { |eng|
|
242
|
-
build_company_header_helper(eng)
|
243
|
-
build_customer_header_helper(eng)
|
244
|
-
build_title_helper(eng)
|
245
|
-
build_order_header_helper(eng)
|
246
|
-
}
|
247
|
-
|
248
|
-
helper(:build_order_header, :engine => :invoice_engine) { |eng|
|
249
|
-
if eng.order_info
|
250
|
-
text_box(eng.order_info, :position => 350)
|
251
|
-
end
|
252
|
-
}
|
253
|
-
|
254
|
-
helper(:build_title, :engine => :invoice_engine) { |eng|
|
255
|
-
pdf.y = @tod
|
256
|
-
if eng.title
|
257
|
-
pdf.text eng.title, :left => 350, :font_size => 14
|
258
|
-
pdf.y -= 10
|
259
|
-
end
|
260
|
-
}
|
261
|
-
|
262
|
-
helper(:build_footer, :engine => :invoice_engine) { |eng|
|
263
|
-
# footer
|
264
|
-
pdf.open_object do |footer|
|
265
|
-
pdf.save_state
|
266
|
-
pdf.stroke_color! Color::RGB::Black
|
267
|
-
pdf.stroke_style! PDF::Writer::StrokeStyle::DEFAULT
|
268
|
-
if eng.comments
|
269
|
-
pdf.y -= 20
|
270
|
-
text_box eng.comments, :position => 110, :width => 400,
|
271
|
-
:font_size => 14
|
272
|
-
end
|
273
|
-
pdf.add_text_wrap( 50, 20, 200, "Printed at " +
|
274
|
-
Time.now.strftime("%H:%M %d/%m/%Y"), 8)
|
275
|
-
|
276
|
-
pdf.restore_state
|
277
|
-
pdf.close_object
|
278
|
-
pdf.add_object(footer, :all_pages)
|
279
|
-
end
|
280
|
-
pdf.stop_page_numbering(true, :current)
|
281
|
-
}
|
282
|
-
|
283
|
-
helper(:build_body, :engine => :invoice_engine) do
|
284
|
-
pdf.start_page_numbering(500, 20, 8, :right)
|
285
|
-
|
286
|
-
# order contents
|
287
|
-
pdf.y = 620
|
288
|
-
|
289
|
-
PDF::SimpleTable.new do |table|
|
290
|
-
table.width = 450
|
291
|
-
table.orientation = :center
|
292
|
-
table.data = data
|
293
|
-
table.show_lines = :outer
|
294
|
-
table.column_order = data.column_names
|
295
|
-
table.render_on(pdf)
|
296
|
-
table.font_size = 12
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
# Order details
|
301
|
-
helper(:build_customer_header, :engine => :invoice_engine) { |eng|
|
302
|
-
pdf.y -= 10
|
303
|
-
text_box(eng.customer_info)
|
304
|
-
}
|
305
|
-
|
306
|
-
def self.text_box(content,options={})
|
307
|
-
PDF::SimpleTable.new do |table|
|
308
|
-
table.data = content.to_a.inject([]) do |s,line|
|
309
|
-
s << { "value" => line }
|
310
|
-
end
|
311
|
-
table.column_order = "value"
|
312
|
-
table.show_headings = false
|
313
|
-
table.show_lines = :outer
|
314
|
-
table.shade_rows = :none
|
315
|
-
table.width = options[:width] || 200
|
316
|
-
table.orientation = options[:orientation] || :right
|
317
|
-
table.position = options[:position] || :left
|
318
|
-
table.font_size = options[:font_size] || 10
|
319
|
-
table.render_on(pdf)
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
plugin_name :pdf
|
324
|
-
register_on :table_engine
|
325
|
-
register_on :invoice_engine
|
326
|
-
end
|
327
|
-
|
328
|
-
class HTMLPlugin < Format::Plugin
|
329
|
-
|
330
|
-
rendering_options :red_cloth_enabled => true, :erb_enabled => true
|
331
|
-
|
332
|
-
renderer :document
|
333
|
-
|
334
|
-
renderer :table do
|
335
|
-
rc = data.inject(rendered_field_names) { |s,r|
|
336
|
-
row = r.map { |e| e.to_s.empty? ? " " : e }
|
337
|
-
s + "|#{row.to_a.join('|')}|\n"
|
338
|
-
}
|
339
|
-
Format.document :data => rc, :plugin => :html
|
340
|
-
end
|
341
|
-
|
342
|
-
format_field_names do
|
343
|
-
s = "|_." + data.column_names.join(" |_.") + "|\n"
|
344
|
-
end
|
345
|
-
|
346
|
-
plugin_name :html
|
347
|
-
register_on :table_engine
|
348
|
-
register_on :document_engine
|
349
68
|
|
69
|
+
plugins = %w[text csv pdf svg html]
|
70
|
+
plugins.each { |p| require "ruport/format/plugin/#{p}_plugin" }
|
350
71
|
end
|
351
|
-
|
352
|
-
end
|
72
|
+
end
|
353
73
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Ruport
|
2
|
+
class Format::Plugin
|
3
|
+
class CSVPlugin < Format::Plugin
|
4
|
+
|
5
|
+
helper(:init_plugin) { |eng| require "fastercsv" }
|
6
|
+
|
7
|
+
format_field_names do
|
8
|
+
FasterCSV.generate { |csv| csv << data.column_names }
|
9
|
+
end
|
10
|
+
|
11
|
+
renderer :table do
|
12
|
+
rendered_field_names +
|
13
|
+
FasterCSV.generate { |csv| data.each { |r| csv << r } }
|
14
|
+
end
|
15
|
+
|
16
|
+
plugin_name :csv
|
17
|
+
register_on :table_engine
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Ruport
|
2
|
+
class Format::Plugin
|
3
|
+
class HTMLPlugin < Format::Plugin
|
4
|
+
|
5
|
+
rendering_options :red_cloth_enabled => true, :erb_enabled => true
|
6
|
+
|
7
|
+
renderer :document
|
8
|
+
|
9
|
+
renderer :table do
|
10
|
+
rc = data.inject(rendered_field_names) { |s,r|
|
11
|
+
row = r.map { |e| e.to_s.empty? ? " " : e }
|
12
|
+
s + "|#{row.to_a.join('|')}|\n"
|
13
|
+
}
|
14
|
+
Format.document :data => rc, :plugin => :html
|
15
|
+
end
|
16
|
+
|
17
|
+
format_field_names do
|
18
|
+
s = "|_." + data.column_names.join(" |_.") + "|\n"
|
19
|
+
end
|
20
|
+
|
21
|
+
plugin_name :html
|
22
|
+
register_on :table_engine
|
23
|
+
register_on :document_engine
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Ruport
|
2
|
+
class Format::Plugin
|
3
|
+
class PDFPlugin < Format::Plugin
|
4
|
+
attribute :pdf
|
5
|
+
attribute :paper
|
6
|
+
|
7
|
+
helper(:init_plugin) {
|
8
|
+
require "pdf/writer"
|
9
|
+
require "pdf/simpletable"
|
10
|
+
self.pdf = PDF::Writer.new( :paper => paper || "LETTER" )
|
11
|
+
}
|
12
|
+
|
13
|
+
renderer :table do
|
14
|
+
pre[pdf] if pre
|
15
|
+
PDF::SimpleTable.new do |table|
|
16
|
+
table.maximum_width = 500
|
17
|
+
table.orientation = :center
|
18
|
+
table.data = data
|
19
|
+
m = "Sorry, cant build PDFs from array like things (yet)"
|
20
|
+
raise m if self.rendered_field_names.empty?
|
21
|
+
table.column_order = self.rendered_field_names
|
22
|
+
table.render_on(pdf)
|
23
|
+
end
|
24
|
+
post[pdf] if post
|
25
|
+
pdf.render
|
26
|
+
end
|
27
|
+
|
28
|
+
format_field_names { data.column_names }
|
29
|
+
|
30
|
+
renderer(:invoice) { pdf.render }
|
31
|
+
|
32
|
+
# Company Information in top lefthand corner
|
33
|
+
helper(:build_company_header, :engine => :invoice_engine) { |eng|
|
34
|
+
@tod = pdf.y
|
35
|
+
text_box(eng.company_info)
|
36
|
+
}
|
37
|
+
|
38
|
+
helper(:build_headers, :engine => :invoice_engine) { |eng|
|
39
|
+
build_company_header_helper(eng)
|
40
|
+
build_customer_header_helper(eng)
|
41
|
+
build_title_helper(eng)
|
42
|
+
build_order_header_helper(eng)
|
43
|
+
}
|
44
|
+
|
45
|
+
helper(:build_order_header, :engine => :invoice_engine) { |eng|
|
46
|
+
if eng.order_info
|
47
|
+
text_box(eng.order_info, :position => 350)
|
48
|
+
end
|
49
|
+
}
|
50
|
+
|
51
|
+
helper(:build_title, :engine => :invoice_engine) { |eng|
|
52
|
+
pdf.y = @tod
|
53
|
+
if eng.title
|
54
|
+
pdf.text eng.title, :left => 350, :font_size => 14
|
55
|
+
pdf.y -= 10
|
56
|
+
end
|
57
|
+
}
|
58
|
+
|
59
|
+
helper(:build_footer, :engine => :invoice_engine) { |eng|
|
60
|
+
# footer
|
61
|
+
pdf.open_object do |footer|
|
62
|
+
pdf.save_state
|
63
|
+
pdf.stroke_color! Color::RGB::Black
|
64
|
+
pdf.stroke_style! PDF::Writer::StrokeStyle::DEFAULT
|
65
|
+
if eng.comments
|
66
|
+
pdf.y -= 20
|
67
|
+
text_box eng.comments, :position => 110, :width => 400,
|
68
|
+
:font_size => 14
|
69
|
+
end
|
70
|
+
pdf.add_text_wrap( 50, 20, 200, "Printed at " +
|
71
|
+
Time.now.strftime("%H:%M %d/%m/%Y"), 8)
|
72
|
+
|
73
|
+
pdf.restore_state
|
74
|
+
pdf.close_object
|
75
|
+
pdf.add_object(footer, :all_pages)
|
76
|
+
end
|
77
|
+
pdf.stop_page_numbering(true, :current)
|
78
|
+
}
|
79
|
+
|
80
|
+
helper(:build_body, :engine => :invoice_engine) do
|
81
|
+
pdf.start_page_numbering(500, 20, 8, :right)
|
82
|
+
|
83
|
+
# order contents
|
84
|
+
pdf.y = 620
|
85
|
+
|
86
|
+
PDF::SimpleTable.new do |table|
|
87
|
+
table.width = 450
|
88
|
+
table.orientation = :center
|
89
|
+
table.data = data
|
90
|
+
table.show_lines = :outer
|
91
|
+
table.column_order = data.column_names
|
92
|
+
table.render_on(pdf)
|
93
|
+
table.font_size = 12
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Order details
|
98
|
+
helper(:build_customer_header, :engine => :invoice_engine) { |eng|
|
99
|
+
pdf.y -= 10
|
100
|
+
text_box(eng.customer_info)
|
101
|
+
}
|
102
|
+
|
103
|
+
def self.text_box(content,options={})
|
104
|
+
PDF::SimpleTable.new do |table|
|
105
|
+
table.data = content.to_a.inject([]) do |s,line|
|
106
|
+
s << { "value" => line }
|
107
|
+
end
|
108
|
+
table.column_order = "value"
|
109
|
+
table.show_headings = false
|
110
|
+
table.show_lines = :outer
|
111
|
+
table.shade_rows = :none
|
112
|
+
table.width = options[:width] || 200
|
113
|
+
table.orientation = options[:orientation] || :right
|
114
|
+
table.position = options[:position] || :left
|
115
|
+
table.font_size = options[:font_size] || 10
|
116
|
+
table.render_on(pdf)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
plugin_name :pdf
|
121
|
+
register_on :table_engine
|
122
|
+
register_on :invoice_engine
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Ruport
|
2
|
+
class Format::Plugin
|
3
|
+
class SVGPlugin < Format::Plugin
|
4
|
+
helper(:init_plugin) { |eng|
|
5
|
+
# check the supplied data can be used for graphing
|
6
|
+
data.each { |r|
|
7
|
+
if data.column_names.size != r.data.size
|
8
|
+
raise InvalidGraphDataError,
|
9
|
+
"Column names and data do not match"
|
10
|
+
end
|
11
|
+
r.data.each { |c|
|
12
|
+
begin
|
13
|
+
c = BigDecimal.new(c) unless c.kind_of?(Float) ||
|
14
|
+
c.kind_of?(Fixnum) || c.kind_of?(BigDecimal)
|
15
|
+
rescue
|
16
|
+
raise InvalidGraphDataError,
|
17
|
+
"Unable to convert #{c.to_s} into a number"
|
18
|
+
end
|
19
|
+
}
|
20
|
+
}
|
21
|
+
|
22
|
+
raise InvalidGraphOptionError,
|
23
|
+
'You must provide a width before rendering a graph' if eng.width.nil?
|
24
|
+
raise InvalidGraphOptionError,
|
25
|
+
'You must provide a height before rendering a graph' if eng.height.nil?
|
26
|
+
raise InvalidGraphOptionError,
|
27
|
+
'You must provide a style before rendering a graph' if eng.style.nil?
|
28
|
+
if eng.style != :area && eng.style != :bar &&
|
29
|
+
eng.style != :line &&
|
30
|
+
eng.style != :smiles &&
|
31
|
+
eng.style != :stacked
|
32
|
+
raise InvalidGraphOptionError, 'Invalid graph style'
|
33
|
+
end
|
34
|
+
|
35
|
+
require 'scruffy'
|
36
|
+
@graph = Scruffy::Graph.new
|
37
|
+
@graph.title = eng.title unless eng.title.nil?
|
38
|
+
@graph.theme = Scruffy::Themes::Mephisto.new
|
39
|
+
@graph.point_markers = @data.column_names
|
40
|
+
@graph_style = eng.style
|
41
|
+
@graph_width = eng.width
|
42
|
+
@graph_height = eng.height
|
43
|
+
}
|
44
|
+
|
45
|
+
renderer :graph do
|
46
|
+
|
47
|
+
data.each_with_index { |r,i|
|
48
|
+
@graph.add(@graph_style,
|
49
|
+
r.tags[0] || 'series ' + (i+1).to_s,
|
50
|
+
r.data)
|
51
|
+
}
|
52
|
+
|
53
|
+
# return the rendered graph
|
54
|
+
@graph.render(:size => [@graph_width, @graph_height])
|
55
|
+
end
|
56
|
+
|
57
|
+
plugin_name :svg
|
58
|
+
register_on :graph_engine
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Ruport
|
2
|
+
class Format::Plugin
|
3
|
+
class TextPlugin < Format::Plugin
|
4
|
+
rendering_options :erb_enabled => true, :red_cloth_enabled => false
|
5
|
+
|
6
|
+
renderer :document
|
7
|
+
|
8
|
+
renderer :table do
|
9
|
+
require "ruport/system_extensions"
|
10
|
+
|
11
|
+
th = "#{rendered_field_names}#{hr}"
|
12
|
+
|
13
|
+
data.each { |r|
|
14
|
+
r.each_with_index { |f,i|
|
15
|
+
r[i] = f.to_s.center(max_col_width(i))
|
16
|
+
}
|
17
|
+
}
|
18
|
+
|
19
|
+
a = data.inject(th){ |s,r|
|
20
|
+
s + "| #{r.to_a.join(' | ')} |\n"
|
21
|
+
} << hr
|
22
|
+
|
23
|
+
width = self.right_margin || SystemExtensions.terminal_width
|
24
|
+
|
25
|
+
a.to_a.each { |r|
|
26
|
+
r.gsub!(/\A.{#{width+1},}/) { |m| m[0,width-2] + ">>" }
|
27
|
+
}.join
|
28
|
+
end
|
29
|
+
|
30
|
+
format_field_names do
|
31
|
+
data.column_names.each_with_index { |f,i|
|
32
|
+
data.column_names[i] = f.to_s.center(max_col_width(i))
|
33
|
+
}
|
34
|
+
"#{hr}| #{data.column_names.to_a.join(' | ')} |\n"
|
35
|
+
end
|
36
|
+
|
37
|
+
action :max_col_width do |index|
|
38
|
+
f = data.column_names if data.respond_to? :column_names
|
39
|
+
d = Data::Table.new :column_names => f, :data => data
|
40
|
+
|
41
|
+
cw = d.map { |r| r[index].to_s.length }.max
|
42
|
+
|
43
|
+
return cw unless d.column_names
|
44
|
+
|
45
|
+
nw = (index.kind_of?(Integer) ? d.column_names[index] : index ).to_s.length
|
46
|
+
|
47
|
+
[cw,nw].max
|
48
|
+
end
|
49
|
+
|
50
|
+
action :table_width do
|
51
|
+
f = data.column_names if data.respond_to? :column_names
|
52
|
+
d = Data::Table.new:column_names => f, :data => data
|
53
|
+
|
54
|
+
f = d[0].attributes || (0...d[0].length)
|
55
|
+
|
56
|
+
f.inject(0) { |s,e| s + max_col_width(e) }
|
57
|
+
end
|
58
|
+
|
59
|
+
action :hr do
|
60
|
+
len = data[0].to_a.length * 3 + table_width + 1
|
61
|
+
"+" + "-"*(len-2) + "+\n"
|
62
|
+
end
|
63
|
+
|
64
|
+
attribute :right_margin
|
65
|
+
plugin_name :text
|
66
|
+
register_on :table_engine
|
67
|
+
register_on :document_engine
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
@@ -58,16 +58,16 @@ module Ruport
|
|
58
58
|
# A Unix savvy method to fetch the console columns, and rows.
|
59
59
|
def terminal_size
|
60
60
|
`stty size`.split.map { |x| x.to_i }.reverse
|
61
|
-
end
|
61
|
+
end
|
62
|
+
end
|
62
63
|
|
63
|
-
|
64
|
-
|
65
|
-
|
64
|
+
def terminal_width
|
65
|
+
terminal_size.first
|
66
|
+
end
|
66
67
|
|
67
|
-
|
68
|
-
|
69
|
-
end
|
70
|
-
|
68
|
+
def terminal_height
|
69
|
+
terminal_size.last
|
71
70
|
end
|
71
|
+
|
72
72
|
end
|
73
73
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
***************
|
2
|
+
*** 60,74 ****
|
3
|
+
`stty size`.split.map { |x| x.to_i }.reverse
|
4
|
+
end
|
5
|
+
|
6
|
+
- end
|
7
|
+
-
|
8
|
+
- def terminal_width
|
9
|
+
- terminal_size.first
|
10
|
+
- end
|
11
|
+
-
|
12
|
+
- def terminal_height
|
13
|
+
- terminal_size.last
|
14
|
+
- end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
--- 60,73 ----
|
19
|
+
`stty size`.split.map { |x| x.to_i }.reverse
|
20
|
+
end
|
21
|
+
|
22
|
+
+ def terminal_width
|
23
|
+
+ terminal_size.first
|
24
|
+
+ end
|
25
|
+
|
26
|
+
+ def terminal_height
|
27
|
+
+ terminal_size.last
|
28
|
+
+ end
|
29
|
+
+
|
30
|
+
+ end
|
31
|
+
end
|
32
|
+
end
|
data/test/test_collection.rb
CHANGED
@@ -36,4 +36,9 @@ class TestCollection < Test::Unit::TestCase
|
|
36
36
|
assert_equal @ghost_collection.to_csv, @ghost_collection.as(:csv)
|
37
37
|
end
|
38
38
|
|
39
|
+
def test_invalid_plugin_throws_an_error
|
40
|
+
assert_raise(InvalidPluginError) { @ghost_collection.to_papyrus_scroll }
|
41
|
+
assert_raise(InvalidPluginError) { @ghost_collection.as(:stone_tablet)}
|
42
|
+
end
|
43
|
+
|
39
44
|
end
|
data/test/unit.log
CHANGED
@@ -4718,3 +4718,15 @@ F, [2006-08-28T14:56:51.239122 #2797] FATAL -- : no block given!
|
|
4718
4718
|
F, [2006-08-28T15:03:28.339719 #2839] FATAL -- : Missing host for mailer bar
|
4719
4719
|
F, [2006-08-28T15:03:28.340989 #2839] FATAL -- : Missing DSN for source foo!
|
4720
4720
|
F, [2006-08-28T15:03:29.731996 #2839] FATAL -- : no block given!
|
4721
|
+
F, [2006-08-30T16:45:01.507510 #2290] FATAL -- : Missing host for mailer bar
|
4722
|
+
F, [2006-08-30T16:45:01.509091 #2290] FATAL -- : Missing DSN for source foo!
|
4723
|
+
F, [2006-08-30T16:45:02.931966 #2290] FATAL -- : no block given!
|
4724
|
+
F, [2006-08-31T18:50:43.741551 #2594] FATAL -- : Missing host for mailer bar
|
4725
|
+
F, [2006-08-31T18:50:43.823591 #2594] FATAL -- : Missing DSN for source foo!
|
4726
|
+
F, [2006-08-31T18:50:45.287417 #2594] FATAL -- : no block given!
|
4727
|
+
F, [2006-09-02T14:35:05.490338 #2499] FATAL -- : Missing host for mailer bar
|
4728
|
+
F, [2006-09-02T14:35:05.524813 #2499] FATAL -- : Missing DSN for source foo!
|
4729
|
+
F, [2006-09-02T14:35:07.162616 #2499] FATAL -- : no block given!
|
4730
|
+
F, [2006-09-02T14:41:36.925689 #2530] FATAL -- : Missing host for mailer bar
|
4731
|
+
F, [2006-09-02T14:41:36.927265 #2530] FATAL -- : Missing DSN for source foo!
|
4732
|
+
F, [2006-09-02T14:41:38.446094 #2530] FATAL -- : no block given!
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: ruport
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.5.
|
7
|
-
date: 2006-
|
6
|
+
version: 0.5.1
|
7
|
+
date: 2006-09-02 00:00:00 -04:00
|
8
8
|
summary: A generalized Ruby report generation and templating engine.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -47,6 +47,7 @@ files:
|
|
47
47
|
- lib/ruport/rails
|
48
48
|
- lib/ruport/data
|
49
49
|
- lib/ruport/report
|
50
|
+
- lib/ruport/system_extensions.rb.rej
|
50
51
|
- lib/ruport/system_extensions.rb
|
51
52
|
- lib/ruport/config.rb
|
52
53
|
- lib/ruport/query.rb
|
@@ -59,6 +60,17 @@ files:
|
|
59
60
|
- lib/ruport/query/sql_split.rb
|
60
61
|
- lib/ruport/format/plugin.rb
|
61
62
|
- lib/ruport/format/engine.rb
|
63
|
+
- lib/ruport/format/engine
|
64
|
+
- lib/ruport/format/plugin
|
65
|
+
- lib/ruport/format/engine/document.rb
|
66
|
+
- lib/ruport/format/engine/graph.rb
|
67
|
+
- lib/ruport/format/engine/invoice.rb
|
68
|
+
- lib/ruport/format/engine/table.rb
|
69
|
+
- lib/ruport/format/plugin/csv_plugin.rb
|
70
|
+
- lib/ruport/format/plugin/html_plugin.rb
|
71
|
+
- lib/ruport/format/plugin/pdf_plugin.rb
|
72
|
+
- lib/ruport/format/plugin/svg_plugin.rb
|
73
|
+
- lib/ruport/format/plugin/text_plugin.rb
|
62
74
|
- lib/ruport/rails/reportable.rb
|
63
75
|
- lib/ruport/data/taggable.rb
|
64
76
|
- lib/ruport/data/table.rb
|