ruport 0.4.19 → 0.4.21
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +19 -2
- data/Rakefile +10 -5
- data/examples/new_plugin.rb +2 -2
- data/lib/ruport.rb +2 -2
- data/lib/ruport/data.rb +1 -1
- data/lib/ruport/data/collection.rb +8 -0
- data/lib/ruport/data/record.rb +15 -2
- data/lib/ruport/data/set.rb +42 -0
- data/lib/ruport/data/table.rb +20 -0
- data/lib/ruport/format.rb +0 -6
- data/lib/ruport/format/engine.rb +18 -1
- data/lib/ruport/format/plugin.rb +84 -6
- data/lib/ruport/query.rb +5 -5
- data/lib/ruport/rails/reportable.rb +11 -14
- data/lib/ruport/report.rb +2 -8
- data/test/tc_database.rb +1 -1
- data/test/tc_format_engine.rb +5 -5
- data/test/tc_plugin.rb +12 -12
- data/test/tc_record.rb +32 -1
- data/test/tc_set.rb +93 -0
- data/test/tc_table.rb +27 -0
- data/test/ts_all.rb +3 -2
- data/test/unit.log +428 -0
- metadata +4 -4
- data/lib/ruport/data_row.rb +0 -187
- data/lib/ruport/data_set.rb +0 -308
data/CHANGELOG
CHANGED
@@ -1,6 +1,23 @@
|
|
1
|
-
The current version of Ruby Reports is 0.4.
|
1
|
+
The current version of Ruby Reports is 0.4.21
|
2
2
|
|
3
|
-
changes since 0.4.
|
3
|
+
changes since 0.4.19
|
4
|
+
- Added a rake task to run rcov
|
5
|
+
|
6
|
+
- Removed DataSet / DataRow. Farewell good friends
|
7
|
+
|
8
|
+
- Added Array#to_table, dropped Array#to_ds
|
9
|
+
|
10
|
+
- You can now convert Sets to Tables and vice-versa
|
11
|
+
|
12
|
+
- acts_as_reportable converted to use Data::Table
|
13
|
+
|
14
|
+
- Data::Record's constructor can now take a hash
|
15
|
+
|
16
|
+
- Added initial Data::Set implementation
|
17
|
+
|
18
|
+
- Added smart Data::Table.to_format method_missing functionality
|
19
|
+
|
20
|
+
changes since 0.4.17
|
4
21
|
|
5
22
|
- Made method_missing a lot more friendly all around.
|
6
23
|
|
data/Rakefile
CHANGED
@@ -12,8 +12,7 @@ end
|
|
12
12
|
LEAN=false
|
13
13
|
dir = File.dirname(__FILE__)
|
14
14
|
lib = File.join(dir, "lib", "ruport.rb")
|
15
|
-
version = File.read(lib)[/^\s*VERSION\s*=\s*(['"])(\d+\.\d+\.d+)
|
16
|
-
|
15
|
+
version = File.read(lib)[/^\s*VERSION\s*=\s*(['"])(\d+\.\d+\.d+)['"]/,1]
|
17
16
|
task :default => [:test]
|
18
17
|
|
19
18
|
Rake::TestTask.new do |test|
|
@@ -24,7 +23,7 @@ end
|
|
24
23
|
|
25
24
|
spec = Gem::Specification.new do |spec|
|
26
25
|
spec.name = LEAN ? "lean-ruport" : "ruport"
|
27
|
-
spec.version = "0.4.
|
26
|
+
spec.version = "0.4.21"
|
28
27
|
spec.platform = Gem::Platform::RUBY
|
29
28
|
spec.summary = "A generalized Ruby report generation and templating engine."
|
30
29
|
spec.files = Dir.glob("{examples,lib,test}/**/**/*") +
|
@@ -66,10 +65,16 @@ Rake::RDocTask.new do |rdoc|
|
|
66
65
|
rdoc.title = "Ruport Documentation"
|
67
66
|
end
|
68
67
|
|
69
|
-
|
70
68
|
Rake::GemPackageTask.new(spec) do |pkg|
|
71
69
|
pkg.need_zip = true
|
72
70
|
pkg.need_tar = true
|
73
71
|
end
|
74
72
|
|
75
|
-
|
73
|
+
begin
|
74
|
+
require 'rcov/rcovtask'
|
75
|
+
Rcov::RcovTask.new do |t|
|
76
|
+
t.test_files = [ "test/ts_all.rb" ]
|
77
|
+
end
|
78
|
+
rescue LoadError
|
79
|
+
nil
|
80
|
+
end
|
data/examples/new_plugin.rb
CHANGED
@@ -11,11 +11,11 @@ class Format::Plugin::Text < Ruport::Format::Plugin
|
|
11
11
|
end
|
12
12
|
|
13
13
|
format_field_names do
|
14
|
-
data.
|
14
|
+
data.column_names.join("---") << "\n"
|
15
15
|
end
|
16
16
|
|
17
17
|
register_on :table_engine
|
18
18
|
end
|
19
19
|
|
20
|
-
puts Format.table({ :data => [[1,2],[3,4]].
|
20
|
+
puts Format.table({ :data => [[1,2],[3,4]].to_table(:column_names =>%w[a b]),
|
21
21
|
:plugin => :text })
|
data/lib/ruport.rb
CHANGED
@@ -14,7 +14,7 @@ module Ruport
|
|
14
14
|
|
15
15
|
#begin; require 'rubygems'; rescue LoadError; nil end
|
16
16
|
|
17
|
-
VERSION = "0.4.
|
17
|
+
VERSION = "0.4.21"
|
18
18
|
|
19
19
|
# Ruports logging and error interface.
|
20
20
|
# Can generate warnings or raise fatal errors
|
@@ -67,6 +67,6 @@ module Ruport
|
|
67
67
|
end
|
68
68
|
|
69
69
|
|
70
|
-
%w[config meta_tools report format query
|
70
|
+
%w[config meta_tools report format query data].each { |lib|
|
71
71
|
require "ruport/#{lib}"
|
72
72
|
}
|
data/lib/ruport/data.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
%w[taggable record collection table].each { |l| require "ruport/data/#{l}" }
|
1
|
+
%w[taggable record collection table set].each { |l| require "ruport/data/#{l}" }
|
@@ -12,7 +12,15 @@ module Ruport::Data
|
|
12
12
|
def as(type)
|
13
13
|
Ruport::Format.table :data => self, :plugin => type
|
14
14
|
end
|
15
|
+
|
16
|
+
def to_set
|
17
|
+
Set.new :data => data
|
18
|
+
end
|
15
19
|
|
20
|
+
def to_table(options={})
|
21
|
+
Table.new({:data => data.map { |r| r.to_a }}.merge(options))
|
22
|
+
end
|
23
|
+
|
16
24
|
attr_reader :data
|
17
25
|
def_delegators :@data, :each, :length, :[], :empty?
|
18
26
|
end
|
data/lib/ruport/data/record.rb
CHANGED
@@ -6,8 +6,17 @@ module Ruport::Data
|
|
6
6
|
include Taggable
|
7
7
|
|
8
8
|
def initialize(data,options={})
|
9
|
-
|
10
|
-
|
9
|
+
if data.kind_of?(Hash)
|
10
|
+
if options[:attributes]
|
11
|
+
@attributes = options[:attributes]
|
12
|
+
@data = options[:attributes].map { |k| data[k] }
|
13
|
+
else
|
14
|
+
@attributes, @data = data.to_a.transpose
|
15
|
+
end
|
16
|
+
else
|
17
|
+
@data = data.dup
|
18
|
+
@attributes = options[:attributes]
|
19
|
+
end
|
11
20
|
end
|
12
21
|
|
13
22
|
attr_reader :data
|
@@ -67,6 +76,10 @@ module Ruport::Data
|
|
67
76
|
#FIXME: This does not take into account frozen / tainted state
|
68
77
|
alias_method :clone, :dup
|
69
78
|
|
79
|
+
def hash
|
80
|
+
(attributes.to_a + data.to_a).hash
|
81
|
+
end
|
82
|
+
|
70
83
|
def method_missing(id,*args)
|
71
84
|
id = id.to_s.gsub(/=$/,"")
|
72
85
|
if @attributes.include?(id)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Ruport::Data
|
4
|
+
class Set < Collection
|
5
|
+
|
6
|
+
def initialize(options={})
|
7
|
+
@data = ::Set.new
|
8
|
+
options[:data].each {|e| self << e} if options[:data]
|
9
|
+
end
|
10
|
+
|
11
|
+
def <<(other)
|
12
|
+
case other
|
13
|
+
when Record
|
14
|
+
@data << other
|
15
|
+
when Array
|
16
|
+
@data << Record.new(other)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def ==(other)
|
21
|
+
@data == other.data
|
22
|
+
end
|
23
|
+
|
24
|
+
def |(other)
|
25
|
+
Set.new :data => (@data | other.data)
|
26
|
+
end
|
27
|
+
alias_method :union, :|
|
28
|
+
|
29
|
+
def &(other)
|
30
|
+
Set.new :data => (@data & other.data)
|
31
|
+
end
|
32
|
+
alias_method :intersection, :&
|
33
|
+
|
34
|
+
# Set difference
|
35
|
+
def -(other)
|
36
|
+
Set.new :data => (@data - other.data)
|
37
|
+
end
|
38
|
+
alias_method :difference, :-
|
39
|
+
|
40
|
+
def_delegators :@data, :each
|
41
|
+
end
|
42
|
+
end
|
data/lib/ruport/data/table.rb
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
class Array
|
2
|
+
def to_table(options={})
|
3
|
+
Ruport::Data::Table.new({:data => self}.merge(options))
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
1
7
|
module Ruport::Data
|
2
8
|
class Table < Collection
|
3
9
|
def initialize(options={})
|
@@ -12,6 +18,15 @@ module Ruport::Data
|
|
12
18
|
@column_names = other.dup
|
13
19
|
end
|
14
20
|
|
21
|
+
def eql?(other)
|
22
|
+
data.eql?(other.data) && column_names.eql?(other.column_names)
|
23
|
+
end
|
24
|
+
alias_method :==, :eql?
|
25
|
+
|
26
|
+
def to_s
|
27
|
+
as(:text)
|
28
|
+
end
|
29
|
+
|
15
30
|
def <<(other)
|
16
31
|
case other
|
17
32
|
when Array
|
@@ -86,6 +101,11 @@ module Ruport::Data
|
|
86
101
|
Ruport::Data::Record.new data, :attributes => group
|
87
102
|
end
|
88
103
|
end
|
104
|
+
|
105
|
+
def method_missing(id,*args)
|
106
|
+
return as($1.to_sym) if id.to_s =~ /^to_(.*)/
|
107
|
+
super
|
108
|
+
end
|
89
109
|
|
90
110
|
end
|
91
111
|
end
|
data/lib/ruport/format.rb
CHANGED
@@ -19,17 +19,11 @@ module Ruport
|
|
19
19
|
# There are three main sets of functionality the Ruport::Format model provides.
|
20
20
|
# * Structured printable document support ( Format::Document and friends)
|
21
21
|
# * Text filter support ( Report#render and the Format class)
|
22
|
-
# * Support for DataSet Formatting ( Format::Builder)
|
23
22
|
#
|
24
23
|
# The support for structured printable documents is currently geared towards PDF
|
25
24
|
# support and needs some additional work to be truly useful. Suggestions would
|
26
25
|
# be much appreciated.
|
27
26
|
#
|
28
|
-
# Format::Builder lets you define functions that will be used via DataSet#as
|
29
|
-
# This is primary geared towards tabular data output, but there is no reason why
|
30
|
-
# DataSet#as and the <tt>render_foo</tt> methods of Format::Builder cannot be
|
31
|
-
# adapted to fit whatever needs you may need.
|
32
|
-
#
|
33
27
|
# The filters implemented in the Format class are meant to process strings or
|
34
28
|
# entire templates. The Format class will soon automatically build a
|
35
29
|
# Ruport::Parser for any string input. By default, filters are provided to
|
data/lib/ruport/format/engine.rb
CHANGED
@@ -94,6 +94,23 @@ module Ruport
|
|
94
94
|
private_class_method :new
|
95
95
|
end
|
96
96
|
|
97
|
+
class Format::Engine::Invoice < Ruport::Format::Engine
|
98
|
+
|
99
|
+
# order meta data
|
100
|
+
attributes [:customer_info, :company_info]
|
101
|
+
|
102
|
+
renderer do
|
103
|
+
super
|
104
|
+
build_company_header
|
105
|
+
build_customer_header
|
106
|
+
active_plugin.render_invoice
|
107
|
+
end
|
108
|
+
|
109
|
+
alias_engine Invoice, :invoice_engine
|
110
|
+
Ruport::Format.build_interface_for Invoice, :invoice
|
111
|
+
|
112
|
+
end
|
113
|
+
|
97
114
|
class Format::Engine::Table < Format::Engine
|
98
115
|
|
99
116
|
renderer do
|
@@ -109,7 +126,7 @@ module Ruport
|
|
109
126
|
def rewrite_column(key,&block)
|
110
127
|
data.each { |r| r[key] = block[r] }
|
111
128
|
end
|
112
|
-
|
129
|
+
|
113
130
|
def num_cols
|
114
131
|
data[0].to_a.length
|
115
132
|
end
|
data/lib/ruport/format/plugin.rb
CHANGED
@@ -106,7 +106,7 @@ module Ruport
|
|
106
106
|
|
107
107
|
action :max_col_width do |index|
|
108
108
|
f = data.column_names if data.respond_to? :column_names
|
109
|
-
d =
|
109
|
+
d = Data::Table.new :column_names => f, :data => data
|
110
110
|
|
111
111
|
cw = d.map { |r| r[index].to_s.length }.max
|
112
112
|
|
@@ -119,9 +119,11 @@ module Ruport
|
|
119
119
|
|
120
120
|
action :table_width do
|
121
121
|
f = data.column_names if data.respond_to? :column_names
|
122
|
-
d =
|
122
|
+
d = Data::Table.new:column_names => f, :data => data
|
123
123
|
|
124
|
-
d[0].attributes
|
124
|
+
f = d[0].attributes || (0...d[0].length)
|
125
|
+
|
126
|
+
f.inject(0) { |s,e| s + max_col_width(e) }
|
125
127
|
end
|
126
128
|
|
127
129
|
action :hr do
|
@@ -136,10 +138,16 @@ module Ruport
|
|
136
138
|
end
|
137
139
|
|
138
140
|
class PDFPlugin < Format::Plugin
|
139
|
-
|
141
|
+
attribute :pdf
|
142
|
+
attribute :paper
|
143
|
+
|
144
|
+
helper(:init_plugin) {
|
145
|
+
require "pdf/writer"
|
146
|
+
require "pdf/simpletable"
|
147
|
+
self.pdf = PDF::Writer.new( :paper => paper || "LETTER" )
|
148
|
+
}
|
149
|
+
|
140
150
|
renderer :table do
|
141
|
-
require "pdf/writer"; require "pdf/simpletable";
|
142
|
-
pdf = PDF::Writer.new
|
143
151
|
pre[pdf] if pre
|
144
152
|
PDF::SimpleTable.new do |table|
|
145
153
|
table.maximum_width = 500
|
@@ -156,8 +164,78 @@ module Ruport
|
|
156
164
|
|
157
165
|
format_field_names { data.column_names }
|
158
166
|
|
167
|
+
renderer :invoice do
|
168
|
+
return unless defined? PDF::Writer
|
169
|
+
|
170
|
+
pdf.start_page_numbering(500, 20, 8, :right)
|
171
|
+
|
172
|
+
# order contents
|
173
|
+
pdf.y = 620
|
174
|
+
|
175
|
+
PDF::SimpleTable.new do |table|
|
176
|
+
table.width = 450
|
177
|
+
table.orientation = :center
|
178
|
+
table.data = data
|
179
|
+
table.show_lines = :outer
|
180
|
+
table.column_order = data.column_names
|
181
|
+
table.render_on(pdf)
|
182
|
+
table.font_size = 12
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
# footer
|
187
|
+
pdf.open_object do |footer|
|
188
|
+
pdf.save_state
|
189
|
+
pdf.stroke_color! Color::Black
|
190
|
+
pdf.stroke_style! PDF::Writer::StrokeStyle::DEFAULT
|
191
|
+
|
192
|
+
pdf.add_text_wrap( 50, 20, 200, "Printed at " +
|
193
|
+
Time.now.strftime("%H:%M %d/%m/%Y"), 8)
|
194
|
+
|
195
|
+
pdf.restore_state
|
196
|
+
pdf.close_object
|
197
|
+
pdf.add_object(footer, :all_pages)
|
198
|
+
end
|
199
|
+
|
200
|
+
pdf.stop_page_numbering(true, :current)
|
201
|
+
pdf.render
|
202
|
+
end
|
203
|
+
|
204
|
+
# Company Information in top lefthand corner
|
205
|
+
helper(:build_company_header) { |eng|
|
206
|
+
text_box(eng.company_info)
|
207
|
+
}
|
208
|
+
|
209
|
+
|
210
|
+
|
211
|
+
helper(:build_order_helper) { }
|
212
|
+
|
213
|
+
# Order details
|
214
|
+
helper(:build_customer_header) { |eng|
|
215
|
+
pdf.y -= 10
|
216
|
+
text_box(eng.customer_info)
|
217
|
+
}
|
218
|
+
|
219
|
+
def self.text_box(content,options={})
|
220
|
+
PDF::SimpleTable.new do |table|
|
221
|
+
table.data = content.to_a.inject([]) do |s,line|
|
222
|
+
s << { "value" => line }
|
223
|
+
end
|
224
|
+
table.column_order = "value"
|
225
|
+
table.show_headings = false
|
226
|
+
table.show_lines = :outer
|
227
|
+
table.shade_rows = :none
|
228
|
+
table.width = options[:width] || 200
|
229
|
+
table.orientation = options[:orientation] || :right
|
230
|
+
table.position = options[:position] || :left
|
231
|
+
table.font_size = options[:font_size] || 10
|
232
|
+
table.render_on(pdf)
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
159
236
|
plugin_name :pdf
|
160
237
|
register_on :table_engine
|
238
|
+
register_on :invoice_engine
|
161
239
|
end
|
162
240
|
|
163
241
|
class HTMLPlugin < Format::Plugin
|
data/lib/ruport/query.rb
CHANGED
@@ -4,7 +4,7 @@ require "ruport/query/sql_split"
|
|
4
4
|
module Ruport
|
5
5
|
|
6
6
|
# Query offers a way to interact with databases via DBI. It supports
|
7
|
-
# returning result sets in either Ruport's native
|
7
|
+
# returning result sets in either Ruport's native Data::Table, or in their raw
|
8
8
|
# form as DBI::Rows.
|
9
9
|
#
|
10
10
|
# It offers basic caching support, the ability to instantiate a generator for
|
@@ -25,7 +25,7 @@ module Ruport
|
|
25
25
|
# This kind of laziness is supposed to be A Good Thing, and
|
26
26
|
# as long as you keep it in mind, it should not cause any problems.
|
27
27
|
#
|
28
|
-
# The SQL can be single or multistatement, but the resulting
|
28
|
+
# The SQL can be single or multistatement, but the resulting Data::Table will
|
29
29
|
# consist only of the result of the last statement which returns something.
|
30
30
|
#
|
31
31
|
# Options:
|
@@ -106,7 +106,7 @@ module Ruport
|
|
106
106
|
fetch &action
|
107
107
|
end
|
108
108
|
|
109
|
-
# Grabs the result set as a
|
109
|
+
# Grabs the result set as a Data::Table or if in raw_data mode, an array of
|
110
110
|
# DBI:Row objects
|
111
111
|
def result; fetch; end
|
112
112
|
|
@@ -137,7 +137,7 @@ module Ruport
|
|
137
137
|
@cache_enabled = false
|
138
138
|
end
|
139
139
|
|
140
|
-
# Returns a
|
140
|
+
# Returns a Data::Table, even if in raw_data mode
|
141
141
|
# Does not work with raw data if cache is enabled and filled
|
142
142
|
def to_dataset
|
143
143
|
data_flag, @raw_data = @raw_data, false
|
@@ -160,7 +160,7 @@ module Ruport
|
|
160
160
|
|
161
161
|
require "dbi"
|
162
162
|
|
163
|
-
data = @raw_data ? [] :
|
163
|
+
data = @raw_data ? [] : Data::Table.new
|
164
164
|
DBI.connect(@dsn, @user, @password) do |dbh|
|
165
165
|
dbh.execute(query_text) do |sth|
|
166
166
|
return unless sth.fetchable?
|