ruport 1.0.1 → 1.0.2

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/Rakefile CHANGED
@@ -3,7 +3,7 @@ require "rake/testtask"
3
3
  require "rake/gempackagetask"
4
4
 
5
5
 
6
- RUPORT_VERSION = "1.0.1"
6
+ RUPORT_VERSION = "1.0.2"
7
7
 
8
8
  begin
9
9
  require "rubygems"
@@ -32,7 +32,7 @@ class TracSummaryReport
32
32
  table = Table([:title, :date], :record_class => TicketStatus) do |table|
33
33
  (feed/"item").each do |r|
34
34
  title = (r/"title").innerHTML
35
- next unless title =~ /Ticket.*(created|closed|reopene)/
35
+ next unless title =~ /Ticket.*(created|closed|reopened)/
36
36
  table << { :title => title,
37
37
  :date => Date.parse((r/"pubdate").innerHTML) }
38
38
  end
@@ -40,7 +40,7 @@ module Ruport::Data
40
40
  # table = Table.load('mydata.csv',:has_names => false)
41
41
  #
42
42
  # # pass in FasterCSV options, such as column separators
43
- # table = Table.load('mydata.csv',:csv_opts => { :col_sep => "\t" })
43
+ # table = Table.load('mydata.csv',:csv_options => { :col_sep => "\t" })
44
44
  #
45
45
  def load(csv_file, options={},&block)
46
46
  get_table_from_csv(:foreach, csv_file, options,&block)
@@ -144,7 +144,7 @@ module Ruport::Data
144
144
  r.attributes.all? { |a| a.kind_of?(Numeric) }
145
145
  r.to_a
146
146
  else
147
- r.to_hash
147
+ r.to_hash.values_at(*@column_names)
148
148
  end
149
149
  }
150
150
  end
@@ -122,7 +122,9 @@ module Ruport
122
122
  private
123
123
 
124
124
  def render_helper(rend_klass, source_data,options={},&block)
125
- options = {:data => source_data, :io => output}.merge(options)
125
+ options = {:data => source_data,
126
+ :io => output,
127
+ :layout => false }.merge(options)
126
128
  rend_klass.render(format,options) do |rend|
127
129
  block[rend] if block
128
130
  end
@@ -191,7 +191,7 @@ class Ruport::Renderer
191
191
 
192
192
  prepare self.class.first_stage if self.class.first_stage
193
193
 
194
- if formatter.respond_to?(:layout)
194
+ if formatter.respond_to?(:layout) && options.layout != false
195
195
  formatter.layout do execute_stages end
196
196
  else
197
197
  execute_stages
@@ -382,7 +382,7 @@ class Ruport::Renderer
382
382
  rend = build(*args) { |r|
383
383
  yield(r) if block_given?
384
384
  r.setup if r.respond_to? :setup
385
- }
385
+ }
386
386
  if rend.respond_to? :run
387
387
  rend.run
388
388
  else
@@ -0,0 +1,542 @@
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"
data/lib/ruport.rb CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  module Ruport #:nodoc:#
14
14
 
15
- VERSION = "1.0.1"
15
+ VERSION = "1.0.2"
16
16
 
17
17
  class FormatterError < RuntimeError #:nodoc:
18
18
  end
@@ -70,10 +70,15 @@ module Ruport #:nodoc:#
70
70
  rescue LoadError # If we're not on Windows try...
71
71
  # A Unix savvy method to fetch the console columns, and rows.
72
72
  def terminal_size
73
- size = `stty size 2>/dev/null`.split.map { |x| x.to_i }.reverse
74
- return $? == 0 ? size : [80,24]
73
+ size = if /solaris/ =~ RUBY_PLATFORM
74
+ output = `stty`
75
+ [output.match('columns = (\d+)')[1].to_i,
76
+ output.match('rows = (\d+)')[1].to_i]
77
+ else
78
+ `stty size`.split.map { |x| x.to_i }.reverse
79
+ end
80
+ return $? == 0 ? size : [80,24]
75
81
  end
76
-
77
82
  end
78
83
 
79
84
  def terminal_width
@@ -147,6 +147,11 @@ class TestFormatterWithLayout < Test::Unit::TestCase
147
147
  assert_equal "---\nheader\nbody\nfooter\n---\n",
