ruport 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +4 -2
- data/README +4 -3
- data/Rakefile +1 -1
- data/lib/ruport.rb +1 -4
- data/lib/ruport/config.rb +6 -11
- data/lib/ruport/data/collection.rb +8 -2
- data/lib/ruport/data/groupable.rb +0 -2
- data/lib/ruport/data/record.rb +0 -13
- data/lib/ruport/data/set.rb +0 -8
- data/lib/ruport/data/table.rb +1 -18
- data/lib/ruport/data/taggable.rb +1 -6
- data/lib/ruport/format/latex.rb +5 -8
- data/lib/ruport/format/pdf.rb +56 -0
- data/lib/ruport/format/plugin.rb +5 -0
- data/lib/ruport/format/svg.rb +26 -4
- data/lib/ruport/format/text.rb +25 -2
- data/lib/ruport/format/xml.rb +1 -1
- data/lib/ruport/generator.rb +1 -1
- data/lib/ruport/mailer.rb +1 -4
- data/lib/ruport/query.rb +0 -9
- data/lib/ruport/renderer.rb +15 -1
- data/lib/ruport/report.rb +5 -22
- data/test/test_mailer.rb +86 -24
- data/test/test_report.rb +72 -1
- data/test/test_table.rb +15 -0
- data/test/unit.log +7 -277
- metadata +34 -44
- data/lib/ruport.rb~ +0 -69
- data/lib/ruport/data.rb.rej +0 -5
- data/lib/ruport/data.rb~ +0 -1
- data/lib/ruport/data/record.rb~ +0 -236
- data/lib/ruport/data/table.rb.rej +0 -67
- data/lib/ruport/data/table.rb~ +0 -414
- data/test/test_query.rb.rej +0 -161
- data/test/test_query.rb~ +0 -337
- data/test/test_record.rb.rej +0 -46
- data/test/test_table.rb~ +0 -336
data/lib/ruport/data.rb.rej
DELETED
data/lib/ruport/data.rb~
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
%w[taggable record collection table set groupable].each { |l| require "ruport/data/#{l}" }
|
data/lib/ruport/data/record.rb~
DELETED
@@ -1,236 +0,0 @@
|
|
1
|
-
# The Ruport Data Collections.
|
2
|
-
# Authors: Gregory Brown / Dudley Flanders
|
3
|
-
#
|
4
|
-
# This is Free Software. For details, see LICENSE and COPYING
|
5
|
-
# Copyright 2006 by respective content owners, all rights reserved.
|
6
|
-
module Ruport::Data
|
7
|
-
|
8
|
-
#
|
9
|
-
# === Overview
|
10
|
-
#
|
11
|
-
# Data::Records are the work horse of Ruport's data model. These can behave
|
12
|
-
# as Array-like, Hash-like, or Struct-like objects. They are used as the
|
13
|
-
# base element in both Tables and Sets.
|
14
|
-
#
|
15
|
-
class Record
|
16
|
-
require "forwardable"
|
17
|
-
extend Forwardable
|
18
|
-
include Enumerable
|
19
|
-
include Taggable
|
20
|
-
|
21
|
-
#
|
22
|
-
# Creates a new Record object. If the <tt>:attributes</tt>
|
23
|
-
# keyword is specified, Hash-like and Struct-like
|
24
|
-
# access will be enabled. Otherwise, Record elements may be
|
25
|
-
# accessed ordinally, like an Array.
|
26
|
-
#
|
27
|
-
# A Record can accept either a Hash or an Array as its <tt>data</tt>.
|
28
|
-
#
|
29
|
-
# Examples:
|
30
|
-
# a = Record.new [1,2,3]
|
31
|
-
# a[1] #=> 2
|
32
|
-
#
|
33
|
-
# b = Record.new [1,2,3], :attributes => %w[a b c]
|
34
|
-
# b[1] #=> 2
|
35
|
-
# b['a'] #=> 1
|
36
|
-
# b.c #=> 3
|
37
|
-
#
|
38
|
-
# c = Record.new {"a" => 1, "c" => 3, "b" => 2}, :attributes => %w[a b c]
|
39
|
-
# b[1] #=> 2
|
40
|
-
# b['a'] #=> 1
|
41
|
-
# b.c #=> 3
|
42
|
-
#
|
43
|
-
# c = Record.new { "a" => 1, "c" => 3, "b" => 2 }
|
44
|
-
# b[1] #=> ? (without attributes, you cannot rely on order)
|
45
|
-
# b['a'] #=> 1
|
46
|
-
# b.c #=> 3
|
47
|
-
#
|
48
|
-
def initialize(data,options={})
|
49
|
-
if data.kind_of?(Hash)
|
50
|
-
if options[:attributes]
|
51
|
-
@attributes = options[:attributes]
|
52
|
-
@data = options[:attributes].map { |k| data[k] }
|
53
|
-
else
|
54
|
-
@attributes, @data = data.to_a.transpose
|
55
|
-
end
|
56
|
-
else
|
57
|
-
@data = data.dup
|
58
|
-
@attributes = if options[:attributes]
|
59
|
-
options[:attributes]
|
60
|
-
else
|
61
|
-
[]
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# The underlying <tt>data</tt> which is being stored in the record.
|
67
|
-
attr_reader :data
|
68
|
-
|
69
|
-
def_delegators :@data,:each, :length
|
70
|
-
|
71
|
-
#
|
72
|
-
# Allows either Array or Hash-like indexing.
|
73
|
-
#
|
74
|
-
# Examples:
|
75
|
-
#
|
76
|
-
# my_record[1]
|
77
|
-
# my_record["foo"]
|
78
|
-
#
|
79
|
-
def [](index)
|
80
|
-
if index.kind_of? Integer
|
81
|
-
raise "Invalid index" unless index < @data.length
|
82
|
-
@data[index]
|
83
|
-
else
|
84
|
-
raise "Invalid index" unless attributes.index(index.to_s)
|
85
|
-
@data[attributes.index(index.to_s)]
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
|
90
|
-
#
|
91
|
-
# Allows setting a <tt>value</tt> at an <tt>index</tt>.
|
92
|
-
#
|
93
|
-
# Examples:
|
94
|
-
#
|
95
|
-
# my_record[1] = "foo"
|
96
|
-
# my_record["bar"] = "baz"
|
97
|
-
#
|
98
|
-
def []=(index, value)
|
99
|
-
if index.kind_of? Integer
|
100
|
-
raise "Invalid index" unless index < @data.length
|
101
|
-
@data[index] = value
|
102
|
-
else
|
103
|
-
raise "Invalid index" unless attributes.index(index.to_s)
|
104
|
-
@data[attributes.index(index.to_s)] = value
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
#
|
109
|
-
# If <tt>attributes</tt> and <tt>data</tt> are equivalent, then
|
110
|
-
# <tt>==</tt> evaluates to true. Otherwise, <tt>==</tt> returns false.
|
111
|
-
#
|
112
|
-
def ==(other)
|
113
|
-
return false if attributes && !other.attributes
|
114
|
-
return false if other.attributes && !attributes
|
115
|
-
attributes == other.attributes && @data == other.data
|
116
|
-
end
|
117
|
-
|
118
|
-
alias_method :eql?, :==
|
119
|
-
|
120
|
-
#
|
121
|
-
# Converts a Record into an Array.
|
122
|
-
#
|
123
|
-
# Example:
|
124
|
-
#
|
125
|
-
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
126
|
-
# a.to_a #=> [1,2]
|
127
|
-
#
|
128
|
-
def to_a; @data.dup; end
|
129
|
-
|
130
|
-
#
|
131
|
-
# Converts a Record into a Hash. This only works if <tt>attributes</tt>
|
132
|
-
# are specified in the Record.
|
133
|
-
#
|
134
|
-
# Example:
|
135
|
-
#
|
136
|
-
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
137
|
-
# a.to_h #=> {"a" => 1, "b" => 2}
|
138
|
-
#
|
139
|
-
def to_h; Hash[*attributes.zip(data).flatten] end
|
140
|
-
|
141
|
-
#
|
142
|
-
# Returns a copy of the <tt>attributes</tt> from this Record.
|
143
|
-
#
|
144
|
-
# Example:
|
145
|
-
#
|
146
|
-
# a = Data::Record.new([1,2],:attributes => %w[a b])
|
147
|
-
# a.attributes #=> ["a","b"]
|
148
|
-
#
|
149
|
-
def attributes; @attributes.map { |a| a.to_s }; end
|
150
|
-
|
151
|
-
#
|
152
|
-
# Sets the <tt>attribute</tt> list for this Record.
|
153
|
-
#
|
154
|
-
# Example:
|
155
|
-
#
|
156
|
-
# my_record.attributes = %w[foo bar baz]
|
157
|
-
#
|
158
|
-
attr_writer :attributes
|
159
|
-
|
160
|
-
#
|
161
|
-
# Allows you to change the order of or reduce the number of columns in a
|
162
|
-
# Record.
|
163
|
-
#
|
164
|
-
# Example:
|
165
|
-
#
|
166
|
-
# a = Data::Record.new([1,2,3,4],:attributes => %w[a b c d])
|
167
|
-
# b = a.reorder("a","d","b")
|
168
|
-
# b.attributes #=> ["a","d","b"]
|
169
|
-
# b.data #=> [1,4,2]
|
170
|
-
#
|
171
|
-
def reorder(*indices)
|
172
|
-
dup.reorder!(*indices)
|
173
|
-
end
|
174
|
-
|
175
|
-
# Same as Record#reorder but modifies its reciever in place.
|
176
|
-
def reorder!(*indices)
|
177
|
-
indices = reorder_data!(*indices)
|
178
|
-
if attributes
|
179
|
-
if indices.all? { |e| e.kind_of? Integer }
|
180
|
-
@attributes = indices.map { |i| attributes[i] }
|
181
|
-
else
|
182
|
-
@attributes = indices
|
183
|
-
end
|
184
|
-
end; self
|
185
|
-
end
|
186
|
-
|
187
|
-
def reorder_data!(*indices) # :nodoc:
|
188
|
-
indices = indices[0] if indices[0].kind_of?(Array)
|
189
|
-
indices.each do |i|
|
190
|
-
self[i] rescue raise ArgumentError,
|
191
|
-
"you may have specified an invalid column"
|
192
|
-
end
|
193
|
-
@data = indices.map { |i| self[i] }
|
194
|
-
return indices;
|
195
|
-
end
|
196
|
-
|
197
|
-
|
198
|
-
# Makes a fresh copy of the Record.
|
199
|
-
def dup
|
200
|
-
copy = self.class.new(@data,:attributes => attributes)
|
201
|
-
copy.tags = self.tags.dup
|
202
|
-
return copy
|
203
|
-
end
|
204
|
-
|
205
|
-
#FIXME: This does not take into account frozen / tainted state
|
206
|
-
alias_method :clone, :dup
|
207
|
-
|
208
|
-
#
|
209
|
-
# Provides a unique hash value. If a Record contains the same data and
|
210
|
-
# attributes as another Record, they will hash to the same value, even if
|
211
|
-
# they are not the same object. This is similar to the way Array works,
|
212
|
-
# but different from Hash and other objects.
|
213
|
-
#
|
214
|
-
def hash
|
215
|
-
(attributes.to_a + data.to_a).hash
|
216
|
-
end
|
217
|
-
|
218
|
-
#
|
219
|
-
# Provides accessor style methods for attribute access.
|
220
|
-
#
|
221
|
-
# Example:
|
222
|
-
#
|
223
|
-
# my_record.foo = 2
|
224
|
-
# my_record.foo #=> 2
|
225
|
-
#
|
226
|
-
def method_missing(id,*args)
|
227
|
-
id = id.to_s.gsub(/=$/,"")
|
228
|
-
if attributes.include?(id)
|
229
|
-
args.empty? ? self[id] : self[id] = args.first
|
230
|
-
else
|
231
|
-
super
|
232
|
-
end
|
233
|
-
end
|
234
|
-
|
235
|
-
end
|
236
|
-
end
|
@@ -1,67 +0,0 @@
|
|
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~
DELETED
@@ -1,414 +0,0 @@
|
|
1
|
-
# The Ruport Data Collections.
|
2
|
-
# Authors: Gregory Brown / Dudley Flanders
|
3
|
-
#
|
4
|
-
# This is Free Software. For details, see LICENSE and COPYING
|
5
|
-
# Copyright 2006 by respective content owners, all rights reserved.
|
6
|
-
|
7
|
-
class Array
|
8
|
-
|
9
|
-
#
|
10
|
-
# Converts an array to a Ruport::Data::Table object, ready to
|
11
|
-
# use in your reports.
|
12
|
-
#
|
13
|
-
# Example:
|
14
|
-
# [[1,2],[3,4]].to_table(%w[a b])
|
15
|
-
#
|
16
|
-
def to_table(options={})
|
17
|
-
options = { :column_names => options } if options.kind_of? Array
|
18
|
-
Ruport::Data::Table.new({:data => self}.merge(options))
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
module Ruport::Data
|
23
|
-
|
24
|
-
#
|
25
|
-
# === Overview
|
26
|
-
#
|
27
|
-
# This class is one of the core classes for building and working with data
|
28
|
-
# in Ruport. The idea is to get your data into a standard form, regardless
|
29
|
-
# of its source (a database, manual arrays, ActiveRecord, CSVs, etc.).
|
30
|
-
#
|
31
|
-
# Table is intended to be used as the data store for structured, tabular
|
32
|
-
# data - Ruport::Data::Set is an alternate data store intended for less
|
33
|
-
# structured data.
|
34
|
-
#
|
35
|
-
# Once your data is in a Ruport::Data::Table object, it can be manipulated
|
36
|
-
# to suit your needs, then used to build a report.
|
37
|
-
#
|
38
|
-
class Table < Collection
|
39
|
-
include Groupable
|
40
|
-
|
41
|
-
#
|
42
|
-
# Creates a new table based on the supplied 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]
|
52
|
-
#
|
53
|
-
def initialize(options={})
|
54
|
-
@column_names = options[:column_names] ? options[:column_names].dup : []
|
55
|
-
@data = []
|
56
|
-
if options[:data]
|
57
|
-
if options[:data].all? { |r| r.kind_of? Record }
|
58
|
-
record_tags = options[:data].map { |r| r.tags }
|
59
|
-
options[:data] = options[:data].map { |r| r.to_a }
|
60
|
-
end
|
61
|
-
options[:data].each { |e| self << e }
|
62
|
-
each { |r| r.tags = record_tags.shift } if record_tags
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
# This Table's column names.
|
67
|
-
attr_reader :column_names
|
68
|
-
def_delegator :@data, :[]
|
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.
|
73
|
-
#
|
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)
|
82
|
-
@column_names.replace(other.dup)
|
83
|
-
end
|
84
|
-
|
85
|
-
#
|
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
|
-
#
|
97
|
-
# one.eql?(two) #=> true
|
98
|
-
#
|
99
|
-
def eql?(other)
|
100
|
-
data.eql?(other.data) && column_names.eql?(other.column_names)
|
101
|
-
end
|
102
|
-
|
103
|
-
alias_method :==, :eql?
|
104
|
-
|
105
|
-
#
|
106
|
-
# Uses Ruport's built-in text plugin to render this Table into a String
|
107
|
-
#
|
108
|
-
# Example:
|
109
|
-
#
|
110
|
-
# data = Table.new :data => [1,2], [3,4],
|
111
|
-
# :column_names => %w[a b]
|
112
|
-
# puts data.to_s
|
113
|
-
#
|
114
|
-
def to_s
|
115
|
-
as(:text)
|
116
|
-
end
|
117
|
-
|
118
|
-
#
|
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]
|
126
|
-
# data << [8,9]
|
127
|
-
# data << { :a => 4, :b => 5}
|
128
|
-
# data << Record.new [5,6], :attributes => %w[a b]
|
129
|
-
#
|
130
|
-
def <<(other)
|
131
|
-
case other
|
132
|
-
when Array
|
133
|
-
@data << Record.new(other, :attributes => @column_names)
|
134
|
-
when Hash
|
135
|
-
raise ArgumentError unless @column_names
|
136
|
-
arr = @column_names.map { |k| other[k] }
|
137
|
-
@data << Record.new(arr, :attributes => @column_names)
|
138
|
-
when Record
|
139
|
-
raise ArgumentError unless column_names.eql? other.attributes
|
140
|
-
@data << Record.new(other.data, :attributes => @column_names)
|
141
|
-
@data.last.tags = other.tags.dup
|
142
|
-
else
|
143
|
-
raise ArgumentError
|
144
|
-
end
|
145
|
-
self
|
146
|
-
end
|
147
|
-
|
148
|
-
#
|
149
|
-
# Used to combine two Tables. Throws an ArgumentError if the Tables don't
|
150
|
-
# have identical columns.
|
151
|
-
#
|
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
|
-
#
|
160
|
-
# sue = inky + blinky
|
161
|
-
# sue.data #=> [[1,2],[3,4],[5,6]]
|
162
|
-
#
|
163
|
-
def +(other)
|
164
|
-
raise ArgumentError unless other.column_names == @column_names
|
165
|
-
Table.new(:column_names => @column_names, :data => @data + other.data)
|
166
|
-
end
|
167
|
-
|
168
|
-
#
|
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
|
-
#
|
177
|
-
# data.reorder!([1,0])
|
178
|
-
#
|
179
|
-
def reorder!(*indices)
|
180
|
-
indices = indices[0] if indices[0].kind_of? Array
|
181
|
-
if @column_names && !@column_names.empty?
|
182
|
-
x = if indices.all? { |i| i.kind_of? Integer }
|
183
|
-
indices.map { |i| @column_names[i] }
|
184
|
-
else
|
185
|
-
indices
|
186
|
-
end
|
187
|
-
@column_names = x
|
188
|
-
end
|
189
|
-
@data.each { |r|
|
190
|
-
r.reorder_data!(*indices)
|
191
|
-
r.attributes = @column_names
|
192
|
-
}; self
|
193
|
-
end
|
194
|
-
|
195
|
-
#
|
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
|
-
#
|
203
|
-
# two = one.reorder!([1,0])
|
204
|
-
#
|
205
|
-
def reorder(*indices)
|
206
|
-
dup.reorder!(*indices)
|
207
|
-
end
|
208
|
-
|
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.
|
215
|
-
#
|
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
|
-
#
|
223
|
-
def append_column(options={})
|
224
|
-
self.column_names += [options[:name]] if options[:name]
|
225
|
-
if block_given?
|
226
|
-
each { |r| r.data << yield(r) || options[:fill] }
|
227
|
-
else
|
228
|
-
each { |r| r.data << options[:fill] }
|
229
|
-
end; self
|
230
|
-
end
|
231
|
-
|
232
|
-
#
|
233
|
-
# Removes a column from the Table. Any values in the specified column are
|
234
|
-
# lost.
|
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
|
243
|
-
# data = [[1,2],[3,4]].to_table
|
244
|
-
# data.remove_column(1)
|
245
|
-
# data.eql? [[1],[3]].to_table %w[a] #=> true
|
246
|
-
#
|
247
|
-
def remove_column(options={})
|
248
|
-
if options.kind_of? Integer
|
249
|
-
return reorder!((0...data[0].length).to_a - [options])
|
250
|
-
elsif options.kind_of? Hash
|
251
|
-
name = options[:name]
|
252
|
-
else
|
253
|
-
name = options
|
254
|
-
end
|
255
|
-
|
256
|
-
raise ArgumentError unless column_names.include? name
|
257
|
-
reorder! column_names - [name]
|
258
|
-
end
|
259
|
-
|
260
|
-
#
|
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]
|
268
|
-
# two = one.dup
|
269
|
-
#
|
270
|
-
def dup
|
271
|
-
a = self.class.new(:data => @data, :column_names => @column_names)
|
272
|
-
a.tags = tags.dup
|
273
|
-
return a
|
274
|
-
end
|
275
|
-
|
276
|
-
#
|
277
|
-
# Loads a CSV file directly into a Table using the FasterCSV library.
|
278
|
-
#
|
279
|
-
# Example:
|
280
|
-
#
|
281
|
-
# table = Table.load('mydata.csv')
|
282
|
-
#
|
283
|
-
def self.load(csv_file, options={})
|
284
|
-
get_table_from_csv(:foreach, csv_file, options)
|
285
|
-
end
|
286
|
-
|
287
|
-
def self.parse(string, options={}) #:nodoc:
|
288
|
-
get_table_from_csv(:parse,string,options)
|
289
|
-
end
|
290
|
-
|
291
|
-
def self.get_table_from_csv(msg,param,options={}) #:nodoc:
|
292
|
-
options = {:has_names => true,
|
293
|
-
:csv_options => {} }.merge(options)
|
294
|
-
require "fastercsv"
|
295
|
-
loaded_data = self.new
|
296
|
-
|
297
|
-
first_line = true
|
298
|
-
FasterCSV.send(msg,param,options[:csv_options]) do |row|
|
299
|
-
if first_line && options[:has_names]
|
300
|
-
loaded_data.column_names = row
|
301
|
-
first_line = false
|
302
|
-
elsif !block_given?
|
303
|
-
loaded_data << row
|
304
|
-
else
|
305
|
-
yield(loaded_data,row)
|
306
|
-
end
|
307
|
-
end ; loaded_data
|
308
|
-
end
|
309
|
-
|
310
|
-
#
|
311
|
-
# Allows you to split Tables into multiple Tables for grouping.
|
312
|
-
#
|
313
|
-
# Example:
|
314
|
-
#
|
315
|
-
# a = Table.new(:column_name => %w[name a b c])
|
316
|
-
# a << ["greg",1,2,3]
|
317
|
-
# a << ["joe", 2,3,4]
|
318
|
-
# a << ["greg",7,8,9]
|
319
|
-
# a << ["joe", 1,2,3]
|
320
|
-
#
|
321
|
-
# b = a.split :group => "name"
|
322
|
-
#
|
323
|
-
# b.greg.eql? [[1,2,3],[7,8,9]].to_table(%w[a b c]) #=> true
|
324
|
-
# b["joe"].eql? [[2,3,4],[1,2,3]].to_table(%w[a b c]) #=> true
|
325
|
-
#
|
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.
|
328
|
-
#
|
329
|
-
# Example:
|
330
|
-
#
|
331
|
-
# a = Table.new(:column_names => %w[first_name last_name x]
|
332
|
-
# a << %w[greg brown foo]
|
333
|
-
# a << %w[greg gibson bar]
|
334
|
-
# a << %w[greg brown baz]
|
335
|
-
#
|
336
|
-
# b = a.split :group => %w[first_name last_name]
|
337
|
-
# a.greg_brown.length #=> 2
|
338
|
-
# a["greg_gibson"].length #=> 1
|
339
|
-
# a.greg_brown[0].x #=> "foo"
|
340
|
-
#
|
341
|
-
def split(options={})
|
342
|
-
if options[:group].kind_of? Array
|
343
|
-
group = map { |r| options[:group].map { |e| r[e] } }.uniq
|
344
|
-
data = group.inject([]) { |s,g|
|
345
|
-
s + [select { |r| options[:group].map { |e| r[e] }.eql?(g) }]
|
346
|
-
}
|
347
|
-
c = column_names - options[:group]
|
348
|
-
else
|
349
|
-
group = map { |r| r[options[:group]] }.uniq
|
350
|
-
data = group.inject([]) { |s,g|
|
351
|
-
s + [select { |r| r[options[:group]].eql?(g) }]
|
352
|
-
}
|
353
|
-
c = column_names - [options[:group]]
|
354
|
-
|
355
|
-
end
|
356
|
-
data.map! { |g|
|
357
|
-
Ruport::Data::Table.new(
|
358
|
-
:data => g.map { |x| x.reorder(*c) },
|
359
|
-
:column_names => c
|
360
|
-
)
|
361
|
-
}
|
362
|
-
rec = if options[:group].kind_of? Array
|
363
|
-
Ruport::Data::Record.new(data,
|
364
|
-
:attributes => group.map { |e| e.join("_") } )
|
365
|
-
else
|
366
|
-
Ruport::Data::Record.new data, :attributes => group
|
367
|
-
end
|
368
|
-
class << rec
|
369
|
-
def each_group; attributes.each { |a| yield(a) }; end
|
370
|
-
end; rec
|
371
|
-
end
|
372
|
-
|
373
|
-
#
|
374
|
-
# Calculates sums. If a column name or index is given, it will try to
|
375
|
-
# convert each element of that column to an integer or float
|
376
|
-
# and add them together.
|
377
|
-
#
|
378
|
-
# If a block is given, it yields each Record so that you can do your own
|
379
|
-
# calculation.
|
380
|
-
#
|
381
|
-
# Example:
|
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
|
388
|
-
#
|
389
|
-
def sigma(column=nil)
|
390
|
-
inject(0) { |s,r|
|
391
|
-
if column
|
392
|
-
s + if r[column].kind_of? Numeric
|
393
|
-
r[column]
|
394
|
-
else
|
395
|
-
r[column] =~ /\./ ? r[column].to_f : r[column].to_i
|
396
|
-
end
|
397
|
-
else
|
398
|
-
s + yield(r)
|
399
|
-
end
|
400
|
-
}
|
401
|
-
end
|
402
|
-
|
403
|
-
alias_method :sum, :sigma
|
404
|
-
|
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
|
414
|
-
end
|