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,167 @@
1
+ # Ruport : Extensible Reporting System
2
+ #
3
+ # template.rb provides templating support for Ruby Reports.
4
+ #
5
+ # Copyright August 2007, Gregory Brown / Michael Milner. All Rights Reserved.
6
+ #
7
+ # This is free software. Please see the LICENSE and COPYING files for details.
8
+
9
+
10
+ # This class provides templating functionality for Ruport.
11
+ # New templates are created using the Template.create method.
12
+ #
13
+ # Example:
14
+ #
15
+ # Ruport::Formatter::Template.create(:simple) do |t|
16
+ # t.page_layout = :landscape
17
+ # t.grouping_style = :offset
18
+ # end
19
+ #
20
+ # You can then determine how the template should be used by defining
21
+ # an <tt>apply_template</tt> method in your formatter.
22
+ #
23
+ # Example:
24
+ #
25
+ # class Ruport::Formatter::PDF
26
+ # def apply_template
27
+ # options.paper_orientation = template.page_layout
28
+ # options.style = template.grouping_style
29
+ # end
30
+ # end
31
+ #
32
+ # When you're ready to render the output, you can set the :template as an
33
+ # option for the formatter. Using the template remains optional and you can
34
+ # still render the report without it.
35
+ #
36
+ # Example:
37
+ #
38
+ # puts g.to_pdf(:template => :simple) #=> uses the template
39
+ # puts g.to_pdf #=> doesn't use the template
40
+ #
41
+ # The built-in formatters all have <tt>apply_template</tt> methods defined that
42
+ # accept a standard set of options. Each option can be set by supplying a hash
43
+ # with the keys/values listed in the tables below.
44
+ #
45
+ # Example:
46
+ #
47
+ # Ruport::Formatter::Template.create(:simple) do |t|
48
+ # t.page_format = {
49
+ # :size => "LETTER",
50
+ # :layout => :landscape
51
+ # }
52
+ # end
53
+ #
54
+ # ==== PDF Formatter Options
55
+ #
56
+ # Option Key Value
57
+ #
58
+ # page_format :size Any size supported by the :paper
59
+ # option to PDF::Writer.new
60
+ #
61
+ # :layout :portrait, :landscape
62
+ #
63
+ # text_format Any available to Corresponding values
64
+ # PDF::Writer#text
65
+ #
66
+ # table_format All attributes of Corresponding values
67
+ # PDF::SimpleTable
68
+ #
69
+ # :column_options - All attributes of
70
+ # PDF::SimpleTable::Column
71
+ # except :heading
72
+ # - Hash keyed by a column name, whose
73
+ # value is a hash containing any of
74
+ # the other:column_options (sets values
75
+ # for specific columns)
76
+ # - :heading => { All attributes of
77
+ # PDF::SimpleTable::Column::Heading }
78
+ #
79
+ # column_format :alignment :left, :right, :center, :full
80
+ #
81
+ # :width column width
82
+ #
83
+ # heading_format :alignment :left, :right, :center, :full
84
+ #
85
+ # :bold true or false
86
+ #
87
+ # :title heading title (if not set,
88
+ # defaults to column name)
89
+ #
90
+ # grouping_format :style :inline, :justified, :separated, :offset
91
+ #
92
+ #
93
+ # ==== Text Formatter Options
94
+ #
95
+ # Option Key Value
96
+ #
97
+ # table_format :show_headings true or false
98
+ # :width Table width
99
+ # :ignore_width true or false
100
+ #
101
+ # column_format :alignment :center
102
+ # :maximum_width Max column width
103
+ #
104
+ # grouping_format :show_headings true or false
105
+ #
106
+ #
107
+ # ==== HTML Formatter Options
108
+ #
109
+ # Option Key Value
110
+ #
111
+ # table_format :show_headings true or false
112
+ #
113
+ # grouping_format :style :inline, :justified
114
+ # :show_headings true or false
115
+ #
116
+ #
117
+ # ==== CSV Formatter Options
118
+ #
119
+ # Option Key Value
120
+ #
121
+ # table_format :show_headings true or false
122
+ #
123
+ # grouping_format :style :inline, :justified, :raw
124
+ # :show_headings true or false
125
+ #
126
+ # format_options All options Corresponding values
127
+ # available to
128
+ # FasterCSV.new
129
+ #
130
+ class Ruport::Formatter::Template < Ruport::Renderer::Options
131
+
132
+ # Returns all existing templates in a hash keyed by the template names.
133
+ def self.templates
134
+ @templates ||= Hash.new
135
+ end
136
+
137
+ # Creates a new template with a name given by <tt>label</tt>.
138
+ #
139
+ # Example:
140
+ #
141
+ # Ruport::Formatter::Template.create(:simple) do |t|
142
+ # t.page_layout = :landscape
143
+ # t.grouping_style = :offset
144
+ # end
145
+ #
146
+ # You can inherit all the options set in a template by using the :base option
147
+ # and providing an existing template name to use as the base.
148
+ #
149
+ # Example:
150
+ #
151
+ # Ruport::Formatter::Template.create(:derived, :base => :simple)
152
+ #
153
+ def self.create(label,opts={})
154
+ if opts[:base]
155
+ obj = Marshal.load(Marshal.dump(self[opts[:base]]))
156
+ else
157
+ obj = new
158
+ end
159
+ yield(obj) if block_given?
160
+ templates[label] = obj
161
+ end
162
+
163
+ # Returns an existing template with the provided name (label).
164
+ def self.[](label)
165
+ templates[label]
166
+ end
167
+ end
@@ -10,9 +10,10 @@
10
10
  #
