ruport 0.8.14 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/README +42 -107
  2. data/Rakefile +29 -32
  3. data/examples/centered_pdf_text_box.rb +13 -19
  4. data/examples/example.csv +3 -0
  5. data/examples/line_plotter.rb +15 -15
  6. data/examples/pdf_complex_report.rb +10 -23
  7. data/examples/pdf_table_with_title.rb +12 -12
  8. data/examples/rope_examples/itunes/Rakefile +22 -1
  9. data/examples/rope_examples/itunes/config/environment.rb +4 -0
  10. data/examples/rope_examples/itunes/lib/init.rb +32 -2
  11. data/examples/rope_examples/itunes/util/build +50 -16
  12. data/examples/rope_examples/sales_report/README +1 -1
  13. data/examples/rope_examples/sales_report/Rakefile +22 -1
  14. data/examples/rope_examples/sales_report/config/environment.rb +4 -0
  15. data/examples/rope_examples/sales_report/lib/init.rb +32 -2
  16. data/examples/rope_examples/sales_report/lib/reports/sales.rb +10 -16
  17. data/examples/rope_examples/sales_report/util/build +50 -16
  18. data/examples/row_renderer.rb +39 -0
  19. data/examples/ruport_list/png_embed.rb +61 -0
  20. data/examples/ruport_list/roadmap.png +0 -0
  21. data/examples/sample.rb +16 -0
  22. data/examples/simple_pdf_lines.rb +24 -0
  23. data/lib/ruport.rb +143 -57
  24. data/lib/ruport/acts_as_reportable.rb +246 -0
  25. data/lib/ruport/data.rb +1 -2
  26. data/lib/ruport/data/grouping.rb +311 -0
  27. data/lib/ruport/data/record.rb +113 -84
  28. data/lib/ruport/data/table.rb +275 -174
  29. data/lib/ruport/formatter.rb +149 -0
  30. data/lib/ruport/formatter/csv.rb +87 -0
  31. data/lib/ruport/formatter/html.rb +89 -0
  32. data/lib/ruport/formatter/pdf.rb +357 -0
  33. data/lib/ruport/formatter/text.rb +151 -0
  34. data/lib/ruport/generator.rb +127 -30
  35. data/lib/ruport/query.rb +46 -99
  36. data/lib/ruport/renderer.rb +238 -194
  37. data/lib/ruport/renderer/grouping.rb +67 -0
  38. data/lib/ruport/renderer/table.rb +25 -98
  39. data/lib/ruport/report.rb +45 -96
  40. data/test/acts_as_reportable_test.rb +229 -0
  41. data/test/csv_formatter_test.rb +97 -0
  42. data/test/{_test_database.rb → database_test_.rb} +0 -0
  43. data/test/grouping_test.rb +305 -0
  44. data/test/html_formatter_test.rb +104 -0
  45. data/test/pdf_formatter_test.rb +25 -0
  46. data/test/{test_query.rb → query_test.rb} +32 -121
  47. data/test/{test_record.rb → record_test.rb} +40 -23
  48. data/test/renderer_test.rb +344 -0
  49. data/test/{test_report.rb → report_test.rb} +74 -44
  50. data/test/samples/ticket_count.csv +124 -0
  51. data/test/{test_sql_split.rb → sql_split_test.rb} +0 -0
  52. data/test/{test_table.rb → table_test.rb} +255 -44
  53. data/test/text_formatter_test.rb +144 -0
  54. data/util/bench/data/record/bench_as_vs_to.rb +17 -0
  55. data/util/bench/data/record/bench_constructor.rb +46 -0
  56. data/util/bench/data/record/bench_indexing.rb +65 -0
  57. data/util/bench/data/record/bench_reorder.rb +35 -0
  58. data/util/bench/data/record/bench_to_a.rb +19 -0
  59. data/util/bench/data/table/bench_column_manip.rb +103 -0
  60. data/util/bench/data/table/bench_dup.rb +24 -0
  61. data/util/bench/data/table/bench_init.rb +67 -0
  62. data/util/bench/data/table/bench_manip.rb +125 -0
  63. data/util/bench/formatter/bench_csv.rb +14 -0
  64. data/util/bench/formatter/bench_html.rb +14 -0
  65. data/util/bench/formatter/bench_pdf.rb +14 -0
  66. data/util/bench/formatter/bench_text.rb +14 -0
  67. data/util/bench/samples/tattle.csv +1237 -0
  68. metadata +121 -143
  69. data/TODO +0 -21
  70. data/examples/invoice.rb +0 -142
  71. data/examples/invoice_report.rb +0 -29
  72. data/examples/line_graph.rb +0 -38
  73. data/examples/rope_examples/itunes/config/ruport_config.rb +0 -8
  74. data/examples/rope_examples/sales_report/config/ruport_config.rb +0 -8
  75. data/lib/ruport/attempt.rb +0 -63
  76. data/lib/ruport/config.rb +0 -204
  77. data/lib/ruport/data/groupable.rb +0 -93
  78. data/lib/ruport/data/taggable.rb +0 -80
  79. data/lib/ruport/format.rb +0 -1
  80. data/lib/ruport/format/csv.rb +0 -29
  81. data/lib/ruport/format/html.rb +0 -42
  82. data/lib/ruport/format/latex.rb +0 -47
  83. data/lib/ruport/format/pdf.rb +0 -233
  84. data/lib/ruport/format/plugin.rb +0 -31
  85. data/lib/ruport/format/svg.rb +0 -60
  86. data/lib/ruport/format/text.rb +0 -103
  87. data/lib/ruport/format/xml.rb +0 -32
  88. data/lib/ruport/layout.rb +0 -1
  89. data/lib/ruport/layout/component.rb +0 -7
  90. data/lib/ruport/mailer.rb +0 -99
  91. data/lib/ruport/renderer/graph.rb +0 -46
  92. data/lib/ruport/report/graph.rb +0 -14
  93. data/lib/ruport/system_extensions.rb +0 -71
  94. data/test/test_config.rb +0 -88
  95. data/test/test_format_text.rb +0 -63
  96. data/test/test_graph_renderer.rb +0 -97
  97. data/test/test_groupable.rb +0 -56
  98. data/test/test_mailer.rb +0 -170
  99. data/test/test_renderer.rb +0 -151
  100. data/test/test_ruport.rb +0 -58
  101. data/test/test_table_renderer.rb +0 -141
  102. data/test/test_taggable.rb +0 -52
