ruport 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,6 +7,9 @@ require 'set'
7
7
 
8
8
  module Ruport::Data
9
9
 
10
+ #
11
+ # === Overview
12
+ #
10
13
  # This class is one of the core classes for building and working with data
11
14
  # in Ruport. The idea is to get your data into a standard form, regardless
12
15
  # of its source (a database, manual arrays, ActiveRecord, CSVs, etc.).
@@ -17,19 +20,29 @@ module Ruport::Data
17
20
  #
18
21
  # Once your data is in a Ruport::Data::Set object, it can be manipulated
19
22
  # to suit your needs, then used to build a report.
23
+ #
20
24
  class Set < Collection
21
25
 
22
- # Creates a new set containing the elements of options[:data].
26
+ #
27
+ # Creates a new Set containing the elements of <tt>options[:data]</tt>.
28
+ #
29
+ # Example:
23
30
  #
24
31
  # Set.new :data => [%w[one two three] %w[1 2 3] %w[I II III]]
32
+ #
25
33
  def initialize(options={})
26
34
  @data = ::Set.new
27
35
  options[:data].each {|e| self << e} if options[:data]
28
36
  end
29
37
 
30
- # Adds the given object to the set and returns self.
38
+ #
39
+ # Adds the given object to the Set and returns self.
40
+ #
41
+ # Example:
42
+ #
31
43
  # set = Set.new :data => [%w[one two three]]
32
44
  # set << [5,6,7]
45
+ #
33
46
  def add(other)
34
47
  case other
35
48
  when Record
@@ -41,14 +54,18 @@ module Ruport::Data
41
54
  end
42
55
  alias_method :<<, :add
43
56
 
44
- # Produces a shallow copy of the set: the same data elements are
45
- # referenced by both the old and new sets.
57
+ #
58
+ # Produces a shallow copy of the Set: the same data elements are
59
+ # referenced by both the old and new Sets.
60
+ #
61
+ # Example:
46
62
  #
47
63
  # set = Set.new :data => [%w[one two three]]
48
64
  # set2 = set.dup
49
65
  # set == set2 #=> true
50
66
  # set << [8,9,10]
51
67
  # set == set2 #=> false
68
+ #
52
69
  def dup
53
70
  a = self.class.new(:data=>@data)
54
71
  a.tags = tags.dup
@@ -56,62 +73,80 @@ module Ruport::Data
56
73
  end
57
74
  alias_method :clone, :dup
58
75
 
59
- # Equality. Two sets are equal if they contain the same set of objects.
76
+ #
77
+ # Two Sets are equal if they contain the same set of objects.
78
+ #
79
+ # Example:
60
80
  # s1 = Set.new :data => [[1,2,3]]
61
81
  # s2 = Set.new :data => [[1,2,3]]
62
82
  # s1 == s2 #=> true
83
+ #
63
84
  def ==(other)
64
85
  @data == other.data
65
86
  end
66
87
 
67
- # Union. Returns a new set containing the union of the objects contained in
68
- # the two sets.
88
+ # Returns a new Set containing the all of the objects contained in either
89
+ # of the two Sets.
90
+ #
91
+ # Example:
69
92
  #
70
93
  # s1 = Set.new :data => [[1,2,3]]
71
94
  # s2 = Set.new :data => [[4,5,6]]
72
95
  # s3 = s1 | s2
73
96
  # s4 = Set.new :data => [[1,2,3], [4,5,6]]
74
97
  # s3 == s4 #=> true
98
+ #
75
99
  def |(other)
76
100
  Set.new :data => (@data | other.data)
77
101
  end
78
102
  alias_method :union, :|
79
103
  alias_method :+, :|
80
104
 
81
- # Intersection. Returns a new set containing the objects common to the two
82
- # sets.
105
+ #
106
+ # Returns a new Set containing the objects common to the two Sets.
107
+ #
108
+ # Example:
83
109
  #
