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.
@@ -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