data/lib/ruport/data.rb CHANGED
@@ -1,4 +1,3 @@
1
- require "ruport/data/groupable"
2
- require "ruport/data/taggable"
3
1
  require "ruport/data/record"
4
2
  require "ruport/data/table"
3
+ require "ruport/data/grouping"
@@ -0,0 +1,311 @@
1
+ module Ruport::Data
2
+
3
+ # === Overview
4
+ #
5
+ # This class implements a group data structure for Ruport. Group is
6
+ # simply a subclass of Table that adds a :name attribute.
7
+ #
8
+ class Group < Table
9
+
10
+ attr_reader :name, :subgroups
11
+
12
+ # Creates a new Group based on the supplied options.
13
+ #
14
+ # Valid options:
15
+ # <b><tt>:name</tt></b>:: The name of the Group
16
+ # <b><tt>:data</tt></b>:: An Array of Arrays representing the
17
+ # records in this Group
18
+ # <b><tt>:column_names</tt></b>:: An Array containing the column names
19
+ # for this Group.
20
+ #
21
+ # Example:
22
+ #
23
+ # group = Group.new :name => 'My Group',
24
+ # :data => [[1,2,3], [3,4,5]],
25
+ # :column_names => %w[a b c]
26
+ #
27
+ def initialize(options={})
28
+ @name = options.delete(:name)
29
+ @subgroups = {}
30
+ super
31
+ end
32
+
33
+ include Ruport::Renderer::Hooks
34
+ renders_as_group
35
+
36
+ def self.inherited(base)
37
+ base.renders_as_group
38
+ end
39
+
40
+ # Create a copy of the Group: records will be copied as well.
41
+ #
42
+ # Example:
43
+ #
44
+ # one = Group.new :name => 'test',
45
+ # :data => [[1,2], [3,4]],
46
+ # :column_names => %w[a b]
47
+ # two = one.dup
48
+ #
49
+ def initialize_copy(from)
50
+ super
51
+ @name = from.name
52
+ @subgroups = from.subgroups.inject({}) { |h,d|
53
+ h.merge!({ d[0] => d[1].dup }) }
54
+ end
55
+
56
+ # Compares this Group to another Group and returns <tt>true</tt> if
57
+ # the <tt>name</tt>, <tt>data</tt>, and <tt>column_names</tt> are equal.
58
+ #
59
+ # Example:
60
+ #
61
+ # one = Group.new :name => 'test',
62
+ # :data => [[1,2], [3,4]],
63
+ # :column_names => %w[a b]
64
+ #
65
+ # two = Group.new :name => 'test',
66
+ # :data => [[1,2], [3,4]],
67
+ # :column_names => %w[a b]
68
+ #
69
+ # one.eql?(two) #=> true
70
+ #
71
+ def eql?(other)
72
+ name.eql?(other.name) && super
73
+ end
74
+
75
+ alias_method :==, :eql?
76
+
77
+ # Creates subgroups for the group based on the supplied column name. Each
78
+ # subgroup is a hash whose keys are the unique values in the column.
79
+ #
80
+ # Example:
81
+ #
82
+ # main_group = Group.new :name => 'test',
83
+ # :data => [[1,2,3,4,5], [3,4,5,6,7]],
84
+ # :column_names => %w[a b c d e]
85
+ # main_group.create_subgroups("a")
86
+ #
87
+ def create_subgroups(group_column)
88
+ if @subgroups.empty?
89
+ @subgroups = grouped_data(group_column)
90
+ else
91
+ @subgroups.each {|name,group| group.create_subgroups(group_column) }
92
+ end
93
+ end
94
+
95
+ protected
96
+
97
+ attr_writer :name, :subgroups
98
+
99
+ private
100
+
101
+ def grouped_data(group_column) #:nodoc:
102
+ data = {}
103
+ group_names = column(group_column).uniq
104
+ columns = column_names.dup
105
+ columns.delete(group_column)
106
+ group_names.each do |name|
107
+ group_data = sub_table(columns) {|r|
108
+ r.send(group_column) == name
109
+ }
110
+ data[name] = Group.new(:name => name, :data => group_data,
111
+ :column_names => columns,
112
+ :record_class => record_class)
113
+ end
114
+ data
115
+ end
116
+
117
+ end
118
+
119
+
120
+ # === Overview
121
+ #
122
+ # This class implements a grouping data structure for Ruport. A grouping is
123
+ # a collection of groups. It allows you to group the data in a table by one
124
+ # or more columns that you specify.
125
+ #
126
+ # The data for a grouping is a hash of groups, keyed on each unique data
127
+ # point from the grouping column.
128
+ #
129
+ class Grouping
130
+
131
+ include Enumerable
132
+
133
+ # Creates a new Grouping based on the supplied options.
134
+ #
135
+ # Valid options:
136
+ # <b><tt>:by</tt></b>:: A column name or array of column names that the
137
+ # data will be grouped on.
138
+ #
139
+ # Example:
140
+ #
141
+ # table = [[1,2,3],[4,5,6]].to_table(%w[a b c])
142
+ #
143
+ # grouping = Grouping.new(table, :by => "a")
144
+ #
145
+ def initialize(data,options={})
146
+ @grouped_by = options[:by]
147
+ cols = Array(options[:by]).dup
148
+ @data = data.to_group.send(:grouped_data, cols.shift)
149
+ cols.each do |col|
150
+ @data.each do |name,group|
151
+ group.create_subgroups(col)
152
+ end
153
+ end
154
+ end
155
+
156
+ attr_accessor :data
157
+ attr_reader :grouped_by
158
+
159
+ require "forwardable"
160
+ extend Forwardable
161
+ def_delegator :@data, :each
162
+
163
+ # Allows Hash-like indexing of the grouping data.
164
+ #
165
+ # Examples:
166
+ #
167
+ # my_grouping["foo"]
168
+ #
169
+ def [](name)
170
+ @data[name] or
171
+ raise(IndexError,"Group Not Found")
172
+ end
173
+
174
+ # Used to add extra data to the Grouping. <tt>other</tt> should be a Group.
175
+ #
176
+ # Example:
177
+ #
178
+ # table = [[1,2,3],[4,5,6]].to_table(%w[a b c])
179
+ #
180
+ # grouping = Grouping.new(table, :by => "a")
181
+ #
182
+ # group = Group.new :name => 7,
183
+ # :data => [[8,9]],
184
+ # :column_names => %w[b c]
185
+ #
186
+ # grouping << group
187
+ #
188
+ def <<(group)
189
+ if data.has_key? group.name
190
+ raise(ArgumentError, "Group '#{group.name}' exists!")
191
+ end
192
+ @data.merge!({ group.name => group })
193
+ end
194
+
195
+ alias_method :append, :<<
196
+
197
+ # Provides access to the subgroups of a particular group in the Grouping.
198
+ # Supply the name of a group and it returns a Grouping created from the
199
+ # subgroups of the group.
200
+ #
201
+ def subgrouping(name)
202
+ grouping = dup
203
+ grouping.send(:data=, @data[name].subgroups)
204
+ return grouping
205
+ end
206
+
207
+ alias_method :/, :subgrouping
208
+
209
+ # Useful for creating basic summaries from Grouping objects.
210
+ # Takes a field to summarize on, and then for each group,
211
+ # runs the specified procs and returns the results as a Table.
212
+ #
213
+ # The following example would show for each date group,
214
+ # the sum for the attributes or methods :opened and :closed
215
+ # and order them by the :order array.
216
+ #
217
+ # If :order is not specified, you cannot depend on predictable column order
218
+ #
219
+ # grouping.summary :date,
220
+ # :opened => lambda { |g| g.sigma(:opened) },
221
+ # :closed => lambda { |g| g.sigma(:closed) },
222
+ # :order => [:date,:opened,:closed]
223
+ #
224
+ def summary(field,procs)
225
+ if procs[:order].kind_of?(Array)
226
+ cols = procs.delete(:order)
227
+ else
228
+ cols = procs.keys + [field]
229
+ end
230
+ expected = Table(cols) { |t|
231
+ each do |name,group|
232
+ t << procs.inject({field => name}) do |s,r|
233
+ s.merge(r[0] => r[1].call(group))
234
+ end
235
+ end
236
+ t.reorder(cols)
237
+ }
238
+ end
239
+
240
+ # Uses Ruport's built-in text formatter to render this Grouping
241
+ #
242
+ # Example:
243
+ #
244
+ # table = [[1,2,3],[4,5,6]].to_table(%w[a b c])
245
+ #
246
+ # grouping = Grouping.new(table, :by => "a")
247
+ #
248
+ # puts grouping.to_s
249
+ #
250
+ def to_s
251
+ as(:text)
252
+ end
253
+
254
+ include Ruport::Renderer::Hooks
255
+ renders_as_grouping
256
+
257
+ def self.inherited(base)
258
+ base.renders_as_grouping
259
+ end
260
+
261
+
262
+ # Create a copy of the Grouping: groups will be copied as well.
263
+ #
264
+ # Example:
265
+ #
266
+ # table = [[1,2,3],[4,5,6]].to_table(%w[a b c])
267
+ # one = Ruport::Data::Grouping.new(a, :by => "a")
268
+ #
269
+ # two = one.dup
270
+ #
271
+ def initialize_copy(from)
272
+ @grouped_by = from.grouped_by
273
+ @data = from.data.inject({}) { |h,d| h.merge!({ d[0] => d[1].dup }) }
274
+ end
275
+
276
+ # Provides a shortcut for the <tt>as()</tt> method by converting a call to
277
+ # <tt>to_format_name</tt> into a call to <tt>as(:format_name)</tt>.
278
+ #
279
+ def method_missing(id,*args)
280
+ return as($1.to_sym,*args) if id.to_s =~ /^to_(.*)/
281
+ super
282
+ end
283
+
284
+ end
285
+
286
+ end
287
+
288
+ module Kernel
289
+
290
+ # Shortcut interface for creating Data::Grouping
291
+ #
292
+ # Example:
293
+ #
294
+ # a = [[1,2,3],[4,5,6]].to_table(%w[a b c])
295
+ # b = Grouping(a, :by => "a") #=> creates a new grouping on column "a"
296
+ #
297
+ def Grouping(*args)
298
+ Ruport::Data::Grouping.new(*args)
299
+ end
300
+
301
+ # Shortcut interface for creating Data::Group
302
+ #
303
+ # Example:
304
+ #
305
+ # g = Group('mygroup', :data => [[1,2,3],[4,5,6]],
306
+ # :column_names => %w[a b c]) #=> creates a new group named mygroup
307
+ #
308
+ def Group(name,opts={})
309
+ Ruport::Data::Group.new(opts.merge(:name => name))
310
+ end
311
+ end
@@ -14,7 +14,6 @@ module Ruport::Data
14
14
  #