11
11
  # This is free software distributed under the same terms as Ruby 1.8
12
12
  # See LICENSE and COPYING for details.
13
- module Ruport
14
- # This class provides text output for Ruport's Row,Table,Group, and Grouping
15
- # renderers
13
+ module Ruport
14
+
15
+ # This class provides text output for Ruport's Row, Table, Group, and
16
+ # Grouping renderers
16
17
  #
17
18
  # It handles things like automatically truncating tables that go off the
18
19
  # edge of the screen in the console, proper column alignment, and pretty
@@ -24,24 +25,24 @@ module Ruport
24
25
  # | yes | no | yes |
25
26
  # | yes | yes | red snapper |
26
27
  # | what | the | red snapper |
27
- # +------------------------------+
28
+ # +------------------------------+
28
29
  #
29
30
  # === Supported Options
30
31
  #
31
32
  # <tt>:max_col_width:</tt> Ordinal array of column widths. Set automatically
32
- # but can be overridden
33
+ # but can be overridden.
33
34
  #
34
- # <tt>:alignment:</tt> Defaults to left justify text and right justify numbers.
35
- # centers all fields when set to :center
35
+ # <tt>:alignment:</tt> Defaults to left justify text and right justify
36
+ # numbers. Centers all fields when set to :center.
36
37
  #
37
- # <tt>:table_width:</tt> Will truncate rows at this limit.
38
+ # <tt>:table_width:</tt> Will truncate rows at this limit.
38
39
  #
39
40
  # <tt>:show_table_headers:</tt> Defaults to true
40
41
  #
41
42
  # <tt>:show_group_headers:</tt> Defaults to true
42
43
  #
43
44
  # <tt>:ignore_table_width:</tt> When set to true, outputs full table without
44
- # truncating it. Useful for file output
45
+ # truncating it. Useful for file output.
45
46
  class Formatter::Text < Formatter
46
47
 