84
110
  # s1 = Set.new :data => [%w[a b c],[1,2,3]]
85
111
  # s2 = Set.new :data => [%w[a b c],[4,5,6]]
86
112
  # s3 = s1 & s2
87
113
  # s4 = Set.new :data => [%w[a b c]]
88
114
  # s3 == s4 #=> true
115
+ #
89
116
  def &(other)
90
117
  Set.new :data => (@data & other.data)
91
118
  end
92
119
  alias_method :intersection, :&
93
120
 
94
- # Difference. Returns a new set containing those objects present in this
95
- # set but not the other.
121
+ #
122
+ # Returns a new Set containing those objects present in this Set but not
123
+ # the other.
124
+ #
125
+ # Example:
96
126
  #
97
127
  # s1 = Set.new :data => [%w[a b c],[1,2,3]]
98
128
  # s2 = Set.new :data => [%w[a b c],[4,5,6]]
99
129
  # s3 = s1 - s2
100
130
  # s4 = Set.new :data => [[1, 2, 3]]
101
131
  # s3 == s4 #=> true
132
+ #
102
133
  def -(other)
103
134
  Set.new :data => (@data - other.data)
104
135
  end
105
136
  alias_method :difference, :-
106
137
 
107
- # Exclusion. Returns a new set containing those objects in this set or the
108
- # other set but not in both.
138
+ #
139
+ # Returns a new Set containing those objects that are either in this Set
140
+ # or the other Set but not in both.
141
+ #
142
+ # Example:
109
143
  #
110
144
  # s1 = Set.new :data => [%w[a b c],[1,2,3]]
111
145
  # s2 = Set.new :data => [%w[a b c],[4,5,6]]
112
146
  # s3 = s1 ^ s2
113
147
  # s4 = Set.new :data => [[1, 2, 3],[4,5,6]]
114
148
  # s3 == s4 #=> true
149
+ #
115
150
  def ^(other)
116
151
  Set.new :data => (@data ^ other.data)
117
152
  end
@@ -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 are :data and :column_names.
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
- # Sets the column names for this table. The single parameter should be
58
- # an array listing the names of the columns.
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:
59
75
  #
60
- # tbl = Table.new({:data => [1,2,3], [3,4,5], :column_names => %w[a b c]})
61
- # tbl.column_names = %w[e f g]
62
- def column_names=(other)
63
- @column_names.replace(other.dup)
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(new_column_names.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
- # one = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
70
- # two = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
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
- # Uses Ruport's built-in text plugin to render this table into a string
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({:data => [1,2], [3,4], :column_names => %w[a b]})
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
- # data = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
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 << Ruport::Data::Record.new [5,6], :attributes => %w[a b]
128
+ # data << Record.new [5,6], :attributes => %w[a b]
129
+ #
93
130
  def <<(other)
94
131
  case other
95
132
  when Array
@@ -108,26 +145,40 @@ module Ruport::Data
108
145
  self
109
146
  end
110
147
 
111
- # Used to combine two tables. Throws an ArgumentError if the tables don't
148
+ #
149
+ # Used to combine two Tables. Throws an ArgumentError if the Tables don't
112
150
  # have identical columns.
113
151
  #
114
- # inky = Table.new(:data => [[1,2], [3,4]], :column_names => %w[a b])
115
- # blinky = Table.new(:data => [[5,6]], :column_names => %w[a b])
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
- # data = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
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
181
+
131
182
  if @column_names && !@column_names.empty?
132
183
  x = if indices.all? { |i| i.kind_of? Integer }
133
184
  indices.map { |i| @column_names[i] }
@@ -142,21 +193,34 @@ module Ruport::Data
142
193
  }; self
143
194
  end
144
195
 
145
- # Returns a copy of the table with its columns in the requested order.
146
196
  #
