ruport 1.0.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby -w
2
+ require File.join(File.expand_path(File.dirname(__FILE__)), "helpers")
3
+
4
+ class TemplateTest < Test::Unit::TestCase
5
+
6
+ def setup
7
+ @template_class = Ruport::Formatter::Template.dup
8
+ end
9
+
10
+ def test_template_should_exist
11
+ @template_class.create(:foo)
12
+ assert_instance_of @template_class,
13
+ @template_class[:foo]
14
+ end
15
+
16
+ def test_template_creation_yields_an_options_object
17
+ @template_class.create(:foo) do |template|
18
+ template.page_format = { :layout => :landscape,
19
+ :paper_size => :letter }
20
+ end
21
+ assert_equal :landscape, @template_class[:foo].page_format[:layout]
22
+ assert_equal :letter, @template_class[:foo].page_format[:paper_size]
23
+ end
24
+
25
+ def test_create_derived_template
26
+ Ruport::Formatter::Template.create(:foo) do |template|
27
+ template.page_format = { :layout => :landscape,
28
+ :paper_size => :letter }
29
+ end
30
+ Ruport::Formatter::Template.create(:bar, :base => :foo)
31
+ assert_equal :landscape,
32
+ Ruport::Formatter::Template[:bar].page_format[:layout]
33
+ assert_equal :letter,
34
+ Ruport::Formatter::Template[:bar].page_format[:paper_size]
35
+ end
36
+
37
+ end
@@ -1,4 +1,5 @@
1
- require "test/helpers"
1
+ #!/usr/bin/env ruby -w
2
+ require File.join(File.expand_path(File.dirname(__FILE__)), "helpers")
2
3
 
3
4
  class TestRenderTextTable < Test::Unit::TestCase
4
5
 
@@ -60,6 +61,37 @@ class TestRenderTextTable < Test::Unit::TestCase
60
61
  "+-----------+\n"
61
62
  assert_equal expected, a
62
63
  end
64
+
65
+ def test_render_with_template
66
+ Ruport::Formatter::Template.create(:simple) do |t|
67
+ t.table_format = {
68
+ :show_headings => false,
69
+ :width => 50,
70
+ :ignore_width => true
71
+ }
72
+ t.column_format = {
73
+ :maximum_width => 5,
74
+ :alignment => :center
75
+ }
76
+ t.grouping_format = {
77
+ :show_headings => false
78
+ }
79
+ end
80
+
81
+ formatter = Ruport::Formatter::Text.new
82
+ formatter.options = Ruport::Renderer::Options.new
83
+ formatter.options.template = :simple
84
+ formatter.apply_template
85
+
86
+ assert_equal false, formatter.options.show_table_headers
87
+ assert_equal 50, formatter.options.table_width
88
+ assert_equal true, formatter.options.ignore_table_width
89
+
90
+ assert_equal 5, formatter.options.max_col_width
91
+ assert_equal :center, formatter.options.alignment
92
+
93
+ assert_equal false, formatter.options.show_group_headers
94
+ end
63
95
 
64
96
  # -- BUG TRAPS ------------------------------
65
97
 
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: ruport
5
5
  version: !ruby/object:Gem::Version
6
- version: 1.0.2
7
- date: 2007-08-22 00:00:00 -04:00
6
+ version: 1.2.0
7
+ date: 2007-08-28 00:00:00 -04:00
8
8
  summary: A generalized Ruby report generation and templating engine.
9
9
  require_paths:
10
10
  - lib
@@ -40,6 +40,7 @@ files:
40
40
  - examples/row_renderer.rb
41
41
  - examples/RWEmerson.jpg
42
42
  - examples/simple_pdf_lines.rb
43
+ - examples/simple_templating_example.rb
43
44
  - examples/tattle_ruby_version.rb
44
45
  - examples/tattle_rubygems_version.rb
45
46
  - examples/trac_ticket_status.rb
@@ -62,19 +63,21 @@ files:
62
63
  - lib/ruport/query.rb
63
64
  - lib/ruport/renderer
64
65
  - lib/ruport/renderer.rb
65
- - lib/ruport/renderer.rb.orig
66
+ - lib/ruport/data/feeder.rb
66
67
  - lib/ruport/data/grouping.rb
67
68
  - lib/ruport/data/record.rb
68
69
  - lib/ruport/data/table.rb
69
70
  - lib/ruport/formatter/csv.rb
70
71
  - lib/ruport/formatter/html.rb
71
72
  - lib/ruport/formatter/pdf.rb
73
+ - lib/ruport/formatter/template.rb
72
74
  - lib/ruport/formatter/text.rb