47
48
  renders :text, :for => [ Renderer::Row, Renderer::Table,
@@ -51,8 +52,16 @@ module Ruport
51
52
  :show_table_headers, :show_group_headers,
52
53
  :ignore_table_width
53
54
 
55
+ # Hook for setting available options using a template. See the template
56
+ # documentation for the available options and their format.
57
+ def apply_template
58
+ apply_table_format_template(template.table_format)
59
+ apply_column_format_template(template.column_format)
60
+ apply_grouping_format_template(template.grouping_format)
61
+ end
62
+
54
63
  # Checks to ensure the table is not empty and then calls
55
- # calculate_max_col_widths
64
+ # calculate_max_col_widths.
56
65
  #
57
66
  def prepare_table
58
67
  raise Ruport::FormatterError, "Can't output table without " +
@@ -63,7 +72,7 @@ module Ruport
63
72
  # Uses the column names from the given Data::Table to generate a table
64
73
  # header.
65
74
  #
66
- # Calls fit_to_width to truncate table heading if necessary.
75
+ # Calls fit_to_width to truncate the table heading if necessary.
67
76
  #
68
77
  def build_table_header
69
78
  return unless should_render_column_names?
@@ -81,7 +90,7 @@ module Ruport
81
90
  # left justified. Can be changed to support centering of output by
82
91
  # setting options.alignment to :center
83
92
  #
84
- # Uses fit_to_width to truncate table if necessary.
93
+ # Uses fit_to_width to truncate the table if necessary.
85
94
  #
86
95
  def build_table_body
87
96
  output << fit_to_width(hr)
@@ -107,7 +116,7 @@ module Ruport
107
116
  # left justified. Can be changed to support centering of output by
108
117
  # setting options.alignment to :center
109
118
  #
110
- # Uses fit_to_width to truncate table if necessary.
119
+ # Uses fit_to_width to truncate the row if necessary.
111
120
  #
112
121
  def build_row
113
122
  max_col_widths_for_row(data) unless max_col_width
@@ -179,7 +188,7 @@ module Ruport
179
188
  }.join("\n") + "\n"
180
189
  end
181
190
 
182
- # determines the text widths for each column.
191
+ # Determines the text widths for each column.
183
192
  def calculate_max_col_widths
184
193
  # allow override
185
194
  return if max_col_width
@@ -195,7 +204,7 @@ module Ruport
195
204
  data.each { |r| max_col_widths_for_row(r) }
196
205
  end
197
206
 
198
- # used to calculate the <tt>max_col_widths</tt> array.
207
+ # Used to calculate the <tt>max_col_widths</tt> array.
199
208
  # Override this to tweak the automatic column size adjustments.
200
209
  def max_col_widths_for_row(row)
201
210
  options.max_col_width ||= []
@@ -205,6 +214,29 @@ module Ruport
205
214
  end
206
215
  end
207
216
  end
217
+
218
+ private
219
+
220
+ def apply_table_format_template(t)
221
+ t = t || {}
222
+ options.show_table_headers = t[:show_headings] unless
223
+ t[:show_headings].nil?
224
+ options.table_width = t[:width] if t[:width]
225
+ options.ignore_table_width = t[:ignore_width] unless
226
+ t[:ignore_width].nil?
227
+ end
228
+
229
+ def apply_column_format_template(t)
230
+ t = t || {}
231
+ options.max_col_width = t[:maximum_width] if t[:maximum_width]
232
+ options.alignment = t[:alignment] if t[:alignment]
233
+ end
234
+
235
+ def apply_grouping_format_template(t)
236
+ t = t || {}
237
+ options.show_group_headers = t[:show_headings] unless
238
+ t[:show_headings].nil?
239
+ end
208
240
 
209
241
  end
210
242
  end
data/lib/ruport/query.rb CHANGED
@@ -196,7 +196,7 @@ module Ruport
196
196
  row = row.to_a
197
197
  row = Data::Record.new(row, :attributes => names) unless @raw_data
198
198
  yield row if block_given?
199
- data << row if !block_given? || @cache_enabled
199
+ data << row if !block_given?
200
200
  end
201
201
  end
202
202
  end
@@ -26,11 +26,11 @@ class Ruport::Renderer
26
26
  # Structure for holding renderer options.
27
27
  # Simplified version of HashWithIndifferentAccess
28
28
  class Options < OpenStruct
29
- # returns a Hash object. Use this if you need methods other than []
29
+ # Returns a Hash object. Use this if you need methods other than []
30
30
  def to_hash