15
15
  class Record
16
16
 
17
- include Taggable
18
17
  include Enumerable
19
18
 
20
19
  #
@@ -54,7 +53,41 @@ module Ruport::Data
54
53
  @data = data.dup
55
54
  @attributes = options[:attributes] || data.keys
56
55
  end
57
- end
56
+ end
57
+
58
+
59
+ ##############
60
+ # Delegators #
61
+ ##############
62
+
63
+ # Returns a copy of the <tt>attributes</tt> from this Record.
64
+ #
65
+ # Example:
66
+ #
67
+ # a = Data::Record.new([1,2],:attributes => %w[a b])
68
+ # a.attributes #=> ["a","b"]
69
+ #
70
+ def attributes
71
+ @attributes.dup
72
+ end
73
+
74
+ #
75
+ # Sets the <tt>attribute</tt> list for this Record.
76
+ # (Dangerous when used within Table objects!)
77
+ #
78
+ # Example:
79
+ #
80
+ # my_record.attributes = %w[foo bar baz]
81
+ #
82
+ attr_writer :attributes
83
+
84
+ attr_reader :data
85
+ def size; @data.size; end
86
+ alias_method :length, :size
87
+
88
+ ##################
89
+ # Access Methods #
90
+ ##################
58
91
 
59
92
  #
