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.
- data/AUTHORS +48 -0
- data/LICENSE +59 -0
- data/README +114 -0
- data/Rakefile +93 -0
- data/examples/RWEmerson.jpg +0 -0
- data/examples/anon.rb +43 -0
- data/examples/btree/commaleon/commaleon.rb +263 -0
- data/examples/btree/commaleon/sample_data/ticket_count.csv +124 -0
- data/examples/btree/commaleon/sample_data/ticket_count2.csv +119 -0
- data/examples/centered_pdf_text_box.rb +83 -0
- data/examples/data/tattle.dump +82 -0
- data/examples/example.csv +3 -0
- data/examples/line_plotter.rb +61 -0
- data/examples/pdf_report_with_common_base.rb +72 -0
- data/examples/png_embed.rb +54 -0
- data/examples/roadmap.png +0 -0
- data/examples/row_renderer.rb +39 -0
- data/examples/simple_pdf_lines.rb +25 -0
- data/examples/simple_templating_example.rb +34 -0
- data/examples/tattle_ruby_version.rb +39 -0
- data/examples/tattle_rubygems_version.rb +37 -0
- data/examples/trac_ticket_status.rb +59 -0
- data/lib/ruport.rb +127 -0
- data/lib/ruport/controller.rb +616 -0
- data/lib/ruport/controller/grouping.rb +71 -0
- data/lib/ruport/controller/table.rb +54 -0
- data/lib/ruport/data.rb +4 -0
- data/lib/ruport/data/feeder.rb +111 -0
- data/lib/ruport/data/grouping.rb +399 -0
- data/lib/ruport/data/record.rb +297 -0
- data/lib/ruport/data/table.rb +950 -0
- data/lib/ruport/extensions.rb +4 -0
- data/lib/ruport/formatter.rb +254 -0
- data/lib/ruport/formatter/csv.rb +149 -0
- data/lib/ruport/formatter/html.rb +161 -0
- data/lib/ruport/formatter/pdf.rb +591 -0
- data/lib/ruport/formatter/template.rb +187 -0
- data/lib/ruport/formatter/text.rb +231 -0
- data/lib/uport.rb +1 -0
- data/test/controller_test.rb +743 -0
- data/test/csv_formatter_test.rb +164 -0
- data/test/data_feeder_test.rb +88 -0
- data/test/grouping_test.rb +410 -0
- data/test/helpers.rb +11 -0
- data/test/html_formatter_test.rb +201 -0
- data/test/pdf_formatter_test.rb +354 -0
- data/test/record_test.rb +332 -0
- data/test/samples/addressbook.csv +6 -0
- data/test/samples/data.csv +3 -0
- data/test/samples/data.tsv +3 -0
- data/test/samples/dates.csv +1409 -0
- data/test/samples/erb_test.sql +1 -0
- data/test/samples/query_test.sql +1 -0
- data/test/samples/ruport_test.sql +8 -0
- data/test/samples/test.sql +2 -0
- data/test/samples/test.yaml +3 -0
- data/test/samples/ticket_count.csv +124 -0
- data/test/table_pivot_test.rb +134 -0
- data/test/table_test.rb +838 -0
- data/test/template_test.rb +48 -0
- data/test/text_formatter_test.rb +258 -0
- data/util/bench/data/record/bench_as_vs_to.rb +18 -0
- data/util/bench/data/record/bench_constructor.rb +46 -0
- data/util/bench/data/record/bench_indexing.rb +65 -0
- data/util/bench/data/record/bench_reorder.rb +35 -0
- data/util/bench/data/record/bench_to_a.rb +19 -0
- data/util/bench/data/table/bench_column_manip.rb +103 -0
- data/util/bench/data/table/bench_dup.rb +24 -0
- data/util/bench/data/table/bench_init.rb +67 -0
- data/util/bench/data/table/bench_manip.rb +125 -0
- data/util/bench/formatter/bench_csv.rb +14 -0
- data/util/bench/formatter/bench_html.rb +14 -0
- data/util/bench/formatter/bench_pdf.rb +14 -0
- data/util/bench/formatter/bench_text.rb +14 -0
- data/util/bench/samples/tattle.csv +1237 -0
- metadata +176 -0
@@ -0,0 +1,616 @@
|
|
1
|
+
# controller.rb : General purpose control of formatted data for Ruby Reports
|
2
|
+
#
|
3
|
+
# Copyright December 2006, Gregory Brown. All Rights Reserved.
|
4
|
+
#
|
5
|
+
# This is free software. Please see the LICENSE and COPYING files for details.
|
6
|
+
|
7
|
+
|
8
|
+
# This class implements the core controller for Ruport's formatting system.
|
9
|
+
# It is designed to implement the low level tools necessary to build report
|
10
|
+
# controllers for different kinds of tasks. See Controller::Table for a
|
11
|
+
# tabular data controller.
|
12
|
+
#
|
13
|
+
class Ruport::Controller
|
14
|
+
|
15
|
+
class RequiredOptionNotSet < RuntimeError #:nodoc:
|
16
|
+
end
|
17
|
+
class UnknownFormatError < RuntimeError #:nodoc:
|
18
|
+
end
|
19
|
+
class StageAlreadyDefinedError < RuntimeError #:nodoc:
|
20
|
+
end
|
21
|
+
class ControllerNotSetError < RuntimeError #:nodoc:
|
22
|
+
end
|
23
|
+
|
24
|
+
require "ostruct"
|
25
|
+
|
26
|
+
# Structure for holding controller options.
|
27
|
+
# Simplified version of HashWithIndifferentAccess
|
28
|
+
class Options < OpenStruct
|
29
|
+
|
30
|
+
if RUBY_VERSION < "1.9"
|
31
|
+
private :id
|
32
|
+
end
|
33
|
+
|
34
|
+
# Returns a Hash object. Use this if you need methods other than []
|
35
|
+
def to_hash
|
36
|
+
@table
|
37
|
+
end
|
38
|
+
# Indifferent lookup of an attribute, e.g.
|
39
|
+
#
|
40
|
+
# options[:foo] == options["foo"]
|
41
|
+
def [](key)
|
42
|
+
send(key)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Sets an attribute, with indifferent access.
|
46
|
+
#
|
47
|
+
# options[:foo] = "bar"
|
48
|
+
#
|
49
|
+
# options[:foo] == options["foo"] #=> true
|
50
|
+
# options["foo"] == options.foo #=> true
|
51
|
+
# options.foo #=> "bar"
|
52
|
+
def []=(key,value)
|
53
|
+
send("#{key}=",value)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# This module provides hooks into Ruport's formatting system.
|
58
|
+
# It is used to implement the as() method for all of Ruport's data
|
59
|
+
# structures, as well as the renders_with and renders_as_* helpers.
|
60
|
+
#
|
61
|
+
# You can actually use this with any data structure, it will look for a
|
62
|
+
# renderable_data(format) method to pass to the <tt>controller</tt> you
|
63
|
+
# specify, but if that is not defined, it will pass <tt>self</tt>.
|
64
|
+
#
|
65
|
+
# Examples:
|
66
|
+
#
|
67
|
+
# # Render Arrays with Ruport's Row Controller
|
68
|
+
# class Array
|
69
|
+
# include Ruport::Controller::Hooks
|
70
|
+
# renders_as_row
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# # >> [1,2,3].as(:csv)
|
74
|
+
# # => "1,2,3\n"
|
75
|
+
#
|
76
|
+
# # Render Hashes with Ruport's Row Controller
|
77
|
+
# class Hash
|
78
|
+
# include Ruport::Controller::Hooks
|
79
|
+
# renders_as_row
|
80
|
+
# attr_accessor :column_order
|
81
|
+
# def renderable_data(format)
|
82
|
+
# column_order.map { |c| self[c] }
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# # >> a = { :a => 1, :b => 2, :c => 3 }
|
87
|
+
# # >> a.column_order = [:b,:a,:c]
|
88
|
+
# # >> a.as(:csv)
|
89
|
+
# # => "2,1,3\n"
|
90
|
+
module Hooks
|
91
|
+
module ClassMethods
|
92
|
+
|
93
|
+
# Tells the class which controller as() will forward to.
|
94
|
+
#
|
95
|
+
# Usage:
|
96
|
+
#
|
97
|
+
# class MyStructure
|
98
|
+
# include Controller::Hooks
|
99
|
+
# renders_with CustomController
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# You can also specify default rendering options, which will be used
|
103
|
+
# if they are not overriden by the options passed to as().
|
104
|
+
#
|
105
|
+
# class MyStructure
|
106
|
+
# include Controller::Hooks
|
107
|
+
# renders_with CustomController, :font_size => 14
|
108
|
+
# end
|
109
|
+
def renders_with(controller,opts={})
|
110
|
+
@controller = controller
|
111
|
+
@rendering_options=opts
|
112
|
+
end
|
113
|
+
|
114
|
+
# The default rendering options for a class, stored as a hash.
|
115
|
+
def rendering_options
|
116
|
+
@rendering_options
|
117
|
+
end
|
118
|
+
|
119
|
+
# Shortcut for renders_with(Ruport::Controller::Table), you
|
120
|
+
# may wish to override this if you build a custom table controller.
|
121
|
+
def renders_as_table(options={})
|
122
|
+
renders_with Ruport::Controller::Table,options
|
123
|
+
end
|
124
|
+
|
125
|
+
# Shortcut for renders_with(Ruport::Controller::Row), you
|
126
|
+
# may wish to override this if you build a custom row controller.
|
127
|
+
def renders_as_row(options={})
|
128
|
+
renders_with Ruport::Controller::Row, options
|
129
|
+
end
|
130
|
+
|
131
|
+
# Shortcut for renders_with(Ruport::Controller::Group), you
|
132
|
+
# may wish to override this if you build a custom group controller.
|
133
|
+
def renders_as_group(options={})
|
134
|
+
renders_with Ruport::Controller::Group,options
|
135
|
+
end
|
136
|
+
|
137
|
+
# Shortcut for renders_with(Ruport::Controller::Grouping), you
|
138
|
+
# may wish to override this if you build a custom grouping controller.
|
139
|
+
def renders_as_grouping(options={})
|
140
|
+
renders_with Ruport::Controller::Grouping,options
|
141
|
+
end
|
142
|
+
|
143
|
+
# The class of the controller object for the base class.
|
144
|
+
#
|
145
|
+
# Example:
|
146
|
+
#
|
147
|
+
# >> Ruport::Data::Table.controller
|
148
|
+
# => Ruport::Controller::Table
|
149
|
+
def controller
|
150
|
+
@controller
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def self.included(base) #:nodoc:
|
155
|
+
base.extend(ClassMethods)
|
156
|
+
end
|
157
|
+
|
158
|
+
# Uses the Controller specified by renders_with to generate formatted
|
159
|
+
# output. Passes the return value of the <tt>renderable_data(format)</tt>
|
160
|
+
# method if the method is defined, otherwise passes <tt>self</tt> as :data
|
161
|
+
#
|
162
|
+
# The remaining options are converted to a Controller::Options object and
|
163
|
+
# are accessible in both the controller and formatter.
|
164
|
+
#
|
165
|
+
# Example:
|
166
|
+
#
|
167
|
+
# table.as(:csv, :show_table_headers => false)
|
168
|
+
def as(format,options={})
|
169
|
+
raise ControllerNotSetError unless self.class.controller
|
170
|
+
unless self.class.controller.formats.include?(format)
|
171
|
+
raise UnknownFormatError
|
172
|
+
end
|
173
|
+
self.class.controller.render(format,
|
174
|
+
self.class.rendering_options.merge(options)) do |rend|
|
175
|
+
rend.data =
|
176
|
+
respond_to?(:renderable_data) ? renderable_data(format) : self
|
177
|
+
yield(rend) if block_given?
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def save_as(file,options={})
|
182
|
+
file =~ /.*\.(.*)/
|
183
|
+
format = $1
|
184
|
+
as(format.to_sym, options.merge(:file => file))
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
|
190
|
+
class << self
|
191
|
+
|
192
|
+
# Returns a hash that maps format names to their formatter classes, for use
|
193
|
+
# with the formatter shortcut. Supported formats are :html, :csv, :pdf, and
|
194
|
+
# :text by default.
|
195
|
+
#
|
196
|
+
#
|
197
|
+
# Sample override:
|
198
|
+
#
|
199
|
+
# class MyController < Ruport::Controller
|
200
|
+
#
|
201
|
+
# def built_in_formats
|
202
|
+
# super.extend(:xml => MyXMLFormatter,
|
203
|
+
# :json => MyJSONFormatter)
|
204
|
+
# end
|
205
|
+
# end
|
206
|
+
#
|
207
|
+
# This would allow for:
|
208
|
+
#
|
209
|
+
# class ChildController < MyController
|
210
|
+
#
|
211
|
+
# formatter :xml do
|
212
|
+
# # ...
|
213
|
+
# end
|
214
|
+
#
|
215
|
+
# formatter :json do
|
216
|
+
# # ...
|
217
|
+
# end
|
218
|
+
# end
|
219
|
+
#
|
220
|
+
def built_in_formats
|
221
|
+
{ :html => Ruport::Formatter::HTML,
|
222
|
+
:csv => Ruport::Formatter::CSV,
|
223
|
+
:pdf => Ruport::Formatter::PDF,
|
224
|
+
:text => Ruport::Formatter::Text }
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
# Generates an anonymous formatter class and ties it to the Controller.
|
229
|
+
# This method looks up the built in formats in the hash returned by
|
230
|
+
# built_in_formats, but also explicitly specify a custom Formatter class to
|
231
|
+
# subclass from.
|
232
|
+
#
|
233
|
+
# Sample usage:
|
234
|
+
#
|
235
|
+
# class ControllerWithAnonymousFormatters < Ruport::Controller
|
236
|
+
#
|
237
|
+
# stage :report
|
238
|
+
#
|
239
|
+
# formatter :html do
|
240
|
+
# build :report do
|
241
|
+
# output << textile("h1. Hi there")
|
242
|
+
# end
|
243
|
+
# end
|
244
|
+
#
|
245
|
+
# formatter :csv do
|
246
|
+
# build :report do
|
247
|
+
# build_row([1,2,3])
|
248
|
+
# end
|
249
|
+
# end
|
250
|
+
#
|
251
|
+
# formatter :pdf do
|
252
|
+
# build :report do
|
253
|
+
# add_text "hello world"
|
254
|
+
# end
|
255
|
+
# end
|
256
|
+
#
|
257
|
+
# formatter :text do
|
258
|
+
# build :report do
|
259
|
+
# output << "Hello world"
|
260
|
+
# end
|
261
|
+
# end
|
262
|
+
#
|
263
|
+
# formatter :custom => CustomFormatter do
|
264
|
+
#
|
265
|
+
# build :report do
|
266
|
+
# output << "This is "
|
267
|
+
# custom_helper
|
268
|
+
# end
|
269
|
+
#
|
270
|
+
# end
|
271
|
+
#
|
272
|
+
# end
|
273
|
+
#
|
274
|
+
def formatter(*a,&b)
|
275
|
+
case a[0]
|
276
|
+
when Symbol
|
277
|
+
klass = Class.new(built_in_formats[a[0]])
|
278
|
+
klass.renders a[0], :for => self
|
279
|
+
when Hash
|
280
|
+
k,v = a[0].to_a[0]
|
281
|
+
klass = Class.new(v)
|
282
|
+
klass.renders k, :for => self
|
283
|
+
end
|
284
|
+
klass.class_eval(&b)
|
285
|
+
end
|
286
|
+
|
287
|
+
attr_accessor :first_stage,:final_stage,:required_options,:stages #:nodoc:
|
288
|
+
|
289
|
+
# Registers a hook to look for in the Formatter object when the render()
|
290
|
+
# method is called.
|
291
|
+
#
|
292
|
+
# Usage:
|
293
|
+
#
|
294
|
+
# class MyController < Ruport::Controller
|
295
|
+
# # other details omitted...
|
296
|
+
# finalize :apple
|
297
|
+
# end
|
298
|
+
#
|
299
|
+
# class MyFormatter < Ruport::Formatter
|
300
|
+
# renders :example, :for => MyController
|
301
|
+
#
|
302
|
+
# # other details omitted...
|
303
|
+
#
|
304
|
+
# def finalize_apple
|
305
|
+
# # this method will be called when MyController tries to render
|
306
|
+
# # the :example format
|
307
|
+
# end
|
308
|
+
# end
|
309
|
+
#
|
310
|
+
# If a formatter does not implement this hook, it is simply ignored.
|
311
|
+
def finalize(stage)
|
312
|
+
if final_stage
|
313
|
+
raise StageAlreadyDefinedError, 'final stage already defined'
|
314
|
+
end
|
315
|
+
self.final_stage = stage
|
316
|
+
end
|
317
|
+
|
318
|
+
# Registers a hook to look for in the Formatter object when the render()
|
319
|
+
# method is called.
|
320
|
+
#
|
321
|
+
# Usage:
|
322
|
+
#
|
323
|
+
# class MyController < Ruport::Controller
|
324
|
+
# # other details omitted...
|
325
|
+
# prepare :apple
|
326
|
+
# end
|
327
|
+
#
|
328
|
+
# class MyFormatter < Ruport::Formatter
|
329
|
+
# renders :example, :for => MyController
|
330
|
+
#
|
331
|
+
# def prepare_apple
|
332
|
+
# # this method will be called when MyController tries to render
|
333
|
+
# # the :example format
|
334
|
+
# end
|
335
|
+
#
|
336
|
+
# # other details omitted...
|
337
|
+
# end
|
338
|
+
#
|
339
|
+
# If a formatter does not implement this hook, it is simply ignored.
|
340
|
+
def prepare(stage)
|
341
|
+
if first_stage
|
342
|
+
raise StageAlreadyDefinedError, "prepare stage already defined"
|
343
|
+
end
|
344
|
+
self.first_stage = stage
|
345
|
+
end
|
346
|
+
|
347
|
+
# Registers hooks to look for in the Formatter object when the render()
|
348
|
+
# method is called.
|
349
|
+
#
|
350
|
+
# Usage:
|
351
|
+
#
|
352
|
+
# class MyController < Ruport::Controller
|
353
|
+
# # other details omitted...
|
354
|
+
# stage :apple,:banana
|
355
|
+
# end
|
356
|
+
#
|
357
|
+
# class MyFormatter < Ruport::Formatter
|
358
|
+
# renders :example, :for => MyController
|
359
|
+
#
|
360
|
+
# def build_apple
|
361
|
+
# # this method will be called when MyController tries to render
|
362
|
+
# # the :example format
|
363
|
+
# end
|
364
|
+
#
|
365
|
+
# def build_banana
|
366
|
+
# # this method will be called when MyController tries to render
|
367
|
+
# # the :example format
|
368
|
+
# end
|
369
|
+
#
|
370
|
+
# # other details omitted...
|
371
|
+
# end
|
372
|
+
#
|
373
|
+
# If a formatter does not implement these hooks, they are simply ignored.
|
374
|
+
def stage(*stage_list)
|
375
|
+
self.stages ||= []
|
376
|
+
stage_list.each { |stage|
|
377
|
+
self.stages << stage.to_s
|
378
|
+
}
|
379
|
+
end
|
380
|
+
|
381
|
+
# Defines attribute writers for the Controller::Options object shared
|
382
|
+
# between Controller and Formatter. Will throw an error if the user does
|
383
|
+
# not provide values for these options upon rendering.
|
384
|
+
#
|
385
|
+
# usage:
|
386
|
+
#
|
387
|
+
# class MyController < Ruport::Controller
|
388
|
+
# required_option :employee_name, :address
|
389
|
+
# # other details omitted
|
390
|
+
# end
|
391
|
+
def required_option(*opts)
|
392
|
+
self.required_options ||= []
|
393
|
+
opts.each do |opt|
|
394
|
+
self.required_options << opt
|
395
|
+
|
396
|
+
o = opt
|
397
|
+
unless instance_methods(false).include?(o.to_s)
|
398
|
+
define_method(o) { options.send(o.to_s) }
|
399
|
+
end
|
400
|
+
opt = "#{opt}="
|
401
|
+
define_method(opt) {|t| options.send(opt, t) }
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
# Lists the formatters that are currently registered on a controller,
|
406
|
+
# as a hash keyed by format name.
|
407
|
+
#
|
408
|
+
# Example:
|
409
|
+
#
|
410
|
+
# >> Ruport::Controller::Table.formats
|
411
|
+
# => {:html=>Ruport::Formatter::HTML,
|
412
|
+
# ?> :csv=>Ruport::Formatter::CSV,
|
413
|
+
# ?> :text=>Ruport::Formatter::Text,
|
414
|
+
# ?> :pdf=>Ruport::Formatter::PDF}
|
415
|
+
def formats
|
416
|
+
@formats ||= {}
|
417
|
+
end
|
418
|
+
|
419
|
+
# Builds up a controller object, looks up the appropriate formatter,
|
420
|
+
# sets the data and options, and then does the following process:
|
421
|
+
#
|
422
|
+
# * If the controller contains a module Helpers, mix it in to the instance.
|
423
|
+
# * If a block is given, yield the Controller instance.
|
424
|
+
# * If a setup() method is defined on the Controller, call it.
|
425
|
+
# * Call the run() method.
|
426
|
+
# * If the :file option is set to a file name, appends output to the file.
|
427
|
+
# * Return the results of formatter.output
|
428
|
+
#
|
429
|
+
# Please see the examples/ directory for custom controller examples, because
|
430
|
+
# this is not nearly as complicated as it sounds in most cases.
|
431
|
+
def render(format, add_options=nil)
|
432
|
+
rend = build(format, add_options) { |r|
|
433
|
+
yield(r) if block_given?
|
434
|
+
r.setup if r.respond_to? :setup
|
435
|
+
}
|
436
|
+
rend.run
|
437
|
+
rend.formatter.save_output(rend.options.file) if rend.options.file
|
438
|
+
return rend.formatter.output
|
439
|
+
end
|
440
|
+
|
441
|
+
# Allows you to set class-wide default options.
|
442
|
+
#
|
443
|
+
# Example:
|
444
|
+
#
|
445
|
+
# options { |o| o.style = :justified }
|
446
|
+
#
|
447
|
+
def options
|
448
|
+
@options ||= Ruport::Controller::Options.new
|
449
|
+
yield(@options) if block_given?
|
450
|
+
|
451
|
+
return @options
|
452
|
+
end
|
453
|
+
|
454
|
+
private
|
455
|
+
|
456
|
+
# Creates a new instance of the controller and sets it to use the specified
|
457
|
+
# formatter (by name). If a block is given, the controller instance is
|
458
|
+
# yielded.
|
459
|
+
#
|
460
|
+
# Returns the controller instance.
|
461
|
+
#
|
462
|
+
def build(format, add_options=nil)
|
463
|
+
rend = self.new
|
464
|
+
|
465
|
+
rend.send(:use_formatter, format)
|
466
|
+
rend.send(:options=, options.dup)
|
467
|
+
if rend.class.const_defined? :Helpers
|
468
|
+
rend.formatter.extend(rend.class.const_get(:Helpers))
|
469
|
+
end
|
470
|
+
if add_options.kind_of?(Hash)
|
471
|
+
d = add_options.delete(:data)
|
472
|
+
rend.data = d if d
|
473
|
+
add_options.each {|k,v| rend.options.send("#{k}=",v) }
|
474
|
+
end
|
475
|
+
|
476
|
+
yield(rend) if block_given?
|
477
|
+
return rend
|
478
|
+
end
|
479
|
+
|
480
|
+
# Allows you to register a format with the controller.
|
481
|
+
#
|
482
|
+
# Example:
|
483
|
+
#
|
484
|
+
# class MyFormatter < Ruport::Formatter
|
485
|
+
# # formatter code ...
|
486
|
+
# SomeController.add_format self, :my_formatter
|
487
|
+
# end
|
488
|
+
#
|
489
|
+
def add_format(format,name=nil)
|
490
|
+
formats[name] = format
|
491
|
+
end
|
492
|
+
|
493
|
+
end
|
494
|
+
|
495
|
+
# The name of format being used.
|
496
|
+
attr_accessor :format
|
497
|
+
|
498
|
+
# The formatter object being used.
|
499
|
+
attr_writer :formatter
|
500
|
+
|
501
|
+
# The +data+ that has been passed to the active formatter.
|
502
|
+
def data
|
503
|
+
formatter.data
|
504
|
+
end
|
505
|
+
|
506
|
+
# Sets +data+ attribute on the active formatter.
|
507
|
+
def data=(val)
|
508
|
+
formatter.data = val
|
509
|
+
end
|
510
|
+
|
511
|
+
# Controller::Options object which is shared with the current formatter.
|
512
|
+
def options
|
513
|
+
yield(formatter.options) if block_given?
|
514
|
+
formatter.options
|
515
|
+
end
|
516
|
+
|
517
|
+
# Call the _run_ method. You can override this method in your custom
|
518
|
+
# controller if you need to define other actions.
|
519
|
+
def run
|
520
|
+
_run_
|
521
|
+
end
|
522
|
+
|
523
|
+
# If an IO object is given, Formatter#output will use it instead of
|
524
|
+
# the default String. For Ruport's core controllers, we technically
|
525
|
+
# can use any object that supports the << method, but it's meant
|
526
|
+
# for IO objects such as File or STDOUT
|
527
|
+
#
|
528
|
+
def io=(obj)
|
529
|
+
options.io=obj
|
530
|
+
end
|
531
|
+
|
532
|
+
# Returns the active formatter.
|
533
|
+
#
|
534
|
+
# If a block is given, it is evaluated in the context of the formatter.
|
535
|
+
def formatter(&block)
|
536
|
+
@formatter.instance_eval(&block) if block
|
537
|
+
return @formatter
|
538
|
+
end
|
539
|
+
|
540
|
+
# Provides a shortcut to render() to allow
|
541
|
+
# render(:csv) to become render_csv
|
542
|
+
#
|
543
|
+
def self.method_missing(id,*args,&block)
|
544
|
+
id.to_s =~ /^render_(.*)/
|
545
|
+
unless args[0].kind_of? Hash
|
546
|
+
args = [ (args[1] || {}).merge(:data => args[0]) ]
|
547
|
+
end
|
548
|
+
$1 ? render($1.to_sym,*args,&block) : super
|
549
|
+
end
|
550
|
+
|
551
|
+
private
|
552
|
+
|
553
|
+
# Called automatically when the report is rendered. Uses the
|
554
|
+
# data collected from the earlier methods.
|
555
|
+
def _run_
|
556
|
+
unless self.class.required_options.nil?
|
557
|
+
self.class.required_options.each do |opt|
|
558
|
+
if options.__send__(opt).nil?
|
559
|
+
raise RequiredOptionNotSet, "Required option #{opt} not set"
|
560
|
+
end
|
561
|
+
end
|
562
|
+
end
|
563
|
+
|
564
|
+
if formatter.respond_to?(:apply_template) && options.template != false
|
565
|
+
formatter.apply_template if options.template ||
|
566
|
+
Ruport::Formatter::Template.default
|
567
|
+
end
|
568
|
+
|
569
|
+
prepare self.class.first_stage if self.class.first_stage
|
570
|
+
|
571
|
+
if formatter.respond_to?(:layout) && options.layout != false
|
572
|
+
formatter.layout do execute_stages end
|
573
|
+
else
|
574
|
+
execute_stages
|
575
|
+
end
|
576
|
+
|
577
|
+
finalize self.class.final_stage if self.class.final_stage
|
578
|
+
maybe :finalize
|
579
|
+
end
|
580
|
+
|
581
|
+
def execute_stages
|
582
|
+
unless self.class.stages.nil?
|
583
|
+
self.class.stages.each do |stage|
|
584
|
+
maybe("build_#{stage}")
|
585
|
+
end
|
586
|
+
end
|
587
|
+
end
|
588
|
+
|
589
|
+
def prepare(name)
|
590
|
+
maybe "prepare_#{name}"
|
591
|
+
end
|
592
|
+
|
593
|
+
def finalize(name)
|
594
|
+
maybe "finalize_#{name}"
|
595
|
+
end
|
596
|
+
|
597
|
+
def maybe(something)
|
598
|
+
formatter.send something if formatter.respond_to? something
|
599
|
+
end
|
600
|
+
|
601
|
+
def options=(o)
|
602
|
+
formatter.options = o
|
603
|
+
end
|
604
|
+
|
605
|
+
# Selects a formatter for use by format name
|
606
|
+
def use_formatter(format)
|
607
|
+
self.formatter = self.class.formats[format].new
|
608
|
+
self.formatter.format = format
|
609
|
+
rescue NoMethodError
|
610
|
+
raise UnknownFormatError
|
611
|
+
end
|
612
|
+
|
613
|
+
end
|
614
|
+
|
615
|
+
require "ruport/controller/table"
|
616
|
+
require "ruport/controller/grouping"
|