73
75
  - lib/ruport/query/sql_split.rb
74
76
  - lib/ruport/renderer/grouping.rb
75
77
  - lib/ruport/renderer/table.rb
76
78
  - test/acts_as_reportable_test.rb
77
79
  - test/csv_formatter_test.rb
80
+ - test/data_feeder_test.rb
78
81
  - test/grouping_test.rb
79
82
  - test/helpers.rb
80
83
  - test/html_formatter_test.rb
@@ -82,10 +85,10 @@ files:
82
85
  - test/query_test.rb
83
86
  - test/record_test.rb
84
87
  - test/renderer_test.rb
85
- - test/renderer_test.rb.orig
86
88
  - test/samples
87
89
  - test/sql_split_test.rb
88
90
  - test/table_test.rb
91
+ - test/template_test.rb
89
92
  - test/text_formatter_test.rb
90
93
  - test/samples/addressbook.csv
91
94
  - test/samples/data.csv
@@ -123,6 +126,7 @@ files:
123
126
  test_files:
124
127
  - test/acts_as_reportable_test.rb
125
128
  - test/csv_formatter_test.rb
129
+ - test/data_feeder_test.rb
126
130
  - test/grouping_test.rb
127
131
  - test/html_formatter_test.rb
128
132
  - test/pdf_formatter_test.rb
@@ -131,6 +135,7 @@ test_files:
131
135
  - test/renderer_test.rb
132
136
  - test/sql_split_test.rb
133
137
  - test/table_test.rb
138
+ - test/template_test.rb
134
139
  - test/text_formatter_test.rb
135
140
  rdoc_options:
136
141
  - --title