147
- # one = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
197
+ # Returns a copy of the Table with its columns in the requested order.
198
+ #
199
+ # Example:
200
+ #
201
+ # one = Table.new :data => [1,2], [3,4],
202
+ # :column_names => %w[a b]
203
+ #
148
204
  # two = one.reorder!([1,0])
205
+ #
149
206
  def reorder(*indices)
150
207
  dup.reorder!(*indices)
151
208
  end
152
209
 
153
- # Adds an extra column to the table. Accepts an options Hash as its
154
- # only parameter which should contain 2 keys - :name and :fill.
155
- # :name specifies the new columns name, and :fill the default value to
156
- # use for the column in existing rows.
210
+ #
211
+ # Adds an extra column to the Table. Available Options:
212
+ #
213
+ # <b><tt>:name</tt></b>:: The new column's name (required)
214
+ # <b><tt>:fill</tt></b>:: The default value to use for the column in
215
+ # existing rows. Set to nil if not specified.
157
216
  #
158
- # data = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
159
- # data.append_column({:name => 'new_column', :fill => 1)
217
+ # Example:
218
+ #
219
+ # data = Table.new :data => [1,2], [3,4],
220
+ # :column_names => %w[a b]
221
+ #
222
+ # data.append_column :name => 'new_column', :fill => 1
223
+ #
160
224
  def append_column(options={})
161
225
  self.column_names += [options[:name]] if options[:name]
162
226
  if block_given?
@@ -166,17 +230,21 @@ module Ruport::Data
166
230
  end; self
167
231
  end
168
232
 
169
- # Removes a column from the table. Any values in the specified column are
233
+ #
234
+ # Removes a column from the Table. Any values in the specified column are
170
235
  # lost.
171
- # data = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
172
- # data.append_column({:name => 'new_column', :fill => 1)
173
- # data.remove_column({:name => 'new_column')
174
- # data == Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
175
- # #=> true
176
- #
236
+ #
237
+ # Example:
238
+ #
239
+ # data = Table.new :data => [[1,2], [3,4]], :column_names => %w[a b]
240
+ # data.append_column :name => 'new_column', :fill => 1
241
+ # data.remove_column :name => 'new_column'
242
+ # data == Table.new :data => [[1,2], [3,4]],
243
+ # :column_names => %w[a b] #=> true
177
244
  # data = [[1,2],[3,4]].to_table
178
245
  # data.remove_column(1)
179
246
  # data.eql? [[1],[3]].to_table %w[a] #=> true
247
+ #
180
248
  def remove_column(options={})
181
249
  if options.kind_of? Integer
182
250
  return reorder!((0...data[0].length).to_a - [options])
@@ -190,29 +258,50 @@ module Ruport::Data
190
258
  reorder! column_names - [name]
191
259
  end
192
260
 
193
- # Create a shallow copy of the table: the same data elements are referenced
194
- # by both the old and new table.
195
261
  #
196
- # one = Table.new({:data => [1,2], [3,4], :column_names => %w[a b]})
262
+ # Create a copy of the Table: records will be copied as well.
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
- # Loads a CSV file directly into a table using the fasterCSV library.
276
+ #
277
+ # Loads a CSV file directly into a Table using the FasterCSV library.
278
+ #
279
+ # Example:
205
280
  #
206
- # data = Table.load('mydata.csv')
207
- def self.load(csv_file, options={})
208
- get_table_from_csv(:foreach, csv_file, options)
281
+ # # treat first row as column_names
282
+ # table = Table.load('mydata.csv')
283
+ #
284
+ # # do not assume the data has column_names
285
+ # table = Table.load('mydata.csv',:has_names => false)
286
+ #
287
+ # # pass in FasterCSV options, such as column separators
288
+ # table = Table.load('mydata.csv',:csv_opts => { :col_sep => "\t" })
289
+ #
290
+ def self.load(csv_file, options={},&block)
291
+ get_table_from_csv(:foreach, csv_file, options,&block)
209
292
  end
