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
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.4.
|
7
|
-
date: 2006-07
|
6
|
+
version: 0.4.21
|
7
|
+
date: 2006-08-07 00:00:00 -04:00
|
8
8
|
summary: A generalized Ruby report generation and templating engine.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -44,8 +44,6 @@ files:
|
|
44
44
|
- lib/ruport/format
|
45
45
|
- lib/ruport/rails
|
46
46
|
- lib/ruport/data
|
47
|
-
- lib/ruport/data_row.rb
|
48
|
-
- lib/ruport/data_set.rb
|
49
47
|
- lib/ruport/system_extensions.rb
|
50
48
|
- lib/ruport/config.rb
|
51
49
|
- lib/ruport/query.rb
|
@@ -65,6 +63,7 @@ files:
|
|
65
63
|
- lib/ruport/data/taggable.rb
|
66
64
|
- lib/ruport/data/record.rb
|
67
65
|
- lib/ruport/data/collection.rb
|
66
|
+
- lib/ruport/data/set.rb
|
68
67
|
- test/samples
|
69
68
|
- test/tc_element.rb
|
70
69
|
- test/tc_format.rb
|
@@ -87,6 +86,7 @@ files:
|
|
87
86
|
- test/tc_taggable.rb
|
88
87
|
- test/tc_record.rb
|
89
88
|
- test/tc_table.rb
|
89
|
+
- test/tc_set.rb
|
90
90
|
- test/samples/ruport_test.sql
|
91
91
|
- test/samples/test.yaml
|
92
92
|
- test/samples/data.csv
|
data/lib/ruport/data_row.rb
DELETED
@@ -1,187 +0,0 @@
|
|
1
|
-
# --
|
2
|
-
# data_row.rb : Ruby Reports row abstraction
|
3
|
-
#
|
4
|
-
# Author: Gregory T. Brown (gregory.t.brown at gmail dot com)
|
5
|
-
#
|
6
|
-
# Copyright (c) 2006, All Rights Reserved.
|
7
|
-
#
|
8
|
-
# This is free software. You may modify and redistribute this freely under
|
9
|
-
# your choice of the GNU General Public License or the Ruby License.
|
10
|
-
#
|
11
|
-
# See LICENSE and COPYING for details
|
12
|
-
# ++
|
13
|
-
module Ruport
|
14
|
-
|
15
|
-
# DataRows are Enumerable lists which can be accessed by field name or
|
16
|
-
# ordinal position.
|
17
|
-
#
|
18
|
-
# They feature a tagging system, allowing them to be easily
|
19
|
-
# compared or recalled.
|
20
|
-
#
|
21
|
-
# DataRows form the elements of DataSets
|
22
|
-
#
|
23
|
-
class DataRow
|
24
|
-
|
25
|
-
include Enumerable
|
26
|
-
|
27
|
-
# Takes field names as well as some optional parameters and
|
28
|
-
# constructs a DataRow.
|
29
|
-
#
|
30
|
-
# Options:
|
31
|
-
# <tt>:data</tt>:: can be specified in Hash, Array, or DataRow form
|
32
|
-
# <tt>:default</tt>:: The default value for empty fields
|
33
|
-
# <tt>:tags</tt>:: an initial set of tags for the row
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# Examples:
|
37
|
-
# >> Ruport::DataRow.new [:a,:b,:c,:d,:e], :data => [1,2,3,4,5]
|
38
|
-
# :tags => %w[cat dog]
|
39
|
-
# => #<Ruport::DataRow:0xb77e4b04 @fields=[:a, :b, :c, :d, :e],
|
40
|
-
# @data=[1, 2, 3, 4, 5], @tags=["cat", "dog"]>
|
41
|
-
#
|
42
|
-
# >> Ruport::DataRow.new([:a,:b,:c,:d,:e],
|
43
|
-
# :data => { :a => 'moo', :c => 'caw'} ,
|
44
|
-
# :tags => %w[cat dog])
|
45
|
-
# => #<Ruport::DataRow:0xb77c298c @fields=[:a, :b, :c, :d, :e],
|
46
|
-
# @data=["moo", nil, "caw", nil, nil], @tags=["cat", "dog"]>
|
47
|
-
#
|
48
|
-
# >> Ruport::DataRow.new [:a,:b,:c,:d,:e], :data => [1,2,3],
|
49
|
-
# :tags => %w[cat dog], :default => 0
|
50
|
-
# => #<Ruport::DataRow:0xb77bb4d4 @fields=[:a, :b, :c, :d, :e],
|
51
|
-
# @data=[1, 2, 3, 0, 0], @tags=["cat", "dog"]>
|
52
|
-
#
|
53
|
-
def initialize(fields=nil, options={})
|
54
|
-
|
55
|
-
#checks to ensure data is convertable
|
56
|
-
verify options[:data]
|
57
|
-
data = options[:data].dup
|
58
|
-
|
59
|
-
@fields = fields ? fields.dup : ( 0...data.length ).to_a
|
60
|
-
@tags = (options[:tags] || {}).dup
|
61
|
-
@data = []
|
62
|
-
|
63
|
-
nr_action = case(data)
|
64
|
-
when Array
|
65
|
-
lambda {|key, index| @data[index] = data.shift || options[:default]}
|
66
|
-
when DataRow
|
67
|
-
lambda {|key, index| @data = data.to_a}
|
68
|
-
else
|
69
|
-
lambda {|key, index| @data[index] = data[key] || options[:default]}
|
70
|
-
end
|
71
|
-
@fields.each_with_index {|key, index| nr_action.call(key,index)}
|
72
|
-
end
|
73
|
-
|
74
|
-
|
75
|
-
attr_accessor :fields, :tags
|
76
|
-
alias_method :column_names, :fields
|
77
|
-
alias_method :attributes, :fields
|
78
|
-
# Returns a new DataRow
|
79
|
-
def +(other)
|
80
|
-
DataRow.new @fields + other.fields, :data => (@data + other.to_a)
|
81
|
-
end
|
82
|
-
|
83
|
-
# Lets you access individual fields
|
84
|
-
#
|
85
|
-
# i.e. row["phone"] or row[4]
|
86
|
-
def [](key)
|
87
|
-
case(key)
|
88
|
-
when Fixnum
|
89
|
-
@data[key]
|
90
|
-
when Symbol
|
91
|
-
@data[@fields.index(key.to_s)] rescue nil
|
92
|
-
else
|
93
|
-
@data[@fields.index(key)] rescue nil
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
# Lets you set field values
|
98
|
-
#
|
99
|
-
# i.e. row["phone"] = '2038291203', row[7] = "allen"
|
100
|
-
def []=(key,value)
|
101
|
-
case(key)
|
102
|
-
when Fixnum
|
103
|
-
@data[key] = value
|
104
|
-
when Symbol
|
105
|
-
@data[@fields.index(key.to_s)] = value
|
106
|
-
else
|
107
|
-
@data[@fields.index(key)] = value
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# Converts the DataRow to a plain old Array
|
112
|
-
def to_a
|
113
|
-
@data.clone
|
114
|
-
end
|
115
|
-
|
116
|
-
def to_h
|
117
|
-
a = Hash.new
|
118
|
-
@fields.each { |f| a[f] = self[f] }; a
|
119
|
-
end
|
120
|
-
|
121
|
-
# Converts the DataRow to a string representation
|
122
|
-
# for outputting to screen.
|
123
|
-
def to_s
|
124
|
-
"[" + @data.join(",") + "]"
|
125
|
-
end
|
126
|
-
|
127
|
-
# Checks to see row includes the tag given.
|
128
|
-
#
|
129
|
-
# Example:
|
130
|
-
#
|
131
|
-
# >> row.has_tag? :running_balance
|
132
|
-
# => true
|
133
|
-
#
|
134
|
-
def has_tag?(tag)
|
135
|
-
@tags.include?(tag)
|
136
|
-
end
|
137
|
-
|
138
|
-
# Iterates through DataRow elements. Accepts a block.
|
139
|
-
def each(&action)
|
140
|
-
@data.each(&action)
|
141
|
-
end
|
142
|
-
|
143
|
-
# Allows you to add a tag to a row.
|
144
|
-
#
|
145
|
-
# Examples:
|
146
|
-
#
|
147
|
-
# row.tag_as(:jay_cross) if row["product"].eql?("im_courier")
|
148
|
-
# row.tag_as(:running_balance) if row.fields.include?("RB")
|
149
|
-
#
|
150
|
-
def tag_as(something)
|
151
|
-
@tags[something] = true
|
152
|
-
end
|
153
|
-
|
154
|
-
# Compares two DataRow objects. If values and fields are the same
|
155
|
-
# (and in the correct order) returns true. Otherwise returns false.
|
156
|
-
def ==(other)
|
157
|
-
self.to_a.eql?(other.to_a) && @fields.eql?(other.fields)
|
158
|
-
end
|
159
|
-
|
160
|
-
# Synonym for DataRow#==
|
161
|
-
def eql?(other)
|
162
|
-
self == other
|
163
|
-
end
|
164
|
-
|
165
|
-
def clone
|
166
|
-
self.class.new @fields, :data => @data, :tags => @tags
|
167
|
-
end
|
168
|
-
|
169
|
-
alias_method :dup, :clone
|
170
|
-
|
171
|
-
private
|
172
|
-
|
173
|
-
def verify(data)
|
174
|
-
if data.kind_of? String or
|
175
|
-
data.kind_of? Integer or not data.respond_to?(:[])
|
176
|
-
Ruport.complain "Cannot convert data to DataRow",
|
177
|
-
:status => :fatal, :exception => ArgumentError, :level => :log_only
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def method_missing(id,*args)
|
182
|
-
f = id.to_s.gsub(/=$/,'')
|
183
|
-
return super unless fields.include?(f)
|
184
|
-
args.empty? ? self[f] : self[f] = args[0]
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
data/lib/ruport/data_set.rb
DELETED
@@ -1,308 +0,0 @@
|
|
1
|
-
# data_set.rb : Ruby Reports core datastructure.
|
2
|
-
#
|
3
|
-
# Author: Gregory T. Brown (gregory.t.brown at gmail dot com)
|
4
|
-
# Copyright (c) 2006, All Rights Reserved.
|
5
|
-
#
|
6
|
-
# Pseudo keyword argument support, improved <<, and set operations submitted by
|
7
|
-
# Dudley Flanders
|
8
|
-
#
|
9
|
-
# This is free software. You may modify and redistribute this freely under
|
10
|
-
# your choice of the GNU General Public License or the Ruby License.
|
11
|
-
#
|
12
|
-
# See LICENSE and COPYING for details
|
13
|
-
module Ruport
|
14
|
-
|
15
|
-
# The DataSet is the core datastructure for Ruport. It provides methods that
|
16
|
-
# allow you to compare and combine query results, data loaded in from CSVs,
|
17
|
-
# and user-defined sets of data.
|
18
|
-
#
|
19
|
-
# It is tightly integrated with Ruport's formatting and query systems, so if
|
20
|
-
# you'd like to take advantage of these models, you will probably find
|
21
|
-
# DataSet useful.
|
22
|
-
class DataSet
|
23
|
-
#FIXME: Add logging to this class
|
24
|
-
include Enumerable
|
25
|
-
extend Forwardable
|
26
|
-
# DataSets must be given a set of fields to be defined.
|
27
|
-
#
|
28
|
-
# These field names will define the columns for the DataSet and how you
|
29
|
-
# access them.
|
30
|
-
#
|
31
|
-
# data = Ruport::DataSet.new %w[ id name phone ]
|
32
|
-
#
|
33
|
-
# Options:
|
34
|
-
# * <tt>:data</tt> - An Enumerable with the content for this DataSet
|
35
|
-
# * <tt>:default</tt> - The default value for empty cells
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# DataSet supports the following Array methods through delegators
|
39
|
-
# length, empty?, delete_at, first, last, pop
|
40
|
-
#
|
41
|
-
def initialize(fields=nil, options={})
|
42
|
-
@fields = fields.dup if fields
|
43
|
-
@default = options[:default] || default
|
44
|
-
@data = []
|
45
|
-
options[:data].each { |r| self << r } if options[:data]
|
46
|
-
end
|
47
|
-
|
48
|
-
#an array which contains column names
|
49
|
-
attr_accessor :fields
|
50
|
-
alias_method :column_names, :fields
|
51
|
-
alias_method :column_names=, :fields=
|
52
|
-
#the default value to fill empty cells with
|
53
|
-
attr_accessor :default
|
54
|
-
|
55
|
-
#data holds the elements of the Row
|
56
|
-
attr_reader :data
|
57
|
-
|
58
|
-
def_delegators :@data, :length, :[], :empty?,
|
59
|
-
:delete_at, :first, :last, :pop,
|
60
|
-
:each, :reverse_each, :at, :clear
|
61
|
-
|
62
|
-
def delete_if(&block)
|
63
|
-
@data.delete_if █ self
|
64
|
-
end
|
65
|
-
|
66
|
-
#provides a deep copy of the DataSet.
|
67
|
-
def clone
|
68
|
-
self.class.new(@fields, :data => @data)
|
69
|
-
end
|
70
|
-
|
71
|
-
alias_method :dup, :clone
|
72
|
-
|
73
|
-
# Creates a new DataSet with the same shape as this one, but empty.
|
74
|
-
def empty_clone
|
75
|
-
self.class.new(@fields)
|
76
|
-
end
|
77
|
-
|
78
|
-
#allows setting of rows
|
79
|
-
def []=(index,data)
|
80
|
-
@data[index] = DataRow.new @fields, :data => data
|
81
|
-
end
|
82
|
-
|
83
|
-
def [](index)
|
84
|
-
case(index)
|
85
|
-
when Range
|
86
|
-
self.class.new @fields, :data => @data[index]
|
87
|
-
else
|
88
|
-
@data[index]
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Appends a row to the DataSet
|
93
|
-
# Can be added as an array, an array of DataRows, a DataRow, or a keyed
|
94
|
-
# hash-like object.
|
95
|
-
#
|
96
|
-
# Columns left undefined will be filled with DataSet#default values.
|
97
|
-
#
|
98
|
-
# data << [ 1, 2, 3 ]
|
99
|
-
# data << { :some_field_name => 3, :other => 2, :another => 1 }
|
100
|
-
#
|
101
|
-
# FIXME: Appending a datarow is wonky.
|
102
|
-
def << ( stuff, filler=@default )
|
103
|
-
if stuff.kind_of?(DataRow)
|
104
|
-
@data << stuff.clone
|
105
|
-
elsif stuff.kind_of?(Array) && stuff[0].kind_of?(DataRow)
|
106
|
-
@data.concat(stuff)
|
107
|
-
else
|
108
|
-
@data << DataRow.new(@fields, :data => stuff.clone,:default => filler)
|
109
|
-
end
|
110
|
-
return self
|
111
|
-
end
|
112
|
-
alias_method :push, :<<
|
113
|
-
|
114
|
-
# checks if one dataset equals another
|
115
|
-
# FIXME: expand this doc.
|
116
|
-
def eql?(data2)
|
117
|
-
return false unless ( @data.length == data2.data.length and
|
118
|
-
@fields.eql?(data2.fields) )
|
119
|
-
@data.each_with_index do |row, r_index|
|
120
|
-
row.each_with_index do |field, f_index|
|
121
|
-
return false unless field.eql?(data2[r_index][f_index])
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
return true
|
126
|
-
end
|
127
|
-
|
128
|
-
# checks if one dataset equals another
|
129
|
-
def ==(data2)
|
130
|
-
eql?(data2)
|
131
|
-
end
|
132
|
-
|
133
|
-
# Set union - returns a DataSet with the elements that are contained in
|
134
|
-
# in either of the two given sets, with no duplicates.
|
135
|
-
def |(other)
|
136
|
-
clone << other.reject { |x| self.include? x }
|
137
|
-
end
|
138
|
-
alias_method :union, :|
|
139
|
-
|
140
|
-
# Set intersection
|
141
|
-
def &(other)
|
142
|
-
empty_clone << select { |x| other.include? x }
|
143
|
-
end
|
144
|
-
alias_method :intersection, :&
|
145
|
-
|
146
|
-
# Set difference
|
147
|
-
def -(other)
|
148
|
-
empty_clone << reject { |x| other.include? x }
|
149
|
-
end
|
150
|
-
alias_method :difference, :-
|
151
|
-
|
152
|
-
# Checks if one DataSet has the same set of fields as another
|
153
|
-
def shaped_like?(other)
|
154
|
-
return true if @fields.eql?(other.fields)
|
155
|
-
end
|
156
|
-
|
157
|
-
# Concatenates one DataSet onto another if they have the same shape
|
158
|
-
def concat(other)
|
159
|
-
if other.shaped_like?(self)
|
160
|
-
@data.concat(other.data)
|
161
|
-
return self
|
162
|
-
end
|
163
|
-
end
|
164
|
-
alias_method :+, :concat
|
165
|
-
|
166
|
-
# Allows loading of CSV files or YAML dumps. Returns a DataSet
|
167
|
-
#
|
168
|
-
# FasterCSV will be used if it is installed.
|
169
|
-
#
|
170
|
-
# my_data = Ruport::DataSet.load("foo.csv")
|
171
|
-
# my_data = Ruport::DataSet.load("foo.yaml")
|
172
|
-
# my_data = Ruport::DataSet.load("foo.yml")
|
173
|
-
def self.load ( source, options={}, &block)
|
174
|
-
options = {:has_names => true}.merge(options)
|
175
|
-
case source
|
176
|
-
when /\.(yaml|yml)/
|
177
|
-
return YAML.load(File.open(source))
|
178
|
-
when /\.csv/
|
179
|
-
require "fastercsv"
|
180
|
-
input = FasterCSV.read(source) if source =~ /\.csv/
|
181
|
-
loaded_data = self.new
|
182
|
-
|
183
|
-
action = if block
|
184
|
-
lambda { |r| block[loaded_data,r] }
|
185
|
-
else
|
186
|
-
lambda { |r| loaded_data << r }
|
187
|
-
end
|
188
|
-
|
189
|
-
if options[:has_names]
|
190
|
-
loaded_data.fields = input[0] ; input = input[1..-1]
|
191
|
-
end
|
192
|
-
|
193
|
-
loaded_data.default = options[:default]
|
194
|
-
input.each { |row| action[row] }
|
195
|
-
return loaded_data
|
196
|
-
else
|
197
|
-
raise "Invalid file type"
|
198
|
-
end
|
199
|
-
end
|
200
|
-
|
201
|
-
|
202
|
-
# Returns a new DataSet composed of the fields specified.
|
203
|
-
def select_columns(*fields)
|
204
|
-
fields = get_field_names(fields)
|
205
|
-
rows = fields.inject([]) { |s,e| s + [map { |row| row[e] }] }.transpose
|
206
|
-
my_data = DataSet.new(fields, :data => rows)
|
207
|
-
end
|
208
|
-
|
209
|
-
# Prunes the dataset to contain only the fields specified. (DESTRUCTIVE)
|
210
|
-
def select_columns!(*fields)
|
211
|
-
a = select_columns(*fields)
|
212
|
-
@fields = a.fields; @data = a.data
|
213
|
-
end
|
214
|
-
|
215
|
-
#Creates a new dataset with additional columns appending to it
|
216
|
-
def add_columns(*fields)
|
217
|
-
select_columns *(@fields + fields)
|
218
|
-
end
|
219
|
-
|
220
|
-
def add_columns!(*fields)
|
221
|
-
select_columns! *(@fields + fields)
|
222
|
-
end
|
223
|
-
|
224
|
-
# Returns a new DataSet with the specified fields removed
|
225
|
-
def remove_columns(*fields)
|
226
|
-
fields = get_field_names(fields)
|
227
|
-
select_columns(*(@fields-fields))
|
228
|
-
end
|
229
|
-
|
230
|
-
# removes the specified fields from this DataSet (DESTRUCTIVE!)
|
231
|
-
def remove_columns!(*fields)
|
232
|
-
d = remove_columns(*fields)
|
233
|
-
@data = d.data
|
234
|
-
@fields = d.fields
|
235
|
-
end
|
236
|
-
|
237
|
-
def rename_columns(cols)
|
238
|
-
if cols.kind_of?(Array)
|
239
|
-
cols.each_with_index { |c,i| fields[i] = c }
|
240
|
-
else
|
241
|
-
cols.map { |k,v| fields[fields.index(k)] = v }
|
242
|
-
end
|
243
|
-
@data.each { |r| r.fields = fields }
|
244
|
-
end
|
245
|
-
# uses Format::Builder to render DataSets in various ready to output
|
246
|
-
# formats.
|
247
|
-
#
|
248
|
-
# To add new formats to this function, simply re-open Format::Builder
|
249
|
-
# and add methods like <tt>render_my_format_name</tt>.
|
250
|
-
#
|
251
|
-
# This will enable <tt>data.as(:my_format_name)</tt>
|
252
|
-
def as(format)
|
253
|
-
t = Format.table_object(:data => self, :plugin => format)
|
254
|
-
yield(t) if block_given?
|
255
|
-
t.render
|
256
|
-
end
|
257
|
-
|
258
|
-
# Will iterate row by row yielding each row
|
259
|
-
# The result of the block will be added to a running total
|
260
|
-
#
|
261
|
-
# Only works with blocks resulting in numeric values.
|
262
|
-
#
|
263
|
-
# Also allows summing up a specific column, e.g.
|
264
|
-
#
|
265
|
-
# my_set.sigma("col1")
|
266
|
-
def sigma(f = nil)
|
267
|
-
if f
|
268
|
-
inject(0) { |s,r| s + (r[f] || 0) }
|
269
|
-
else
|
270
|
-
inject(0) { |s,r| s + (yield(r) || 0) }
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
alias_method :sum, :sigma
|
275
|
-
|
276
|
-
# Converts a DataSet to an array of arrays
|
277
|
-
def to_a
|
278
|
-
@data.map {|x| x.to_a }
|
279
|
-
end
|
280
|
-
|
281
|
-
# Converts a DataSet to CSV
|
282
|
-
def to_csv; as(:csv) end
|
283
|
-
|
284
|
-
# Converts a Dataset to html
|
285
|
-
def to_html; as(:html) end
|
286
|
-
|
287
|
-
# Readable string representation of the DataSet
|
288
|
-
def to_s; as(:text) end
|
289
|
-
|
290
|
-
private
|
291
|
-
|
292
|
-
def get_field_names(f)
|
293
|
-
f.all? { |e| e.kind_of? Integer } &&
|
294
|
-
f.inject([]) { |s,e| s + [@fields[e]] } || f
|
295
|
-
end
|
296
|
-
|
297
|
-
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
class Array
|
302
|
-
|
303
|
-
# Will convert Arrays of Enumerable objects to DataSets.
|
304
|
-
# May have dragons.
|
305
|
-
def to_ds(fields,options={})
|
306
|
-
Ruport::DataSet.new fields, :data => to_a, :default => options[:default]
|
307
|
-
end
|
308
|
-
end
|