60
93
  # Allows either Array or Hash-like indexing.
@@ -90,11 +123,31 @@ module Ruport::Data
90
123
  @attributes << index unless @attributes.include? index
91
124
  end
92
125
  end
93
-
94
- def size
95
- @data.size
96
- end
97
- alias_method :length, :size
126
+
127
+ # Indifferent access to attributes.
128
+ #
129
+ # Examples:
130
+ #
131
+ # record.get(:foo) # looks for an attribute "foo" or :foo,
132
+ # or calls the method <tt>foo</tt>
133
+ #
134
+ # record.get("foo") # looks for an attribute "foo" or :foo
135
+ #
136
+ # record.get(0) # Gets the first element
137
+ def get(name)
138
+ case name
139
+ when String,Symbol
140
+ self[name] || send(name)
141
+ when Fixnum
142
+ self[name]
143
+ else
144
+ raise ArgumentError, "Whatchu Talkin' Bout, Willis?"
145
+ end
146
+ end
147
+
148
+ ################
149
+ # Conversions #
150
+ ################
98
151
 
99
152
  #
100
153
  # Converts a Record into an Array.
@@ -104,47 +157,23 @@ module Ruport::Data
104
157
  # a = Data::Record.new([1,2],:attributes => %w[a b])
105
158
  # a.to_a #=> [1,2]