31
31
  @table
32
32
  end
33
- # indifferent lookup of an attribute, e.g.
33
+ # Indifferent lookup of an attribute, e.g.
34
34
  #
35
35
  # options[:foo] == options["foo"]
36
36
  def [](key)
@@ -50,12 +50,12 @@ class Ruport::Renderer
50
50
  end
51
51
 
52
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.
53
+ # It is used to implement the as() method for all of Ruport's data
54
+ # structures, as well as the renders_with and renders_as_* helpers.
55
55
  #
56
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>
57
+ # renderable_data(format) method to pass to the <tt>renderer</tt> you
58
+ # specify, but if that is not defined, it will pass <tt>self</tt>.
59
59
  #
60
60
  # Examples:
61
61
  #
@@ -73,10 +73,10 @@ class Ruport::Renderer
73
73
  # include Ruport::Renderer::Hooks
74
74
  # renders_as_row
75
75
  # attr_accessor :column_order
76
- # def renderable_data
77
- # column_order.map { |c| self[c] }
76
+ # def renderable_data(format)
77
+ # column_order.map { |c| self[c] }
78
78
  # end
79
- # end
79
+ # end
80
80
  #
81
81
  # # >> a = { :a => 1, :b => 2, :c => 3 }
82
82
  # # >> a.column_order = [:b,:a,:c]
@@ -87,7 +87,7 @@ class Ruport::Renderer
87
87
 
88
88
  # Tells the class which renderer as() will forward to.
89
89
  #
90
- # usage:
90
+ # Usage:
91
91
  #
92
92
  # class MyStructure
93
93
  # include Renderer::Hooks
@@ -95,11 +95,11 @@ class Ruport::Renderer
95
95
  # end
96
96
  #
97
97
  # You can also specify default rendering options, which will be used
98
- # if they are not overriden by the options passed to as()
98
+ # if they are not overriden by the options passed to as().
99
99
  #
100
100
  # class MyStructure
101
- # include Renderer::Hooks
102
- # renders_with CustomRenderer, :font_size => 14
101
+ # include Renderer::Hooks
102
+ # renders_with CustomRenderer, :font_size => 14
103
103
  # end
104
104
  def renders_with(renderer,opts={})
105
105
  @renderer = renderer.name
@@ -137,7 +137,7 @@ class Ruport::Renderer
137
137
 
138
138
  # The class of the renderer object for the base class.
139
139
  #
140
- # Example
140
+ # Example:
141
141
  #
142
142
  # >> Ruport::Data::Table.renderer
143
143
  # => Ruport::Renderer::Table
@@ -152,11 +152,11 @@ class Ruport::Renderer
152
152
  end
153
153
 
154
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
155
+ # output. Passes the return value of the <tt>renderable_data(format)</tt>
156
+ # method if the method is defined, otherwise passes <tt>self</tt> as :data
157
157
  #
158
158
  # The remaining options are converted to a Renderer::Options object and
159
- # accessible in both the renderer and formatter.
159
+ # are accessible in both the renderer and formatter.
160
160
  #
161
161
  # Example:
162
162
  #
@@ -167,16 +167,17 @@ class Ruport::Renderer
167
167
  raise UnknownFormatError
168
168
  end
169
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?
170
+ self.class.rendering_options.merge(options)) do |rend|
171
+ rend.data =
172
+ respond_to?(:renderable_data) ? renderable_data(format) : self
173
+ yield(rend) if block_given?
173
174
  end
174
175
  end
175
176
  end
176
-
177
-
177
+
178
+
178
179
  module AutoRunner #:nodoc:
179
- # called automagically when the report is rendered. Uses the
180
+ # Called automatically when the report is rendered. Uses the
180
181
  # data collected from the earlier methods.
181
182
  def _run_
182
183
 
@@ -189,6 +190,10 @@ class Ruport::Renderer
189
190
  end
190
191
  end
191
192
 
