jsanders-ruport 1.7.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.
Files changed (76) hide show
  1. data/AUTHORS +48 -0
  2. data/LICENSE +59 -0
  3. data/README +114 -0
  4. data/Rakefile +93 -0
  5. data/examples/RWEmerson.jpg +0 -0
  6. data/examples/anon.rb +43 -0
  7. data/examples/btree/commaleon/commaleon.rb +263 -0
  8. data/examples/btree/commaleon/sample_data/ticket_count.csv +124 -0
  9. data/examples/btree/commaleon/sample_data/ticket_count2.csv +119 -0
  10. data/examples/centered_pdf_text_box.rb +83 -0
  11. data/examples/data/tattle.dump +82 -0
  12. data/examples/example.csv +3 -0
  13. data/examples/line_plotter.rb +61 -0
  14. data/examples/pdf_report_with_common_base.rb +72 -0
  15. data/examples/png_embed.rb +54 -0
  16. data/examples/roadmap.png +0 -0
  17. data/examples/row_renderer.rb +39 -0
  18. data/examples/simple_pdf_lines.rb +25 -0
  19. data/examples/simple_templating_example.rb +34 -0
  20. data/examples/tattle_ruby_version.rb +39 -0
  21. data/examples/tattle_rubygems_version.rb +37 -0
  22. data/examples/trac_ticket_status.rb +59 -0
  23. data/lib/ruport.rb +127 -0
  24. data/lib/ruport/controller.rb +616 -0
  25. data/lib/ruport/controller/grouping.rb +71 -0
  26. data/lib/ruport/controller/table.rb +54 -0
  27. data/lib/ruport/data.rb +4 -0
  28. data/lib/ruport/data/feeder.rb +111 -0
  29. data/lib/ruport/data/grouping.rb +399 -0
  30. data/lib/ruport/data/record.rb +297 -0
  31. data/lib/ruport/data/table.rb +950 -0
  32. data/lib/ruport/extensions.rb +4 -0
  33. data/lib/ruport/formatter.rb +254 -0
  34. data/lib/ruport/formatter/csv.rb +149 -0
  35. data/lib/ruport/formatter/html.rb +161 -0
  36. data/lib/ruport/formatter/pdf.rb +591 -0
  37. data/lib/ruport/formatter/template.rb +187 -0
  38. data/lib/ruport/formatter/text.rb +231 -0
  39. data/lib/uport.rb +1 -0
  40. data/test/controller_test.rb +743 -0
  41. data/test/csv_formatter_test.rb +164 -0
  42. data/test/data_feeder_test.rb +88 -0
  43. data/test/grouping_test.rb +410 -0
  44. data/test/helpers.rb +11 -0
  45. data/test/html_formatter_test.rb +201 -0
  46. data/test/pdf_formatter_test.rb +354 -0
  47. data/test/record_test.rb +332 -0
  48. data/test/samples/addressbook.csv +6 -0
  49. data/test/samples/data.csv +3 -0
  50. data/test/samples/data.tsv +3 -0
  51. data/test/samples/dates.csv +1409 -0
  52. data/test/samples/erb_test.sql +1 -0
  53. data/test/samples/query_test.sql +1 -0
  54. data/test/samples/ruport_test.sql +8 -0
  55. data/test/samples/test.sql +2 -0
  56. data/test/samples/test.yaml +3 -0
  57. data/test/samples/ticket_count.csv +124 -0
  58. data/test/table_pivot_test.rb +134 -0
  59. data/test/table_test.rb +838 -0
  60. data/test/template_test.rb +48 -0
  61. data/test/text_formatter_test.rb +258 -0
  62. data/util/bench/data/record/bench_as_vs_to.rb +18 -0
  63. data/util/bench/data/record/bench_constructor.rb +46 -0
  64. data/util/bench/data/record/bench_indexing.rb +65 -0
  65. data/util/bench/data/record/bench_reorder.rb +35 -0
  66. data/util/bench/data/record/bench_to_a.rb +19 -0
  67. data/util/bench/data/table/bench_column_manip.rb +103 -0
  68. data/util/bench/data/table/bench_dup.rb +24 -0
  69. data/util/bench/data/table/bench_init.rb +67 -0
  70. data/util/bench/data/table/bench_manip.rb +125 -0
  71. data/util/bench/formatter/bench_csv.rb +14 -0
  72. data/util/bench/formatter/bench_html.rb +14 -0
  73. data/util/bench/formatter/bench_pdf.rb +14 -0
  74. data/util/bench/formatter/bench_text.rb +14 -0
  75. data/util/bench/samples/tattle.csv +1237 -0
  76. metadata +176 -0