106
159
  #
107
- # Note: in earlier versions of Ruport, to_a was aliased to data.
108
- # From now on to_a only for array representation!
109
160
  def to_a
110
161
  @attributes.map { |a| @data[a] }
111
162
  end
112
-
113
- attr_reader :data
114
-
115
- #
116
- # Converts a Record into a Hash.
117
- #
118
- # Example:
119
- #
120
- # a = Data::Record.new([1,2],:attributes => %w[a b])
121
- # a.to_h #=> {"a" => 1, "b" => 2}
122
- def to_h
123
- @data.dup
124
- end
125
-
126
- #alias_method :data,:to_a
127
-
128
- # Returns a copy of the <tt>attributes</tt> from this Record.
163
+
164
+ # Converts a Record into a Hash.
129
165
  #
130
166
  # Example:
131
167
  #
132
168
  # a = Data::Record.new([1,2],:attributes => %w[a b])
133
- # a.attributes #=> ["a","b"]
134
- #
135
- def attributes
136
- @attributes.dup
169
+ # a.to_hash #=> {"a" => 1, "b" => 2}
170
+ def to_hash
171
+ @data.dup
137
172
  end
138
-
139
- #
140
- # Sets the <tt>attribute</tt> list for this Record.
141
- # (Dangerous when used within Table objects!)
142
- #
143
- # Example:
144
- #
145
- # my_record.attributes = %w[foo bar baz]
146
- #
147
- attr_writer :attributes
173
+
174
+ ################
175
+ # Comparisons #
176
+ ################
148
177
 
149
178
  #
150
179
  # If <tt>attributes</tt> and <tt>to_a</tt> are equivalent, then
@@ -155,11 +184,28 @@ module Ruport::Data
155
184
  to_a == other.to_a