193
+ if formatter.respond_to?(:apply_template) && options.template
194
+ formatter.apply_template
195
+ end
196
+
192
197
  prepare self.class.first_stage if self.class.first_stage
193
198
 
194
199
  if formatter.respond_to?(:layout) && options.layout != false
@@ -198,14 +203,13 @@ class Ruport::Renderer
198
203
  end
199
204
 
200
205
  finalize self.class.final_stage if self.class.final_stage
201
-
202
206
  end
203
207
 
204
208
  def execute_stages
205
209
  # call each stage to build the report
206
210
  unless self.class.stages.nil?
207
211
  self.class.stages.each do |stage|
208
- self.send(:build,stage)
212
+ maybe("build_#{stage}")
209
213
  end
210
214
  end
211
215
  end
@@ -308,7 +312,7 @@ class Ruport::Renderer
308
312
  end
309
313
 
310
314
  # Defines attribute writers for the Renderer::Options object shared
311
- # between Renderer and Formatter
315
+ # between Renderer and Formatter.
312
316
  #
313
317
  # usage:
314
318
  #
@@ -366,12 +370,13 @@ class Ruport::Renderer
366
370
  # sets the data and options, and then does the following process:
367
371
  #
368
372
  # * 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
373
+ # * If a block is given, yield the Renderer instance.
374
+ # * If a setup() method is defined on the Renderer, call it.
375
+ # * If the renderer has defined a run() method, call it. Otherwise,
376
+ # include Renderer::AutoRunner (you usually won't need a run() method).
377
+ # * Call _run_ if it exists (this is provided by default, by AutoRunner).
378
+ # * If the :file option is set to a file name, appends output to the file.
379
+ # * Return the results of formatter.output
375
380
  #
376
381
  # Note that the only time you will need a run() method is if you can't
377
382
  # do what you need to via a helpers module or via setup()
@@ -389,10 +394,11 @@ class Ruport::Renderer
389
394
  include AutoRunner
390
395
  end
391
396
  rend._run_ if rend.respond_to? :_run_
397
+ rend.formatter.save_output(rend.options.file) if rend.options.file
392
398
  return rend.formatter.output
393
399
  end
394
400
 
395
- # Allows you to set class_wide default options
401
+ # Allows you to set class-wide default options.
396
402
  #
397
403
  # Example:
398
404
  #
@@ -433,7 +439,7 @@ class Ruport::Renderer
433
439
 
434
440
  # Allows you to register a format with the renderer.
435
441
  #
436
- # example:
442
+ # Example:
437
443
  #
438
444
  # class MyFormatter < Ruport::Formatter
439
445
  # # formatter code ...
@@ -446,10 +452,10 @@ class Ruport::Renderer
446
452
 
447
453
  end
448
454
 
449
- # The name of format being used
455
+ # The name of format being used.
450
456
  attr_accessor :format
451
457
 
452
- # The formatter object being used
458
+ # The formatter object being used.
453
459
  attr_writer :formatter
454
460
 
455
461
  # The +data+ that has been passed to the active formatter.
@@ -477,22 +483,11 @@ class Ruport::Renderer
477
483
  options.io=obj
478
484
  end
479
485
 
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.
486
+ # Returns the active formatter.
487
487
  #
488
+ # If a block is given, it is evaluated in the context of the formatter.
488
489
  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
490
+ @formatter.instance_eval(&block) if block
496
491
  return @formatter
497
492
  end
498
493
 
@@ -513,11 +508,6 @@ class Ruport::Renderer
513
508
  maybe "prepare_#{name}"
514
509
  end
515
510
 
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
511
  def finalize(name)
522
512
  maybe "finalize_#{name}"
523
513
  end
@@ -530,7 +520,7 @@ class Ruport::Renderer
530
520
  formatter.options = o
531
521
  end
532
522
 
533
- # selects a formatter for use by format name
523
+ # Selects a formatter for use by format name
534
524
  def use_formatter(format)
535
525
  self.formatter = self.class.formats[format].new
536
526
  self.formatter.format = format