ruport 1.0.2 → 1.2.0

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