ruport 0.4.19 → 0.4.21
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 +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?
|