210
-
211
- def self.parse(string, options={})
212
- get_table_from_csv(:parse,string,options)
293
+
294
+ #
295
+ # Creates a Table from a CSV string using FasterCSV. See Table.load for
296
+ # additional examples.
297
+ #
298
+ # table = Table.parse("a,b,c\n1,2,3\n4,5,6\n")
299
+ #
300
+ def self.parse(string, options={},&block)
301
+ get_table_from_csv(:parse,string,options,&block)
213
302
  end
214
303
 
215
- def self.get_table_from_csv(msg,param,options={})
304
+ def self.get_table_from_csv(msg,param,options={},&block) #:nodoc:
216
305
  options = {:has_names => true,
217
306
  :csv_options => {} }.merge(options)
218
307
  require "fastercsv"
@@ -223,15 +312,16 @@ module Ruport::Data
223
312
  if first_line && options[:has_names]
224
313
  loaded_data.column_names = row
225
314
  first_line = false
226
- elsif !block_given?
315
+ elsif !block
227
316
  loaded_data << row
228
317
  else
229
- yield(loaded_data,row)
318
+ block[loaded_data,row]
230
319
  end
231
320
  end ; loaded_data
232
321
  end
233
322
 
234
- # Allows you to split tables into multiple tables for grouping.
323
+ #
324
+ # Allows you to split Tables into multiple Tables for grouping.
235
325
  #
236
326
  # Example:
237
327
  #
@@ -246,8 +336,8 @@ module Ruport::Data
246
336
  # b.greg.eql? [[1,2,3],[7,8,9]].to_table(%w[a b c]) #=> true
247
337
  # b["joe"].eql? [[2,3,4],[1,2,3]].to_table(%w[a b c]) #=> true
248
338
  #
249
- # You can also pass an array to :group, and the resulting attributes in
250
- # the group will be joined by an underscore.
339
+ # You can also pass an Array to <tt>:group</tt>, and the resulting
340
+ # attributes in the group will be joined by an underscore.
251
341
  #
252
342
  # Example:
253
343
  #
@@ -260,6 +350,7 @@ module Ruport::Data
260
350
  # a.greg_brown.length #=> 2
261
351
  # a["greg_gibson"].length #=> 1
262
352
  # a.greg_brown[0].x #=> "foo"
353
+ #
263
354
  def split(options={})
264
355
  if options[:group].kind_of? Array
265
356
  group = map { |r| options[:group].map { |e| r[e] } }.uniq
@@ -292,23 +383,22 @@ module Ruport::Data
292
383
  end; rec
293
384
  end
294
385
 
295
- # Calculates sums. If a column name or index is given, it will try to
386
+ #
387
+ # Calculates sums. If a column name or index is given, it will try to
296
388
  # convert each element of that column to an integer or float
297
- # and add it together
298
- #
299
- # If a block is given, yields each Record so that you can do a calculation.
389
+ # and add them together.
300
390
  #
391
+ # If a block is given, it yields each Record so that you can do your own
392
+ # calculation.
301
393
  #
302
394
  # Example:
303
395
  #
396
+ # table = [[1,2],[3,4],[5,6]].to_table(%w[col1 col2])
397
+ # table.sigma("col1") #=> 9
398
+ # table.sigma(0) #=> 9
399
+ # table.sigma { |r| r.col1 + r.col2 } #=> 21
400
+ # table.sigma { |r| r.col2 + 1 } #=> 15
304
401
  #
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
402
  def sigma(column=nil)
313
403
  inject(0) { |s,r|
314
404
  if column
@@ -329,7 +419,7 @@ module Ruport::Data
329
419
 
330
420
  end
331
421
 
332
- module Ruport::Data::TableHelper
422
+ module Ruport::Data::TableHelper #:nodoc:
333
423
  def table(names=[])
334
424
  t = [].to_table(names)
335
425
  yield(t) if block_given?; t