ruport 0.6.0 → 0.6.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/AUTHORS +4 -0
- data/CHANGELOG +10 -1
- data/Rakefile +1 -1
- data/examples/simple_table_interface.rb +20 -0
- data/lib/ruport.rb +87 -69
- data/lib/ruport/attempt.rb +1 -1
- data/lib/ruport/config.rb +198 -166
- data/lib/ruport/data.rb +6 -1
- data/lib/ruport/data/collection.rb +15 -8
- data/lib/ruport/data/groupable.rb +68 -6
- data/lib/ruport/data/record.rb +73 -34
- data/lib/ruport/data/record.rb~ +236 -0
- data/lib/ruport/data/set.rb +48 -13
- data/lib/ruport/data/table.rb +164 -74
- data/lib/ruport/data/table.rb.rej +67 -0
- data/lib/ruport/data/table.rb~ +153 -68
- data/lib/ruport/data/taggable.rb +37 -9
- data/lib/ruport/format.rb +1 -1
- data/lib/ruport/mailer.rb +41 -27
- data/lib/ruport/meta_tools.rb +26 -11
- data/lib/ruport/query.rb +102 -68
- data/lib/ruport/query/sql_split.rb +1 -1
- data/lib/ruport/report.rb +84 -58
- data/lib/ruport/report/graph.rb +1 -1
- data/lib/ruport/report/invoice.rb +1 -1
- data/test/test_query.rb +305 -48
- data/test/test_query.rb.rej +161 -0
- data/test/test_query.rb~ +337 -0
- data/test/test_record.rb +6 -0
- data/test/test_table.rb +18 -0
- data/test/test_table.rb~ +336 -0
- data/test/unit.log +180 -6
- metadata +8 -2
@@ -0,0 +1,67 @@
|
|
1
|
+
***************
|
2
|
+
*** 287,294 ****
|
3
|
+
# # pass in FasterCSV options, such as column separators
|
4
|
+
# table = Table.load('mydata.csv',:csv_opts => { :col_sep => "\t" })
|
5
|
+
#
|
6
|
+
- def self.load(csv_file, options={})
|
7
|
+
- get_table_from_csv(:foreach, csv_file, options)
|
8
|
+
end
|
9
|
+
|
10
|
+
#
|
11
|
+
--- 287,294 ----
|
12
|
+
# # pass in FasterCSV options, such as column separators
|
13
|
+
# table = Table.load('mydata.csv',:csv_opts => { :col_sep => "\t" })
|
14
|
+
#
|
15
|
+
+ def self.load(csv_file, options={},&block)
|
16
|
+
+ get_table_from_csv(:foreach, csv_file, options,&block)
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
***************
|
21
|
+
*** 297,307 ****
|
22
|
+
#
|
23
|
+
# table = Table.parse("a,b,c\n1,2,3\n4,5,6\n")
|
24
|
+
#
|
25
|
+
- def self.parse(string, options={})
|
26
|
+
- get_table_from_csv(:parse,string,options)
|
27
|
+
end
|
28
|
+
|
29
|
+
- def self.get_table_from_csv(msg,param,options={}) #:nodoc:
|
30
|
+
options = {:has_names => true,
|
31
|
+
:csv_options => {} }.merge(options)
|
32
|
+
require "fastercsv"
|
33
|
+
--- 297,307 ----
|
34
|
+
#
|
35
|
+
# table = Table.parse("a,b,c\n1,2,3\n4,5,6\n")
|
36
|
+
#
|
37
|
+
+ def self.parse(string, options={},&block)
|
38
|
+
+ get_table_from_csv(:parse,string,options,&block)
|
39
|
+
end
|
40
|
+
|
41
|
+
+ def self.get_table_from_csv(msg,param,options={},&block) #:nodoc:
|
42
|
+
options = {:has_names => true,
|
43
|
+
:csv_options => {} }.merge(options)
|
44
|
+
require "fastercsv"
|
45
|
+
***************
|
46
|
+
*** 312,321 ****
|
47
|
+
if first_line && options[:has_names]
|
48
|
+
loaded_data.column_names = row
|
49
|
+
first_line = false
|
50
|
+
- elsif !block_given?
|
51
|
+
loaded_data << row
|
52
|
+
else
|
53
|
+
- yield(loaded_data,row)
|
54
|
+
end
|
55
|
+
end ; loaded_data
|
56
|
+
end
|
57
|
+
--- 312,321 ----
|
58
|
+
if first_line && options[:has_names]
|
59
|
+
loaded_data.column_names = row
|
60
|
+
first_line = false
|
61
|
+
+ elsif !block
|
62
|
+
loaded_data << row
|
63
|
+
else
|
64
|
+
+ block[loaded_data,row]
|
65
|
+
end
|
66
|
+
end ; loaded_data
|
67
|
+
end
|
data/lib/ruport/data/table.rb~
CHANGED
@@ -5,10 +5,14 @@
|
|
5
5
|
# Copyright 2006 by respective content owners, all rights reserved.
|
6
6
|
|
7
7
|
class Array
|
8
|
+
|
9
|
+
#
|
8
10
|
# Converts an array to a Ruport::Data::Table object, ready to
|
9
11
|
# use in your reports.
|
10
12
|
#
|
13
|
+
# Example:
|
11
14
|
# [[1,2],[3,4]].to_table(%w[a b])
|
15
|
+
#
|
12
16
|
def to_table(options={})
|
13
17
|
options = { :column_names => options } if options.kind_of? Array
|
14
18
|
Ruport::Data::Table.new({:data => self}.merge(options))
|
@@ -16,7 +20,10 @@ class Array
|
|
16
20
|
end
|
17
21
|
|
18
22
|
module Ruport::Data
|
19
|
-
|
23
|
+
|
24
|
+
#
|
25
|
+
# === Overview
|
26
|
+
#
|
20
27
|
# This class is one of the core classes for building and working with data
|
21
28
|
# in Ruport. The idea is to get your data into a standard form, regardless
|
22
29
|
# of its source (a database, manual arrays, ActiveRecord, CSVs, etc.).
|
@@ -28,17 +35,21 @@ module Ruport::Data
|
|
28
35
|
# Once your data is in a Ruport::Data::Table object, it can be manipulated
|
29
36
|
# to suit your needs, then used to build a report.
|
30
37
|
#
|
31
|
-
# Included in this class are methods to create Tables manually and from CSV
|
32
|
-
# files.
|
33
|
-
#
|
34
|
-
# For building a table using ActiveRecord, have a look at Ruport::Reportable.
|
35
38
|
class Table < Collection
|
36
|
-
|
39
|
+
include Groupable
|
40
|
+
|
41
|
+
#
|
37
42
|
# Creates a new table based on the supplied options.
|
38
|
-
# Valid options
|
43
|
+
# Valid options:
|
44
|
+
# <b><tt>:data</tt></b>:: An Array of Arrays representing the
|
45
|
+
# records in this Table
|
46
|
+
# <b><tt>:column_names</tt></b>:: An Array containing the column names
|
47
|
+
# for this Table.
|
48
|
+
# Example:
|
49
|
+
#
|
50
|
+
# table = Table.new :data => [[1,2,3], [3,4,5]],
|
51
|
+
# :column_names => %w[a b c]
|
39
52
|
#
|
40
|
-
# table = Table.new({:data => [1,2,3], [3,4,5],
|
41
|
-
# :column_names => %w[a b c]})
|
42
53
|
def initialize(options={})
|
43
54
|
@column_names = options[:column_names] ? options[:column_names].dup : []
|
44
55
|
@data = []
|
@@ -52,44 +63,70 @@ module Ruport::Data
|
|
52
63
|
end
|
53
64
|
end
|
54
65
|
|
66
|
+
# This Table's column names.
|
55
67
|
attr_reader :column_names
|
56
68
|
def_delegator :@data, :[]
|
57
|
-
|
58
|
-
#
|
69
|
+
|
70
|
+
#
|
71
|
+
# Sets the column names for this table. <tt>new_column_names</tt> should
|
72
|
+
# be an array listing the names of the columns.
|
59
73
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
|
74
|
+
# Example:
|
75
|
+
#
|
76
|
+
# table = Table.new :data => [1,2,3], [3,4,5],
|
77
|
+
# :column_names => %w[a b c]
|
78
|
+
#
|
79
|
+
# table.column_names = %w[e f g]
|
80
|
+
#
|
81
|
+
def column_names=(new_column_names)
|
63
82
|
@column_names.replace(other.dup)
|
64
83
|
end
|
65
84
|
|
66
|
-
# Compares this table to another table and returns true if
|
67
|
-
# both the data and column names are equal
|
68
85
|
#
|
69
|
-
#
|
70
|
-
#
|
86
|
+
# Compares this Table to another Table and returns <tt>true</tt> if
|
87
|
+
# both the <tt>data</tt> and <tt>column_names</tt> are equal.
|
88
|
+
#
|
89
|
+
# Example:
|
90
|
+
#
|
91
|
+
# one = Table.new :data => [1,2], [3,4],
|
92
|
+
# :column_names => %w[a b]
|
93
|
+
#
|
94
|
+
# two = Table.new :data => [1,2], [3,4],
|
95
|
+
# :column_names => %w[a b]
|
96
|
+
#
|
71
97
|
# one.eql?(two) #=> true
|
98
|
+
#
|
72
99
|
def eql?(other)
|
73
100
|
data.eql?(other.data) && column_names.eql?(other.column_names)
|
74
101
|
end
|
75
102
|
|
76
103
|
alias_method :==, :eql?
|
77
104
|
|
78
|
-
#
|
105
|
+
#
|
106
|
+
# Uses Ruport's built-in text plugin to render this Table into a String
|
107
|
+
#
|
108
|
+
# Example:
|
79
109
|
#
|
80
|
-
# data = Table.new
|
110
|
+
# data = Table.new :data => [1,2], [3,4],
|
111
|
+
# :column_names => %w[a b]
|
81
112
|
# puts data.to_s
|
113
|
+
#
|
82
114
|
def to_s
|
83
115
|
as(:text)
|
84
116
|
end
|
85
117
|
|
86
|
-
# Used to add extra data to the table. The single parameter can be an
|
87
|
-
# Array, Hash or Ruport::Data::Record.
|
88
118
|
#
|
89
|
-
#
|
119
|
+
# Used to add extra data to the Table. <tt>other</tt> can be an Array,
|
120
|
+
# Hash or Record.
|
121
|
+
#
|
122
|
+
# Example:
|
123
|
+
#
|
124
|
+
# data = Table.new :data => [1,2], [3,4],
|
125
|
+
# :column_names => %w[a b]
|
90
126
|
# data << [8,9]
|
91
127
|
# data << { :a => 4, :b => 5}
|
92
|
-
# data <<
|
128
|
+
# data << Record.new [5,6], :attributes => %w[a b]
|
129
|
+
#
|
93
130
|
def <<(other)
|
94
131
|
case other
|
95
132
|
when Array
|
@@ -108,24 +145,37 @@ module Ruport::Data
|
|
108
145
|
self
|
109
146
|
end
|
110
147
|
|
111
|
-
#
|
148
|
+
#
|
149
|
+
# Used to combine two Tables. Throws an ArgumentError if the Tables don't
|
112
150
|
# have identical columns.
|
113
151
|
#
|
114
|
-
#
|
115
|
-
#
|
152
|
+
# Example:
|
153
|
+
#
|
154
|
+
# inky = Table.new :data => [[1,2], [3,4]],
|
155
|
+
# :column_names => %w[a b]
|
156
|
+
#
|
157
|
+
# blinky = Table.new :data => [[5,6]],
|
158
|
+
# :column_names => %w[a b]
|
159
|
+
#
|
116
160
|
# sue = inky + blinky
|
117
161
|
# sue.data #=> [[1,2],[3,4],[5,6]]
|
118
|
-
|
162
|
+
#
|
119
163
|
def +(other)
|
120
164
|
raise ArgumentError unless other.column_names == @column_names
|
121
165
|
Table.new(:column_names => @column_names, :data => @data + other.data)
|
122
166
|
end
|
123
167
|
|
124
|
-
# Reorders the columns that exist in the table. Operates directly
|
125
|
-
# on this table.
|
126
168
|
#
|
127
|
-
#
|
169
|
+
# Reorders the columns that exist in the Table. Modifies this Table
|
170
|
+
# in-place.
|
171
|
+
#
|
172
|
+
# Example:
|
173
|
+
#
|
174
|
+
# data = Table.new :data => [1,2], [3,4],
|
175
|
+
# :column_names => %w[a b]
|
176
|
+
#
|
128
177
|
# data.reorder!([1,0])
|
178
|
+
#
|
129
179
|
def reorder!(*indices)
|
130
180
|
indices = indices[0] if indices[0].kind_of? Array
|
131
181
|
if @column_names && !@column_names.empty?
|
@@ -142,21 +192,34 @@ module Ruport::Data
|
|
142
192
|
}; self
|
143
193
|
end
|
144
194
|
|
145
|
-
# Returns a copy of the table with its columns in the requested order.
|
146
195
|
#
|
147
|
-
#
|
196
|
+
# Returns a copy of the Table with its columns in the requested order.
|
197
|
+
#
|
198
|
+
# Example:
|
199
|
+
#
|
200
|
+
# one = Table.new :data => [1,2], [3,4],
|
201
|
+
# :column_names => %w[a b]
|
202
|
+
#
|
148
203
|
# two = one.reorder!([1,0])
|
204
|
+
#
|
149
205
|
def reorder(*indices)
|
150
206
|
dup.reorder!(*indices)
|
151
207
|
end
|
152
208
|
|
153
|
-
#
|
154
|
-
#
|
155
|
-
#
|
156
|
-
#
|
209
|
+
#
|
210
|
+
# Adds an extra column to the Table. Required options:
|
211
|
+
#
|
212
|
+
# <b><tt>:name</tt></b>:: The new column's name
|
213
|
+
# <b><tt>:fill</tt></b>:: The default value to use for the column in
|
214
|
+
# existing rows.
|
157
215
|
#
|
158
|
-
#
|
159
|
-
#
|
216
|
+
# Example:
|
217
|
+
#
|
218
|
+
# data = Table.new :data => [1,2], [3,4],
|
219
|
+
# :column_names => %w[a b]
|
220
|
+
#
|
221
|
+
# data.append_column :name => 'new_column', :fill => 1
|
222
|
+
#
|
160
223
|
def append_column(options={})
|
161
224
|
self.column_names += [options[:name]] if options[:name]
|
162
225
|
if block_given?
|
@@ -166,17 +229,21 @@ module Ruport::Data
|
|
166
229
|
end; self
|
167
230
|
end
|
168
231
|
|
169
|
-
#
|
232
|
+
#
|
233
|
+
# Removes a column from the Table. Any values in the specified column are
|
170
234
|
# lost.
|
171
|
-
#
|
172
|
-
#
|
173
|
-
#
|
174
|
-
# data
|
175
|
-
#
|
176
|
-
#
|
235
|
+
#
|
236
|
+
# Example:
|
237
|
+
#
|
238
|
+
# data = Table.new :data => [[1,2], [3,4]], :column_names => %w[a b]
|
239
|
+
# data.append_column :name => 'new_column', :fill => 1
|
240
|
+
# data.remove_column :name => 'new_column'
|
241
|
+
# data == Table.new :data => [[1,2], [3,4]],
|
242
|
+
# :column_names => %w[a b] #=> true
|
177
243
|
# data = [[1,2],[3,4]].to_table
|
178
244
|
# data.remove_column(1)
|
179
245
|
# data.eql? [[1],[3]].to_table %w[a] #=> true
|
246
|
+
#
|
180
247
|
def remove_column(options={})
|
181
248
|
if options.kind_of? Integer
|
182
249
|
return reorder!((0...data[0].length).to_a - [options])
|
@@ -190,29 +257,38 @@ module Ruport::Data
|
|
190
257
|
reorder! column_names - [name]
|
191
258
|
end
|
192
259
|
|
193
|
-
# Create a shallow copy of the table: the same data elements are referenced
|
194
|
-
# by both the old and new table.
|
195
260
|
#
|
196
|
-
#
|
261
|
+
# Create a shallow copy of the Table: the same data elements are referenced
|
262
|
+
# by both the old and new Table.
|
263
|
+
#
|
264
|
+
# Example:
|
265
|
+
#
|
266
|
+
# one = Table.new :data => [1,2], [3,4],
|
267
|
+
# :column_names => %w[a b]
|
197
268
|
# two = one.dup
|
269
|
+
#
|
198
270
|
def dup
|
199
271
|
a = self.class.new(:data => @data, :column_names => @column_names)
|
200
272
|
a.tags = tags.dup
|
201
273
|
return a
|
202
274
|
end
|
203
275
|
|
204
|
-
#
|
276
|
+
#
|
277
|
+
# Loads a CSV file directly into a Table using the FasterCSV library.
|
278
|
+
#
|
279
|
+
# Example:
|
205
280
|
#
|
206
|
-
#
|
281
|
+
# table = Table.load('mydata.csv')
|
282
|
+
#
|
207
283
|
def self.load(csv_file, options={})
|
208
284
|
get_table_from_csv(:foreach, csv_file, options)
|
209
285
|
end
|
210
|
-
|
211
|
-
def self.parse(string, options={})
|
286
|
+
|
287
|
+
def self.parse(string, options={}) #:nodoc:
|
212
288
|
get_table_from_csv(:parse,string,options)
|
213
289
|
end
|
214
290
|
|
215
|
-
def self.get_table_from_csv(msg,param,options={})
|
291
|
+
def self.get_table_from_csv(msg,param,options={}) #:nodoc:
|
216
292
|
options = {:has_names => true,
|
217
293
|
:csv_options => {} }.merge(options)
|
218
294
|
require "fastercsv"
|
@@ -231,7 +307,8 @@ module Ruport::Data
|
|
231
307
|
end ; loaded_data
|
232
308
|
end
|
233
309
|
|
234
|
-
#
|
310
|
+
#
|
311
|
+
# Allows you to split Tables into multiple Tables for grouping.
|
235
312
|
#
|
236
313
|
# Example:
|
237
314
|
#
|
@@ -246,8 +323,8 @@ module Ruport::Data
|
|
246
323
|
# b.greg.eql? [[1,2,3],[7,8,9]].to_table(%w[a b c]) #=> true
|
247
324
|
# b["joe"].eql? [[2,3,4],[1,2,3]].to_table(%w[a b c]) #=> true
|
248
325
|
#
|
249
|
-
# You can also pass an
|
250
|
-
# the group will be joined by an underscore.
|
326
|
+
# You can also pass an Array to <tt>:group</tt>, and the resulting
|
327
|
+
# attributes in the group will be joined by an underscore.
|
251
328
|
#
|
252
329
|
# Example:
|
253
330
|
#
|
@@ -260,6 +337,7 @@ module Ruport::Data
|
|
260
337
|
# a.greg_brown.length #=> 2
|
261
338
|
# a["greg_gibson"].length #=> 1
|
262
339
|
# a.greg_brown[0].x #=> "foo"
|
340
|
+
#
|
263
341
|
def split(options={})
|
264
342
|
if options[:group].kind_of? Array
|
265
343
|
group = map { |r| options[:group].map { |e| r[e] } }.uniq
|
@@ -292,23 +370,22 @@ module Ruport::Data
|
|
292
370
|
end; rec
|
293
371
|
end
|
294
372
|
|
295
|
-
#
|
373
|
+
#
|
374
|
+
# Calculates sums. If a column name or index is given, it will try to
|
296
375
|
# convert each element of that column to an integer or float
|
297
|
-
# and add
|
298
|
-
#
|
299
|
-
# If a block is given, yields each Record so that you can do a calculation.
|
376
|
+
# and add them together.
|
300
377
|
#
|
378
|
+
# If a block is given, it yields each Record so that you can do your own
|
379
|
+
# calculation.
|
301
380
|
#
|
302
381
|
# Example:
|
303
382
|
#
|
383
|
+
# table = [[1,2],[3,4],[5,6]].to_table(%w[col1 col2])
|
384
|
+
# table.sigma("col1") #=> 9
|
385
|
+
# table.sigma(0) #=> 9
|
386
|
+
# table.sigma { |r| r.col1 + r.col2 } #=> 21
|
387
|
+
# table.sigma { |r| r.col2 + 1 } #=> 15
|
304
388
|
#
|
305
|
-
# table = [[1,2],[3,4],[5,6]].to_table(%w[col1 col2])
|
306
|
-
# table.sigma("col1") #=> 9
|
307
|
-
# table.sigma(0) #=> 9
|
308
|
-
# table.sigma { |r| r.col1 + r.col2 } #=> 21
|
309
|
-
# table.sigma { |r| r.col2 + 1 } #=> 15
|
310
|
-
#
|
311
|
-
# For the non-mathy, this has been aliased as Table#sum
|
312
389
|
def sigma(column=nil)
|
313
390
|
inject(0) { |s,r|
|
314
391
|
if column
|
@@ -326,4 +403,12 @@ module Ruport::Data
|
|
326
403
|
alias_method :sum, :sigma
|
327
404
|
|
328
405
|
end
|
406
|
+
|
407
|
+
end
|
408
|
+
|
409
|
+
module Ruport::Data::TableHelper #:nodoc:
|
410
|
+
def table(names=[])
|
411
|
+
t = [].to_table(names)
|
412
|
+
yield(t) if block_given?; t
|
413
|
+
end
|
329
414
|
end
|
data/lib/ruport/data/taggable.rb
CHANGED
@@ -5,38 +5,66 @@
|
|
5
5
|
# Copyright 2006 by respective content owners, all rights reserved.
|
6
6
|
module Ruport::Data
|
7
7
|
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
8
|
+
#
|
9
|
+
# === Overview
|
10
|
+
#
|
11
|
+
# This module provides a simple mechanism for tagging arbitrary objects.
|
12
|
+
# It provides the necessary methods to set and retrieve tags which can
|
13
|
+
# consist of any Ruby object.
|
14
|
+
#
|
12
15
|
module Taggable
|
13
16
|
|
14
|
-
#
|
17
|
+
#
|
18
|
+
# Adds a tag to the object.
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
#
|
15
22
|
# taggable_obj.tag :spiffy
|
23
|
+
#
|
16
24
|
def tag(tag_name)
|
17
25
|
tags << tag_name unless has_tag? tag_name
|
18
26
|
end
|
19
27
|
|
20
|
-
#
|
28
|
+
#
|
29
|
+
# Removes a tag from the object.
|
30
|
+
#
|
31
|
+
# Example:
|
32
|
+
#
|
21
33
|
# taggable_obj.delete_tag :not_so_spiffy
|
34
|
+
#
|
22
35
|
def delete_tag(tag_name)
|
23
36
|
tags.delete tag_name
|
24
37
|
end
|
25
38
|
|
26
|
-
#
|
39
|
+
#
|
40
|
+
# Checks to see if a tag is present.
|
41
|
+
#
|
42
|
+
# Example:
|
43
|
+
#
|
27
44
|
# taggable_obj.has_tag? :spiffy #=> true
|
45
|
+
#
|
28
46
|
def has_tag?(tag_name)
|
29
47
|
tags.include? tag_name
|
30
48
|
end
|
31
49
|
|
32
|
-
#
|
50
|
+
#
|
51
|
+
# Returns an Array of the object's tags.
|
52
|
+
#
|
53
|
+
# Example:
|
54
|
+
#
|
33
55
|
# taggable_obj.tags #=> [:spiffy, :kind_of_spiffy]
|
56
|
+
#
|
34
57
|
def tags
|
35
58
|
@ruport_tags ||= []
|
36
59
|
end
|
37
60
|
|
38
|
-
#
|
61
|
+
#
|
62
|
+
# Sets the tags to some Array.
|
63
|
+
#
|
64
|
+
# Example:
|
65
|
+
#
|
39
66
|
# taggable_obj.tags = [:really_dang_spiffy, :the_most_spiffy]
|
67
|
+
#
|
40
68
|
def tags=(tags_list)
|
41
69
|
@ruport_tags = tags_list
|
42
70
|
end
|