@@ -1,542 +0,0 @@
1
- # renderer.rb : General purpose formatted data renderer 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 renderer for Ruport's formatting system. It is
9
- # designed to implement the low level tools necessary to build report renderers
10
- # for different kinds of tasks. See Renderer::Table for a tabular data
11
- # renderer.
12
- #
13
- class Ruport::Renderer
14
-
15
- class RequiredOptionNotSet < RuntimeError #:nodoc:
16
- end
17
- class UnknownFormatError < RuntimeError #:nodoc:
18
- end
19
- class StageAlreadyDefinedError < RuntimeError #:nodoc:
20
- end
21
- class RendererNotSetError < RuntimeError #:nodoc:
22
- end
23
-
24
- require "ostruct"
25
-
26
- # Structure for holding renderer options.
27
- # Simplified version of HashWithIndifferentAccess
28
- class Options < OpenStruct
29
- # returns a Hash object. Use this if you need methods other than []
30
- def to_hash
31
- @table
32
- end
33
- # indifferent lookup of an attribute, e.g.
34
- #
35
- # options[:foo] == options["foo"]
36
- def [](key)
37
- send(key)
38
- end
39
-
40
- # Sets an attribute, with indifferent access.
41
- #
42
- # options[:foo] = "bar"
43
- #
44
- # options[:foo] == options["foo"] #=> true
45
- # options["foo"] == options.foo #=> true
46
- # options.foo #=> "bar"
47
- def []=(key,value)
48
- send("#{key}=",value)
49
- end
50
- end
51
-
52
- # This module provides hooks into Ruport's formatting system.
53
- # It is used to implement the as() method for all of Ruport's datastructures,
54
- # as well as the renders_with and renders_as_* helpers.
55
- #
56
- # You can actually use this with any data structure, it will look for a
57
- # renderable_data method to pass to the <tt>renderer</tt> you specify,
58
- # but if that is not defined, it will pass <tt>self</tt>
59
- #
60
- # Examples:
61
- #
62
- # # Render Arrays with Ruport's Row Renderer
63
- # class Array
64
- # include Ruport::Renderer::Hooks
65
- # renders_as_row
66
- # end
67
- #
68
- # # >> [1,2,3].as(:csv)
69
- # # => "1,2,3\n"
70
- #
71
- # # Render Hashes with Ruport's Row Renderer
72
- # class Hash
73
- # include Ruport::Renderer::Hooks
74
- # renders_as_row
75
- # attr_accessor :column_order
76
- # def renderable_data
77
- # column_order.map { |c| self[c] }
78
- # end
79
- # end
80
- #
81
- # # >> a = { :a => 1, :b => 2, :c => 3 }
82
- # # >> a.column_order = [:b,:a,:c]
83
- # # >> a.as(:csv)
84
- # # => "2,1,3\n"
85
- module Hooks
86
- module ClassMethods
87
-
88
- # Tells the class which renderer as() will forward to.
89
- #
90
- # usage:
91
- #
92
- # class MyStructure
93
- # include Renderer::Hooks
94
- # renders_with CustomRenderer
95
- # end
96
- #
97
- # You can also specify default rendering options, which will be used
98
- # if they are not overriden by the options passed to as()
99
- #
100
- # class MyStructure
101
- # include Renderer::Hooks
102
- # renders_with CustomRenderer, :font_size => 14
103
- # end
104
- def renders_with(renderer,opts={})
105
- @renderer = renderer.name
106
- @rendering_options=opts
107
- end
108
-
109
- # The default rendering options for a class, stored as a hash.
110
- def rendering_options
111
- @rendering_options
112
- end
113
-
114
- # Shortcut for renders_with(Ruport::Renderer::Table), you
115
- # may wish to override this if you build a custom table renderer.
116
- def renders_as_table(options={})
117
- renders_with Ruport::Renderer::Table,options
118
- end
119
-
120
- # Shortcut for renders_with(Ruport::Renderer::Row), you
121
- # may wish to override this if you build a custom row renderer.
122
- def renders_as_row(options={})
123
- renders_with Ruport::Renderer::Row, options
124
- end
125
-
126
- # Shortcut for renders_with(Ruport::Renderer::Group), you
127
- # may wish to override this if you build a custom group renderer.
128
- def renders_as_group(options={})
129
- renders_with Ruport::Renderer::Group,options
130
- end
131
-
132
- # Shortcut for renders_with(Ruport::Renderer::Grouping), you
133
- # may wish to override this if you build a custom grouping renderer.
134
- def renders_as_grouping(options={})
135
- renders_with Ruport::Renderer::Grouping,options
136
- end
137
-
138
- # The class of the renderer object for the base class.
139
- #
140
- # Example
141
- #
142
- # >> Ruport::Data::Table.renderer
143
- # => Ruport::Renderer::Table
144
- def renderer
145
- return unless @renderer
146
- @renderer.split("::").inject(Class) { |c,el| c.const_get(el) }
147
- end
148
- end
149
-
150
- def self.included(base) #:nodoc:
151
- base.extend(ClassMethods)
152
- end
153
-
154
- # Uses the Renderer specified by renders_with to generate formatted
155
- # output. Passes the return value of the <tt>renderable_data</tt> method if
156
- # the method is defined, otherwise passes <tt>self</tt> as :data
157
- #
158
- # The remaining options are converted to a Renderer::Options object and
159
- # accessible in both the renderer and formatter.
160
- #
161
- # Example:
162
- #
163
- # table.as(:csv, :show_table_headers => false)
164
- def as(format,options={})
165
- raise RendererNotSetError unless self.class.renderer
166
- unless self.class.renderer.formats.include?(format)
167
- raise UnknownFormatError
168
- end
169
- self.class.renderer.render(format,
170
- self.class.rendering_options.merge(options)) do |rend|
171
- rend.data = respond_to?(:renderable_data) ? renderable_data : self
172
- yield(rend) if block_given?
173
- end
174
- end
175
- end
176
-
177
-
178
- module AutoRunner #:nodoc:
179
- # called automagically when the report is rendered. Uses the
180
- # data collected from the earlier methods.
181
- def _run_
182
-
183
- # ensure all the required options have been set
184
- unless self.class.required_options.nil?
185
- self.class.required_options.each do |opt|
186
- if options.__send__(opt).nil?
187
- raise RequiredOptionNotSet, "Required option #{opt} not set"
188
- end
189
- end
190
- end
191
-
192
- prepare self.class.first_stage if self.class.first_stage
193
-
194
- if formatter.respond_to?(:layout)
195
- formatter.layout do execute_stages end
196
- else
197
- execute_stages
198
- end
199
-
200
- finalize self.class.final_stage if self.class.final_stage
201
-
202
- end
203
-
204
- def execute_stages
205
- # call each stage to build the report
206
- unless self.class.stages.nil?
207
- self.class.stages.each do |stage|
208
- self.send(:build,stage)
209
- end
210
- end
211
- end
212
- end
213
-
214
- class << self
215
-
216
- attr_accessor :first_stage,:final_stage,:required_options,:stages #:nodoc:
217
-
218
- # Registers a hook to look for in the Formatter object when the render()
219
- # method is called.
220
- #
221
- # Usage:
222
- #
223
- # class MyRenderer < Ruport::Renderer
224
- # # other details omitted...
225
- # finalize :apple
226
- # end
227
- #
228
- # class MyFormatter < Ruport::Formatter
229
- # renders :example, :for => MyRenderer
230
- #
231
- # # other details omitted...
232
- #
233
- # def finalize_apple
234
- # # this method will be called when MyRenderer tries to render
235
- # # the :example format
236
- # end
237
- # end
238
- #
239
- # If a formatter does not implement this hook, it is simply ignored.
240
- def finalize(stage)
241
- if final_stage
242
- raise StageAlreadyDefinedError, 'final stage already defined'
243
- end
244
- self.final_stage = stage
245
- end
246
-
247
- # Registers a hook to look for in the Formatter object when the render()
248
- # method is called.
249
- #
250
- # Usage:
251
- #
252
- # class MyRenderer < Ruport::Renderer
253
- # # other details omitted...
254
- # prepare :apple
255
- # end
256
- #
257
- # class MyFormatter < Ruport::Formatter
258
- # renders :example, :for => MyRenderer
259
- #
260
- # def prepare_apple
261
- # # this method will be called when MyRenderer tries to render
262
- # # the :example format
263
- # end
264
- #
265
- # # other details omitted...
266
- # end
267
- #
268
- # If a formatter does not implement this hook, it is simply ignored.
269
- def prepare(stage)
270
- if first_stage
271
- raise StageAlreadyDefinedError, "prepare stage already defined"
272
- end
273
- self.first_stage = stage
274
- end
275
-
276
- # Registers hooks to look for in the Formatter object when the render()
277
- # method is called.
278
- #
279
- # Usage:
280
- #
281
- # class MyRenderer < Ruport::Renderer
282
- # # other details omitted...
283
- # stage :apple,:banana
284
- # end
285
- #
286
- # class MyFormatter < Ruport::Formatter
287
- # renders :example, :for => MyRenderer
288
- #
289
- # def build_apple
290
- # # this method will be called when MyRenderer tries to render
291
- # # the :example format
292
- # end
293
- #
294
- # def build_banana
295
- # # this method will be called when MyRenderer tries to render
296
- # # the :example format
297
- # end
298
- #
299
- # # other details omitted...
300
- # end
301
- #
302
- # If a formatter does not implement these hooks, they are simply ignored.
303
- def stage(*stage_list)
304
- self.stages ||= []
305
- stage_list.each { |stage|
306
- self.stages << stage.to_s
307
- }
308
- end
309
-
310
- # Defines attribute writers for the Renderer::Options object shared
311
- # between Renderer and Formatter
312
- #
313
- # usage:
314
- #
315
- # class MyRenderer < Ruport::Renderer
316
- # option :font_size, :font_style
317
- # # other details omitted
318
- # end
319
- def option(*opts)
320
- opts.each do |opt|
321
- o = opt
322
- unless instance_methods(false).include?(o.to_s)
323
- define_method(o) {
324
- options.send(o.to_s)
325
- }
326
- end
327
- opt = "#{opt}="
328
- define_method(opt) {|t| options.send(opt, t) }
329
- end
330
- end
331
-
332
- # Defines attribute writers for the Renderer::Options object shared
333
- # between Renderer and Formatter. Will throw an error if the user does
334
- # not provide values for these options upon rendering.
335
- #
336
- # usage:
337
- #
338
- # class MyRenderer < Ruport::Renderer
339
- # required_option :employee_name, :address
340
- # # other details omitted
341
- # end
342
- def required_option(*opts)
343
- self.required_options ||= []
344
- opts.each do |opt|
345
- self.required_options << opt
346
- option opt
347
- end
348
- end
349
-
350
-
351
- # Lists the formatters that are currently registered on a renderer,
352
- # as a hash keyed by format name.
353
- #
354
- # Example:
355
- #
356
- # >> Ruport::Renderer::Table.formats
357
- # => {:html=>Ruport::Formatter::HTML,
358
- # ?> :csv=>Ruport::Formatter::CSV,
359
- # ?> :text=>Ruport::Formatter::Text,
360
- # ?> :pdf=>Ruport::Formatter::PDF}
361
- def formats
362
- @formats ||= {}
363
- end
364
-
365
- # Builds up a renderer object, looks up the appropriate formatter,
366
- # sets the data and options, and then does the following process:
367
- #
368
- # * If the renderer contains a module Helpers, mix it in to the instance.
369
- # * If a block is given, yield the Renderer instance
370
- # * If a setup() method is defined on the Renderer, call it
371
- # * If the renderer has defined a run() method, call it, otherwise,
372
- # include Renderer::AutoRunner. (you usually won't need a run() method )
373
- # * call _run_ if it exists (This is provided by default, by AutoRunner)
374
- # * return the results of formatter.output
375
- #
376
- # Note that the only time you will need a run() method is if you can't
377
- # do what you need to via a helpers module or via setup()
378
- #
379
- # Please see the examples/ directory for custom renderer examples, because
380
- # this is not nearly as complicated as it sounds in most cases.
381
- def render(*args)
382
- rend = build(*args) { |r|
383
- yield(r) if block_given?
384
- r.setup if r.respond_to? :setup
385
- }
386
- if rend.respond_to? :run
387
- rend.run
388
- else
389
- include AutoRunner
390
- end
391
- rend._run_ if rend.respond_to? :_run_
392
- return rend.formatter.output
393
- end
394
-
395
- # Allows you to set class_wide default options
396
- #
397
- # Example:
398
- #
399
- # options { |o| o.style = :justified }
400
- #
401
- def options
402
- @options ||= Ruport::Renderer::Options.new
403
- yield(@options) if block_given?
404
-
405
- return @options
406
- end
407
-
408
- # Creates a new instance of the renderer and sets it to use the specified
409
- # formatter (by name). If a block is given, the renderer instance is
410
- # yielded.
411
- #
412
- # Returns the renderer instance.
413
- #
414
- def build(*args)
415
- rend = self.new
416
-
417
- rend.send(:use_formatter,args[0])
418
- rend.send(:options=, options.dup)
419
- if rend.class.const_defined? :Helpers
420
- rend.formatter.extend(rend.class.const_get(:Helpers))
421
- end
422
- if args[1].kind_of?(Hash)
423
- d = args[1].delete(:data)
424
- rend.data = d if d
425
- args[1].each {|k,v| rend.options.send("#{k}=",v) }
426
- end
427
-
428
- yield(rend) if block_given?
429
- return rend
430
- end
431
-
432
- private
433
-
434
- # Allows you to register a format with the renderer.
435
- #
436
- # example:
437
- #
438
- # class MyFormatter < Ruport::Formatter
439
- # # formatter code ...
440
- # SomeRenderer.add_format self, :my_formatter
441
- # end
442
- #
443
- def add_format(format,name=nil)
444
- formats[name] = format
445
- end
446
-
447
- end
448
-
449
- # The name of format being used
450
- attr_accessor :format
451
-
452
- # The formatter object being used
453
- attr_writer :formatter
454
-
455
- # The +data+ that has been passed to the active formatter.
456
- def data
457
- formatter.data
458
- end
459
-
460
- # Sets +data+ attribute on the active formatter.
461
- def data=(val)
462
- formatter.data = val.dup
463
- end
464
-
465
- # Renderer::Options object which is shared with the current formatter.
466
- def options
467
- yield(formatter.options) if block_given?
468
- formatter.options
469
- end
470
-
471
- # If an IO object is given, Formatter#output will use it instead of
472
- # the default String. For Ruport's core renderers, we technically
473
- # can use any object that supports the << method, but it's meant
474
- # for IO objects such as File or STDOUT
475
- #
476
- def io=(obj)
477
- options.io=obj
478
- end
479
-
480
- # When no block is given, returns active formatter.
481
- #
482
- # When a block is given with a block variable, sets the block variable to the
483
- # formatter.
484
- #
485
- # When a block is given without block variables, instance_evals the block
486
- # within the context of the formatter.
487
- #
488
- def formatter(&block)
489
- if block.nil?
490
- return @formatter
491
- elsif block.arity > 0
492
- yield(@formatter)
493
- else
494
- @formatter.instance_eval(&block)
495
- end
496
- return @formatter
497
- end
498
-
499
- # Provides a shortcut to render() to allow
500
- # render(:csv) to become render_csv
501
- #
502
- def self.method_missing(id,*args,&block)
503
- id.to_s =~ /^render_(.*)/
504
- unless args[0].kind_of? Hash
505
- args = [ (args[1] || {}).merge(:data => args[0]) ]
506
- end
507
- $1 ? render($1.to_sym,*args,&block) : super
508
- end
509
-
510
- private
511
-
512
- def prepare(name)
513
- maybe "prepare_#{name}"
514
- end
515
-
516
- def build(names,prefix=nil)
517
- return maybe("build_#{names}") if prefix.nil?
518
- names.each { |n| maybe "build_#{prefix}_#{n}" }
519
- end
520
-
521
- def finalize(name)
522
- maybe "finalize_#{name}"
523
- end
524
-
525
- def maybe(something)
526
- formatter.send something if formatter.respond_to? something
527
- end
528
-
529
- def options=(o)
530
- formatter.options = o
531
- end
532
-
533
- # selects a formatter for use by format name
534
- def use_formatter(format)
535
- self.formatter = self.class.formats[format].new
536
- self.formatter.format = format
537
- end
538
-
539
- end
540
-
541
- require "ruport/renderer/table"
542
- require "ruport/renderer/grouping"