148
148
  VanillaRenderer.render_text_with_layout
149
149
  end
150
+
151
+ def test_layout_disabled
152
+ assert_equal "header\nbody\nfooter\n",
153
+ VanillaRenderer.render_text_with_layout(:layout => false)
154
+ end
150
155
 
151
156
  end
152
157
 
@@ -0,0 +1,512 @@
1
+ require 'test/helpers'
2
+
3
+ ###########################################################################
4
+ #
5
+ # NOTE:
6
+ #
7
+ # As it stands, we haven't found a more clever way to test the formatting
8
+ # system than to just create a bunch of renderers and basic formatters for
9
+ # different concepts we're trying to test. Patches and ideas welcome:
10
+ #
11
+ # list.rubyreports.org
12
+ ############################################################################
13
+
14
+ #============================================================================
15
+ # These two renderers represent the two styles that can be used when defining
16
+ # renderers in Ruport. The OldSchoolRenderer approach has largely been
17
+ # deprecated, but still has uses in edge cases that we need to support.
18
+ #============================================================================
19
+
20
+ class OldSchoolRenderer < Ruport::Renderer
21
+
22
+ def run
23
+ formatter do
24
+ build_header
25
+ build_body
26
+ build_footer
27
+ end
28
+ end
29
+
30
+ end
31
+
32
+ class VanillaRenderer < Ruport::Renderer
33
+ stage :header,:body,:footer
34
+ end
35
+
36
+
37
+ # This formatter implements some junk output so we can be sure
38
+ # that the hooks are being set up right. Perhaps these could
39
+ # be replaced by mock objects in the future.
40
+ class DummyText < Ruport::Formatter
41
+
42
+ renders :text, :for => OldSchoolRenderer
43
+
44
+ def prepare_document
45
+ output << "p"
46
+ end
47
+
48
+ def build_header
49
+ output << "header\n"
50
+ end
51
+
52
+ def build_body
53
+ output << "body\n"
54
+ end
55
+
56
+ def build_footer
57
+ output << "footer\n"
58
+ end
59
+
60
+ def finalize_document
61
+ output << "f"
62
+ end
63
+ end
64
+
65
+
66
+ class TestRenderer < Test::Unit::TestCase
67
+
68
+ def test_trivial
69
+ actual = OldSchoolRenderer.render(:text)
70
+ assert_equal "header\nbody\nfooter\n", actual
71
+ end
72
+
73
+ def test_using_io
74
+ require "stringio"
75
+ out = StringIO.new
76
+ a = OldSchoolRenderer.render(:text) { |r| r.io = out }
77
+ out.rewind
78
+ assert_equal "header\nbody\nfooter\n", out.read
79
+ assert_equal "", out.read
80
+ end
81
+
82
+ def test_formats
83
+ assert_equal( {}, Ruport::Renderer.formats )
84
+ assert_equal( { :text => DummyText },OldSchoolRenderer.formats )
85
+ end
86
+
87
+ def test_method_missing
88
+ actual = OldSchoolRenderer.render_text
89
+ assert_equal "header\nbody\nfooter\n", actual
90
+ end
91
+
92
+ def test_formatter
93
+ # normal instance mode
94
+ rend = OldSchoolRenderer.new
95
+ rend.send(:use_formatter,:text)
96
+
97
+ assert_kind_of Ruport::Formatter, rend.formatter
98
+ assert_kind_of DummyText, rend.formatter
99
+
100
+ # render mode
101
+ OldSchoolRenderer.render_text do |r|
102
+ assert_kind_of Ruport::Formatter, r.formatter
103
+ assert_kind_of DummyText, r.formatter
104
+ end
105
+
106
+ assert_equal "body\n", rend.formatter { build_body }.output
107
+
108
+ rend.formatter.clear_output
109
+ assert_equal "", rend.formatter.output
110
+ end
111
+
112
+ def test_options_act_like_indifferent_hash
113
+ opts = Ruport::Renderer::Options.new
114
+ opts.foo = "bar"
115
+ assert_equal "bar", opts[:foo]
116
+ assert_equal "bar", opts["foo"]
117
+
118
+ opts["f"] = "bar"
119
+ assert_equal "bar", opts[:f]
120
+ assert_equal "bar", opts.f
121
+ assert_equal "bar", opts["f"]
122
+
123
+ opts[:apple] = "banana"
124
+ assert_equal "banana", opts.apple
125
+ assert_equal "banana", opts["apple"]
126
+ assert_equal "banana", opts[:apple]
127
+ end
128
+
129
+ end
130
+
131
+
132
+ class TestFormatterWithLayout < Test::Unit::TestCase
133
+ # This formatter is meant to check out a special case in Ruport's renderer,
134
+ # in which a layout method is called and yielded to when defined
135
+ class WithLayout < DummyText
136
+ renders :text_with_layout, :for => VanillaRenderer
137
+
138
+ def layout
139
+ output << "---\n"
140
+ yield
141
+ output << "---\n"
142
+ end
143
+
144
+ end
145
+
146
+ def test_layout
147
+ assert_equal "---\nheader\nbody\nfooter\n---\n",
148
+ VanillaRenderer.render_text_with_layout
149
+ end
150
+
151
+ end
152
+
153
+
154
+ class TestRendererWithManyHooks < Test::Unit::TestCase
155
+ # This provides a way to check several hooks that Renderer supports
156
+ class RendererWithManyHooks < Ruport::Renderer
157
+
158
+ add_format DummyText, :text
159
+
160
+ prepare :document
161
+
162
+ option :subtitle, :subsubtitle
163
+
164
+ stage :header
165
+ stage :body
166
+ stage :footer
167
+
168
+ finalize :document
169
+
170
+ def setup
171
+ options.apple = true
172
+ end
173
+
174
+ end
175
+
176
+ def test_hash_options_setters
177
+ a = RendererWithManyHooks.render(:text, :subtitle => "foo",
178
+ :subsubtitle => "bar") { |r|
179
+ assert_equal "foo", r.options.subtitle
180
+ assert_equal "bar", r.options.subsubtitle
181
+ }
182
+ end
183
+
184
+ def test_data_accessors
185
+ a = RendererWithManyHooks.render(:text, :data => [1,2,4]) { |r|
186
+ assert_equal [1,2,4], r.data
187
+ }
188
+
189
+ b = RendererWithManyHooks.render_text(%w[a b c]) { |r|
190
+ assert_equal %w[a b c], r.data
191
+ }
192
+
193
+ c = RendererWithManyHooks.render_text(%w[a b f],:snapper => :red) { |r|
194
+ assert_equal %w[a b f], r.data
195
+ assert_equal :red, r.options.snapper
196
+ }
197
+ end
198
+
199
+ def test_stage_helper
200
+ assert RendererWithManyHooks.stages.include?('body')
201
+ end
202
+
203
+ def test_finalize_helper
204
+ assert_equal :document, RendererWithManyHooks.final_stage
205
+ end
206
+
207
+ def test_prepare_helper
208
+ assert_equal :document, RendererWithManyHooks.first_stage
209
+ end
210
+
211
+ def test_finalize_again
212
+ assert_raise(Ruport::Renderer::StageAlreadyDefinedError) {
213
+ RendererWithManyHooks.finalize :report
214
+ }
215
+ end
216
+
217
+ def test_prepare_again
218
+ assert_raise(Ruport::Renderer::StageAlreadyDefinedError) {
219
+ RendererWithManyHooks.prepare :foo
220
+ }
221
+ end
222
+
223
+ def test_renderer_using_helpers
224
+ actual = RendererWithManyHooks.render(:text)
225
+ assert_equal "pheader\nbody\nfooter\nf", actual
226
+
227
+ actual = RendererWithManyHooks.render_text
228
+ assert_equal "pheader\nbody\nfooter\nf", actual
229
+ end
230
+
231
+ def test_option_helper
232
+ RendererWithManyHooks.render_text do |r|
233
+ r.subtitle = "Test Report"
234
+ assert_equal "Test Report", r.options.subtitle
235
+ end
236
+ end
237
+
238
+ def test_required_option_helper
239
+ a = RendererWithManyHooks.dup
240
+ a.required_option :title
241
+
242
+ a.render_text do |r|
243
+ r.title = "Test Report"
244
+ assert_equal "Test Report", r.options.title
245
+ end
246
+
247
+ end
248
+
249
+ def test_without_required_option
250
+ a = RendererWithManyHooks.dup
251
+ a.required_option :title
252
+
253
+ assert_raise(Ruport::Renderer::RequiredOptionNotSet) { a.render(:text) }
254
+ end
255
+
256
+ end
257
+
258
+
259
+ class TestRendererWithRunHook < Test::Unit::TestCase
260
+
261
+ class RendererWithRunHook < Ruport::Renderer
262
+
263
+ include AutoRunner
264
+
265
+ add_format DummyText, :text
266
+
267
+ required_option :foo,:bar
268
+ stage :header
269
+ stage :body
270
+ stage :footer
271
+
272
+ def run
273
+ formatter.output << "|"
274
+ end
275
+
276
+ end
277
+
278
+ def test_renderer_with_run_hooks
279
+ assert_equal "|header\nbody\nfooter\n",
280
+ RendererWithRunHook.render_text(:foo => "bar",:bar => "baz")
281
+ end
282
+
283
+ end
284
+
285
+
286
+ class TestRendererWithHelperModule < Test::Unit::TestCase
287
+
288
+ class RendererWithHelperModule < VanillaRenderer
289
+
290
+ add_format DummyText, :stub
291
+
292
+ module Helpers
293
+ def say_hello
294
+ "Hello Dolly"
295
+ end
296
+ end
297
+ end
298
+
299
+ def test_renderer_helper_module
300
+ RendererWithHelperModule.render_stub do |r|
301
+ assert_equal "Hello Dolly", r.formatter.say_hello
302
+ end
303
+ end
304
+ end
305
+
306
+
307
+ class TestMultiPurposeFormatter < Test::Unit::TestCase
308
+ # This provides a way to check the multi-format hooks for the Renderer
309
+ class MultiPurposeFormatter < Ruport::Formatter
310
+
311
+ renders [:html,:text], :for => VanillaRenderer
312
+
313
+ def build_header
314
+ a = 10
315
+
316
+ text { output << "Foo: #{a}\n" }
317
+ html { output << "<b>Foo: #{a}</b>\n" }
318
+ end
319
+
320
+ def build_body
321
+ html { output << "<pre>\n" }
322
+ output << options.body_text
323
+ html { output << "\n</pre>\n" }
324
+ end
325
+
326
+ end
327
+
328
+ def test_multi_purpose
329
+ text = VanillaRenderer.render_text(:body_text => "foo")
330
+ assert_equal "Foo: 10\nfoo", text
331
+ html = VanillaRenderer.render_html(:body_text => "bar")
332
+ assert_equal "<b>Foo: 10</b>\n<pre>\nbar\n</pre>\n",html
333
+ end
334
+
335
+
336
+ def test_method_missing_hack_formatter
337
+ assert_equal [:html,:text], MultiPurposeFormatter.formats
338
+
339
+ a = MultiPurposeFormatter.new
340
+ a.format = :html
341
+
342
+ visited = false
343
+ a.html { visited = true }
344
+
345
+ assert visited
346
+
347
+ visited = false
348
+ a.text { visited = true }
349
+ assert !visited
350
+
351
+ assert_raises(NoMethodError) do
352
+ a.pdf { 'do nothing' }
353
+ end
354
+ end
355
+
356
+ end
357
+
358
+
359
+ class TestFormatterErbHelper < Test::Unit::TestCase
360
+ class ErbFormatter < Ruport::Formatter
361
+
362
+ renders :terb, :for => VanillaRenderer
363
+
364
+ def build_body
365
+ # demonstrate local binding
366
+ @foo = "bar"
367
+ if options.binding
368
+ output << erb("Binding Override: <%= reverse %>",
369
+ :binding => options.binding)
370
+ else
371
+ output << erb("Default Binding: <%= @foo %>")
372
+ end
373
+ end
374
+
375
+ end
376
+
377
+ #FIXME: need to test file
378
+
379
+ def test_self_bound
380
+ assert_equal "Default Binding: bar", VanillaRenderer.render_terb
381
+ end
382
+
383
+ def test_custom_bound
384
+ a = [1,2,3]
385
+ arr_binding = a.instance_eval { binding }
386
+ assert_equal "Binding Override: 321",
387
+ VanillaRenderer.render_terb(:binding => arr_binding)
388
+ end
389
+ end
390
+
391
+
392
+ class TestOptionReaders < Test::Unit::TestCase
393
+
394
+ class RendererForCheckingOptionReaders < Ruport::Renderer
395
+ option :foo
396
+ end
397
+
398
+ class RendererForCheckingPassivity < Ruport::Renderer
399
+ def foo
400
+ "apples"
401
+ end
402
+ option :foo
403
+ end
404
+
405
+ def setup
406
+ @renderer = RendererForCheckingOptionReaders.new
407
+ @renderer.formatter = Ruport::Formatter.new
408
+
409
+ @passive = RendererForCheckingPassivity.new
410
+ @passive.formatter = Ruport::Formatter.new
411
+ end
412
+
413
+ def test_options_are_readable
414
+ @renderer.foo = 5
415
+ assert_equal 5, @renderer.foo
416
+ end
417
+
418
+ def test_methods_are_not_overridden
419
+ @passive.foo = 5
420
+ assert_equal "apples", @passive.foo
421
+ assert_equal 5, @passive.options.foo
422
+ assert_equal 5, @passive.formatter.options.foo
423
+ end
424
+
425
+ end
426
+
427
+ class TestSetupOrdering < Test::Unit::TestCase
428
+
429
+ class RendererWithSetup < Ruport::Renderer
430
+ option :foo
431
+ stage :bar
432
+ def setup
433
+ foo.capitalize!
434
+ end
435
+ end
436
+
437
+ class BasicFormatter < Ruport::Formatter
438
+ renders :text, :for => RendererWithSetup
439
+
440
+ def build_bar
441
+ output << options.foo
442
+ end
443
+ end
444
+
445
+ def test_render_hash_options_should_be_called_before_setup
446
+ assert_equal "Hello", RendererWithSetup.render_text(:foo => "hello")
447
+ end
448
+
449
+ def test_render_block_should_be_called_before_setup
450
+ assert_equal "Hello",
451
+ RendererWithSetup.render_text { |r| r.foo = "hello" }
452
+ end
453
+
454
+ end
455
+
456
+ class TestRendererHooks < Test::Unit::TestCase
457
+
458
+ context "when renderable_data omitted" do
459
+
460
+ require "mocha"
461
+
462
+ class DummyObject
463
+ include Ruport::Renderer::Hooks
464
+ renders_as_table
465
+ end
466
+
467
+ def specify_should_return_self
468
+ a = DummyObject.new
469
+ rend = mock("renderer")
470
+ rend.expects(:data=).with(a)
471
+ Ruport::Renderer::Table.expects(:render).with(:csv,{}).yields(rend)
472
+ a.as(:csv)
473
+ end
474
+
475
+ end
476
+
477
+ context "when using renderable_data" do
478
+
479
+ class DummyObject2
480
+ include Ruport::Renderer::Hooks
481
+ renders_as_table
482
+
483
+ def renderable_data
484
+ 1
485
+ end
486
+ end
487
+
488
+ def specify_should_return_results_of_renderable_data
489
+ a = DummyObject2.new
490
+ rend = mock("renderer")
491
+ rend.expects(:data=).with(1)
492
+ Ruport::Renderer::Table.expects(:render).with(:csv,{}).yields(rend)
493
+ a.as(:csv)
494
+ end
495
+
496
+ class DummyObject3
497
+ include Ruport::Renderer::Hooks
498
+ renders_as_table
499
+
500
+ def renderable_data
501
+ raise ArgumentError
502
+ end
503
+ end
504
+
505
+ def specify_should_not_mask_errors
506
+ assert_raises(ArgumentError) { DummyObject3.new.as(:csv) }
507
+ end
508
+
509
+
510
+ end
511
+
512
+ end
data/test/table_test.rb CHANGED
@@ -82,6 +82,14 @@ class TestTable < Test::Unit::TestCase
82
82
  table.sub_table(2..-1)