@@ -0,0 +1,71 @@
1
+ # Ruport : Extensible Reporting System
2
+ #
3
+ # controller/grouping.rb : Group data controller for Ruby Reports
4
+ #
5
+ # Written by Michael Milner, 2007.
6
+ # Copyright (C) 2007, All Rights Reserved
7
+ #
8
+ # This is free software distributed under the same terms as Ruby 1.8
9
+ # See LICENSE and COPYING for details.
10
+ #
11
+ module Ruport
12
+
13
+ # This class implements the basic controller for a single group of data.
14
+ #
15
+ # == Supported Formatters
16
+ #
17
+ # * Formatter::CSV
18
+ # * Formatter::Text
19
+ # * Formatter::HTML
20
+ # * Formatter::PDF
21
+ #
22
+ # == Default layout options
23
+ #
24
+ # * <tt>show_table_headers</tt> #=> true
25
+ #
26
+ # == Formatter hooks called (in order)
27
+ #
28
+ # * build_group_header
29
+ # * build_group_body
30
+ # * build_group_footer
31
+ #
32
+ class Controller::Group < Controller
33
+ options { |o| o.show_table_headers = true }
34
+
35
+ stage :group_header, :group_body, :group_footer
36
+ end
37
+
38
+ # This class implements the basic controller for data groupings in Ruport
39
+ # (a collection of Groups).
40
+ #
41
+ # == Supported Formatters
42
+ #
43
+ # * Formatter::CSV
44
+ # * Formatter::Text
45
+ # * Formatter::HTML
46
+ # * Formatter::PDF
47
+ #
48
+ # == Default layout options
49
+ #
50
+ # * <tt>show_group_headers</tt> #=> true
51
+ # * <tt>style</tt> #=> :inline
52
+ #
53
+ # == Formatter hooks called (in order)
54
+ #
55
+ # * build_grouping_header
56
+ # * build_grouping_body
57
+ # * build_grouping_footer
58
+ # * finalize_grouping
59
+ #
60
+ class Controller::Grouping < Controller
61
+ options do |o|
62
+ o.show_group_headers = true
63
+ o.style = :inline
64
+ end
65
+
66
+ stage :grouping_header, :grouping_body, :grouping_footer
67
+
68
+ finalize :grouping
69
+ end
70
+
71
+ end
@@ -0,0 +1,54 @@
1
+ # controller/table.rb : Tabular data controller for Ruby Reports
2
+ #
3
+ # Written by Gregory Brown, December 2006. Copyright 2006, All Rights Reserved
4
+ # This is Free Software, please see LICENSE and COPYING for details.
5
+
6
+ module Ruport
7
+
8
+ # This class implements the basic controller for table rows.
9
+ #
10
+ # == Supported Formatters
11
+ #
12
+ # * Formatter::CSV
13
+ # * Formatter::Text
14
+ # * Formatter::HTML
15
+ #
16
+ # == Formatter hooks called (in order)
17
+ #
18
+ # * build_row
19
+ #
20
+ class Controller::Row < Controller
21
+ stage :row
22
+ end
23
+
24
+ # This class implements the basic tabular data controller for Ruport.
25
+ #
26
+ # == Supported Formatters
27
+ #
28
+ # * Formatter::CSV
29
+ # * Formatter::Text
30
+ # * Formatter::HTML
31
+ # * Formatter::PDF
32
+ #
33
+ # == Default layout options
34
+ #
35
+ # * <tt>show_table_headers</tt> #=> true
36
+ #
37
+ # == Formatter hooks called (in order)
38
+ #
39
+ # * prepare_table
40
+ # * build_table_header
41
+ # * build_table_body
42
+ # * build_table_footer
43
+ # * finalize_table
44
+ #
45
+ class Controller::Table < Controller
46
+ options { |o| o.show_table_headers = true }
47
+
48
+ prepare :table
49
+
50
+ stage :table_header, :table_body, :table_footer
51
+
52
+ finalize :table
53
+ end
54
+ end
@@ -0,0 +1,4 @@
1
+ require "ruport/data/record"
2
+ require "ruport/data/table"
3
+ require "ruport/data/grouping"
4
+ require "ruport/data/feeder"
@@ -0,0 +1,111 @@
1
+ # Ruport : Extensible Reporting System
2
+ #
3
+ # data/feeder.rb provides a data transformation and filtering proxy for ruport
4
+ #
5
+ # Copyright August 2007, Gregory Brown / Michael Milner. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+
10
+ # This class provides a simple way to apply transformations and filters that
11
+ # get run while you are aggregating data. This is used primarily to build
12
+ # constrained wrappers to Ruport::Data::Table, but can be used with abstract
13
+ # data structures as well.
14
+ #
15
+ # Table Example:
16
+ #
17
+ # t = Table(%w[a b c]) do |feeder|
18
+ # feeder.filter { |r| r.a < 5 }
19
+ # feeder.transform { |r| r.b = "B: #{r.b}"}
20
+ # feeder << [1,2,3]
21
+ # feeder << [7,1,2]
22
+ # feeder << { "a" => 3, "b" => 6, "c" => 7 }
23
+ # end
24
+ # t.length #=> 2
25
+ # t.column("b") #=> ["B: 2","B: 6"]
26
+ #
27
+ # Filters and transforms are added in a sequential order to a single list of
28
+ # constraints. You could add some constraints and then append some data, then
29
+ # add additional constraints, or even build up dynamic constraints if you'd
30
+ # like.
31
+ #
32
+ # Wrapping an arbitrary data object:
33
+ #
34
+ # In order to make Data::Feeder work with an object other than Data::Table, it
35
+ # must implement two things:
36
+ #
37
+ # * A method called feed_element that accepts a single argument.
38
+ # When Feeder#<< is called, the object to be appended is converted by this
39
+ # method, and then yielded to the filters / transforms.
40
+ #
41
+ # * A meaningful #<< method. Feeder#<< simply delegates this to the wrapped
42
+ # object once the filters and transforms have been applied, so be sure that
43
+ # the object returned by feed_element is one that can be used by your #<<
44
+ # method.
45
+ #
46
+ # Here is a sample implementation of wrapping a feeder around an Array.
47
+ #
48
+ # class Array
49
+ # def feed_element(element)
50
+ # element
51
+ # end
52
+ # end
53
+ #
54
+ # int_array = []
55
+ # feeder = Ruport::Data::Feeder.new(int_array)
56
+ # feeder.filter { |r| r.kind_of?(Integer) }
57
+ #
58
+ # feeder << 1 << "5" << 4.7 << "kitten" << 4
59
+ # int_array #=> [1, 4]
60
+ #
61
+ class Ruport::Data::Feeder
62
+
63
+ # Creates a new Data::Feeder, wrapping the data object provided.
64
+ def initialize(data)
65
+ @data = data
66
+ @constraints = []
67
+ end
68
+
69
+ # Accesses the underlying data object directly
70
+ attr_reader :data
71
+
72
+ # Constrained append operation.
73
+ #
74
+ # Before filters and transforms are run, the element to be appended is first
75
+ # converted by data.feed_element(some_element)
76
+ #
77
+ # Filters and transforms are then run sequentially, and if the constraints
78
+ # are met, it is appended using data << some_element.
79
+ #
80
+ def <<(element)
81
+ feed_element = data.feed_element(element)
82
+
83
+ @constraints.each do |type,block|
84
+ if type == :filter
85
+ return self unless block[feed_element]
86
+ else
87
+ block[feed_element]
88
+ end
89
+ end
90
+
91
+ data << feed_element
92
+ return self
93
+ end
94
+
95
+ # Creates a filter which must be satisfied for an object to be appended via
96
+ # the feeder.
97
+ #
98
+ # feeder.filter { |r| r.length < 4 }
99
+ #
100
+ def filter(&block)
101
+ @constraints << [:filter,block]
102
+ end
103
+
104
+ # Creates a transformation which may change the object as it is appended.
105
+ #
106
+ # feeder.transform { |r| r.a += 10 }
107
+ def transform(&block)
108
+ @constraints << [:transform,block]
109
+ end
110
+
111
+ end
@@ -0,0 +1,399 @@
1
+ # Ruport : Extensible Reporting System
2
+ #
3
+ # data/grouping.rb provides group and grouping data structures for Ruport.
4
+ #
5
+ # Created by Michael Milner / Gregory Brown, 2007
6
+ # Copyright (C) 2007 Michael Milner / Gregory Brown, All Rights Reserved.
7
+ #
8
+ # This is free software distributed under the same terms as Ruby 1.8
9
+ # See LICENSE and COPYING for details.
10
+ #
11
+ module Ruport::Data
12
+
13
+ # === Overview
14
+ #
15
+ # This class implements a group data structure for Ruport. Group is
16
+ # simply a subclass of Table that adds a <tt>:name</tt> attribute.
17
+ #
18
+ class Group < Table
19
+
20
+ # The name of the group
21
+ attr_reader :name
22
+
23
+ # A hash of subgroups
24
+ attr_reader :subgroups
25
+
26
+ # Creates a new Group based on the supplied options.
27
+ #
28
+ # Valid options:
29
+ # <b><tt>:name</tt></b>:: The name of the Group
30
+ # <b><tt>:data</tt></b>:: An Array of Arrays representing the
31
+ # records in this Group
32
+ # <b><tt>:column_names</tt></b>:: An Array containing the column names
33
+ # for this Group.
34
+ #
35
+ # Example:
36
+ #
37
+ # group = Group.new :name => 'My Group',
38
+ # :data => [[1,2,3], [3,4,5]],
39
+ # :column_names => %w[a b c]
40
+ #
41
+ def initialize(options={})
42
+ @name = options.delete(:name)
43
+ @subgroups = {}
44
+ super
45
+ end
46
+
47
+ include Ruport::Controller::Hooks
48
+ renders_as_group
49
+
50
+ def self.inherited(base) #:nodoc:
51
+ base.renders_as_group
52
+ end
53
+
54
+ def initialize_copy(from) #:nodoc:
55
+ super
56
+ @name = from.name
57
+ @subgroups = from.subgroups.inject({}) { |h,d|
58
+ h.merge!({ d[0] => d[1].dup }) }
59
+ end
60
+
61
+ # Compares this Group to another Group and returns <tt>true</tt> if
62
+ # the <tt>name</tt>, <tt>data</tt>, and <tt>column_names</tt> are equal.
63
+ #
64
+ # Example:
65
+ #
66
+ # one = Group.new :name => 'test',
67
+ # :data => [[1,2], [3,4]],
68
+ # :column_names => %w[a b]
69
+ #
70
+ # two = Group.new :name => 'test',
71
+ # :data => [[1,2], [3,4]],
72
+ # :column_names => %w[a b]
73
+ #
74
+ # one.eql?(two) #=> true
75
+ #
76
+ def eql?(other)
77
+ name.eql?(other.name) && super
78
+ end
79
+
80
+ alias_method :==, :eql?
81
+
82
+ protected
83
+
84
+ attr_writer :name, :subgroups #:nodoc:
85
+
86
+ private
87
+
88
+ # Creates subgroups for the group based on the supplied column name. Each
89
+ # subgroup is a hash whose keys are the unique values in the column.
90
+ #
91
+ # Example:
92
+ #
93
+ # main_group = Group.new :name => 'test',
94
+ # :data => [[1,2,3,4,5], [3,4,5,6,7]],
95
+ # :column_names => %w[a b c d e]
96
+ # main_group.create_subgroups("a")
97
+ #
98
+ def create_subgroups(group_column)
99
+ if @subgroups.empty?
100
+ @subgroups = grouped_data(group_column)
101
+ else
102
+ @subgroups.each {|name,group|
103
+ group.send(:create_subgroups, group_column)
104
+ }
105
+ end
106
+ end
107
+
108
+ def grouped_data(group_column) #:nodoc:
109
+ data = {}
110
+ group_names = column(group_column).uniq
111
+ columns = column_names.dup
112
+ columns.delete(group_column)
113
+ group_names.each do |name|
114
+ group_data = sub_table(columns) {|r|
115
+ r.send(group_column) == name
116
+ }
117
+ data[name] = Group.new(:name => name, :data => group_data,
118
+ :column_names => columns,
119
+ :record_class => record_class)
120
+ end
121
+ data
122
+ end
123
+
124
+ end
125
+
126
+
127
+ # === Overview
128
+ #
129
+ # This class implements a grouping data structure for Ruport. A grouping is
130
+ # a collection of groups. It allows you to group the data in a table by one
131
+ # or more columns that you specify.
132
+ #
133
+ # The data for a grouping is a hash of groups, keyed on each unique data
134
+ # point from the grouping column.
135
+ #
136
+ class Grouping
137
+
138
+ include Enumerable
139
+
140
+ # Creates a new Grouping based on the supplied options.
141
+ #
142
+ # Valid options:
143
+ # <b><tt>:by</tt></b>:: A column name or array of column names that the
144
+ # data will be grouped on.
145
+ # <b><tt>:order</tt></b>:: Determines the iteration and presentation order
146
+ # of a Grouping object. Set to :name to order by
147
+ # Group names. You can also provide a lambda which
148
+ # will be passed Group objects, and use semantics
149
+ # similar to Enumerable#group_by
150
+ #
151
+ # Examples:
152
+ #
153
+ # table = [[1,2,3],[4,5,6],[1,1,2]].to_table(%w[a b c])
154
+ #
155
+ # # unordered
156
+ # grouping = Grouping.new(table, :by => "a")
157
+ #
158
+ # # ordered by group name
159
+ # grouping = Grouping.new(table, :by => "a", :order => :name)
160
+ #
161
+ # # ordered by group size
162
+ # grouping = Grouping.new(table, :by => "a",
163
+ # :order => lambda { |g| g.size } )
164
+ def initialize(data={},options={})
165
+ if data.kind_of?(Hash)
166
+ @grouped_by = data[:by]
167
+ @order = data[:order]
168
+ @data = {}
169
+ else
170
+ @grouped_by = options[:by]
171
+ @order = options[:order]
172
+ cols = Array(options[:by]).dup
173
+ @data = data.to_group.send(:grouped_data, cols.shift)
174
+ cols.each do |col|
175
+ @data.each do |name,group|
176
+ group.send(:create_subgroups, col)
177
+ end
178
+ end
179
+ end
180
+ end
181
+
182
+ # The grouping's data
183
+ attr_accessor :data
184
+
185
+ # The name of the column used to group the data
186
+ attr_reader :grouped_by
187
+
188
+ # Allows Hash-like indexing of the grouping data.
189
+ #
190
+ # Examples:
191
+ #
192
+ # my_grouping["foo"]
193
+ #
194
+ def [](name)
195
+ @data[name] or
196
+ raise(IndexError,"Group Not Found")
197
+ end
198
+
199
+ # Iterates through the Grouping, yielding each group name and Group object
200
+ #
201
+ def each
202
+ if @order.respond_to?(:call)
203
+ @data.sort_by { |n,g| @order[g] }.each { |n,g| yield(n,g) }
204
+ elsif @order == :name
205
+ @data.sort_by { |n,g| n }.each { |name,group| yield(name,group) }
206
+ else
207
+ @data.each { |name,group| yield(name,group) }
208
+ end
209
+ end
210
+
211
+
212
+ # Returns a new grouping with the specified sort order.
213
+ # You can sort by Group name or an arbitrary block
214
+ #
215
+ # by_name = grouping.sort_grouping_by(:name)
216
+ # by_size = grouping.sort_grouping_by { |g| g.size }
217
+ def sort_grouping_by(type=nil,&block)
218
+ a = Grouping.new(:by => @grouped_by, :order => type || block)
219
+ each { |n,g| a << g }
220
+ return a
221
+ end
222
+
223
+ # Applies the specified sort order to an existing Grouping object.
224
+ #
225
+ # grouping.sort_grouping_by!(:name)
226
+ # grouping.sort_grouping_by! { |g| g.size }
227
+ def sort_grouping_by!(type=nil,&block)
228
+ @order = type || block
229
+ end
230
+
231
+ # Used to add extra data to the Grouping. <tt>group</tt> should be a Group.
232
+ #
233
+ # Example:
234
+ #
235
+ # table = [[1,2,3],[4,5,6]].to_table(%w[a b c])
236
+ #
237
+ # grouping = Grouping.new(table, :by => "a")
238
+ #
239
+ # group = Group.new :name => 7,
240
+ # :data => [[8,9]],
241
+ # :column_names => %w[b c]
242
+ #
243
+ # grouping << group
244
+ #
245
+ def <<(group)
246
+ if data.has_key? group.name
247
+ raise(ArgumentError, "Group '#{group.name}' exists!")
248
+ end
249
+ @data.merge!({ group.name => group })
250
+ end
251
+
252
+ alias_method :append, :<<
253
+
254
+ # Provides access to the subgroups of a particular group in the Grouping.
255
+ # Supply the name of a group and it returns a Grouping created from the
256
+ # subgroups of the group.
257
+ #
258
+ def subgrouping(name)
259
+ grouping = dup
260
+ grouping.send(:data=, @data[name].subgroups)
261
+ return grouping
262
+ end
263
+
264
+ alias_method :/, :subgrouping
265
+
266
+ # Useful for creating basic summaries from Grouping objects.
267
+ # Takes a field to summarize on, and then for each group,
268
+ # runs the specified procs and returns the results as a Table.
269
+ #
270
+ # The following example would show for each date group,
271
+ # the sum for the attributes or methods <tt>:opened</tt> and
272
+ # <tt>:closed</tt> and order them by the <tt>:order</tt> array.
273
+ #
274
+ # If <tt>:order</tt> is not specified, you cannot depend on predictable
275
+ # column order.
276
+ #
277
+ # grouping.summary :date,
278
+ # :opened => lambda { |g| g.sigma(:opened) },
279
+ # :closed => lambda { |g| g.sigma(:closed) },
280
+ # :order => [:date,:opened,:closed]
281
+ #
282
+ def summary(field,procs)
283
+ if procs[:order].kind_of?(Array)
284
+ cols = procs.delete(:order)
285
+ else
286
+ cols = procs.keys + [field]
287
+ end
288
+ expected = Table(cols) { |t|
289
+ each do |name,group|
290
+ t << procs.inject({field => name}) do |s,r|
291
+ s.merge(r[0] => r[1].call(group))
292
+ end
293
+ end
294
+ t.data.reorder(cols)
295
+ }
296
+ end
297
+
298
+ # Uses Ruport's built-in text formatter to render this Grouping
299
+ #
300
+ # Example:
301
+ #
302
+ # table = [[1,2,3],[4,5,6]].to_table(%w[a b c])
303
+ #
304
+ # grouping = Grouping.new(table, :by => "a")
305
+ #
306
+ # puts grouping.to_s
307
+ #
308
+ def to_s
309
+ as(:text)
310
+ end
311
+
312
+ # Calculates sums. If a column name or index is given, it will try to
313
+ # convert each element of that column to an integer or float
314
+ # and add them together. The sum is calculated across all groups in
315
+ # the grouping.
316
+ #
317
+ # If a block is given, it yields each Record in each Group so that you can
318
+ # do your own calculation.
319
+ #
320
+ # Example:
321
+ #
322
+ # table = [[1,2,3],[3,4,5],[5,6,7]].to_table(%w[col1 col2 col3])
323
+ # grouping = Grouping(table, :by => "col1")
324
+ # grouping.sigma("col2") #=> 12
325
+ # grouping.sigma(0) #=> 12
326
+ # grouping.sigma { |r| r.col2 + r.col3 } #=> 27
327
+ # grouping.sigma { |r| r.col2 + 1 } #=> 15
328
+ #
329
+ def sigma(column=nil)
330
+ inject(0) do |s, (group_name, group)|
331
+ if column
332
+ s + group.sigma(column)
333
+ else
334
+ s + group.sigma do |r|
335
+ yield(r)
336
+ end
337
+ end
338
+ end
339
+ end
340
+
341
+ alias_method :sum, :sigma
342
+
343
+ include Ruport::Controller::Hooks
344
+ renders_as_grouping
345
+
346
+ def self.inherited(base) #:nodoc:
347
+ base.renders_as_grouping
348
+ end
349
+
350
+ # Create a copy of the Grouping. Groups will be copied as well.
351
+ #
352
+ # Example:
353
+ #
354
+ # table = [[1,2,3],[4,5,6]].to_table(%w[a b c])
355
+ # one = Ruport::Data::Grouping.new(a, :by => "a")
356
+ #
357
+ # two = one.dup
358
+ #
359
+ def initialize_copy(from) #:nodoc:
360
+ @grouped_by = from.grouped_by
361
+ @data = from.data.inject({}) { |h,d| h.merge!({ d[0] => d[1].dup }) }
362
+ end
363
+
364
+ # Provides a shortcut for the <tt>as()</tt> method by converting a call to
365
+ # <tt>to_format_name</tt> into a call to <tt>as(:format_name)</tt>.
366
+ #
367
+ def method_missing(id,*args)
368
+ return as($1.to_sym,*args) if id.to_s =~ /^to_(.*)/
369
+ super
370
+ end
371
+
372
+ end
373
+
374
+ end
375
+
376
+ module Kernel
377
+
378
+ # Shortcut interface for creating Data::Grouping
379
+ #
380
+ # Example:
381
+ #
382
+ # a = Table(%w[a b c], :data => [[1,2,3],[4,5,6]])
383
+ # b = Grouping(a, :by => "a") #=> creates a new grouping on column "a"
384
+ #
385
+ def Grouping(*args)
386
+ Ruport::Data::Grouping.new(*args)
387
+ end
388
+
389
+ # Shortcut interface for creating Data::Group
390
+ #
391
+ # Example:
392
+ #
393
+ # g = Group('mygroup', :data => [[1,2,3],[4,5,6]],
394
+ # :column_names => %w[a b c]) #=> creates a new group named mygroup
395
+ #
396
+ def Group(name,opts={})
397
+ Ruport::Data::Group.new(opts.merge(:name => name))
398
+ end
399
+ end