156
185
  end
157
186
 
158
- alias_method :eql?, :==
187
+ alias_method :eql?, :==
188
+
189
+
190
+ #############
191
+ # Iterators #
192
+ #############
159
193
 
160
194
  # Yields each element of the Record. Does not provide attribute names
161
195
  def each
162
196
  to_a.each { |e| yield(e) }
197
+ end
198
+
199
+ #################
200
+ # Manipulations #
201
+ #################
202
+
203
+ # Takes an old name and a new name and renames an attribute.
204
+ #
205
+ # The third option, update_index is for internal use.
206
+ def rename_attribute(old_name,new_name,update_index=true)
207
+ @attributes[@attributes.index(old_name)] = new_name if update_index
208
+ @data[new_name] = @data.delete(old_name)
163
209
  end
164
210
 
165
211
  #
@@ -182,16 +228,19 @@ module Ruport::Data
182
228
  self.attributes = indices
183
229
  end
184
230
  self
185
- end
186
-
187
- # Takes an old name and a new name and renames an attribute.
188
- #
189
- # The third option, update_index is for internal use.
190
- def rename_attribute(old_name,new_name,update_index=true)
191
- @attributes[@attributes.index(old_name)] = new_name if update_index
192
- @data[new_name] = @data.delete(old_name)
193
231
  end
194
-
232
+
233
+ #######################
234
+ # Internals / Helpers #
235
+ #######################
236
+
237
+ include Ruport::Renderer::Hooks
238
+ renders_as_row
239
+
240
+ def self.inherited(base)
241
+ base.renders_as_row
242
+ end
243
+
195
244
  #
196
245
  # Provides a unique hash value. If a Record contains the same data and
197
246
  # attributes as another Record, they will hash to the same value, even if
@@ -202,13 +251,11 @@ module Ruport::Data
202
251
  @attributes.hash + to_a.hash
203
252
  end
204
253
 
205
- # Makes a fresh copy of the Record.
206
- def dup
207
- r = Record.new(@data.dup,:attributes => @attributes.dup)
208
- r.tags = tags.dup
209
- return r
254
+ def initialize_copy(from)
255
+ @data = from.data.dup
256
+ @attributes = from.attributes.dup
210
257
  end
211
-
258
+
212
259
  # Provides accessor style methods for attribute access.
213
260
  #
214
261
  # Example:
@@ -216,37 +263,20 @@ module Ruport::Data
216
263
  # my_record.foo = 2
217
264
  # my_record.foo #=> 2
218
265
  #
219
- def method_missing(id,*args)
266
+ # Also provides a shortcut for the <tt>as()</tt> method by converting a
267
+ # call to <tt>to_format_name</tt> into a call to <tt>as(:format_name)</tt>
268
+ #
269
+ def method_missing(id,*args,&block)
220
270
  k = id.to_s.gsub(/=$/,"")
221
- if(key = @attributes.find { |r| r.to_s.eql?(k) })
222
- args[0] ? @data[key] = args[0] : @data[key]
223
- else
224
- super
225
- end
226
- end
271
+ key_index = @attributes.index(k) || @attributes.index(k.to_sym)
227
272
 
228
- # Indifferent access to attributes.
229
- #
230
- # Examples:
231
- #
232
- # record.get(:foo) # looks for an attribute "foo" or :foo,
233
- # or calls the method <tt>foo</tt>
234
- #
235
- # record.get("foo") # looks for an attribute "foo" or :foo
236
- #
237
- # record.get(0) # Gets the first element
238
- def get(name)
239
- case name
240
- when Symbol
241
- send(name)
242
- when String
243
- self[attributes.find { |a| a.to_s.eql?(name)}]
244
- when Fixnum
245
- self[name]
273
+ if key_index
274
+ args[0] ? self[key_index] = args[0] : self[key_index]
246
275
  else
247
- raise "Whatchu Talkin' Bout, Willis?"
276
+ return as($1.to_sym,*args,&block) if id.to_s =~ /^to_(.*)/
277
+ super
248
278
  end
249
- end
279
+ end
250
280
 
251
281
  private
252
282
 
@@ -255,7 +285,6 @@ module Ruport::Data
255
285
  @attributes.delete(key)
256
286
  end
257
287
 
258
-
259
288
  def reindex(new_attributes)
260
289
  @attributes = new_attributes
261
290
  end