83
83
 
84
84
  end
85
+
86
+ def test_subtable_records_have_correct_data
87
+ table = [ [1,2,3,4],[5,6,7,9],
88
+ [10,11,12,13],[14,15,16,17] ].to_table(%w[a b c d])
89
+ sub = table.sub_table(%w[b c d]) {|r| r.a == 1 }
90
+ assert_equal({"b"=>2, "c"=>3, "d"=>4}, sub[0].data)
91
+ assert_equal(["b", "c", "d"], sub[0].attributes)
92
+ end
85
93
 
86
94
  def test_reduce
87
95
  table = [ [1,2,3,4],[5,6,7,9],
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.1
7
- date: 2007-06-12 00:00:00 -04:00
6
+ version: 1.0.2
7
+ date: 2007-08-22 00:00:00 -04:00
8
8
  summary: A generalized Ruby report generation and templating engine.
9
9
  require_paths:
10
10
  - lib
@@ -30,106 +30,108 @@ authors:
30
30
  - Gregory Brown
31
31
  files:
32
32
  - examples/btree
33
- - examples/btree/commaleon
34
- - examples/btree/commaleon/sample_data
35
- - examples/btree/commaleon/sample_data/ticket_count.csv
36
- - examples/btree/commaleon/sample_data/ticket_count2.csv
37
- - examples/btree/commaleon/commaleon.rb
38
- - examples/data
39
- - examples/data/tattle.dump
40
- - examples/row_renderer.rb
41
- - examples/RWEmerson.jpg
42
33
  - examples/centered_pdf_text_box.rb
43
- - examples/tattle_rubygems_version.rb
44
- - examples/trac_ticket_status.rb
34
+ - examples/data
35
+ - examples/example.csv
45
36
  - examples/line_plotter.rb
46
- - examples/png_embed.rb
47
37
  - examples/pdf_report_with_common_base.rb
48
- - examples/example.csv
49
- - examples/tattle_ruby_version.rb
38
+ - examples/png_embed.rb
50
39
  - examples/roadmap.png
40
+ - examples/row_renderer.rb
41
+ - examples/RWEmerson.jpg
51
42
  - examples/simple_pdf_lines.rb
43
+ - examples/tattle_ruby_version.rb
44
+ - examples/tattle_rubygems_version.rb
45
+ - examples/trac_ticket_status.rb
46
+ - examples/btree/commaleon
47
+ - examples/btree/commaleon/commaleon.rb
48
+ - examples/btree/commaleon/sample_data
49
+ - examples/btree/commaleon/sample_data/ticket_count.csv
50
+ - examples/btree/commaleon/sample_data/ticket_count2.csv
51
+ - examples/data/tattle.dump
52
52
  - lib/ruport
53
- - lib/ruport/formatter
54
- - lib/ruport/formatter/html.rb
55
- - lib/ruport/formatter/text.rb
56
- - lib/ruport/formatter/pdf.rb
57
- - lib/ruport/formatter/csv.rb
58
- - lib/ruport/query
59
- - lib/ruport/query/sql_split.rb
60
- - lib/ruport/renderer
61
- - lib/ruport/renderer/table.rb
62
- - lib/ruport/renderer/grouping.rb
53
+ - lib/ruport.rb
54
+ - lib/uport.rb
55
+ - lib/ruport/acts_as_reportable.rb
63
56
  - lib/ruport/data
64
- - lib/ruport/data/record.rb
65
- - lib/ruport/data/table.rb
66
- - lib/ruport/data/grouping.rb
57
+ - lib/ruport/data.rb
67
58
  - lib/ruport/extensions.rb
59
+ - lib/ruport/formatter
68
60
  - lib/ruport/formatter.rb
61
+ - lib/ruport/query
69
62
  - lib/ruport/query.rb
63
+ - lib/ruport/renderer
70
64
  - lib/ruport/renderer.rb
71
- - lib/ruport/data.rb
72
- - lib/ruport/acts_as_reportable.rb
73
- - lib/uport.rb
74
- - lib/ruport.rb
75
- - test/samples
76
- - test/samples/test.yaml
77
- - test/samples/query_test.sql
78
- - test/samples/data.csv
79
- - test/samples/data.tsv
80
- - test/samples/erb_test.sql
81
- - test/samples/ticket_count.csv
82
- - test/samples/ruport_test.sql
83
- - test/samples/addressbook.csv
84
- - test/samples/dates.csv
85
- - test/samples/test.sql
65
+ - lib/ruport/renderer.rb.orig
66
+ - lib/ruport/data/grouping.rb
67
+ - lib/ruport/data/record.rb
68
+ - lib/ruport/data/table.rb
69
+ - lib/ruport/formatter/csv.rb
70
+ - lib/ruport/formatter/html.rb
71
+ - lib/ruport/formatter/pdf.rb
72
+ - lib/ruport/formatter/text.rb
73
+ - lib/ruport/query/sql_split.rb
74
+ - lib/ruport/renderer/grouping.rb
75
+ - lib/ruport/renderer/table.rb
76
+ - test/acts_as_reportable_test.rb
86
77
  - test/csv_formatter_test.rb
87
- - test/record_test.rb
78
+ - test/grouping_test.rb
88
79
  - test/helpers.rb
80
+ - test/html_formatter_test.rb
81
+ - test/pdf_formatter_test.rb
89
82
  - test/query_test.rb
83
+ - test/record_test.rb
90
84
  - test/renderer_test.rb
91
- - test/html_formatter_test.rb
85
+ - test/renderer_test.rb.orig
86
+ - test/samples
87
+ - test/sql_split_test.rb
92
88
  - test/table_test.rb
93
89
  - test/text_formatter_test.rb
94
- - test/grouping_test.rb
95
- - test/sql_split_test.rb
96
- - test/acts_as_reportable_test.rb
97
- - test/pdf_formatter_test.rb
90
+ - test/samples/addressbook.csv
91
+ - test/samples/data.csv
92
+ - test/samples/data.tsv
93
+ - test/samples/dates.csv
94
+ - test/samples/erb_test.sql
95
+ - test/samples/query_test.sql
96
+ - test/samples/ruport_test.sql
97
+ - test/samples/test.sql
98
+ - test/samples/test.yaml
99
+ - test/samples/ticket_count.csv
100
+ - util/bench/data
98
101
  - util/bench/formatter
99
- - util/bench/formatter/bench_html.rb
100
- - util/bench/formatter/bench_text.rb
101
- - util/bench/formatter/bench_pdf.rb
102
- - util/bench/formatter/bench_csv.rb
103
102
  - util/bench/samples
104
- - util/bench/samples/tattle.csv
105
- - util/bench/data
103
+ - util/bench/data/record
106
104
  - util/bench/data/table
105
+ - util/bench/data/record/bench_as_vs_to.rb
106
+ - util/bench/data/record/bench_constructor.rb
107
+ - util/bench/data/record/bench_indexing.rb
108
+ - util/bench/data/record/bench_reorder.rb
109
+ - util/bench/data/record/bench_to_a.rb
107
110
  - util/bench/data/table/bench_column_manip.rb
111
+ - util/bench/data/table/bench_dup.rb
108
112
  - util/bench/data/table/bench_init.rb
109
113
  - util/bench/data/table/bench_manip.rb
110
- - util/bench/data/table/bench_dup.rb
111
- - util/bench/data/record
112
- - util/bench/data/record/bench_reorder.rb
113
- - util/bench/data/record/bench_to_a.rb
114
- - util/bench/data/record/bench_constructor.rb
115
- - util/bench/data/record/bench_indexing.rb
116
- - util/bench/data/record/bench_as_vs_to.rb
114
+ - util/bench/formatter/bench_csv.rb
115
+ - util/bench/formatter/bench_html.rb
116
+ - util/bench/formatter/bench_pdf.rb
117
+ - util/bench/formatter/bench_text.rb
118
+ - util/bench/samples/tattle.csv
117
119
  - Rakefile
118
120
  - README
119
121
  - LICENSE
120
122
  - AUTHORS
121
123
  test_files:
124
+ - test/acts_as_reportable_test.rb
122
125
  - test/csv_formatter_test.rb
123
- - test/record_test.rb
126
+ - test/grouping_test.rb
127
+ - test/html_formatter_test.rb
128
+ - test/pdf_formatter_test.rb
124
129
  - test/query_test.rb
130
+ - test/record_test.rb
125
131
  - test/renderer_test.rb
126
- - test/html_formatter_test.rb
132
+ - test/sql_split_test.rb
127
133
  - test/table_test.rb
128
134
  - test/text_formatter_test.rb
129
- - test/grouping_test.rb
130
- - test/sql_split_test.rb
131
- - test/acts_as_reportable_test.rb
132
- - test/pdf_formatter_test.rb
133
135
  rdoc_options:
134
136
  - --title
135
137
  - Ruport Documentation